Merge "Offer to measure disk stats using quotas." am: f8720b95be am: 4b80a4a372
am: 1e1cdbead1

Change-Id: Ic00acfb0efb6188a15357322528075d41467310b
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8f42467..857a361 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -509,10 +509,23 @@
      * Common-path handling of app data dir creation
      */
     private static File ensurePrivateDirExists(File file) {
+        return ensurePrivateDirExists(file, 0771, -1);
+    }
+
+    private static File ensurePrivateCacheDirExists(File file) {
+        final int gid = UserHandle.getCacheAppGid(Process.myUid());
+        return ensurePrivateDirExists(file, 02771, gid);
+    }
+
+    private static File ensurePrivateDirExists(File file, int mode, int gid) {
         if (!file.exists()) {
+            final String path = file.getAbsolutePath();
             try {
-                Os.mkdir(file.getAbsolutePath(), 0771);
-                Os.chmod(file.getAbsolutePath(), 0771);
+                Os.mkdir(path, mode);
+                Os.chmod(path, mode);
+                if (gid != -1) {
+                    Os.chown(path, -1, gid);
+                }
             } catch (ErrnoException e) {
                 if (e.errno == OsConstants.EEXIST) {
                     // We must have raced with someone; that's okay
@@ -581,7 +594,7 @@
             if (mCacheDir == null) {
                 mCacheDir = new File(getDataDir(), "cache");
             }
-            return ensurePrivateDirExists(mCacheDir);
+            return ensurePrivateCacheDirExists(mCacheDir);
         }
     }
 
@@ -591,7 +604,7 @@
             if (mCodeCacheDir == null) {
                 mCodeCacheDir = new File(getDataDir(), "code_cache");
             }
-            return ensurePrivateDirExists(mCodeCacheDir);
+            return ensurePrivateCacheDirExists(mCodeCacheDir);
         }
     }
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 151239b..f6edee0 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -783,7 +783,7 @@
      */
     public static boolean isBuildConsistent() {
         // Don't care on eng builds.  Incremental build may trigger false negative.
-        if ("eng".equals(TYPE)) return true;
+        if (IS_ENG) return true;
 
         final String system = SystemProperties.get("ro.build.fingerprint");
         final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
@@ -847,6 +847,10 @@
     public static final boolean IS_DEBUGGABLE =
             SystemProperties.getInt("ro.debuggable", 0) == 1;
 
+    /** {@hide} */
+    public static final boolean IS_ENG =
+            "eng".equals(getString("ro.build.type"));
+
     /**
      * Specifies whether the permissions needed by a legacy app should be
      * reviewed before any of its components can run. A legacy app is one
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9cd1a42..d6688e3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -184,6 +184,11 @@
      */
     public static final int LAST_SHARED_APPLICATION_GID = 59999;
 
+    /** {@hide} */
+    public static final int FIRST_APPLICATION_CACHE_GID = 20000;
+    /** {@hide} */
+    public static final int LAST_APPLICATION_CACHE_GID = 29999;
+
     /**
      * Standard priority of application threads.
      * Use with {@link #setThreadPriority(int)} and
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index b3f4453..535a05a 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -213,6 +213,15 @@
     }
 
     /**
+     * Returns the cache GID for a given UID or appId.
+     * @hide
+     */
+    public static int getCacheAppGid(int id) {
+        return Process.FIRST_APPLICATION_CACHE_GID + (id % PER_USER_RANGE)
+                - Process.FIRST_APPLICATION_UID;
+    }
+
+    /**
      * Generate a text representation of the uid, breaking out its individual
      * components -- user, app, isolated, etc.
      * @hide
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 74ff41c..6296375 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -154,7 +154,8 @@
 3110 unknown_sources_enabled (value|1)
 # Package Manager critical info
 3120 pm_critical_info (msg|3)
-
+# Disk usage stats for verifying quota correctness
+3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2)
 
 # ---------------------------
 # WindowManagerService.java
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b2559ab..f838e46 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3730,13 +3730,14 @@
                  * resources like shared libraries and access user-wide resources
                  */
                 if (ArrayUtils.isEmpty(permGids)) {
-                    gids = new int[2];
+                    gids = new int[3];
                 } else {
-                    gids = new int[permGids.length + 2];
-                    System.arraycopy(permGids, 0, gids, 2, permGids.length);
+                    gids = new int[permGids.length + 3];
+                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
                 }
                 gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
-                gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
             }
             checkTime(startTime, "startProcess: building args");
             if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1f83d9e..605fa5d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -54,6 +54,7 @@
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
     public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+    public static final int FLAG_USE_QUOTA = 1 << 12;
 
     private final boolean mIsolated;
 
