Initial API shape for storage statistics.
This API is designed to provide both UID-level stats and overall
summary data for a given storage device, as identified by UUID.
The use of UID-level granularity might appear a bit clunky, but it
matches other usage statistics (such as network and battery), and it
allows us to implement it using an extremely fast quota kernel
feature.
A future CL will wire up the implementation to installd.
Test: builds, boots
Bug: 32206268
Change-Id: I7b51877682d0370c2402c19346f57809f0e7ac53
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1d550d2..a22581c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -165,6 +165,8 @@
"com.android.server.LockSettingsService$Lifecycle";
private static final String STORAGE_MANAGER_SERVICE_CLASS =
"com.android.server.StorageManagerService$Lifecycle";
+ private static final String STORAGE_STATS_SERVICE_CLASS =
+ "com.android.server.usage.StorageStatsService$Lifecycle";
private static final String SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.search.SearchManagerService$Lifecycle";
private static final String THERMAL_OBSERVER_CLASS =
@@ -792,7 +794,7 @@
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableStorage &&
- !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+ !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
traceBeginAndSlog("StartStorageManagerService");
try {
/*
@@ -803,7 +805,15 @@
storageManager = IStorageManager.Stub.asInterface(
ServiceManager.getService("mount"));
} catch (Throwable e) {
- reportWtf("starting StorageManager Service", e);
+ reportWtf("starting StorageManagerService", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartStorageStatsService");
+ try {
+ mSystemServiceManager.startService(STORAGE_STATS_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting StorageStatsService", e);
}
traceEnd();
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
new file mode 100644
index 0000000..cb9cb121
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import android.app.AppOpsManager;
+import android.app.usage.IStorageStatsManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageSummary;
+import android.content.Context;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+
+import com.android.server.SystemService;
+import com.android.server.pm.Installer;
+
+public class StorageStatsService extends IStorageStatsManager.Stub {
+ private static final String TAG = "StorageStatsService";
+
+ public static class Lifecycle extends SystemService {
+ private StorageStatsService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new StorageStatsService(getContext());
+ publishBinderService(Context.STORAGE_STATS_SERVICE, mService);
+ }
+ }
+
+ private final Context mContext;
+ private final AppOpsManager mAppOps;
+ private final StorageManager mStorage;
+ private final Installer mInstaller;
+
+ public StorageStatsService(Context context) {
+ mContext = context;
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mStorage = context.getSystemService(StorageManager.class);
+ mInstaller = new Installer(context);
+ }
+
+ private void enforcePermission(int callingUid, String callingPackage) {
+ final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+ callingUid, callingPackage);
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED:
+ return;
+ case AppOpsManager.MODE_DEFAULT:
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
+ default:
+ throw new SecurityException("Blocked by mode " + mode);
+ }
+ }
+
+ @Override
+ public StorageStats queryStats(String volumeUuid, int uid, String callingPackage) {
+ enforcePermission(Binder.getCallingUid(), callingPackage);
+ if (UserHandle.getUserId(uid) != UserHandle.getCallingUserId()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
+ }
+
+ // TODO: call installd to collect quota stats
+ return null;
+ }
+
+ @Override
+ public StorageSummary querySummary(String volumeUuid, String callingPackage) {
+ enforcePermission(Binder.getCallingUid(), callingPackage);
+
+ // TODO: call installd to collect quota stats
+ return null;
+ }
+}