Use the fast track for the AppCollector.
This replaces some of the threading shenanigans done to get this
working with the regular PackageManager call. By swapping this
out, we can get results faster, using less power, and with a
simpler implementation and testing strategy.
Bug: 35807386
Test: FrameworkServicesTest
Change-Id: Ib94fd7eba838b5e728f8f2615bcb4d9c82f21885
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index cb9039b..c746af4 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -19,6 +19,9 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
/**
* implementation of PackageStats associated with a
@@ -173,4 +176,31 @@
dest.writeLong(externalMediaSize);
dest.writeLong(externalObbSize);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PackageStats)) {
+ return false;
+ }
+
+ final PackageStats otherStats = (PackageStats) obj;
+ return ((TextUtils.equals(packageName, otherStats.packageName))
+ && userHandle == otherStats.userHandle
+ && codeSize == otherStats.codeSize
+ && dataSize == otherStats.dataSize
+ && cacheSize == otherStats.cacheSize
+ && externalCodeSize == otherStats.externalCodeSize
+ && externalDataSize == otherStats.externalDataSize
+ && externalCacheSize == otherStats.externalCacheSize
+ && externalMediaSize == otherStats.externalMediaSize
+ && externalObbSize == otherStats.externalObbSize);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageName, userHandle, codeSize, dataSize,
+ cacheSize, externalCodeSize, externalDataSize, externalCacheSize, externalMediaSize,
+ externalObbSize);
+ }
+
}
diff --git a/services/core/java/com/android/server/storage/AppCollector.java b/services/core/java/com/android/server/storage/AppCollector.java
index ee9c5bf..25880fb 100644
--- a/services/core/java/com/android/server/storage/AppCollector.java
+++ b/services/core/java/com/android/server/storage/AppCollector.java
@@ -17,6 +17,8 @@
package com.android.server.storage;
import android.annotation.NonNull;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
@@ -64,7 +66,8 @@
mBackgroundHandler = new BackgroundHandler(BackgroundThread.get().getLooper(),
volume,
context.getPackageManager(),
- (UserManager) context.getSystemService(Context.USER_SERVICE));
+ (UserManager) context.getSystemService(Context.USER_SERVICE),
+ (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE));
}
/**
@@ -93,39 +96,20 @@
return value;
}
- private class StatsObserver extends IPackageStatsObserver.Stub {
- private AtomicInteger mCount;
- private final ArrayList<PackageStats> mPackageStats;
-
- public StatsObserver(int count) {
- mCount = new AtomicInteger(count);
- mPackageStats = new ArrayList<>(count);
- }
-
- @Override
- public void onGetStatsCompleted(PackageStats packageStats, boolean succeeded)
- throws RemoteException {
- if (succeeded) {
- mPackageStats.add(packageStats);
- }
-
- if (mCount.decrementAndGet() == 0) {
- mStats.complete(mPackageStats);
- }
- }
- }
-
private class BackgroundHandler extends Handler {
static final int MSG_START_LOADING_SIZES = 0;
private final VolumeInfo mVolume;
private final PackageManager mPm;
private final UserManager mUm;
+ private final StorageStatsManager mStorageStatsManager;
- BackgroundHandler(Looper looper, @NonNull VolumeInfo volume, PackageManager pm, UserManager um) {
+ BackgroundHandler(Looper looper, @NonNull VolumeInfo volume,
+ PackageManager pm, UserManager um, StorageStatsManager storageStatsManager) {
super(looper);
mVolume = volume;
mPm = pm;
mUm = um;
+ mStorageStatsManager = storageStatsManager;
}
@Override
@@ -149,14 +133,20 @@
mStats.complete(new ArrayList<>());
}
- // Kick off the async package size query for all apps.
- final StatsObserver observer = new StatsObserver(count);
+ List<PackageStats> stats = new ArrayList<>();
for (UserInfo user : users) {
for (ApplicationInfo app : volumeApps) {
- mPm.getPackageSizeInfoAsUser(app.packageName, user.id,
- observer);
+ PackageStats packageStats = new PackageStats(app.packageName, user.id);
+ StorageStats storageStats = mStorageStatsManager.queryStatsForPackage(
+ app.volumeUuid, app.packageName, user.getUserHandle());
+ packageStats.cacheSize = storageStats.getCacheBytes();
+ packageStats.codeSize = storageStats.getCodeBytes();
+ packageStats.dataSize = storageStats.getDataBytes();
+ stats.add(packageStats);
}
}
+
+ mStats.complete(stats);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
index 29185e9..8cf7c8a 100644
--- a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
@@ -16,12 +16,15 @@
package com.android.server.storage;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.pm.UserInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.VolumeInfo;
import android.test.AndroidTestCase;
@@ -53,6 +56,7 @@
@Mock private Context mContext;
@Mock private PackageManager mPm;
@Mock private UserManager mUm;
+ @Mock private StorageStatsManager mSsm;
private List<ApplicationInfo> mApps;
private List<UserInfo> mUsers;
@@ -63,6 +67,7 @@
mApps = new ArrayList<>();
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUm);
+ when(mContext.getSystemService(Context.STORAGE_STATS_SERVICE)).thenReturn(mSsm);
// Set up the app list.
when(mPm.getInstalledApplications(anyInt())).thenReturn(mApps);
@@ -100,39 +105,9 @@
AppCollector collector = new AppCollector(mContext, volume);
PackageStats stats = new PackageStats("com.test.app");
- // Set up this to handle the asynchronous call to the PackageManager. This returns the
- // package info for the specified package.
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- try {
- ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
- .onGetStatsCompleted(stats, true);
- } catch (Exception e) {
- // We fail instead of just letting the exception fly because throwing
- // out of the callback like this on the background thread causes the test
- // runner to crash, rather than reporting the failure.
- fail();
- }
- return null;
- }
- }).when(mPm).getPackageSizeInfoAsUser(eq("com.test.app"), eq(0), any());
-
-
- // Because getPackageStats is a blocking call, we block execution of the test until the
- // call finishes. In order to finish the call, we need the above answer to execute.
- List<PackageStats> myStats = new ArrayList<>();
- CountDownLatch latch = new CountDownLatch(1);
- new Thread(new Runnable() {
- @Override
- public void run() {
- myStats.addAll(collector.getPackageStats(TIMEOUT));
- latch.countDown();
- }
- }).start();
- latch.await();
-
- assertThat(myStats).containsExactly(stats);
+ when(mSsm.queryStatsForPackage(eq("testuuid"),
+ eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
+ assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats);
}
@Test
@@ -151,43 +126,11 @@
PackageStats otherStats = new PackageStats("com.test.app");
otherStats.userHandle = 1;
- // Set up this to handle the asynchronous call to the PackageManager. This returns the
- // package info for our packages.
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- try {
- ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
- .onGetStatsCompleted(stats, true);
-
- // Now callback for the other uid.
- ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
- .onGetStatsCompleted(otherStats, true);
- } catch (Exception e) {
- // We fail instead of just letting the exception fly because throwing
- // out of the callback like this on the background thread causes the test
- // runner to crash, rather than reporting the failure.
- fail();
- }
- return null;
- }
- }).when(mPm).getPackageSizeInfoAsUser(eq("com.test.app"), eq(0), any());
-
-
- // Because getPackageStats is a blocking call, we block execution of the test until the
- // call finishes. In order to finish the call, we need the above answer to execute.
- List<PackageStats> myStats = new ArrayList<>();
- CountDownLatch latch = new CountDownLatch(1);
- new Thread(new Runnable() {
- @Override
- public void run() {
- myStats.addAll(collector.getPackageStats(TIMEOUT));
- latch.countDown();
- }
- }).start();
- latch.await();
-
- assertThat(myStats).containsAllOf(stats, otherStats);
+ when(mSsm.queryStatsForPackage(eq("testuuid"),
+ eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
+ when(mSsm.queryStatsForPackage(eq("testuuid"),
+ eq("com.test.app"), eq(UserHandle.of(1)))).thenReturn(new StorageStats());
+ assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats, otherStats);
}
@Test(expected=NullPointerException.class)