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;
+    }
+}