import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle;
-import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
Log.d(TAG, "Loading apps");
List<ApplicationInfo> applicationInfos =
mPackageManager.getInstalledApplicationsAsUser(0, userId);
- ArraySet<Integer> seenUid = new ArraySet<>(); // some apps share a uid
AppsStorageResult result = new AppsStorageResult();
+ UserHandle myUser = UserHandle.of(userId);
for (int i = 0, size = applicationInfos.size(); i < size; i++) {
ApplicationInfo app = applicationInfos.get(i);
- if (seenUid.contains(app.uid)) {
- continue;
+ StorageStatsSource.AppStorageStats stats =
+ mStatsManager.getStatsForPackage(mUuid, app.packageName, myUser);
+
+ long attributedAppSizeInBytes = stats.getDataBytes();
+ // This matches how the package manager calculates sizes -- by zeroing out code sizes of
+ // system apps which are not updated. My initial tests suggest that this results in the
+ // original code size being counted for updated system apps when they shouldn't, but
+ // I am not sure how to avoid this problem without specifically going in to find that
+ // code size.
+ if (!app.isSystemApp() || app.isUpdatedSystemApp()) {
+ attributedAppSizeInBytes += stats.getCodeBytes();
}
- seenUid.add(app.uid);
-
- StorageStatsSource.AppStorageStats stats = mStatsManager.getStatsForUid(mUuid, app.uid);
- // Note: This omits cache intentionally -- we are not attributing it to the apps.
- long appSize = stats.getCodeBytes() + stats.getDataBytes();
switch (app.category) {
case CATEGORY_GAME:
- result.gamesSize += appSize;
+ result.gamesSize += attributedAppSizeInBytes;
break;
case CATEGORY_AUDIO:
- result.musicAppsSize += appSize;
+ result.musicAppsSize += attributedAppSizeInBytes;
break;
default:
- result.otherAppsSize += appSize;
+ result.otherAppsSize += attributedAppSizeInBytes;
break;
}
}
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class StorageAsyncLoaderTest {
private static final int PRIMARY_USER_ID = 0;
private static final int SECONDARY_USER_ID = 10;
+ private static final String PACKAGE_NAME_1 = "com.blah.test";
+ private static final String PACKAGE_NAME_2 = "com.blah.test2";
@Mock
private StorageStatsSource mSource;
@Test
public void testLoadingApps() throws Exception {
- addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
- addPackage(1002, 0, 100, 1000, ApplicationInfo.CATEGORY_UNDEFINED);
+ addPackage(PACKAGE_NAME_1, 0, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+ addPackage(PACKAGE_NAME_2, 0, 100, 1000, ApplicationInfo.CATEGORY_UNDEFINED);
SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
@Test
public void testGamesAreFiltered() throws Exception {
- addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_GAME);
+ addPackage(PACKAGE_NAME_1, 0, 1, 10, ApplicationInfo.CATEGORY_GAME);
SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
}
@Test
- public void testDuplicateUidsAreSkipped() throws Exception {
- addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
- addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
-
- SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
-
- assertThat(result.size()).isEqualTo(1);
- assertThat(result.get(PRIMARY_USER_ID).otherAppsSize).isEqualTo(11L);
- }
-
- @Test
public void testCacheIsIgnored() throws Exception {
- addPackage(1001, 100, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+ addPackage(PACKAGE_NAME_1, 100, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
assertThat(result.get(SECONDARY_USER_ID).externalStats.totalBytes).isEqualTo(10L);
}
- private void addPackage(int uid, long cacheSize, long codeSize, long dataSize, int category) {
+ @Test
+ public void testSystemAppsBaseSizeIsIgnored() throws Exception {
+ ApplicationInfo systemApp =
+ addPackage(PACKAGE_NAME_1, 100, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+ systemApp.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
+
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.get(PRIMARY_USER_ID).otherAppsSize).isEqualTo(10L);
+ }
+
+ @Test
+ public void testUpdatedSystemAppCodeSizeIsCounted() throws Exception {
+ ApplicationInfo systemApp =
+ addPackage(PACKAGE_NAME_1, 100, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+ systemApp.flags = ApplicationInfo.FLAG_SYSTEM & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
+
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.get(PRIMARY_USER_ID).otherAppsSize).isEqualTo(11L);
+ }
+
+ private ApplicationInfo addPackage(
+ String packageName, long cacheSize, long codeSize, long dataSize, int category) {
StorageStatsSource.AppStorageStats storageStats =
mock(StorageStatsSource.AppStorageStats.class);
when(storageStats.getCodeBytes()).thenReturn(codeSize);
when(storageStats.getDataBytes()).thenReturn(dataSize);
- when(mSource.getStatsForUid(anyString(), eq(uid))).thenReturn(storageStats);
+ when(mSource.getStatsForPackage(anyString(), eq(packageName), any(UserHandle.class)))
+ .thenReturn(storageStats);
ApplicationInfo info = new ApplicationInfo();
- info.uid = uid;
+ info.packageName = packageName;
info.category = category;
mInfo.add(info);
+ return info;
}
}