@@ -198,12 +199,13 @@
         }
     }
 
-    public void getAppSize(String uuid, String packageName, int userId, int flags, long ceDataInode,
-            String codePath, PackageStats stats) throws InstallerException {
+    public void getAppSize(String uuid, String packageName, int userId, int flags, int appId,
+            long ceDataInode, String codePath, String externalUuid, PackageStats stats)
+            throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, ceDataInode,
-                    codePath);
+            final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, appId,
+                    ceDataInode, codePath, externalUuid);
             stats.codeSize += res[0];
             stats.dataSize += res[1];
             stats.cacheSize += res[2];
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3562680..37cca44 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -370,6 +370,9 @@
     private static final boolean DISABLE_EPHEMERAL_APPS = false;
     private static final boolean HIDE_EPHEMERAL_APIS = true;
 
+    private static final boolean ENABLE_QUOTA =
+            SystemProperties.getBoolean("persist.fw.quota", false);
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -16901,6 +16904,11 @@
         mHandler.sendMessage(msg);
     }
 
+    private boolean equals(PackageStats a, PackageStats b) {
+        return (a.codeSize == b.codeSize) && (a.dataSize == b.dataSize)
+                && (a.cacheSize == b.cacheSize);
+    }
+
     private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
         final PackageSetting ps;
         synchronized (mPackages) {
@@ -16910,20 +16918,51 @@
                 return false;
             }
         }
+
+        final long ceDataInode = ps.getCeDataInode(userId);
+        final PackageStats quotaStats = new PackageStats(stats.packageName, stats.userHandle);
+
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        final String externalUuid = storage.getPrimaryStorageUuid();
         try {
-            mInstaller.getAppSize(ps.volumeUuid, packageName, userId,
-                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE,
-                    ps.getCeDataInode(userId), ps.codePathString, stats);
+            final long start = SystemClock.elapsedRealtimeNanos();
+            mInstaller.getAppSize(ps.volumeUuid, packageName, userId, 0,
+                    ps.appId, ceDataInode, ps.codePathString, externalUuid, stats);
+            final long stopManual = SystemClock.elapsedRealtimeNanos();
+            if (ENABLE_QUOTA) {
+                mInstaller.getAppSize(ps.volumeUuid, packageName, userId, Installer.FLAG_USE_QUOTA,
+                        ps.appId, ceDataInode, ps.codePathString, externalUuid, quotaStats);
+            }
+            final long stopQuota = SystemClock.elapsedRealtimeNanos();
+
+            // For now, ignore code size of packages on system partition
+            if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
+                stats.codeSize = 0;
+                quotaStats.codeSize = 0;
+            }
+
+            if (ENABLE_QUOTA && Build.IS_ENG && !ps.isSharedUser()) {
+                if (!equals(stats, quotaStats)) {
+                    Log.w(TAG, "Found discrepancy between statistics:");
+                    Log.w(TAG, "Manual: " + stats);
+                    Log.w(TAG, "Quota:  " + quotaStats);
+                }
+                final long manualTime = stopManual - start;
+                final long quotaTime = stopQuota - stopManual;
+                EventLogTags.writePmPackageStats(manualTime, quotaTime,
+                        stats.dataSize, quotaStats.dataSize,
+                        stats.cacheSize, quotaStats.cacheSize);
+            }
+
+            // External clients expect these to be tracked separately
+            stats.dataSize -= stats.cacheSize;
+            quotaStats.dataSize -= quotaStats.cacheSize;
+
         } catch (InstallerException e) {
             Slog.w(TAG, String.valueOf(e));
             return false;
         }
 
-        // For now, ignore code size of packages on system partition
-        if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
-            stats.codeSize = 0;
-        }
-
         return true;
     }