1 /* 2 * Copyright (C) 2017 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.settingslib.applications; 18 19 import static android.os.UserHandle.MU_ENABLED; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.any; 25 import static org.mockito.Mockito.anyInt; 26 import static org.mockito.Mockito.anyLong; 27 import static org.mockito.Mockito.anyString; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.spy; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 import static org.robolectric.shadow.api.Shadow.extract; 34 35 import android.annotation.UserIdInt; 36 import android.app.Application; 37 import android.app.ApplicationPackageManager; 38 import android.app.usage.StorageStats; 39 import android.app.usage.StorageStatsManager; 40 import android.content.ComponentName; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.pm.ActivityInfo; 45 import android.content.pm.ApplicationInfo; 46 import android.content.pm.IPackageManager; 47 import android.content.pm.ModuleInfo; 48 import android.content.pm.PackageManager; 49 import android.content.pm.ParceledListSlice; 50 import android.content.pm.ResolveInfo; 51 import android.content.res.Resources; 52 import android.graphics.drawable.ColorDrawable; 53 import android.graphics.drawable.Drawable; 54 import android.os.Handler; 55 import android.os.RemoteException; 56 import android.os.UserHandle; 57 import android.os.UserManager; 58 import android.text.TextUtils; 59 import android.util.IconDrawableFactory; 60 61 import com.android.settingslib.applications.ApplicationsState.AppEntry; 62 import com.android.settingslib.applications.ApplicationsState.Callbacks; 63 import com.android.settingslib.applications.ApplicationsState.Session; 64 import com.android.settingslib.testutils.shadow.ShadowUserManager; 65 66 import org.junit.After; 67 import org.junit.Before; 68 import org.junit.Test; 69 import org.junit.runner.RunWith; 70 import org.mockito.ArgumentCaptor; 71 import org.mockito.Captor; 72 import org.mockito.Mock; 73 import org.mockito.MockitoAnnotations; 74 import org.robolectric.RobolectricTestRunner; 75 import org.robolectric.RuntimeEnvironment; 76 import org.robolectric.annotation.Config; 77 import org.robolectric.annotation.Implementation; 78 import org.robolectric.annotation.Implements; 79 import org.robolectric.shadow.api.Shadow; 80 import org.robolectric.shadows.ShadowContextImpl; 81 import org.robolectric.shadows.ShadowLooper; 82 83 import java.util.ArrayList; 84 import java.util.Arrays; 85 import java.util.List; 86 import java.util.UUID; 87 88 @RunWith(RobolectricTestRunner.class) 89 @Config(shadows = {ShadowUserManager.class, 90 ApplicationsStateRoboTest.ShadowIconDrawableFactory.class, 91 ApplicationsStateRoboTest.ShadowPackageManager.class}) 92 public class ApplicationsStateRoboTest { 93 94 private final static String HOME_PACKAGE_NAME = "com.android.home"; 95 private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable"; 96 97 private static final int PROFILE_USERID = 10; 98 99 private static final String PKG_1 = "PKG1"; 100 private static final int OWNER_UID_1 = 1001; 101 private static final int PROFILE_UID_1 = UserHandle.getUid(PROFILE_USERID, OWNER_UID_1); 102 103 private static final String PKG_2 = "PKG2"; 104 private static final int OWNER_UID_2 = 1002; 105 private static final int PROFILE_UID_2 = UserHandle.getUid(PROFILE_USERID, OWNER_UID_2); 106 107 private static final String PKG_3 = "PKG3"; 108 private static final int OWNER_UID_3 = 1003; 109 110 /** Class under test */ 111 private ApplicationsState mApplicationsState; 112 private Session mSession; 113 114 private Application mApplication; 115 116 @Mock 117 private Callbacks mCallbacks; 118 @Captor 119 private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor; 120 @Mock 121 private StorageStatsManager mStorageStatsManager; 122 @Mock 123 private IPackageManager mPackageManagerService; 124 125 @Implements(value = IconDrawableFactory.class) 126 public static class ShadowIconDrawableFactory { 127 128 @Implementation getBadgedIcon(ApplicationInfo appInfo)129 protected Drawable getBadgedIcon(ApplicationInfo appInfo) { 130 return new ColorDrawable(0); 131 } 132 } 133 134 @Implements(value = ApplicationPackageManager.class) 135 public static class ShadowPackageManager extends 136 org.robolectric.shadows.ShadowApplicationPackageManager { 137 138 // test installed modules, 2 regular, 2 hidden 139 private final String[] mModuleNames = { 140 "test.module.1", "test.hidden.module.2", "test.hidden.module.3", "test.module.4"}; 141 private final List<ModuleInfo> mInstalledModules = new ArrayList<>(); 142 143 @Implementation getHomeActivities(List<ResolveInfo> outActivities)144 protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) { 145 ResolveInfo resolveInfo = new ResolveInfo(); 146 resolveInfo.activityInfo = new ActivityInfo(); 147 resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME; 148 resolveInfo.activityInfo.enabled = true; 149 outActivities.add(resolveInfo); 150 return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo"); 151 } 152 153 @Implementation getInstalledModules(int flags)154 public List<ModuleInfo> getInstalledModules(int flags) { 155 if (mInstalledModules.isEmpty()) { 156 for (String moduleName : mModuleNames) { 157 mInstalledModules.add(createModuleInfo(moduleName)); 158 } 159 } 160 return mInstalledModules; 161 } 162 queryIntentActivitiesAsUser(Intent intent, @PackageManager.ResolveInfoFlagsBits int flags, @UserIdInt int userId)163 public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, 164 @PackageManager.ResolveInfoFlagsBits int flags, @UserIdInt int userId) { 165 List<ResolveInfo> resolveInfos = new ArrayList<>(); 166 ResolveInfo resolveInfo = new ResolveInfo(); 167 resolveInfo.activityInfo = new ActivityInfo(); 168 resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME; 169 resolveInfo.activityInfo.enabled = true; 170 resolveInfo.filter = new IntentFilter(); 171 resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER); 172 resolveInfos.add(resolveInfo); 173 return resolveInfos; 174 } 175 createModuleInfo(String packageName)176 private ModuleInfo createModuleInfo(String packageName) { 177 final ModuleInfo info = new ModuleInfo(); 178 info.setName(packageName); 179 info.setPackageName(packageName); 180 // will treat any app with package name that contains "hidden" as hidden module 181 info.setHidden(!TextUtils.isEmpty(packageName) && packageName.contains("hidden")); 182 return info; 183 } 184 } 185 186 @Before setUp()187 public void setUp() throws Exception { 188 MockitoAnnotations.initMocks(this); 189 190 // Robolectric does not know about the StorageStatsManager as a system service. 191 // Registering a mock of this service as a replacement. 192 ShadowContextImpl shadowContext = Shadow.extract( 193 RuntimeEnvironment.application.getBaseContext()); 194 shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager); 195 mApplication = spy(RuntimeEnvironment.application); 196 StorageStats storageStats = new StorageStats(); 197 storageStats.codeBytes = 10; 198 storageStats.cacheBytes = 30; 199 // Data bytes are a superset of cache bytes. 200 storageStats.dataBytes = storageStats.cacheBytes + 20; 201 when(mStorageStatsManager.queryStatsForPackage(any(UUID.class), 202 anyString(), any(UserHandle.class))).thenReturn(storageStats); 203 204 // Set up 3 installed apps, in which 1 is hidden module 205 final List<ApplicationInfo> infos = new ArrayList<>(); 206 infos.add(createApplicationInfo("test.package.1")); 207 infos.add(createApplicationInfo("test.hidden.module.2")); 208 infos.add(createApplicationInfo("test.package.3")); 209 when(mPackageManagerService.getInstalledApplications( 210 anyLong() /* flags */, anyInt() /* userId */)).thenReturn(new ParceledListSlice(infos)); 211 212 ApplicationsState.sInstance = null; 213 mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService); 214 mApplicationsState.clearEntries(); 215 216 mSession = mApplicationsState.newSession(mCallbacks); 217 } 218 219 @After tearDown()220 public void tearDown() { 221 mSession.onDestroy(); 222 } 223 createApplicationInfo(String packageName)224 private ApplicationInfo createApplicationInfo(String packageName) { 225 return createApplicationInfo(packageName, 0); 226 } 227 createApplicationInfo(String packageName, int uid)228 private ApplicationInfo createApplicationInfo(String packageName, int uid) { 229 ApplicationInfo appInfo = new ApplicationInfo(); 230 appInfo.sourceDir = "foo"; 231 appInfo.flags |= ApplicationInfo.FLAG_INSTALLED; 232 appInfo.storageUuid = UUID.randomUUID(); 233 appInfo.packageName = packageName; 234 appInfo.uid = uid; 235 return appInfo; 236 } 237 createAppEntry(ApplicationInfo appInfo, int id)238 private AppEntry createAppEntry(ApplicationInfo appInfo, int id) { 239 AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id); 240 appEntry.label = "label"; 241 appEntry.mounted = true; 242 return appEntry; 243 } 244 addApp(String packageName, int id)245 private void addApp(String packageName, int id) { 246 addApp(packageName, id, 0); 247 } 248 addApp(String packageName, int id, int userId)249 private void addApp(String packageName, int id, int userId) { 250 ApplicationInfo appInfo = createApplicationInfo(packageName, id); 251 AppEntry appEntry = createAppEntry(appInfo, id); 252 mApplicationsState.mAppEntries.add(appEntry); 253 mApplicationsState.mEntriesMap.get(userId).put(appInfo.packageName, appEntry); 254 } 255 processAllMessages()256 private void processAllMessages() { 257 Handler mainHandler = mApplicationsState.mMainHandler; 258 Handler bkgHandler = mApplicationsState.mBackgroundHandler; 259 ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper()); 260 ShadowLooper shadowMainLooper = extract(mainHandler.getLooper()); 261 shadowBkgLooper.idle(); 262 shadowMainLooper.idle(); 263 } 264 findAppEntry(List<AppEntry> appEntries, long id)265 private AppEntry findAppEntry(List<AppEntry> appEntries, long id) { 266 for (AppEntry appEntry : appEntries) { 267 if (appEntry.id == id) { 268 return appEntry; 269 } 270 } 271 return null; 272 } 273 274 @Test testDefaultSession_isResumed_LoadsAll()275 public void testDefaultSession_isResumed_LoadsAll() { 276 mSession.onResume(); 277 278 addApp(HOME_PACKAGE_NAME, 1); 279 addApp(LAUNCHABLE_PACKAGE_NAME, 2); 280 mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 281 processAllMessages(); 282 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 283 284 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 285 assertThat(appEntries.size()).isEqualTo(2); 286 287 for (AppEntry appEntry : appEntries) { 288 assertThat(appEntry.size).isGreaterThan(0L); 289 assertThat(appEntry.icon).isNotNull(); 290 } 291 292 AppEntry homeEntry = findAppEntry(appEntries, 1); 293 assertThat(homeEntry.isHomeApp).isTrue(); 294 assertThat(homeEntry.hasLauncherEntry).isFalse(); 295 296 AppEntry launchableEntry = findAppEntry(appEntries, 2); 297 assertThat(launchableEntry.hasLauncherEntry).isTrue(); 298 assertThat(launchableEntry.launcherEntryEnabled).isTrue(); 299 } 300 301 @Test testDefaultSession_isPaused_NotLoadsAll()302 public void testDefaultSession_isPaused_NotLoadsAll() { 303 mSession.onResume(); 304 305 addApp(HOME_PACKAGE_NAME, 1); 306 addApp(LAUNCHABLE_PACKAGE_NAME, 2); 307 mSession.mResumed = false; 308 mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 309 processAllMessages(); 310 311 verify(mCallbacks, never()).onRebuildComplete(mAppEntriesCaptor.capture()); 312 } 313 314 @Test testCustomSessionLoadsIconsOnly()315 public void testCustomSessionLoadsIconsOnly() { 316 mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS); 317 mSession.onResume(); 318 319 addApp(LAUNCHABLE_PACKAGE_NAME, 1); 320 mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 321 processAllMessages(); 322 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 323 324 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 325 assertThat(appEntries.size()).isEqualTo(1); 326 327 AppEntry launchableEntry = findAppEntry(appEntries, 1); 328 assertThat(launchableEntry.icon).isNotNull(); 329 assertThat(launchableEntry.size).isEqualTo(-1); 330 assertThat(launchableEntry.hasLauncherEntry).isFalse(); 331 } 332 333 @Test testCustomSessionLoadsSizesOnly()334 public void testCustomSessionLoadsSizesOnly() { 335 mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES); 336 mSession.onResume(); 337 338 addApp(LAUNCHABLE_PACKAGE_NAME, 1); 339 mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 340 processAllMessages(); 341 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 342 343 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 344 assertThat(appEntries.size()).isEqualTo(1); 345 346 AppEntry launchableEntry = findAppEntry(appEntries, 1); 347 assertThat(launchableEntry.hasLauncherEntry).isFalse(); 348 assertThat(launchableEntry.size).isGreaterThan(0L); 349 } 350 351 @Test testCustomSessionLoadsHomeOnly()352 public void testCustomSessionLoadsHomeOnly() { 353 mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP); 354 mSession.onResume(); 355 356 addApp(HOME_PACKAGE_NAME, 1); 357 mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 358 processAllMessages(); 359 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 360 361 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 362 assertThat(appEntries.size()).isEqualTo(1); 363 364 AppEntry launchableEntry = findAppEntry(appEntries, 1); 365 assertThat(launchableEntry.hasLauncherEntry).isFalse(); 366 assertThat(launchableEntry.size).isEqualTo(-1); 367 assertThat(launchableEntry.isHomeApp).isTrue(); 368 } 369 370 @Test testCustomSessionLoadsLeanbackOnly()371 public void testCustomSessionLoadsLeanbackOnly() { 372 mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER); 373 mSession.onResume(); 374 375 addApp(LAUNCHABLE_PACKAGE_NAME, 1); 376 mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 377 processAllMessages(); 378 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 379 380 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 381 assertThat(appEntries.size()).isEqualTo(1); 382 383 AppEntry launchableEntry = findAppEntry(appEntries, 1); 384 assertThat(launchableEntry.size).isEqualTo(-1); 385 assertThat(launchableEntry.isHomeApp).isFalse(); 386 assertThat(launchableEntry.hasLauncherEntry).isTrue(); 387 assertThat(launchableEntry.launcherEntryEnabled).isTrue(); 388 } 389 390 @Test onResume_shouldNotIncludeSystemHiddenModule()391 public void onResume_shouldNotIncludeSystemHiddenModule() { 392 mSession.onResume(); 393 394 final List<ApplicationInfo> mApplications = mApplicationsState.mApplications; 395 assertThat(mApplications).hasSize(2); 396 assertThat(mApplications.get(0).packageName).isEqualTo("test.package.1"); 397 assertThat(mApplications.get(1).packageName).isEqualTo("test.package.3"); 398 } 399 400 @Test removeAndInstall_noWorkprofile_doResumeIfNeededLocked_shouldClearEntries()401 public void removeAndInstall_noWorkprofile_doResumeIfNeededLocked_shouldClearEntries() 402 throws RemoteException { 403 // scenario: only owner user 404 // (PKG_1, PKG_2) -> (PKG_2, PKG_3) 405 // PKG_1 is removed and PKG_3 is installed before app is resumed. 406 ApplicationsState.sInstance = null; 407 mApplicationsState = spy( 408 ApplicationsState 409 .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); 410 411 // Previous Applications: 412 ApplicationInfo appInfo; 413 final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); 414 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 415 prevAppList.add(appInfo); 416 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 417 prevAppList.add(appInfo); 418 mApplicationsState.mApplications = prevAppList; 419 420 // Previous Entries: 421 // (PKG_1, PKG_2) 422 addApp(PKG_1, OWNER_UID_1, 0); 423 addApp(PKG_2, OWNER_UID_2, 0); 424 425 // latest Applications: 426 // (PKG_2, PKG_3) 427 final ArrayList<ApplicationInfo> appList = new ArrayList<>(); 428 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 429 appList.add(appInfo); 430 appInfo = createApplicationInfo(PKG_3, OWNER_UID_3); 431 appList.add(appInfo); 432 setupDoResumeIfNeededLocked(appList, null); 433 434 mApplicationsState.doResumeIfNeededLocked(); 435 436 verify(mApplicationsState).clearEntries(); 437 } 438 439 @Test noAppRemoved_noWorkprofile_doResumeIfNeededLocked_shouldNotClearEntries()440 public void noAppRemoved_noWorkprofile_doResumeIfNeededLocked_shouldNotClearEntries() 441 throws RemoteException { 442 // scenario: only owner user 443 // (PKG_1, PKG_2) 444 ApplicationsState.sInstance = null; 445 mApplicationsState = spy( 446 ApplicationsState 447 .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); 448 449 ApplicationInfo appInfo; 450 // Previous Applications 451 final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); 452 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 453 prevAppList.add(appInfo); 454 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 455 prevAppList.add(appInfo); 456 mApplicationsState.mApplications = prevAppList; 457 458 // Previous Entries: 459 // (pk1, PKG_2) 460 addApp(PKG_1, OWNER_UID_1, 0); 461 addApp(PKG_2, OWNER_UID_2, 0); 462 463 // latest Applications: 464 // (PKG_2, PKG_3) 465 final ArrayList<ApplicationInfo> appList = new ArrayList<>(); 466 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 467 appList.add(appInfo); 468 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 469 appList.add(appInfo); 470 setupDoResumeIfNeededLocked(appList, null); 471 472 mApplicationsState.doResumeIfNeededLocked(); 473 474 verify(mApplicationsState, never()).clearEntries(); 475 } 476 477 @Test removeProfileApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries()478 public void removeProfileApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries() 479 throws RemoteException { 480 if (!MU_ENABLED) { 481 return; 482 } 483 // [Preconditions] 484 // 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state 485 // 2 apps (PKG_1, PKG_2) for non-owner. 486 // 487 // [Actions] 488 // profile user's PKG_2 is removed before resume 489 // 490 // Applications: 491 // owner - (PKG_1 - uninstalled, PKG_2) -> (PKG_1 - uninstalled, PKG_2) 492 // profile - (PKG_1, PKG_2) -> (PKG_1) 493 // 494 // Previous Entries: 495 // owner - (PKG_2) 496 // profile - (PKG_1, PKG_2) 497 498 ShadowUserManager shadowUserManager = Shadow 499 .extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); 500 shadowUserManager.addProfile(PROFILE_USERID, "profile"); 501 502 ApplicationsState.sInstance = null; 503 mApplicationsState = spy( 504 ApplicationsState 505 .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); 506 507 ApplicationInfo appInfo; 508 // Previous Applications 509 // owner - (PKG_1 - uninstalled, PKG_2) 510 // profile - (PKG_1, PKG_2) 511 final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); 512 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 513 appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; 514 prevAppList.add(appInfo); 515 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 516 prevAppList.add(appInfo); 517 518 appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); 519 prevAppList.add(appInfo); 520 appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); 521 prevAppList.add(appInfo); 522 523 mApplicationsState.mApplications = prevAppList; 524 // Previous Entries: 525 // owner (PKG_2), profile (pk1, PKG_2) 526 // PKG_1 is not installed for owner, hence it's removed from entries 527 addApp(PKG_2, OWNER_UID_2, 0); 528 addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID); 529 addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID); 530 531 // latest Applications: 532 // owner (PKG_1, PKG_2), profile (PKG_1) 533 // owner's PKG_1 is still listed and is in non-installed state 534 // profile user's PKG_2 is removed by a user before resume 535 //owner 536 final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>(); 537 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 538 appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; 539 ownerAppList.add(appInfo); 540 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 541 ownerAppList.add(appInfo); 542 //profile 543 appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); 544 setupDoResumeIfNeededLocked(ownerAppList, new ArrayList<>(Arrays.asList(appInfo))); 545 546 mApplicationsState.doResumeIfNeededLocked(); 547 548 verify(mApplicationsState).clearEntries(); 549 } 550 551 @Test removeOwnerApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries()552 public void removeOwnerApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries() 553 throws RemoteException { 554 if (!MU_ENABLED) { 555 return; 556 } 557 // [Preconditions] 558 // 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state 559 // 2 apps (PKG_1, PKG_2) for non-owner. 560 // 561 // [Actions] 562 // Owner user's PKG_2 is removed before resume 563 // 564 // Applications: 565 // owner - (PKG_1 - uninstalled, PKG_2) -> (PKG_1 - uninstalled, PKG_2 - uninstalled) 566 // profile - (PKG_1, PKG_2) -> (PKG_1, PKG_2) 567 // 568 // Previous Entries: 569 // owner - (PKG_2) 570 // profile - (PKG_1, PKG_2) 571 572 ShadowUserManager shadowUserManager = Shadow 573 .extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); 574 shadowUserManager.addProfile(PROFILE_USERID, "profile"); 575 576 ApplicationsState.sInstance = null; 577 mApplicationsState = spy( 578 ApplicationsState 579 .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); 580 581 ApplicationInfo appInfo; 582 // Previous Applications: 583 // owner - (PKG_1 - uninstalled, PKG_2) 584 // profile - (PKG_1, PKG_2) 585 final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); 586 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 587 appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; 588 prevAppList.add(appInfo); 589 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 590 prevAppList.add(appInfo); 591 592 appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); 593 prevAppList.add(appInfo); 594 appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); 595 prevAppList.add(appInfo); 596 597 mApplicationsState.mApplications = prevAppList; 598 599 // Previous Entries: 600 // owner (PKG_2), profile (pk1, PKG_2) 601 // PKG_1 is not installed for owner, hence it's removed from entries 602 addApp(PKG_2, OWNER_UID_2, 0); 603 addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID); 604 addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID); 605 606 // latest Applications: 607 // owner (PKG_1 - uninstalled, PKG_2 - uninstalled), profile (PKG_1, PKG_2) 608 // owner's PKG_1, PKG_2 is still listed and is in non-installed state 609 // profile user's PKG_2 is removed before resume 610 //owner 611 final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>(); 612 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 613 appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; 614 ownerAppList.add(appInfo); 615 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 616 appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; 617 ownerAppList.add(appInfo); 618 619 //profile 620 final ArrayList<ApplicationInfo> profileAppList = new ArrayList<>(); 621 appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); 622 profileAppList.add(appInfo); 623 appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); 624 profileAppList.add(appInfo); 625 setupDoResumeIfNeededLocked(ownerAppList, profileAppList); 626 627 mApplicationsState.doResumeIfNeededLocked(); 628 629 verify(mApplicationsState).clearEntries(); 630 } 631 632 @Test noAppRemoved_workprofileExists_doResumeIfNeededLocked_shouldNotClearEntries()633 public void noAppRemoved_workprofileExists_doResumeIfNeededLocked_shouldNotClearEntries() 634 throws RemoteException { 635 if (!MU_ENABLED) { 636 return; 637 } 638 // [Preconditions] 639 // 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state 640 // 2 apps (PKG_1, PKG_2) for non-owner. 641 // 642 // Applications: 643 // owner - (PKG_1 - uninstalled, PKG_2) 644 // profile - (PKG_1, PKG_2) 645 // 646 // Previous Entries: 647 // owner - (PKG_2) 648 // profile - (PKG_1, PKG_2) 649 650 ShadowUserManager shadowUserManager = Shadow 651 .extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); 652 shadowUserManager.addProfile(PROFILE_USERID, "profile"); 653 654 ApplicationsState.sInstance = null; 655 mApplicationsState = spy( 656 ApplicationsState 657 .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); 658 659 ApplicationInfo appInfo; 660 // Previous Applications: 661 // owner - (PKG_1 - uninstalled, PKG_2) 662 // profile - (PKG_1, PKG_2) 663 final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); 664 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 665 appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; 666 prevAppList.add(appInfo); 667 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 668 prevAppList.add(appInfo); 669 670 appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); 671 prevAppList.add(appInfo); 672 appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); 673 prevAppList.add(appInfo); 674 675 mApplicationsState.mApplications = prevAppList; 676 // Previous Entries: 677 // owner (PKG_2), profile (pk1, PKG_2) 678 // PKG_1 is not installed for owner, hence it's removed from entries 679 addApp(PKG_2, OWNER_UID_2, 0); 680 addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID); 681 addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID); 682 683 // latest Applications: 684 // owner (PKG_1 - uninstalled, PKG_2), profile (PKG_1, PKG_2) 685 // owner's PKG_1 is still listed and is in non-installed state 686 687 // owner 688 final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>(); 689 appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); 690 appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; 691 ownerAppList.add(appInfo); 692 appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); 693 ownerAppList.add(appInfo); 694 695 // profile 696 final ArrayList<ApplicationInfo> profileAppList = new ArrayList<>(); 697 appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); 698 profileAppList.add(appInfo); 699 appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); 700 profileAppList.add(appInfo); 701 setupDoResumeIfNeededLocked(ownerAppList, profileAppList); 702 703 mApplicationsState.doResumeIfNeededLocked(); 704 705 verify(mApplicationsState, never()).clearEntries(); 706 } 707 708 @Test testDefaultSession_enabledAppIconCache_shouldSkipPreloadIcon()709 public void testDefaultSession_enabledAppIconCache_shouldSkipPreloadIcon() { 710 when(mApplication.getPackageName()).thenReturn("com.android.settings"); 711 mSession.onResume(); 712 713 addApp(HOME_PACKAGE_NAME, 1); 714 addApp(LAUNCHABLE_PACKAGE_NAME, 2); 715 mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 716 processAllMessages(); 717 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 718 719 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 720 for (AppEntry appEntry : appEntries) { 721 assertThat(appEntry.icon).isNull(); 722 } 723 } 724 setupDoResumeIfNeededLocked(ArrayList<ApplicationInfo> ownerApps, ArrayList<ApplicationInfo> profileApps)725 private void setupDoResumeIfNeededLocked(ArrayList<ApplicationInfo> ownerApps, 726 ArrayList<ApplicationInfo> profileApps) 727 throws RemoteException { 728 729 if (ownerApps != null) { 730 when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(0))) 731 .thenReturn(new ParceledListSlice<>(ownerApps)); 732 } 733 if (profileApps != null) { 734 when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(PROFILE_USERID))) 735 .thenReturn(new ParceledListSlice<>(profileApps)); 736 } 737 final InterestingConfigChanges configChanges = mock(InterestingConfigChanges.class); 738 when(configChanges.applyNewConfig(any(Resources.class))).thenReturn(false); 739 mApplicationsState.setInterestingConfigChanges(configChanges); 740 } 741 } 742