Merge "Offer to measure disk stats using quotas."
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 5b02c79..bc03901 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3729,13 +3729,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 7ce5aa8..614230c 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;
@@ -16869,6 +16872,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) {
@@ -16878,20 +16886,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;
}