Merge "Fix code accounting bugs, track external app data." into oc-dev am: 4b32f7c676
am: 90d020d979
Change-Id: I7988898a2f2404e9af76b95190bd6ad839b85d77
diff --git a/api/current.txt b/api/current.txt
index 4a72fd9..275fb7f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6927,6 +6927,7 @@
public final class ExternalStorageStats implements android.os.Parcelable {
method public int describeContents();
+ method public long getAppBytes();
method public long getAudioBytes();
method public long getImageBytes();
method public long getTotalBytes();
diff --git a/api/system-current.txt b/api/system-current.txt
index ff03d20..8de6a4e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7393,6 +7393,7 @@
public final class ExternalStorageStats implements android.os.Parcelable {
method public int describeContents();
+ method public long getAppBytes();
method public long getAudioBytes();
method public long getImageBytes();
method public long getTotalBytes();
diff --git a/api/test-current.txt b/api/test-current.txt
index 725dd40..33182ba 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6957,6 +6957,7 @@
public final class ExternalStorageStats implements android.os.Parcelable {
method public int describeContents();
+ method public long getAppBytes();
method public long getAudioBytes();
method public long getImageBytes();
method public long getTotalBytes();
diff --git a/core/java/android/app/usage/ExternalStorageStats.java b/core/java/android/app/usage/ExternalStorageStats.java
index 1166df0..10c9b5f 100644
--- a/core/java/android/app/usage/ExternalStorageStats.java
+++ b/core/java/android/app/usage/ExternalStorageStats.java
@@ -31,6 +31,7 @@
/** {@hide} */ public long audioBytes;
/** {@hide} */ public long videoBytes;
/** {@hide} */ public long imageBytes;
+ /** {@hide} */ public long appBytes;
/**
* Return the total bytes used by all files in the shared/external storage
@@ -64,6 +65,17 @@
return imageBytes;
}
+ /**
+ * Return the total bytes used by app files in the shared/external storage
+ * hosted on this volume.
+ * <p>
+ * This data is already accounted against individual apps as returned
+ * through {@link StorageStats}.
+ */
+ public long getAppBytes() {
+ return appBytes;
+ }
+
/** {@hide} */
public ExternalStorageStats() {
}
@@ -74,6 +86,7 @@
this.audioBytes = in.readLong();
this.videoBytes = in.readLong();
this.imageBytes = in.readLong();
+ this.appBytes = in.readLong();
}
@Override
@@ -87,6 +100,7 @@
dest.writeLong(audioBytes);
dest.writeLong(videoBytes);
dest.writeLong(imageBytes);
+ dest.writeLong(appBytes);
}
public static final Creator<ExternalStorageStats> CREATOR = new Creator<ExternalStorageStats>() {
diff --git a/core/java/android/app/usage/StorageStats.java b/core/java/android/app/usage/StorageStats.java
index b3104f6..26c702c0 100644
--- a/core/java/android/app/usage/StorageStats.java
+++ b/core/java/android/app/usage/StorageStats.java
@@ -22,12 +22,8 @@
import android.os.UserHandle;
/**
- * Storage statistics for a UID or {@link UserHandle} on a single storage
- * volume.
- * <p class="note">
- * Note: multiple packages using the same {@code sharedUserId} in their manifest
- * will be merged into a single UID.
- * </p>
+ * Storage statistics for a UID, package, or {@link UserHandle} on a single
+ * storage volume.
*
* @see StorageStatsManager
*/
@@ -40,6 +36,9 @@
* Return the size of all code. This includes {@code APK} files and
* optimized compiler output.
* <p>
+ * If the primary external/shared storage is hosted on this storage device,
+ * then this includes files stored under {@link Context#getObbDir()}.
+ * <p>
* Code is shared between all users on a multiuser device.
*/
public long getCodeBytes() {
@@ -51,6 +50,12 @@
* {@link Context#getDataDir()}, {@link Context#getCacheDir()},
* {@link Context#getCodeCacheDir()}.
* <p>
+ * If the primary external/shared storage is hosted on this storage device,
+ * then this includes files stored under
+ * {@link Context#getExternalFilesDir(String)},
+ * {@link Context#getExternalCacheDir()}, and
+ * {@link Context#getExternalMediaDirs()}.
+ * <p>
* Data is isolated for each user on a multiuser device.
*/
public long getDataBytes() {
@@ -61,6 +66,10 @@
* Return the size of all cached data. This includes files stored under
* {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}.
* <p>
+ * If the primary external/shared storage is hosted on this storage device,
+ * then this includes files stored under
+ * {@link Context#getExternalCacheDir()}.
+ * <p>
* Cached data is isolated for each user on a multiuser device.
*/
public long getCacheBytes() {
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 4b6479a..5497d57 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -73,7 +73,8 @@
}
/**
- * Return the total size of the media hosting this storage volume.
+ * Return the total size of the underlying media that is hosting this
+ * storage volume.
* <p>
* To reduce end user confusion, this value matches the total storage size
* advertised in a retail environment, which is typically larger than the
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 1c15004..baa29b0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -730,7 +730,7 @@
*/
public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
Preconditions.checkNotNull(path);
- final String pathString = path.getAbsolutePath();
+ final String pathString = path.getCanonicalPath();
if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
return UUID_DEFAULT;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1e2b743..c5cefc8 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -258,10 +258,11 @@
}
}
- public long[] getExternalSize(String uuid, int userId, int flags) throws InstallerException {
+ public long[] getExternalSize(String uuid, int userId, int flags, int[] appIds)
+ throws InstallerException {
if (!checkBeforeRemote()) return new long[4];
try {
- return mInstalld.getExternalSize(uuid, userId, flags);
+ return mInstalld.getExternalSize(uuid, userId, flags, appIds);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
index b5a6178..3fd1d55 100644
--- a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
@@ -120,15 +120,7 @@
}
public void testGetUserSize() throws Exception {
- int[] appIds = null;
-
- final PackageManager pm = getContext().getPackageManager();
- for (ApplicationInfo app : pm.getInstalledApplications(0)) {
- final int appId = UserHandle.getAppId(app.uid);
- if (!ArrayUtils.contains(appIds, appId)) {
- appIds = ArrayUtils.appendInt(appIds, appId);
- }
- }
+ final int[] appIds = getAppIds(UserHandle.USER_SYSTEM);
final PackageStats stats = new PackageStats("android");
final PackageStats quotaStats = new PackageStats("android");
@@ -147,13 +139,15 @@
}
public void testGetExternalSize() throws Exception {
+ final int[] appIds = getAppIds(UserHandle.USER_SYSTEM);
+
mManual.start();
- final long[] stats = mInstaller.getExternalSize(null, UserHandle.USER_SYSTEM, 0);
+ final long[] stats = mInstaller.getExternalSize(null, UserHandle.USER_SYSTEM, 0, appIds);
mManual.stop();
mQuota.start();
final long[] quotaStats = mInstaller.getExternalSize(null, UserHandle.USER_SYSTEM,
- Installer.FLAG_USE_QUOTA);
+ Installer.FLAG_USE_QUOTA, appIds);
mQuota.stop();
for (int i = 0; i < stats.length; i++) {
@@ -161,6 +155,18 @@
}
}
+ private int[] getAppIds(int userId) {
+ int[] appIds = null;
+ for (ApplicationInfo app : getContext().getPackageManager().getInstalledApplicationsAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)) {
+ final int appId = UserHandle.getAppId(app.uid);
+ if (!ArrayUtils.contains(appIds, appId)) {
+ appIds = ArrayUtils.appendInt(appIds, appId);
+ }
+ }
+ return appIds;
+ }
+
private static void checkEquals(String msg, PackageStats a, PackageStats b) {
checkEquals(msg + " codeSize", a.codeSize, b.codeSize);
checkEquals(msg + " dataSize", a.dataSize, b.dataSize);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 094c7bd..2ebf5fc 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -235,7 +235,14 @@
final int appId = UserHandle.getUserId(appInfo.uid);
final String[] packageNames = new String[] { packageName };
final long[] ceDataInodes = new long[1];
- final String[] codePaths = new String[] { appInfo.getCodePath() };
+ String[] codePaths = new String[0];
+
+ if (appInfo.isSystemApp() && !appInfo.isUpdatedSystemApp()) {
+ // We don't count code baked into system image
+ } else {
+ codePaths = ArrayUtils.appendElement(String.class, codePaths,
+ appInfo.getCodePath());
+ }
final PackageStats stats = new PackageStats(TAG);
try {
@@ -261,12 +268,18 @@
final String[] packageNames = mPackage.getPackagesForUid(uid);
final long[] ceDataInodes = new long[packageNames.length];
- final String[] codePaths = new String[packageNames.length];
+ String[] codePaths = new String[0];
for (int i = 0; i < packageNames.length; i++) {
try {
- codePaths[i] = mPackage.getApplicationInfoAsUser(packageNames[i],
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId).getCodePath();
+ final ApplicationInfo appInfo = mPackage.getApplicationInfoAsUser(packageNames[i],
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ if (appInfo.isSystemApp() && !appInfo.isUpdatedSystemApp()) {
+ // We don't count code baked into system image
+ } else {
+ codePaths = ArrayUtils.appendElement(String.class, codePaths,
+ appInfo.getCodePath());
+ }
} catch (NameNotFoundException e) {
throw new ParcelableException(e);
}
@@ -297,15 +310,7 @@
android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
}
- int[] appIds = null;
- for (ApplicationInfo app : mPackage.getInstalledApplicationsAsUser(
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)) {
- final int appId = UserHandle.getAppId(app.uid);
- if (!ArrayUtils.contains(appIds, appId)) {
- appIds = ArrayUtils.appendInt(appIds, appId);
- }
- }
-
+ final int[] appIds = getAppIds(userId);
final PackageStats stats = new PackageStats(TAG);
try {
mInstaller.getUserSize(volumeUuid, userId, getDefaultFlags(), appIds, stats);
@@ -330,12 +335,14 @@
android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
}
+ final int[] appIds = getAppIds(userId);
final long[] stats;
try {
- stats = mInstaller.getExternalSize(volumeUuid, userId, getDefaultFlags());
+ stats = mInstaller.getExternalSize(volumeUuid, userId, getDefaultFlags(), appIds);
if (SystemProperties.getBoolean(PROP_VERIFY_STORAGE, false)) {
- final long[] manualStats = mInstaller.getExternalSize(volumeUuid, userId, 0);
+ final long[] manualStats = mInstaller.getExternalSize(volumeUuid, userId, 0,
+ appIds);
checkEquals("External " + userId, manualStats, stats);
}
} catch (InstallerException e) {
@@ -347,9 +354,22 @@
res.audioBytes = stats[1];
res.videoBytes = stats[2];
res.imageBytes = stats[3];
+ res.appBytes = stats[4];
return res;
}
+ private int[] getAppIds(int userId) {
+ int[] appIds = null;
+ for (ApplicationInfo app : mPackage.getInstalledApplicationsAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)) {
+ final int appId = UserHandle.getAppId(app.uid);
+ if (!ArrayUtils.contains(appIds, appId)) {
+ appIds = ArrayUtils.appendInt(appIds, appId);
+ }
+ }
+ return appIds;
+ }
+
private static int getDefaultFlags() {
if (SystemProperties.getBoolean(PROP_DISABLE_QUOTA, false)) {
return 0;