• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
21 import static java.lang.annotation.RetentionPolicy.SOURCE;
22 
23 import android.app.Activity;
24 import android.app.ActivityManager;
25 import android.app.ActivityOptions;
26 import android.app.admin.DevicePolicyManager;
27 import android.car.Car;
28 import android.car.CarNotConnectedException;
29 import android.car.content.pm.CarPackageManager;
30 import android.car.media.CarMediaManager;
31 import android.content.ComponentName;
32 import android.content.ContentResolver;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.pm.ApplicationInfo;
36 import android.content.pm.LauncherActivityInfo;
37 import android.content.pm.LauncherApps;
38 import android.content.pm.PackageManager;
39 import android.content.pm.ResolveInfo;
40 import android.net.Uri;
41 import android.os.Bundle;
42 import android.os.Process;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.provider.Settings;
46 import android.service.media.MediaBrowserService;
47 import android.text.TextUtils;
48 import android.util.ArraySet;
49 import android.util.Log;
50 import android.util.Pair;
51 import android.view.View;
52 
53 import androidx.annotation.IntDef;
54 import androidx.annotation.NonNull;
55 import androidx.annotation.Nullable;
56 
57 import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup;
58 
59 import com.google.common.collect.Sets;
60 
61 import java.lang.annotation.Retention;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collections;
65 import java.util.Comparator;
66 import java.util.HashMap;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Objects;
70 import java.util.Set;
71 import java.util.function.Consumer;
72 
73 /**
74  * Util class that contains helper method used by app launcher classes.
75  */
76 public class AppLauncherUtils {
77     private static final String TAG = "AppLauncherUtils";
78     private static final String ANDROIDX_CAR_APP_LAUNCHABLE = "androidx.car.app.launchable";
79 
80     @Retention(SOURCE)
81     @IntDef({APP_TYPE_LAUNCHABLES, APP_TYPE_MEDIA_SERVICES})
82     @interface AppTypes {}
83 
84     static final int APP_TYPE_LAUNCHABLES = 1;
85     static final int APP_TYPE_MEDIA_SERVICES = 2;
86 
87     static final String PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR = ";";
88 
89     // Max no. of uses tags in automotiveApp XML. This is an arbitrary limit to be defensive
90     // to bad input.
91     private static final int MAX_APP_TYPES = 64;
92     private static final String PACKAGE_URI_PREFIX = "package:";
93 
AppLauncherUtils()94     private AppLauncherUtils() {
95     }
96 
97     /**
98      * Comparator for {@link AppMetaData} that sorts the list
99      * by the "displayName" property in ascending order.
100      */
101     static final Comparator<AppMetaData> ALPHABETICAL_COMPARATOR = Comparator
102             .comparing(AppMetaData::getDisplayName, String::compareToIgnoreCase);
103 
104     /**
105      * Helper method that launches the app given the app's AppMetaData.
106      *
107      * @param app the requesting app's AppMetaData
108      */
launchApp(Context context, Intent intent)109     static void launchApp(Context context, Intent intent) {
110         ActivityOptions options = ActivityOptions.makeBasic();
111         options.setLaunchDisplayId(context.getDisplayId());
112         context.startActivity(intent, options.toBundle());
113     }
114 
115     /** Bundles application and services info. */
116     static class LauncherAppsInfo {
117         /*
118          * Map of all car launcher components' (including launcher activities and media services)
119          * metadata keyed by ComponentName.
120          */
121         private final Map<ComponentName, AppMetaData> mLaunchables;
122 
123         /** Map of all the media services keyed by ComponentName. */
124         private final Map<ComponentName, ResolveInfo> mMediaServices;
125 
LauncherAppsInfo(@onNull Map<ComponentName, AppMetaData> launchablesMap, @NonNull Map<ComponentName, ResolveInfo> mediaServices)126         LauncherAppsInfo(@NonNull Map<ComponentName, AppMetaData> launchablesMap,
127                 @NonNull Map<ComponentName, ResolveInfo> mediaServices) {
128             mLaunchables = launchablesMap;
129             mMediaServices = mediaServices;
130         }
131 
132         /** Returns true if all maps are empty. */
isEmpty()133         boolean isEmpty() {
134             return mLaunchables.isEmpty() && mMediaServices.isEmpty();
135         }
136 
137         /**
138          * Returns whether the given componentName is a media service.
139          */
isMediaService(ComponentName componentName)140         boolean isMediaService(ComponentName componentName) {
141             return mMediaServices.containsKey(componentName);
142         }
143 
144         /** Returns the {@link AppMetaData} for the given componentName. */
145         @Nullable
getAppMetaData(ComponentName componentName)146         AppMetaData getAppMetaData(ComponentName componentName) {
147             return mLaunchables.get(componentName);
148         }
149 
150         /** Returns a new list of all launchable components' {@link AppMetaData}. */
151         @NonNull
getLaunchableComponentsList()152         List<AppMetaData> getLaunchableComponentsList() {
153             return new ArrayList<>(mLaunchables.values());
154         }
155 
156         /** Returns list of Media Services for the launcher **/
157         @NonNull
getMediaServices()158         Map<ComponentName, ResolveInfo> getMediaServices() {
159             return mMediaServices;
160         }
161     }
162 
163     private final static LauncherAppsInfo EMPTY_APPS_INFO = new LauncherAppsInfo(
164             Collections.emptyMap(), Collections.emptyMap());
165 
166     /*
167      * Gets the media source in a given package. If there are multiple sources in the package,
168      * returns the first one.
169      */
getMediaSource(@onNull PackageManager packageManager, @NonNull String packageName)170     static ComponentName getMediaSource(@NonNull PackageManager packageManager,
171             @NonNull String packageName) {
172         Intent mediaIntent = new Intent();
173         mediaIntent.setPackage(packageName);
174         mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
175 
176         List<ResolveInfo> mediaServices = packageManager.queryIntentServices(mediaIntent,
177                 PackageManager.GET_RESOLVED_FILTER);
178 
179         if (mediaServices == null || mediaServices.isEmpty()) {
180             return null;
181         }
182         String defaultService = mediaServices.get(0).serviceInfo.name;
183         if (!TextUtils.isEmpty(defaultService)) {
184             return new ComponentName(packageName, defaultService);
185         }
186         return null;
187     }
188 
189     /**
190      * Gets all the components that we want to see in the launcher in unsorted order, including
191      * launcher activities and media services.
192      *
193      * @param appsToHide            A (possibly empty) list of apps (package names) to hide
194      * @param appTypes              Types of apps to show (e.g.: all, or media sources only)
195      * @param openMediaCenter       Whether launcher should navigate to media center when the
196      *                              user selects a media source.
197      * @param launcherApps          The {@link LauncherApps} system service
198      * @param carPackageManager     The {@link CarPackageManager} system service
199      * @param packageManager        The {@link PackageManager} system service
200      *                              of such apps are always excluded.
201      * @param carMediaManager       The {@link CarMediaManager} system service
202      * @return a new {@link LauncherAppsInfo}
203      */
204     @NonNull
getLauncherApps( Context context, @NonNull Set<String> appsToHide, @AppTypes int appTypes, boolean openMediaCenter, LauncherApps launcherApps, CarPackageManager carPackageManager, PackageManager packageManager, CarMediaManager carMediaManager, ShortcutsListener shortcutsListener, String mirroringAppPkgName, Intent mirroringAppRedirect)205     static LauncherAppsInfo getLauncherApps(
206             Context context,
207             @NonNull Set<String> appsToHide,
208             @AppTypes int appTypes,
209             boolean openMediaCenter,
210             LauncherApps launcherApps,
211             CarPackageManager carPackageManager,
212             PackageManager packageManager,
213             CarMediaManager carMediaManager,
214             ShortcutsListener shortcutsListener,
215             String mirroringAppPkgName,
216             Intent mirroringAppRedirect) {
217 
218         if (launcherApps == null || carPackageManager == null || packageManager == null
219                 || carMediaManager == null) {
220             return EMPTY_APPS_INFO;
221         }
222 
223         // Using new list since we require a mutable list to do removeIf.
224         List<ResolveInfo> mediaServices = new ArrayList<>();
225         mediaServices.addAll(
226                 packageManager.queryIntentServices(
227                         new Intent(MediaBrowserService.SERVICE_INTERFACE),
228                         PackageManager.GET_RESOLVED_FILTER));
229 
230         List<LauncherActivityInfo> availableActivities =
231                 launcherApps.getActivityList(null, Process.myUserHandle());
232 
233         int launchablesSize = mediaServices.size() + availableActivities.size();
234         Map<ComponentName, AppMetaData> launchablesMap = new HashMap<>(launchablesSize);
235         Map<ComponentName, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
236         Set<String> mEnabledPackages = new ArraySet<>(launchablesSize);
237 
238         Set<String> customMediaComponents = Sets.newHashSet(
239                 context.getResources().getStringArray(
240                         com.android.car.media.common.R.array.custom_media_packages));
241 
242         // Process media services
243         if ((appTypes & APP_TYPE_MEDIA_SERVICES) != 0) {
244             for (ResolveInfo info : mediaServices) {
245                 String packageName = info.serviceInfo.packageName;
246                 String className = info.serviceInfo.name;
247                 ComponentName componentName = new ComponentName(packageName, className);
248                 mediaServicesMap.put(componentName, info);
249                 mEnabledPackages.add(packageName);
250                 if (shouldAddToLaunchables(packageManager, componentName, appsToHide,
251                         customMediaComponents, appTypes, APP_TYPE_MEDIA_SERVICES)) {
252                     final boolean isDistractionOptimized = true;
253 
254                     Intent intent = new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
255                     intent.putExtra(Car.CAR_EXTRA_MEDIA_COMPONENT, componentName.flattenToString());
256 
257                     CharSequence displayName = info.serviceInfo.loadLabel(packageManager);
258                     AppMetaData appMetaData = new AppMetaData(
259                             displayName,
260                             componentName,
261                             info.serviceInfo.loadIcon(packageManager),
262                             isDistractionOptimized,
263                             /* isMirroring = */ false,
264                             contextArg -> {
265                                 if (openMediaCenter) {
266                                     AppLauncherUtils.launchApp(contextArg, intent);
267                                 } else {
268                                     selectMediaSourceAndFinish(contextArg, componentName,
269                                             carMediaManager);
270                                 }
271                             },
272                             buildShortcuts(packageName, displayName, shortcutsListener));
273                     launchablesMap.put(componentName, appMetaData);
274                 }
275             }
276         }
277 
278         // Process activities
279         if ((appTypes & APP_TYPE_LAUNCHABLES) != 0) {
280             for (LauncherActivityInfo info : availableActivities) {
281                 ComponentName componentName = info.getComponentName();
282                 String packageName = componentName.getPackageName();
283                 mEnabledPackages.add(packageName);
284                 if (shouldAddToLaunchables(packageManager, componentName, appsToHide,
285                         customMediaComponents, appTypes, APP_TYPE_LAUNCHABLES)) {
286                     boolean isDistractionOptimized =
287                             isActivityDistractionOptimized(carPackageManager, packageName,
288                                     info.getName());
289 
290                     Intent intent = new Intent(Intent.ACTION_MAIN)
291                             .setComponent(componentName)
292                             .addCategory(Intent.CATEGORY_LAUNCHER)
293                             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
294 
295                     CharSequence displayName = info.getLabel();
296                     boolean isMirroring = packageName.equals(mirroringAppPkgName);
297                     AppMetaData appMetaData = new AppMetaData(
298                             displayName,
299                             componentName,
300                             info.getBadgedIcon(0),
301                             isDistractionOptimized,
302                             isMirroring,
303                             contextArg -> {
304                                 if (packageName.equals(mirroringAppPkgName)) {
305                                     Log.d(TAG, "non-media service package name "
306                                             + "equals mirroring pkg name");
307                                 }
308                                 AppLauncherUtils.launchApp(contextArg,
309                                         isMirroring ? mirroringAppRedirect : intent);
310                             },
311                             buildShortcuts(packageName, displayName, shortcutsListener));
312                     launchablesMap.put(componentName, appMetaData);
313                 }
314             }
315 
316             List<ResolveInfo> disabledActivities = getDisabledActivities(context, packageManager,
317                     mEnabledPackages);
318             for (ResolveInfo info : disabledActivities) {
319                 String packageName = info.activityInfo.packageName;
320                 String className = info.activityInfo.name;
321                 ComponentName componentName = new ComponentName(packageName, className);
322                 if (!shouldAddToLaunchables(packageManager, componentName, appsToHide,
323                         customMediaComponents, appTypes, APP_TYPE_LAUNCHABLES)) {
324                     continue;
325                 }
326                 boolean isDistractionOptimized =
327                         isActivityDistractionOptimized(carPackageManager, packageName, className);
328 
329                 Intent intent = new Intent(Intent.ACTION_MAIN)
330                         .setComponent(componentName)
331                         .addCategory(Intent.CATEGORY_LAUNCHER)
332                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
333 
334                 CharSequence displayName = info.activityInfo.loadLabel(packageManager);
335                 AppMetaData appMetaData = new AppMetaData(
336                         displayName,
337                         componentName,
338                         info.activityInfo.loadIcon(packageManager),
339                         isDistractionOptimized,
340                         /* isMirroring = */ false,
341                         contextArg -> {
342                             packageManager.setApplicationEnabledSetting(packageName,
343                                     PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
344                             /* Fetch the current enabled setting to make sure the setting is synced
345                              * before launching the activity. Otherwise, the activity may not
346                              * launch.
347                              */
348                             if (packageManager.getApplicationEnabledSetting(packageName)
349                                     != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
350                                 throw new IllegalStateException(
351                                         "Failed to enable the disabled package [" + packageName
352                                                 + "]");
353                             }
354                             Log.i(TAG, "Successfully enabled package [" + packageName + "]");
355                             AppLauncherUtils.launchApp(contextArg, intent);
356                         },
357                         buildShortcuts(packageName, displayName, shortcutsListener));
358                 launchablesMap.put(componentName, appMetaData);
359             }
360         }
361 
362         return new LauncherAppsInfo(launchablesMap, mediaServicesMap);
363     }
364 
365     /**
366      * Determine if the given media browse service is supported through media templates
367      */
isMediaTemplate(PackageManager pm, ComponentName mbsComponentName)368     public static boolean isMediaTemplate(PackageManager pm, ComponentName mbsComponentName) {
369         Bundle metaData = null;
370         try {
371             metaData = pm.getServiceInfo(
372                     mbsComponentName,
373                     PackageManager.GET_META_DATA)
374                     .metaData;
375         } catch (PackageManager.NameNotFoundException e) {
376             Log.e(TAG, "Service for Component " + mbsComponentName + " was not found");
377             return false;
378         }
379 
380         if (metaData != null && metaData.containsKey(ANDROIDX_CAR_APP_LAUNCHABLE)) {
381             boolean launchable = metaData.getBoolean(ANDROIDX_CAR_APP_LAUNCHABLE);
382             if (Log.isLoggable(TAG, Log.DEBUG)) {
383                 Log.d(TAG, "MBS for " + mbsComponentName
384                         + " is opted " + (launchable ? "in" : "out"));
385             }
386             return launchable;
387         }
388 
389         // No explicit declaration. For backward compatibility, keep MBS only for media apps
390         String packageName = mbsComponentName.getPackageName();
391         try {
392             if (isLegacyMediaApp(pm, packageName)) {
393                 Log.d(TAG, "Including " + mbsComponentName + "  for media app " + packageName);
394                 return true;
395             }
396         } catch (PackageManager.NameNotFoundException e) {
397             Log.e(TAG, "Package " + packageName + " was not found");
398         }
399         Log.d(TAG, "Skipping MBS for " + mbsComponentName
400                 + " belonging to non media app " + packageName);
401         return false;
402     }
403 
404     /** Determine if it's a legacy media app that doesn't have a launcher activity*/
isLegacyMediaApp( PackageManager pm, String packageName)405     private static boolean isLegacyMediaApp(
406             PackageManager pm, String packageName) throws PackageManager.NameNotFoundException {
407         // a media app doesn't have a launcher activity
408         return pm.getLaunchIntentForPackage(packageName) == null;
409     }
410 
buildShortcuts(String packageName, CharSequence displayName, ShortcutsListener shortcutsListener)411     private static Consumer<Pair<Context, View>> buildShortcuts(String packageName,
412             CharSequence displayName, ShortcutsListener shortcutsListener) {
413         return pair -> {
414             CarUiShortcutsPopup carUiShortcutsPopup = new CarUiShortcutsPopup.Builder()
415                     .addShortcut(
416                             buildForceStopShortcut(packageName, displayName, pair.first,
417                                     shortcutsListener)
418                     )
419                     .addShortcut(buildAppInfoShortcut(packageName, pair.first))
420                     .build(pair.first,
421                             pair.second
422                     );
423 
424             carUiShortcutsPopup.show();
425             shortcutsListener.onShortcutsShow(carUiShortcutsPopup);
426         };
427     }
428 
buildForceStopShortcut(String packageName, CharSequence displayName, Context context, ShortcutsListener shortcutsListener)429     private static CarUiShortcutsPopup.ShortcutItem buildForceStopShortcut(String packageName,
430             CharSequence displayName,
431             Context context,
432             ShortcutsListener shortcutsListener) {
433         return new CarUiShortcutsPopup.ShortcutItem() {
434             @Override
435             public CarUiShortcutsPopup.ItemData data() {
436                 return new CarUiShortcutsPopup.ItemData(
437                         R.drawable.ic_force_stop_caution_icon,
438                         context.getResources().getString(
439                                 R.string.app_launcher_stop_app_action));
440             }
441 
442             @Override
443             public boolean onClick() {
444                 shortcutsListener.onShortcutsItemClick(packageName, displayName,
445                         /* allowStopApp= */ true);
446                 return true;
447             }
448 
449             @Override
450             public boolean isEnabled() {
451                 return shouldAllowStopApp(packageName, context);
452             }
453         };
454     }
455 
456     private static CarUiShortcutsPopup.ShortcutItem buildAppInfoShortcut(String packageName,
457             Context context) {
458         return new CarUiShortcutsPopup.ShortcutItem() {
459             @Override
460             public CarUiShortcutsPopup.ItemData data() {
461                 return new CarUiShortcutsPopup.ItemData(
462                         R.drawable.ic_app_info,
463                         context.getResources().getString(
464                                 R.string.app_launcher_app_info_action));
465             }
466 
467             @Override
468             public boolean onClick() {
469                 Uri packageURI = Uri.parse(PACKAGE_URI_PREFIX + packageName);
470                 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
471                         packageURI);
472                 context.startActivity(intent);
473                 return true;
474             }
475 
476             @Override
477             public boolean isEnabled() {
478                 return true;
479             }
480         };
481     }
482 
483     /**
484      * Force stops an app
485      * <p>Note: Uses hidden apis<p/>
486      */
487     public static void forceStop(String packageName, Context context, CharSequence displayName,
488             CarMediaManager carMediaManager, Map<ComponentName, ResolveInfo> mediaServices,
489             ShortcutsListener listener) {
490         ActivityManager activityManager = context.getSystemService(ActivityManager.class);
491         if (activityManager != null) {
492             maybeReplaceMediaSource(carMediaManager, packageName, mediaServices,
493                     CarMediaManager.MEDIA_SOURCE_MODE_BROWSE);
494             maybeReplaceMediaSource(carMediaManager, packageName, mediaServices,
495                     CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK);
496             activityManager.forceStopPackage(packageName);
497             String message = context.getResources()
498                     .getString(R.string.app_launcher_stop_app_success_toast_text, displayName);
499             listener.onStopAppSuccess(message);
500         }
501     }
502 
503     private static boolean isCurrentMediaSource(CarMediaManager carMediaManager,
504             String packageName, @CarMediaManager.MediaSourceMode int mode) {
505         ComponentName componentName = carMediaManager.getMediaSource(mode);
506         if (componentName == null) {
507             //There is no current media source.
508             return false;
509         }
510         return Objects.equals(componentName.getPackageName(), packageName);
511     }
512 
513     /***
514      * Updates the MediaSource to second most recent if {@code  packageName} is current media source
515      * Sets to MediaSource to null if no previous MediaSource exists.
516      */
517     private static void maybeReplaceMediaSource(CarMediaManager carMediaManager, String packageName,
518             Map<ComponentName, ResolveInfo> allMediaServices,
519             @CarMediaManager.MediaSourceMode int mode) {
520         if (!isCurrentMediaSource(carMediaManager, packageName, mode)) {
521             return;
522         }
523         //find the most recent source from history not equal to force-stopping package.
524         List<ComponentName> mediaSources = carMediaManager.getLastMediaSources(mode);
525         ComponentName componentName = mediaSources.stream().filter(c-> (!c.getPackageName()
526                 .equals(packageName))).findFirst().orElse(null);
527         if (componentName == null) {
528             //no recent package found, find from all available media services.
529             componentName = allMediaServices.keySet().stream().filter(
530                     c -> (!c.getPackageName().equals(packageName))).findFirst().orElse(null);
531             if (componentName == null) {
532                 Log.e(TAG, "Stop-app, no alternative media service found");
533             }
534         }
535         carMediaManager.setMediaSource(componentName, mode);
536     }
537 
538     /**
539      * <p>Note: Uses hidden apis<p/>
540      * @return true if the user has restrictions to force stop an app with {@code appInfo}
541      */
542     private static boolean hasUserRestriction(ApplicationInfo appInfo, Context context) {
543         String restriction = UserManager.DISALLOW_APPS_CONTROL;
544         UserManager userManager = context.getSystemService(UserManager.class);
545         if (userManager == null) {
546             Log.e(TAG, " Disabled because , UserManager is null");
547             return true;
548         }
549         if (!userManager.hasUserRestriction(restriction)) {
550             return false;
551         }
552         UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
553         if (userManager.hasBaseUserRestriction(restriction, user)) {
554             Log.d(TAG, " Disabled because " + user + " has " + restriction
555                     + " restriction");
556             return true;
557         }
558         // Not disabled for this User
559         return false;
560     }
561 
562     /**
563      * <p>Note: uses hidden apis</p>
564      *
565      * @param packageName name of the package to stop the app
566      * @param context     app context
567      * @return true if an app should show the Stop app action
568      */
569     private static boolean shouldAllowStopApp(String packageName, Context context) {
570         DevicePolicyManager dm = context.getSystemService(DevicePolicyManager.class);
571         if (dm == null || dm.packageHasActiveAdmins(packageName)) {
572             return false;
573         }
574         try {
575             ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(packageName,
576                     PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA));
577             // Show only if the User has no restrictions to force stop this app
578             if (hasUserRestriction(appInfo, context)) {
579                 return false;
580             }
581             // Show only if the app is running
582             if ((appInfo.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
583                 return true;
584             }
585         } catch (PackageManager.NameNotFoundException e) {
586             Log.d(TAG, "shouldAllowStopApp() Package " + packageName + " was not found");
587         }
588         return false;
589     }
590 
591     private static List<ResolveInfo> getDisabledActivities(Context context,
592             PackageManager packageManager, Set<String> enabledPackages) {
593         ContentResolver contentResolverForUser = context.createContextAsUser(
594                         UserHandle.getUserHandleForUid(Process.myUid()), /* flags= */ 0)
595                 .getContentResolver();
596         String settingsValue = Settings.Secure.getString(contentResolverForUser,
597                 KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE);
598         Set<String> disabledPackages = TextUtils.isEmpty(settingsValue) ? new ArraySet<>()
599                 : new ArraySet<>(Arrays.asList(settingsValue.split(
600                         PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR)));
601         if (disabledPackages.isEmpty()) {
602             return Collections.emptyList();
603         }
604 
605         List<ResolveInfo> allActivities = packageManager.queryIntentActivities(
606                 new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
607                 PackageManager.ResolveInfoFlags.of(PackageManager.GET_RESOLVED_FILTER
608                         | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS));
609 
610         List<ResolveInfo> disabledActivities = new ArrayList<>();
611         for (int i = 0; i < allActivities.size(); ++i) {
612             ResolveInfo info = allActivities.get(i);
613             if (!enabledPackages.contains(info.activityInfo.packageName)
614                     && disabledPackages.contains(info.activityInfo.packageName)) {
615                 disabledActivities.add(info);
616             }
617         }
618         return disabledActivities;
619     }
620 
621     private static boolean shouldAddToLaunchables(PackageManager packageManager,
622             @NonNull ComponentName componentName,
623             @NonNull Set<String> appsToHide,
624             @NonNull Set<String> customMediaComponents,
625             @AppTypes int appTypesToShow,
626             @AppTypes int componentAppType) {
627         if (appsToHide.contains(componentName.getPackageName())) {
628             return false;
629         }
630         switch (componentAppType) {
631             // Process media services
632             case APP_TYPE_MEDIA_SERVICES:
633                 // For a media service in customMediaComponents, if its application's launcher
634                 // activity will be shown in the Launcher, don't show the service's icon in the
635                 // Launcher.
636                 if (customMediaComponents.contains(componentName.flattenToString())) {
637                     if ((appTypesToShow & APP_TYPE_LAUNCHABLES) != 0) {
638                         if (Log.isLoggable(TAG, Log.DEBUG)) {
639                             Log.d(TAG, "MBS for custom media app " + componentName
640                                     + " is skipped in app launcher");
641                         }
642                         return false;
643                     }
644                     // Media switcher use case should still show
645                     if (Log.isLoggable(TAG, Log.DEBUG)) {
646                         Log.d(TAG, "MBS for custom media app " + componentName
647                                 + " is included in media switcher");
648                     }
649                     return true;
650                 }
651                 // Only Keep MBS that is a media template
652                 return isMediaTemplate(packageManager, componentName);
653             // Process activities
654             case APP_TYPE_LAUNCHABLES:
655                 return true;
656             default:
657                 Log.e(TAG, "Invalid componentAppType : " + componentAppType);
658                 return false;
659         }
660     }
661 
662     private static void selectMediaSourceAndFinish(Context context, ComponentName componentName,
663             CarMediaManager carMediaManager) {
664         try {
665             carMediaManager.setMediaSource(componentName, CarMediaManager.MEDIA_SOURCE_MODE_BROWSE);
666             if (context instanceof Activity) {
667                 ((Activity) context).finish();
668             }
669         } catch (CarNotConnectedException e) {
670             Log.e(TAG, "Car not connected", e);
671         }
672     }
673 
674     /**
675      * Gets if an activity is distraction optimized.
676      *
677      * @param carPackageManager The {@link CarPackageManager} system service
678      * @param packageName       The package name of the app
679      * @param activityName      The requested activity name
680      * @return true if the supplied activity is distraction optimized
681      */
682     static boolean isActivityDistractionOptimized(
683             CarPackageManager carPackageManager, String packageName, String activityName) {
684         boolean isDistractionOptimized = false;
685         // try getting distraction optimization info
686         try {
687             if (carPackageManager != null) {
688                 isDistractionOptimized =
689                         carPackageManager.isActivityDistractionOptimized(packageName, activityName);
690             }
691         } catch (CarNotConnectedException e) {
692             Log.e(TAG, "Car not connected when getting DO info", e);
693         }
694         return isDistractionOptimized;
695     }
696 
697     /**
698      * Callback when a ShortcutsPopup View is shown
699      */
700     protected interface ShortcutsListener {
701 
702         void onShortcutsShow(CarUiShortcutsPopup carUiShortcutsPopup);
703 
704         void onShortcutsItemClick(String packageName, CharSequence displayName,
705                 boolean allowStopApp);
706 
707         void onStopAppSuccess(String message);
708     }
709 }
710