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