1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.storage; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.Mockito.when; 26 27 import android.app.usage.StorageStats; 28 import android.content.Context; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.UserInfo; 33 import android.os.UserHandle; 34 import android.util.DataUnit; 35 import android.util.SparseArray; 36 37 import androidx.test.core.app.ApplicationProvider; 38 import androidx.test.ext.junit.runners.AndroidJUnit4; 39 40 import com.android.car.settings.profiles.ProfileHelper; 41 import com.android.settingslib.applications.StorageStatsSource; 42 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.Mock; 47 import org.mockito.MockitoAnnotations; 48 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Collections; 52 import java.util.List; 53 54 @RunWith(AndroidJUnit4.class) 55 public class StorageAsyncLoaderTest { 56 57 private static final int PRIMARY_USER_ID = 0; 58 private static final int SECONDARY_USER_ID = 10; 59 private static final String PACKAGE_NAME_1 = "com.blah.test"; 60 private static final String PACKAGE_NAME_2 = "com.blah.test2"; 61 private static final long DEFAULT_QUOTA = DataUnit.MEBIBYTES.toBytes(64); 62 63 private Context mContext = ApplicationProvider.getApplicationContext(); 64 private List<UserInfo> mUsers; 65 66 private StorageAsyncLoader mLoader; 67 68 @Mock 69 private StorageStatsSource mMockSource; 70 @Mock 71 private PackageManager mMockPackageManager; 72 @Mock 73 private ProfileHelper mMockProfileHelper; 74 75 @Before setUp()76 public void setUp() throws Exception { 77 MockitoAnnotations.initMocks(this); 78 79 mLoader = new StorageAsyncLoader(mContext, mMockSource, mMockPackageManager, 80 mMockProfileHelper); 81 UserInfo info = new UserInfo(); 82 info.id = PRIMARY_USER_ID; 83 mUsers = new ArrayList<>(); 84 mUsers.add(info); 85 when(mMockProfileHelper.getAllProfiles()).thenReturn(mUsers); 86 when(mMockSource.getCacheQuotaBytes(any(), anyInt())).thenReturn(DEFAULT_QUOTA); 87 } 88 89 @Test testLoadingApps()90 public void testLoadingApps() throws Exception { 91 ApplicationInfo appInfo1 = createAppInfo(PACKAGE_NAME_1, 0, 1, 10, 92 ApplicationInfo.CATEGORY_UNDEFINED); 93 ApplicationInfo appInfo2 = createAppInfo(PACKAGE_NAME_2, 0, 100, 1000, 94 ApplicationInfo.CATEGORY_UNDEFINED); 95 when(mMockPackageManager.getInstalledApplicationsAsUser( 96 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 97 .thenReturn(Arrays.asList(appInfo1, appInfo2)); 98 99 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 100 101 assertThat(result.size()).isEqualTo(1); 102 assertThat(result.get(PRIMARY_USER_ID).getGamesSize()).isEqualTo(0L); 103 assertThat(result.get(PRIMARY_USER_ID).getOtherAppsSize()).isEqualTo(2200L); 104 } 105 106 @Test testGamesAreFiltered()107 public void testGamesAreFiltered() throws Exception { 108 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 0, 1, 10, 109 ApplicationInfo.CATEGORY_GAME); 110 when(mMockPackageManager.getInstalledApplicationsAsUser( 111 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 112 .thenReturn(Collections.singletonList(appInfo)); 113 114 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 115 116 assertThat(result.size()).isEqualTo(1); 117 assertThat(result.get(PRIMARY_USER_ID).getGamesSize()).isEqualTo(11L); 118 assertThat(result.get(PRIMARY_USER_ID).getOtherAppsSize()).isEqualTo(0L); 119 } 120 121 @Test testLegacyGamesAreFiltered()122 public void testLegacyGamesAreFiltered() throws Exception { 123 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 0, 1, 10, 124 ApplicationInfo.CATEGORY_UNDEFINED); 125 appInfo.flags = ApplicationInfo.FLAG_IS_GAME; 126 when(mMockPackageManager.getInstalledApplicationsAsUser( 127 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 128 .thenReturn(Collections.singletonList(appInfo)); 129 130 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 131 132 assertThat(result.size()).isEqualTo(1); 133 assertThat(result.get(PRIMARY_USER_ID).getGamesSize()).isEqualTo(11L); 134 assertThat(result.get(PRIMARY_USER_ID).getOtherAppsSize()).isEqualTo(0L); 135 } 136 137 @Test testCacheIsNotIgnored()138 public void testCacheIsNotIgnored() throws Exception { 139 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 100, 1, 10, 140 ApplicationInfo.CATEGORY_UNDEFINED); 141 when(mMockPackageManager.getInstalledApplicationsAsUser( 142 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 143 .thenReturn(Collections.singletonList(appInfo)); 144 145 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 146 147 assertThat(result.size()).isEqualTo(1); 148 assertThat(result.get(PRIMARY_USER_ID).getOtherAppsSize()).isEqualTo(111L); 149 } 150 151 @Test testMultipleUsers()152 public void testMultipleUsers() throws Exception { 153 UserInfo info = new UserInfo(); 154 info.id = SECONDARY_USER_ID; 155 mUsers.add(info); 156 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 100, 1, 10, 157 ApplicationInfo.CATEGORY_UNDEFINED); 158 when(mMockPackageManager.getInstalledApplicationsAsUser( 159 /* getAllInstalledApplications= */ eq(0), anyInt())) 160 .thenReturn(Collections.singletonList(appInfo)); 161 when(mMockSource.getExternalStorageStats(any(), eq(UserHandle.SYSTEM))) 162 .thenReturn(new StorageStatsSource.ExternalStorageStats(9, 2, 3, 4, 0)); 163 when(mMockSource.getExternalStorageStats(any(), eq(new UserHandle(SECONDARY_USER_ID)))) 164 .thenReturn(new StorageStatsSource.ExternalStorageStats(10, 3, 3, 4, 0)); 165 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 166 167 assertThat(result.size()).isEqualTo(2); 168 assertThat(result.get(PRIMARY_USER_ID).getExternalStats().totalBytes).isEqualTo(9L); 169 assertThat(result.get(SECONDARY_USER_ID).getExternalStats().totalBytes).isEqualTo(10L); 170 } 171 172 @Test testUpdatedSystemAppCodeSizeIsCounted()173 public void testUpdatedSystemAppCodeSizeIsCounted() throws Exception { 174 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 100, 1, 10, 175 ApplicationInfo.CATEGORY_UNDEFINED); 176 appInfo.flags = ApplicationInfo.FLAG_SYSTEM & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 177 when(mMockPackageManager.getInstalledApplicationsAsUser( 178 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 179 .thenReturn(Collections.singletonList(appInfo)); 180 181 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 182 183 assertThat(result.size()).isEqualTo(1); 184 assertThat(result.get(PRIMARY_USER_ID).getOtherAppsSize()).isEqualTo(111L); 185 } 186 187 @Test testVideoAppsAreFiltered()188 public void testVideoAppsAreFiltered() throws Exception { 189 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 0, 1, 10, 190 ApplicationInfo.CATEGORY_VIDEO); 191 when(mMockPackageManager.getInstalledApplicationsAsUser( 192 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 193 .thenReturn(Collections.singletonList(appInfo)); 194 195 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 196 197 assertThat(result.size()).isEqualTo(1); 198 assertThat(result.get(PRIMARY_USER_ID).getVideoAppsSize()).isEqualTo(11L); 199 assertThat(result.get(PRIMARY_USER_ID).getOtherAppsSize()).isEqualTo(0L); 200 } 201 202 @Test testRemovedPackageDoesNotCrash()203 public void testRemovedPackageDoesNotCrash() throws Exception { 204 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 0, 1, 10, 205 ApplicationInfo.CATEGORY_UNDEFINED); 206 when(mMockPackageManager.getInstalledApplicationsAsUser( 207 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 208 .thenReturn(Collections.singletonList(appInfo)); 209 when(mMockSource.getStatsForPackage(any(), anyString(), any(UserHandle.class))) 210 .thenThrow(new PackageManager.NameNotFoundException()); 211 212 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 213 214 // Should not crash. 215 } 216 217 @Test testPackageIsNotDoubleCounted()218 public void testPackageIsNotDoubleCounted() throws Exception { 219 UserInfo info = new UserInfo(); 220 info.id = SECONDARY_USER_ID; 221 mUsers.add(info); 222 when(mMockSource.getExternalStorageStats(anyString(), eq(UserHandle.SYSTEM))) 223 .thenReturn(new StorageStatsSource.ExternalStorageStats(9, 2, 3, 224 4, 0)); 225 when(mMockSource.getExternalStorageStats(anyString(), 226 eq(new UserHandle(SECONDARY_USER_ID)))) 227 .thenReturn(new StorageStatsSource.ExternalStorageStats(10, 3, 3, 228 4, 0)); 229 230 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, 0, 1, 10, 231 ApplicationInfo.CATEGORY_VIDEO); 232 when(mMockPackageManager.getInstalledApplicationsAsUser( 233 /* getAllInstalledApplications= */ eq(0), anyInt())) 234 .thenReturn(Collections.singletonList(appInfo)); 235 236 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 237 238 assertThat(result.size()).isEqualTo(2); 239 assertThat(result.get(PRIMARY_USER_ID).getVideoAppsSize()).isEqualTo(11L); 240 // No code size for the second user. 241 assertThat(result.get(SECONDARY_USER_ID).getVideoAppsSize()).isEqualTo(10L); 242 } 243 244 @Test testCacheOveragesAreCountedAsFree()245 public void testCacheOveragesAreCountedAsFree() throws Exception { 246 ApplicationInfo appInfo = createAppInfo(PACKAGE_NAME_1, DEFAULT_QUOTA + 100, 1, 10, 247 ApplicationInfo.CATEGORY_UNDEFINED); 248 249 when(mMockPackageManager.getInstalledApplicationsAsUser( 250 /* getAllInstalledApplications= */ 0, PRIMARY_USER_ID)) 251 .thenReturn(Collections.singletonList(appInfo)); 252 253 SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground(); 254 255 assertThat(result.size()).isEqualTo(1); 256 assertThat(result.get(PRIMARY_USER_ID).getOtherAppsSize()).isEqualTo(DEFAULT_QUOTA + 11); 257 } 258 createAppInfo(String packageName, long cacheSize, long codeSize, long dataSize, int category)259 private ApplicationInfo createAppInfo(String packageName, long cacheSize, long codeSize, 260 long dataSize, int category) throws Exception { 261 StorageStats stats = new StorageStats(); 262 stats.codeBytes = codeSize; 263 stats.dataBytes = dataSize + cacheSize; 264 stats.cacheBytes = cacheSize; 265 StorageStatsSource.AppStorageStats storageStats = 266 new StorageStatsSource.AppStorageStatsImpl(stats); 267 268 when(mMockSource.getStatsForPackage(any(), anyString(), any(UserHandle.class))) 269 .thenReturn(storageStats); 270 271 ApplicationInfo info = new ApplicationInfo(); 272 info.packageName = packageName; 273 info.category = category; 274 PackageInfo packageInfo = new PackageInfo(); 275 packageInfo.applicationInfo = info; 276 packageInfo.packageName = packageName; 277 278 return info; 279 } 280 } 281