1 /* 2 * Copyright (C) 2016 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.settings.applications; 18 19 import static com.android.settings.testutils.ApplicationTestUtils.buildInfo; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.argThat; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.Mockito.atLeast; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.verifyNoMoreInteractions; 30 import static org.mockito.Mockito.when; 31 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.pm.ApplicationInfo; 35 import android.content.pm.FakeFeatureFlagsImpl; 36 import android.content.pm.FeatureFlags; 37 import android.content.pm.Flags; 38 import android.content.pm.PackageManager; 39 import android.content.pm.PackageManager.ApplicationInfoFlags; 40 import android.content.pm.ResolveInfo; 41 import android.content.pm.UserInfo; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.platform.test.flag.junit.SetFlagsRule; 45 46 import org.junit.Before; 47 import org.junit.Ignore; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.ArgumentMatcher; 52 import org.mockito.Mock; 53 import org.mockito.junit.MockitoJUnit; 54 import org.mockito.junit.MockitoRule; 55 import org.robolectric.RobolectricTestRunner; 56 import org.robolectric.shadows.ShadowApplication; 57 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.Collections; 61 import java.util.List; 62 import java.util.Set; 63 64 @RunWith(RobolectricTestRunner.class) 65 public final class InstalledAppCounterTest { 66 67 @Rule 68 public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 69 70 private static final String APP_1 = "app1"; 71 private static final String APP_2 = "app2"; 72 private static final String APP_3 = "app3"; 73 private static final String APP_4 = "app4"; 74 private static final String APP_5 = "app5"; 75 private static final String APP_6 = "app6"; 76 private static final String APP_7 = "app7"; 77 78 private final int MAIN_USER_ID = 0; 79 private final int MANAGED_PROFILE_ID = 10; 80 81 private final int PER_USER_UID_RANGE = 100000; 82 private final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE; 83 private final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE; 84 85 @Mock 86 private UserManager mUserManager; 87 @Mock 88 private Context mContext; 89 @Mock 90 private PackageManager mPackageManager; 91 92 @Rule 93 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 94 95 private int mInstalledAppCount = -1; 96 private ApplicationInfo mApp1; 97 private ApplicationInfo mApp2; 98 private ApplicationInfo mApp3; 99 private ApplicationInfo mApp4; 100 private ApplicationInfo mApp5; 101 private ApplicationInfo mApp6; 102 private ApplicationInfo mApp7; 103 104 private FakeFeatureFlagsImpl mFakeFeatureFlags; 105 106 @Before setUp()107 public void setUp() { 108 when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); 109 mFakeFeatureFlags = new FakeFeatureFlagsImpl(); 110 mFakeFeatureFlags.setFlag(Flags.FLAG_ARCHIVING, true); 111 112 mApp1 = buildInfo(MAIN_USER_APP_UID, APP_1, 113 ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */); 114 mApp2 = buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */, 115 0 /* targetSdkVersion */); 116 mApp3 = buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM, 117 0 /* targetSdkVersion */); 118 mApp4 = buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM, 119 0 /* targetSdkVersion */); 120 mApp5 = buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */, 121 0 /* targetSdkVersion */); 122 mApp6 = buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM, 123 0 /* targetSdkVersion */); 124 mApp7 = buildInfo(MAIN_USER_APP_UID, APP_7, 0 /* flags */, 125 0 /* targetSdkVersion */); 126 mApp7.isArchived = true; 127 } 128 expectQueryIntentActivities(int userId, String packageName, boolean launchable)129 private void expectQueryIntentActivities(int userId, String packageName, boolean launchable) { 130 when(mPackageManager.queryIntentActivitiesAsUser( 131 argThat(isLaunchIntentFor(packageName)), 132 eq(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE 133 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE), 134 eq(userId))).thenReturn(launchable 135 ? Collections.singletonList(new ResolveInfo()) 136 : new ArrayList<>()); 137 } 138 testCountInstalledAppsAcrossAllUsers(boolean async)139 private void testCountInstalledAppsAcrossAllUsers(boolean async) { 140 // There are two users. 141 when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList( 142 new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN), 143 new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0))); 144 configurePackageManager(); 145 146 // Count the number of all apps installed, irrespective of install reason. 147 count(InstalledAppCounter.IGNORE_INSTALL_REASON, async); 148 assertThat(mInstalledAppCount).isEqualTo(5); 149 150 // Verify that installed packages were retrieved the current user and the user's managed 151 // profile only. 152 verify(mPackageManager) 153 .getInstalledApplicationsAsUser( 154 any(ApplicationInfoFlags.class), 155 eq(MAIN_USER_ID)); 156 verify(mPackageManager) 157 .getInstalledApplicationsAsUser( 158 any(ApplicationInfoFlags.class), 159 eq(MANAGED_PROFILE_ID)); 160 verify(mPackageManager, atLeast(0)) 161 .queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()); 162 verifyNoMoreInteractions(mPackageManager); 163 164 // Count once more, considering apps installed by enterprise policy only. 165 count(PackageManager.INSTALL_REASON_POLICY, async); 166 assertThat(mInstalledAppCount).isEqualTo(3); 167 } 168 169 @Test testIncludeInCount()170 public void testIncludeInCount() { 171 configurePackageManager(); 172 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 173 mPackageManager, mApp1)).isTrue(); 174 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 175 mPackageManager, mApp2)).isTrue(); 176 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 177 mPackageManager, mApp3)).isTrue(); 178 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 179 mPackageManager, mApp4)).isFalse(); 180 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 181 mPackageManager, mApp5)).isTrue(); 182 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 183 mPackageManager, mApp6)).isTrue(); 184 185 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 186 mPackageManager, mApp1)).isTrue(); 187 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 188 mPackageManager, mApp2)).isFalse(); 189 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 190 mPackageManager, mApp3)).isTrue(); 191 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 192 mPackageManager, mApp4)).isFalse(); 193 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 194 mPackageManager, mApp5)).isTrue(); 195 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 196 mPackageManager, mApp6)).isFalse(); 197 } 198 199 @Ignore("b/313578776") 200 @Test testCountInstalledAppsAcrossAllUsersSync()201 public void testCountInstalledAppsAcrossAllUsersSync() { 202 testCountInstalledAppsAcrossAllUsers(false /* async */); 203 } 204 205 @Ignore("b/313578776") 206 @Test testCountInstalledAppsAcrossAllUsersAsync()207 public void testCountInstalledAppsAcrossAllUsersAsync() { 208 testCountInstalledAppsAcrossAllUsers(true /* async */); 209 } 210 211 @Test testCountInstalledApps_archivingDisabled()212 public void testCountInstalledApps_archivingDisabled() { 213 when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(List.of( 214 new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN))); 215 // The user has four apps installed: 216 // * app2 is a user-installed app. It should be counted. 217 // * app7 is a user-archived app. It should not be counted. 218 when(mPackageManager.getInstalledApplicationsAsUser( 219 argThat(isApplicationInfoFlagsEqualTo( 220 ApplicationInfoFlags.of( 221 PackageManager.GET_DISABLED_COMPONENTS 222 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS 223 | PackageManager.MATCH_ANY_USER))), 224 eq(MAIN_USER_ID))).thenReturn(Arrays.asList(mApp2)); 225 226 mFakeFeatureFlags.setFlag(Flags.FLAG_ARCHIVING, false); 227 mSetFlagsRule.disableFlags(com.android.settings.flags.Flags.FLAG_APP_ARCHIVING); 228 // Count the number of all apps installed, irrespective of install reason. 229 count(InstalledAppCounter.IGNORE_INSTALL_REASON, mFakeFeatureFlags); 230 assertThat(mInstalledAppCount).isEqualTo(1); 231 } 232 233 @Test testCountInstalledApps_archivingEnabled()234 public void testCountInstalledApps_archivingEnabled() { 235 when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(List.of( 236 new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN))); 237 // The user has four apps installed: 238 // * app2 is a user-installed app. It should be counted. 239 // * app7 is a user-archived app. It should be counted. 240 when(mPackageManager.getInstalledApplicationsAsUser( 241 argThat(isApplicationInfoFlagsEqualTo( 242 ApplicationInfoFlags.of( 243 PackageManager.GET_DISABLED_COMPONENTS 244 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS 245 | PackageManager.MATCH_ANY_USER 246 | PackageManager.MATCH_ARCHIVED_PACKAGES))), 247 eq(MAIN_USER_ID))).thenReturn(Arrays.asList(mApp2, mApp7)); 248 249 // Count the number of all apps installed, irrespective of install reason. 250 count(InstalledAppCounter.IGNORE_INSTALL_REASON, mFakeFeatureFlags); 251 assertThat(mInstalledAppCount).isEqualTo(2); 252 } 253 count(int installReason, boolean async)254 private void count(int installReason, boolean async) { 255 mInstalledAppCount = -1; 256 final InstalledAppCounterTestable counter = new InstalledAppCounterTestable(installReason); 257 if (async) { 258 counter.execute(); 259 // Wait for the background task to finish. 260 ShadowApplication.runBackgroundTasks(); 261 } else { 262 counter.executeInForeground(); 263 } 264 } 265 count(int installReason, FeatureFlags featureFlags)266 private void count(int installReason, FeatureFlags featureFlags) { 267 mInstalledAppCount = -1; 268 final InstalledAppCounterTestable counter = 269 new InstalledAppCounterTestable(installReason, featureFlags); 270 counter.executeInForeground(); 271 } 272 configurePackageManager()273 private void configurePackageManager() { 274 // The first user has four apps installed: 275 // * app1 is an updated system app. It should be counted. 276 // * app2 is a user-installed app. It should be counted. 277 // * app3 is a system app that provides a launcher icon. It should be counted. 278 // * app4 is a system app that provides no launcher icon. It should not be counted. 279 ApplicationInfoFlags infoFlags1 = ApplicationInfoFlags.of( 280 PackageManager.GET_DISABLED_COMPONENTS 281 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS 282 | PackageManager.MATCH_ANY_USER); 283 when(mPackageManager.getInstalledApplicationsAsUser( 284 argThat(isApplicationInfoFlagsEqualTo(infoFlags1)), 285 eq(MAIN_USER_ID)) 286 ).thenReturn(Arrays.asList(mApp1, mApp2, mApp3, mApp4)); 287 // For system apps, InstalledAppCounter checks whether they handle the default launcher 288 // intent to decide whether to include them in the count of installed apps or not. 289 expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */); 290 expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */); 291 292 // app1, app3 and app4 are installed by enterprise policy. 293 final UserHandle mainUser = new UserHandle(MAIN_USER_ID); 294 when(mPackageManager.getInstallReason(APP_1, mainUser)) 295 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 296 when(mPackageManager.getInstallReason(APP_2, mainUser)) 297 .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); 298 when(mPackageManager.getInstallReason(APP_3, mainUser)) 299 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 300 when(mPackageManager.getInstallReason(APP_4, mainUser)) 301 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 302 303 // The second user has two apps installed: 304 // * app5 is a user-installed app. It should be counted. 305 // * app6 is a system app that provides a launcher icon. It should be counted. 306 ApplicationInfoFlags infoFlags2 = ApplicationInfoFlags.of( 307 PackageManager.GET_DISABLED_COMPONENTS 308 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); 309 when(mPackageManager.getInstalledApplicationsAsUser( 310 argThat(isApplicationInfoFlagsEqualTo(infoFlags2)), eq(MANAGED_PROFILE_ID)) 311 ).thenReturn(Arrays.asList(mApp5, mApp6)); 312 expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */); 313 314 // app5 is installed by enterprise policy. 315 final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID); 316 when(mPackageManager.getInstallReason(APP_5, managedProfileUser)) 317 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 318 when(mPackageManager.getInstallReason(APP_6, managedProfileUser)) 319 .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); 320 } 321 322 private class InstalledAppCounterTestable extends InstalledAppCounter { InstalledAppCounterTestable(int installReason)323 private InstalledAppCounterTestable(int installReason) { 324 super(mContext, installReason, mPackageManager); 325 } 326 InstalledAppCounterTestable(int installReason, FeatureFlags featureFlags)327 private InstalledAppCounterTestable(int installReason, FeatureFlags featureFlags) { 328 super(mContext, installReason, mPackageManager, featureFlags); 329 } 330 331 @Override onCountComplete(int num)332 protected void onCountComplete(int num) { 333 mInstalledAppCount = num; 334 } 335 } 336 isLaunchIntentFor(String packageName)337 private ArgumentMatcher<Intent> isLaunchIntentFor(String packageName) { 338 return intent -> { 339 if (intent == null) { 340 return false; 341 } 342 if (!Intent.ACTION_MAIN.equals(intent.getAction())) { 343 return false; 344 } 345 final Set<String> categories = intent.getCategories(); 346 if (categories == null || categories.size() != 1 || 347 !categories.contains(Intent.CATEGORY_LAUNCHER)) { 348 return false; 349 } 350 if (!packageName.equals(intent.getPackage())) { 351 return false; 352 } 353 return true; 354 }; 355 } 356 357 private ArgumentMatcher<ApplicationInfoFlags> isApplicationInfoFlagsEqualTo( 358 ApplicationInfoFlags infoFlags) { 359 return flags -> { 360 if (flags == null) { 361 return false; 362 } 363 return flags.getValue() == infoFlags.getValue(); 364 }; 365 } 366 } 367