• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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