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