1 /*
2  * Copyright (C) 2023 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.car.carlauncher;
18 
19 import static android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE;
20 import static android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS;
21 import static android.content.pm.ApplicationInfo.CATEGORY_AUDIO;
22 import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
23 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
24 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
25 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
26 
27 import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_LAUNCHABLES;
28 import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_MEDIA_SERVICES;
29 import static com.android.car.carlauncher.AppLauncherUtils.PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR;
30 import static com.android.car.carlauncher.AppLauncherUtils.TOS_DISABLED_APPS_SEPARATOR;
31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
32 
33 import static org.junit.Assert.assertEquals;
34 import static org.mockito.ArgumentMatchers.any;
35 import static org.mockito.ArgumentMatchers.anyInt;
36 import static org.mockito.ArgumentMatchers.argThat;
37 import static org.mockito.ArgumentMatchers.eq;
38 import static org.mockito.Mockito.mock;
39 import static org.mockito.Mockito.never;
40 import static org.mockito.Mockito.times;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.when;
43 
44 import android.app.ActivityManager;
45 import android.car.Car;
46 import android.car.content.pm.CarPackageManager;
47 import android.car.media.CarMediaManager;
48 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
49 import android.content.ComponentName;
50 import android.content.ContentResolver;
51 import android.content.Context;
52 import android.content.Intent;
53 import android.content.pm.ActivityInfo;
54 import android.content.pm.ApplicationInfo;
55 import android.content.pm.LauncherActivityInfo;
56 import android.content.pm.LauncherApps;
57 import android.content.pm.PackageManager;
58 import android.content.pm.ResolveInfo;
59 import android.content.pm.ServiceInfo;
60 import android.content.res.Resources;
61 import android.os.Bundle;
62 import android.os.UserHandle;
63 import android.provider.Settings;
64 import android.service.media.MediaBrowserService;
65 import android.util.ArraySet;
66 
67 import androidx.test.ext.junit.runners.AndroidJUnit4;
68 import androidx.test.filters.SmallTest;
69 
70 import org.junit.After;
71 import org.junit.Before;
72 import org.junit.Test;
73 import org.junit.runner.RunWith;
74 import org.mockito.ArgumentCaptor;
75 import org.mockito.Mock;
76 import org.mockito.Mockito;
77 
78 import java.util.ArrayList;
79 import java.util.Arrays;
80 import java.util.Collections;
81 import java.util.List;
82 import java.util.Set;
83 import java.util.function.Consumer;
84 import java.util.stream.Collectors;
85 
86 @RunWith(AndroidJUnit4.class)
87 @SmallTest
88 public final class AppLauncherUtilsTest extends AbstractExtendedMockitoTestCase {
89     private static final String TEST_DISABLED_APP_1 = "com.android.car.test.disabled1";
90     private static final String TEST_DISABLED_APP_2 = "com.android.car.test.disabled2";
91     private static final String TEST_ENABLED_APP = "com.android.car.test.enabled";
92     private static final String TEST_TOS_DISABLED_APP_1 = "com.android.car.test.tosdisabled1";
93     private static final String TEST_TOS_DISABLED_APP_2 = "com.android.car.test.tosdisabled2";
94     private static final String TEST_VIDEO_APP = "com.android.car.test.video";
95     // Default media app
96     private static final String TEST_MEDIA_TEMPLATE_MBS = "com.android.car.test.mbs";
97     // Video app that has a MBS defined but has its own launch activity
98     private static final String TEST_VIDEO_MBS = "com.android.car.test.video.mbs";
99     // NDO App that has opted in its MBS to launch in car
100     private static final String TEST_NDO_MBS_LAUNCHABLE = "com.android.car.test.mbs.launchable";
101     // NDO App that has opted out its MBS to launch in car
102     private static final String TEST_NDO_MBS_NOT_LAUNCHABLE =
103             "com.android.car.test.mbs.notlaunchable";
104 
105     private static final String CUSTOM_MEDIA_PACKAGE = "com.android.car.radio";
106     private static final String CUSTOM_MEDIA_CLASS = "com.android.car.radio.service";
107     private static final String CUSTOM_MEDIA_COMPONENT = CUSTOM_MEDIA_PACKAGE
108             + "/" + CUSTOM_MEDIA_CLASS;
109     private static final String TEST_MIRROR_APP_PKG = "com.android.car.test.mirroring";
110     private static final String TOS_INTENT_NAME = "intent:#Intent;action="
111             + "com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=true;"
112             + "S.mini_flow_extra=GTOS_GATED_FLOW;end";
113     private static final String TOS_INTENT_VERIFY = "#Intent;action="
114             + "com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=true;"
115             + "S.mini_flow_extra=GTOS_GATED_FLOW;end";
116 
117 
118     @Mock private Context mMockContext;
119     @Mock private LauncherApps mMockLauncherApps;
120     @Mock private PackageManager mMockPackageManager;
121     @Mock private AppLauncherUtils.ShortcutsListener mMockShortcutsListener;
122 
123     @Mock private Resources mResources;
124 
125     @Mock private LauncherActivityInfo mRadioLauncherActivityInfo;
126 
127     private CarMediaManager mCarMediaManager;
128     private CarPackageManager mCarPackageManager;
129     private Car mCar;
130 
131     @Before
setUp()132     public void setUp() throws Exception {
133         // Need for CarMediaManager to get the user from the context.
134         when(mMockContext.getUser()).thenReturn(UserHandle.of(ActivityManager.getCurrentUser()));
135 
136         mCar = Car.createCar(mMockContext, /* handler = */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
137                 (car, ready) -> {
138                     if (!ready) {
139                         mCarPackageManager = null;
140                         mCarMediaManager = null;
141                         return;
142                     }
143                     mCarPackageManager = (CarPackageManager) car.getCarManager(Car.PACKAGE_SERVICE);
144                     mCarPackageManager = Mockito.spy(mCarPackageManager);
145                     mCarMediaManager = (CarMediaManager) car.getCarManager(Car.CAR_MEDIA_SERVICE);
146                     when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
147                 });
148     }
149 
150     @After
tearDown()151     public void tearDown() throws Exception {
152         if (mCar != null && mCar.isConnected()) {
153             mCar.disconnect();
154             mCar = null;
155         }
156     }
157 
158     @Override
onSessionBuilder(CustomMockitoSessionBuilder session)159     protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
160         session.spyStatic(Settings.Secure.class);
161     }
162 
163     @Test
testGetLauncherApps_MediaCenterAppSwitcher()164     public void testGetLauncherApps_MediaCenterAppSwitcher() {
165         mockSettingsStringCalls();
166         mockPackageManagerQueries();
167 
168         when(mMockContext.getResources()).thenReturn(mResources);
169         when(mResources.getStringArray(eq(
170                 com.android.car.media.common.R.array.custom_media_packages)))
171                 .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
172 
173         // Setup custom media component
174         when(mMockLauncherApps.getActivityList(any(), any()))
175                 .thenReturn(List.of(mRadioLauncherActivityInfo));
176         when(mRadioLauncherActivityInfo.getComponentName())
177                 .thenReturn(new ComponentName(CUSTOM_MEDIA_PACKAGE, CUSTOM_MEDIA_CLASS));
178         when(mRadioLauncherActivityInfo.getName())
179                 .thenReturn(CUSTOM_MEDIA_CLASS);
180 
181         AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
182                 mMockContext, /* appsToHide= */ new ArraySet<>(),
183                 /* appTypes= */ APP_TYPE_MEDIA_SERVICES,
184                 /* openMediaCenter= */ false, mMockLauncherApps, mCarPackageManager,
185                 mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
186                 TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
187 
188         List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
189 
190         // Only media apps should be present
191         assertEquals(Set.of(
192                         TEST_MEDIA_TEMPLATE_MBS,
193                         TEST_NDO_MBS_LAUNCHABLE,
194                         CUSTOM_MEDIA_PACKAGE),
195                 appMetaData.stream()
196                         .map(am -> am.getComponentName().getPackageName())
197                         .collect(Collectors.toSet()));
198 
199         // This should include all MBS discovered
200         assertEquals(5, launcherAppsInfo.getMediaServices().size());
201 
202         mockPmGetApplicationEnabledSetting(COMPONENT_ENABLED_STATE_ENABLED, TEST_DISABLED_APP_1,
203                 TEST_DISABLED_APP_2);
204 
205         launchAllApps(appMetaData);
206 
207         // Media apps should do only switching and not launch activity
208         verify(mMockContext, never()).startActivity(any(), any());
209     }
210 
211     @Test
testGetLauncherApps_Launcher()212     public void testGetLauncherApps_Launcher() {
213         mockSettingsStringCalls();
214         mockPackageManagerQueries();
215 
216         when(mMockContext.getResources()).thenReturn(mResources);
217         when(mResources.getStringArray(eq(
218                 com.android.car.media.common.R.array.custom_media_packages)))
219                 .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
220 
221         // Setup custom media component
222         when(mMockLauncherApps.getActivityList(any(), any()))
223                 .thenReturn(List.of(mRadioLauncherActivityInfo));
224         when(mRadioLauncherActivityInfo.getComponentName())
225                 .thenReturn(new ComponentName(CUSTOM_MEDIA_PACKAGE, CUSTOM_MEDIA_CLASS));
226         when(mRadioLauncherActivityInfo.getName())
227                 .thenReturn(CUSTOM_MEDIA_CLASS);
228 
229         AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
230                 mMockContext, /* appsToHide= */ new ArraySet<>(),
231                 /* appTypes= */ APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
232                 /* openMediaCenter= */ true, mMockLauncherApps, mCarPackageManager,
233                 mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
234                 TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
235 
236         List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
237         // mMockLauncherApps is never stubbed, only services & disabled activities are expected.
238 
239         assertEquals(Set.of(
240                         TEST_MEDIA_TEMPLATE_MBS,
241                         TEST_NDO_MBS_LAUNCHABLE,
242                         CUSTOM_MEDIA_PACKAGE,
243                         TEST_DISABLED_APP_1,
244                         TEST_DISABLED_APP_2),
245                 appMetaData.stream()
246                         .map(am -> am.getComponentName().getPackageName())
247                         .collect(Collectors.toSet()));
248 
249 
250         // This should include all MBS discovered
251         assertEquals(5, launcherAppsInfo.getMediaServices().size());
252 
253         mockPmGetApplicationEnabledSetting(COMPONENT_ENABLED_STATE_ENABLED, TEST_DISABLED_APP_1,
254                 TEST_DISABLED_APP_2);
255 
256         launchAllApps(appMetaData);
257 
258         verify(mMockPackageManager).setApplicationEnabledSetting(
259                 eq(TEST_DISABLED_APP_1), eq(COMPONENT_ENABLED_STATE_ENABLED), eq(0));
260 
261         verify(mMockPackageManager).setApplicationEnabledSetting(
262                 eq(TEST_DISABLED_APP_2), eq(COMPONENT_ENABLED_STATE_ENABLED), eq(0));
263 
264         verify(mMockContext, times(5)).startActivity(any(), any());
265 
266         verify(mMockPackageManager, never()).setApplicationEnabledSetting(
267                 eq(TEST_ENABLED_APP), anyInt(), eq(0));
268     }
269 
270 
271     @Test
testGetLauncherAppsWithEnableAndTosDisabledApps()272     public void testGetLauncherAppsWithEnableAndTosDisabledApps() {
273         mockSettingsStringCalls();
274         mockTosPackageManagerQueries();
275 
276         when(mMockContext.getResources()).thenReturn(mResources);
277         when(mResources.getStringArray(eq(
278                 com.android.car.media.common.R.array.custom_media_packages)))
279                 .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
280 
281         AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
282                 mMockContext, /* appsToHide= */ new ArraySet<>(),
283                 /* appTypes= */ APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
284                 /* openMediaCenter= */ false, mMockLauncherApps, mCarPackageManager,
285                 mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
286                 TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
287 
288         List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
289 
290         // mMockLauncherApps is never stubbed, only services & disabled activities are expected.
291         assertEquals(3, appMetaData.size());
292 
293         Resources resources = mock(Resources.class);
294         when(mMockContext.getResources()).thenReturn(resources);
295         when(resources.getString(anyInt())).thenReturn(TOS_INTENT_NAME);
296 
297         launchAllApps(appMetaData);
298 
299         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
300         verify(mMockContext, times(2)).startActivity(intentCaptor.capture(), any());
301 
302         String intentUri = intentCaptor.getAllValues().get(0).toUri(0);
303         assertEquals(TOS_INTENT_VERIFY, intentUri);
304     }
305 
306     @Test
testGetLauncherAppsWithEnableAndTosDisabledDistractionOptimizedApps()307     public void testGetLauncherAppsWithEnableAndTosDisabledDistractionOptimizedApps() {
308         mockSettingsStringCalls();
309         mockTosPackageManagerQueries();
310 
311         when(mMockContext.getResources()).thenReturn(mResources);
312         when(mResources.getStringArray(eq(
313                 com.android.car.media.common.R.array.custom_media_packages)))
314                 .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
315 
316         doReturn(true)
317                 .when(mCarPackageManager)
318                 .isActivityDistractionOptimized(eq(TEST_TOS_DISABLED_APP_1), any());
319         doReturn(true)
320                 .when(mCarPackageManager)
321                 .isActivityDistractionOptimized(eq(TEST_TOS_DISABLED_APP_2), any());
322 
323         AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
324                 mMockContext, /* appsToHide= */ new ArraySet<>(),
325                 /* appTypes= */ APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
326                 /* openMediaCenter= */ false, mMockLauncherApps, mCarPackageManager,
327                 mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
328                 TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
329 
330         List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
331 
332         // mMockLauncherApps is never stubbed, only services & disabled activities are expected.
333         assertEquals(3, appMetaData.size());
334 
335         Resources resources = mock(Resources.class);
336         when(mMockContext.getResources()).thenReturn(resources);
337         when(resources.getString(anyInt())).thenReturn(TOS_INTENT_NAME);
338 
339         launchAllApps(appMetaData);
340 
341         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
342         verify(mMockContext, times(2)).startActivity(intentCaptor.capture(), any());
343 
344         String intentUri = intentCaptor.getAllValues().get(0).toUri(0);
345         assertEquals(TOS_INTENT_VERIFY, intentUri);
346     }
347 
mockPackageManagerQueries()348     private void mockPackageManagerQueries() {
349         // setup a media template app that uses media service
350         ApplicationInfo mbsAppInfo = new ApplicationInfo();
351         mbsAppInfo.category = CATEGORY_AUDIO;
352         ResolveInfo mbs = constructServiceResolveInfo(TEST_MEDIA_TEMPLATE_MBS);
353 
354         try {
355             Intent mbsIntent = new Intent();
356             mbsIntent.setComponent(mbs.getComponentInfo().getComponentName());
357             mbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
358 
359             when(mMockPackageManager.getApplicationInfo(mbs.getComponentInfo().packageName, 0))
360                     .thenReturn(mbsAppInfo);
361 
362             doReturn(Arrays.asList(mbs)).when(mMockPackageManager).queryIntentServices(
363                     argThat((Intent i) -> i != null
364                             && mbs.getComponentInfo().getComponentName().equals(i.getComponent())),
365                     eq(PackageManager.GET_META_DATA));
366 
367             when(mMockPackageManager.getLaunchIntentForPackage(mbs.getComponentInfo().packageName))
368                     .thenReturn(null);
369         } catch (PackageManager.NameNotFoundException e) {
370             throw new RuntimeException(e);
371         }
372 
373         // setup a NDO Video app that has MBS but also its own activity, MBS won't be surfaced
374         ApplicationInfo videoAppInfo = new ApplicationInfo();
375         videoAppInfo.category = CATEGORY_VIDEO;
376         ResolveInfo videoApp = constructServiceResolveInfo(TEST_VIDEO_MBS);
377         try {
378             Intent videoMbsIntent = new Intent();
379             videoMbsIntent.setComponent(videoApp.getComponentInfo().getComponentName());
380             videoMbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
381 
382             when(mMockPackageManager.getApplicationInfo(videoApp.getComponentInfo().packageName,
383                     0))
384                     .thenReturn(videoAppInfo);
385 
386             doReturn(Arrays.asList(videoApp)).when(mMockPackageManager).queryIntentServices(
387                     argThat((Intent i) -> i != null
388                             && videoApp.getComponentInfo().getComponentName()
389                                     .equals(i.getComponent())),
390                     eq(PackageManager.GET_META_DATA));
391 
392             when(mMockPackageManager.getLaunchIntentForPackage(
393                     videoApp.getComponentInfo().packageName))
394                     .thenReturn(new Intent());
395         } catch (PackageManager.NameNotFoundException e) {
396             throw new RuntimeException(e);
397         }
398 
399         // setup a NDO app that has MBS opted out of launch in car
400         ApplicationInfo notlaunchableMBSInfo = new ApplicationInfo();
401         notlaunchableMBSInfo.category = CATEGORY_VIDEO;
402         ResolveInfo notlaunchableMBSApp = constructServiceResolveInfo(TEST_NDO_MBS_NOT_LAUNCHABLE);
403 
404         try {
405             Intent notlaunachableMbsIntent = new Intent();
406             notlaunachableMbsIntent.setComponent(
407                     notlaunchableMBSApp.getComponentInfo().getComponentName());
408             notlaunachableMbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
409 
410             when(mMockPackageManager.getApplicationInfo(
411                     notlaunchableMBSApp.getComponentInfo().packageName, 0))
412                     .thenReturn(notlaunchableMBSInfo);
413 
414 
415             notlaunchableMBSApp.serviceInfo.metaData = new Bundle();
416             notlaunchableMBSApp.serviceInfo.metaData
417                     .putBoolean("androidx.car.app.launchable", false);
418 
419             doReturn(Arrays.asList(notlaunchableMBSApp))
420                     .when(mMockPackageManager).queryIntentServices(
421                     argThat((Intent i) -> i != null
422                             && notlaunchableMBSApp.getComponentInfo().getComponentName()
423                                     .equals(i.getComponent())),
424                     eq(PackageManager.GET_META_DATA));
425 
426             when(mMockPackageManager.getLaunchIntentForPackage(
427                     notlaunchableMBSApp.getComponentInfo().packageName))
428                     .thenReturn(new Intent());
429         } catch (PackageManager.NameNotFoundException e) {
430             throw new RuntimeException(e);
431         }
432 
433 
434         // setup a NDO app that has MBS opted in to launch in car
435         ApplicationInfo launchableMBSInfo = new ApplicationInfo();
436         launchableMBSInfo.category = CATEGORY_VIDEO;
437         ResolveInfo launchableMBSApp = constructServiceResolveInfo(TEST_NDO_MBS_LAUNCHABLE);
438         try {
439             Intent mbsIntent = new Intent();
440             mbsIntent.setComponent(launchableMBSApp.getComponentInfo().getComponentName());
441             mbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
442 
443             when(mMockPackageManager.getApplicationInfo(
444                     launchableMBSApp.getComponentInfo().packageName,
445                     0))
446                     .thenReturn(launchableMBSInfo);
447 
448 
449             launchableMBSApp.serviceInfo.metaData = new Bundle();
450             launchableMBSApp.serviceInfo.metaData.putBoolean("androidx.car.app.launchable", true);
451 
452             doReturn(Arrays.asList(launchableMBSApp)).when(mMockPackageManager).queryIntentServices(
453                     argThat((Intent i) -> i != null
454                             && launchableMBSApp.getComponentInfo().getComponentName()
455                             .equals(i.getComponent())),
456                     eq(PackageManager.GET_META_DATA));
457 
458             when(mMockPackageManager.getLaunchIntentForPackage(
459                     launchableMBSApp.getComponentInfo().packageName))
460                     .thenReturn(new Intent());
461         } catch (PackageManager.NameNotFoundException e) {
462             throw new RuntimeException(e);
463         }
464 
465         when(mMockPackageManager.queryIntentServices(any(), eq(PackageManager.GET_RESOLVED_FILTER)))
466                 .thenAnswer(args -> {
467             Intent intent = args.getArgument(0);
468             if (intent.getAction().equals(MediaBrowserService.SERVICE_INTERFACE)) {
469                 return Arrays.asList(mbs, videoApp, notlaunchableMBSApp, launchableMBSApp,
470                         constructServiceResolveInfo(CUSTOM_MEDIA_PACKAGE));
471             }
472             return new ArrayList<>();
473         });
474 
475         // setup activities
476         when(mMockPackageManager.queryIntentActivities(any(), any())).thenAnswer(args -> {
477             Intent intent = args.getArgument(0);
478             PackageManager.ResolveInfoFlags flags = args.getArgument(1);
479             List<ResolveInfo> resolveInfoList = new ArrayList<>();
480             if (intent.getAction().equals(Intent.ACTION_MAIN)) {
481                 if ((flags.getValue() & MATCH_DISABLED_UNTIL_USED_COMPONENTS) != 0) {
482                     resolveInfoList.add(constructActivityResolveInfo(TEST_DISABLED_APP_1));
483                     resolveInfoList.add(constructActivityResolveInfo(TEST_DISABLED_APP_2));
484                 }
485                 // Keep custom media component in both MBS and Activity with Launch Intent
486                 resolveInfoList.add(constructActivityResolveInfo(CUSTOM_MEDIA_PACKAGE));
487                 // Add apps which will have their own Launcher Activity
488                 resolveInfoList.add(constructActivityResolveInfo(TEST_VIDEO_MBS));
489                 resolveInfoList.add(constructActivityResolveInfo(TEST_NDO_MBS_LAUNCHABLE));
490                 resolveInfoList.add(constructActivityResolveInfo(TEST_NDO_MBS_NOT_LAUNCHABLE));
491             }
492 
493             return resolveInfoList;
494         });
495     }
496 
mockTosPackageManagerQueries()497     private void mockTosPackageManagerQueries() {
498         ResolveInfo resolveInfo = constructServiceResolveInfo(TEST_ENABLED_APP);
499         try {
500             when(mMockPackageManager.getServiceInfo(
501                     resolveInfo
502                             .getComponentInfo().getComponentName(),
503                     PackageManager.GET_META_DATA))
504                     .thenReturn(new ServiceInfo());
505         } catch (PackageManager.NameNotFoundException e) {
506             throw new RuntimeException(e);
507         }
508         when(mMockPackageManager.queryIntentServices(any(), anyInt())).thenAnswer(args -> {
509             Intent intent = args.getArgument(0);
510             if (intent.getAction().equals(MediaBrowserService.SERVICE_INTERFACE)) {
511                 return Collections.singletonList(resolveInfo);
512             }
513             return new ArrayList<>();
514         });
515         when(mMockPackageManager.queryIntentActivities(any(), any())).thenAnswer(args -> {
516             Intent intent = args.getArgument(0);
517             PackageManager.ResolveInfoFlags flags = args.getArgument(1);
518             List<ResolveInfo> resolveInfoList = new ArrayList<>();
519             if (intent.getAction().equals(Intent.ACTION_MAIN)) {
520                 if ((flags.getValue() & MATCH_DISABLED_COMPONENTS) != 0) {
521                     resolveInfoList.add(constructActivityResolveInfo(TEST_TOS_DISABLED_APP_1));
522                     resolveInfoList.add(constructActivityResolveInfo(TEST_TOS_DISABLED_APP_2));
523                 }
524                 resolveInfoList.add(constructActivityResolveInfo(TEST_ENABLED_APP));
525             }
526             return resolveInfoList;
527         });
528     }
529 
mockPmGetApplicationEnabledSetting(int enabledState, String... packages)530     private void mockPmGetApplicationEnabledSetting(int enabledState, String... packages) {
531         for (String pkg : packages) {
532             when(mMockPackageManager.getApplicationEnabledSetting(pkg)).thenReturn(enabledState);
533         }
534     }
535 
mockSettingsStringCalls()536     private void mockSettingsStringCalls() {
537         when(mMockContext.createContextAsUser(any(UserHandle.class), anyInt()))
538                 .thenAnswer(args -> {
539                     Context context = mock(Context.class);
540                     ContentResolver contentResolver = mock(ContentResolver.class);
541                     when(context.getContentResolver()).thenReturn(contentResolver);
542                     return context;
543                 });
544 
545         doReturn(TEST_DISABLED_APP_1 + PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR
546                 + TEST_DISABLED_APP_2)
547                 .when(() -> Settings.Secure.getString(any(ContentResolver.class),
548                         eq(KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE)));
549 
550         doReturn(TEST_TOS_DISABLED_APP_1 + TOS_DISABLED_APPS_SEPARATOR
551                 + TEST_TOS_DISABLED_APP_2)
552                 .when(() -> Settings.Secure.getString(any(ContentResolver.class),
553                         eq(KEY_UNACCEPTED_TOS_DISABLED_APPS)));
554     }
555 
launchAllApps(List<AppMetaData> appMetaData)556     private void launchAllApps(List<AppMetaData> appMetaData) {
557         for (AppMetaData meta : appMetaData) {
558             Consumer<Context> launchCallback = meta.getLaunchCallback();
559             launchCallback.accept(mMockContext);
560         }
561     }
562 
constructActivityResolveInfo(String packageName)563     private static ResolveInfo constructActivityResolveInfo(String packageName) {
564         ResolveInfo info = new ResolveInfo();
565         info.activityInfo = new ActivityInfo();
566         info.activityInfo.packageName = packageName;
567         info.activityInfo.name = packageName + ".activity";
568         info.activityInfo.applicationInfo = new ApplicationInfo();
569         return info;
570     }
571 
constructServiceResolveInfo(String packageName)572     private static ResolveInfo constructServiceResolveInfo(String packageName) {
573         ResolveInfo info = new ResolveInfo();
574         info.serviceInfo = new ServiceInfo();
575         info.serviceInfo.packageName = packageName;
576         info.serviceInfo.name = packageName + ".service";
577         info.serviceInfo.applicationInfo = new ApplicationInfo();
578         return info;
579     }
580 }
581