Use component name instead of package name to get media service

Bug: 134776956
Test: manual
Change-Id: I92e95c3f7e63ab5f826353578cda7ca4abb7ab53
(cherry picked from commit 496bbf057bbb55c05e459dac3083f34b083d31ff)
diff --git a/src/com/android/car/carlauncher/AppGridActivity.java b/src/com/android/car/carlauncher/AppGridActivity.java
index a4a8bed..b0cb0d4 100644
--- a/src/com/android/car/carlauncher/AppGridActivity.java
+++ b/src/com/android/car/carlauncher/AppGridActivity.java
@@ -41,8 +41,8 @@
 
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
-
 import androidx.recyclerview.widget.RecyclerView;
+
 import com.android.car.carlauncher.AppLauncherUtils.LauncherAppsInfo;
 
 import java.util.ArrayList;
@@ -155,7 +155,7 @@
         Set<String> blackList = mShowAllApps ? Collections.emptySet() : mHiddenApps;
         LauncherAppsInfo appsInfo = AppLauncherUtils.getAllLauncherApps(blackList,
                 getSystemService(LauncherApps.class), mCarPackageManager, mPackageManager);
-        mGridAdapter.setAllApps(appsInfo.getApplicationsList());
+        mGridAdapter.setAllApps(appsInfo.getLaunchableComponentsList());
         mGridAdapter.setMostRecentApps(getMostRecentApps(appsInfo));
     }
 
@@ -238,8 +238,12 @@
                 continue;
             }
 
+            // TODO(b/136222320): UsageStats is obtained per package, but a package may contain
+            //  multiple media services. We need to find a way to get the usage stats per service.
+            ComponentName componentName = AppLauncherUtils.getMediaSource(mPackageManager,
+                    packageName);
             // Exempt media services from background and launcher checks
-            if (!appsInfo.isMediaService(packageName)) {
+            if (!appsInfo.isMediaService(componentName)) {
                 // do not include apps that only ran in the background
                 if (usageStats.getTotalTimeInForeground() == 0) {
                     continue;
@@ -252,7 +256,7 @@
                 }
             }
 
-            AppMetaData app = appsInfo.getAppMetaData(packageName);
+            AppMetaData app = appsInfo.getAppMetaData(componentName);
             // Prevent duplicated entries
             // e.g. app is used at 2017/12/31 23:59, and 2018/01/01 00:00
             if (app != null && !apps.contains(app)) {
diff --git a/src/com/android/car/carlauncher/AppLauncherUtils.java b/src/com/android/car/carlauncher/AppLauncherUtils.java
index dbeff4d..398bb3e 100644
--- a/src/com/android/car/carlauncher/AppLauncherUtils.java
+++ b/src/com/android/car/carlauncher/AppLauncherUtils.java
@@ -21,6 +21,7 @@
 import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.content.pm.CarPackageManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
@@ -29,6 +30,7 @@
 import android.content.pm.ResolveInfo;
 import android.os.Process;
 import android.service.media.MediaBrowserService;
+import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -37,6 +39,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -45,7 +48,6 @@
  * Util class that contains helper method used by app launcher classes.
  */
 class AppLauncherUtils {
-
     private static final String TAG = "AppLauncherUtils";
 
     private AppLauncherUtils() {
@@ -71,49 +73,77 @@
 
     /** Bundles application and services info. */
     static class LauncherAppsInfo {
-        /** Map of all apps' metadata keyed by package name. */
-        private final Map<String, AppMetaData> mApplications;
+        /*
+         * Map of all car launcher components' (including launcher activities and media services)
+          * metadata keyed by ComponentName.
+         */
+        private final Map<ComponentName, AppMetaData> mLaunchables;
 
-        /** Map of all the media services keyed by package name. */
-        private final Map<String, ResolveInfo> mMediaServices;
+        /** Map of all the media services keyed by ComponentName. */
+        private final Map<ComponentName, ResolveInfo> mMediaServices;
 
-        LauncherAppsInfo(@NonNull Map<String, AppMetaData> apps,
-                @NonNull Map<String, ResolveInfo> mediaServices) {
-            mApplications = apps;
+        LauncherAppsInfo(@NonNull Map<ComponentName, AppMetaData> components,
+                @NonNull Map<ComponentName, ResolveInfo> mediaServices) {
+            mLaunchables = components;
             mMediaServices = mediaServices;
         }
 
         /** Returns true if all maps are empty. */
         boolean isEmpty() {
-            return mApplications.isEmpty() && mMediaServices.isEmpty();
+            return mLaunchables.isEmpty() && mMediaServices.isEmpty();
         }
 
-        /** Returns whether the given package name is a media service. */
-        boolean isMediaService(String packageName) {
-            return mMediaServices.containsKey(packageName);
+        /**
+         * Returns whether the given componentName is a media service.
+         */
+        boolean isMediaService(ComponentName componentName) {
+            return mMediaServices.containsKey(componentName);
         }
 
-        /** Returns the {@link AppMetaData} for the given package name. */
+        /** Returns the {@link AppMetaData} for the given componentName. */
         @Nullable
-        AppMetaData getAppMetaData(String packageName) {
-            return mApplications.get(packageName);
+        AppMetaData getAppMetaData(ComponentName componentName) {
+            return mLaunchables.get(componentName);
         }
 
-        /** Returns a new list of the applications' {@link AppMetaData}. */
+        /** Returns a new list of all launchable components' {@link AppMetaData}. */
         @NonNull
-        List<AppMetaData> getApplicationsList() {
-            return new ArrayList<>(mApplications.values());
+        List<AppMetaData> getLaunchableComponentsList() {
+            return new ArrayList<>(mLaunchables.values());
         }
     }
 
     private final static LauncherAppsInfo EMPTY_APPS_INFO = new LauncherAppsInfo(
             Collections.emptyMap(), Collections.emptyMap());
 
+    /*
+     * Gets the media source in a given package. If there are multiple sources in the package,
+     * returns the first one.
+     */
+    static ComponentName getMediaSource(@NonNull PackageManager packageManager,
+            @NonNull String packageName) {
+        Intent mediaIntent = new Intent();
+        mediaIntent.setPackage(packageName);
+        mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
+
+        List<ResolveInfo> mediaServices = packageManager.queryIntentServices(mediaIntent,
+                PackageManager.GET_RESOLVED_FILTER);
+
+        if (mediaServices == null || mediaServices.isEmpty()) {
+            return null;
+        }
+        String defaultService = mediaServices.get(0).serviceInfo.name;
+        if (!TextUtils.isEmpty(defaultService)) {
+            return new ComponentName(packageName, defaultService);
+        }
+        return null;
+    }
+
     /**
-     * Gets all the apps that we want to see in the launcher in unsorted order. Includes media
-     * services without launcher activities.
+     * Gets all the components that we want to see in the launcher in unsorted order, including
+     * launcher activities and media services.
      *
-     * @param blackList         A (possibly empty) list of apps to hide
+     * @param blackList         A (possibly empty) list of apps (package names) to hide
      * @param launcherApps      The {@link LauncherApps} system service
      * @param carPackageManager The {@link CarPackageManager} system service
      * @param packageManager    The {@link PackageManager} system service
@@ -136,56 +166,68 @@
         List<LauncherActivityInfo> availableActivities =
                 launcherApps.getActivityList(null, Process.myUserHandle());
 
-        Map<String, AppMetaData> apps = new HashMap<>(
+        Map<ComponentName, AppMetaData> components = new HashMap<>(
                 mediaServices.size() + availableActivities.size());
-        Map<String, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
+        Map<ComponentName, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
+        Set<String> mediaPackages = new HashSet<>();
 
         // Process media services
         for (ResolveInfo info : mediaServices) {
             String packageName = info.serviceInfo.packageName;
-            mediaServicesMap.put(packageName, info);
-            if (shouldAdd(packageName, apps, blackList)) {
+            String className = info.serviceInfo.name;
+            ComponentName componentName = new ComponentName(packageName, className);
+            mediaServicesMap.put(componentName, info);
+            if (shouldAdd(componentName, components, blackList)) {
+                mediaPackages.add(packageName);
                 final boolean isDistractionOptimized = true;
 
                 Intent intent = new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
-                intent.putExtra(Car.CAR_EXTRA_MEDIA_PACKAGE, packageName);
+                intent.putExtra(Car.CAR_EXTRA_MEDIA_COMPONENT, componentName.flattenToString());
 
                 AppMetaData appMetaData = new AppMetaData(
                         info.serviceInfo.loadLabel(packageManager),
-                        packageName,
+                        componentName,
                         info.serviceInfo.loadIcon(packageManager),
                         isDistractionOptimized,
                         intent,
                         packageManager.getLaunchIntentForPackage(packageName));
-                apps.put(packageName, appMetaData);
+                components.put(componentName, appMetaData);
             }
         }
 
         // Process activities
         for (LauncherActivityInfo info : availableActivities) {
-            String packageName = info.getComponentName().getPackageName();
-            if (shouldAdd(packageName, apps, blackList)) {
+            ComponentName componentName = info.getComponentName();
+            String packageName = componentName.getPackageName();
+            // If a media service has been added to the map, don't add the activity belonging to the
+            // same package.
+            if (mediaPackages.contains(packageName)) {
+                continue;
+            }
+            if (shouldAdd(componentName, components, blackList)) {
                 boolean isDistractionOptimized =
                         isActivityDistractionOptimized(carPackageManager, packageName,
                                 info.getName());
 
                 AppMetaData appMetaData = new AppMetaData(
                         info.getLabel(),
-                        packageName,
+                        componentName,
                         info.getBadgedIcon(0),
                         isDistractionOptimized,
                         packageManager.getLaunchIntentForPackage(packageName),
                         null);
-                apps.put(packageName, appMetaData);
+                components.put(componentName, appMetaData);
             }
         }
 
-        return new LauncherAppsInfo(apps, mediaServicesMap);
+        return new LauncherAppsInfo(components, mediaServicesMap);
     }
 
-    private static boolean shouldAdd(String packageName, Map<String, AppMetaData> apps,
+    private static boolean shouldAdd(ComponentName componentName,
+            Map<ComponentName, AppMetaData> components,
             @NonNull Set<String> blackList) {
-        return !apps.containsKey(packageName) && !blackList.contains(packageName);
+        return !components.containsKey(componentName) && !blackList.contains(
+                componentName.getPackageName());
     }
 
     /**
diff --git a/src/com/android/car/carlauncher/AppMetaData.java b/src/com/android/car/carlauncher/AppMetaData.java
index affc472..14c6a9f 100644
--- a/src/com/android/car/carlauncher/AppMetaData.java
+++ b/src/com/android/car/carlauncher/AppMetaData.java
@@ -17,11 +17,12 @@
 package com.android.car.carlauncher;
 
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 
 /**
- * Meta data of an app including the display name, the full package name, the icon drawable, and an
+ * Meta data of an app including the display name, the component name, the icon drawable, and an
  * intent to either open the app or the media center (for media services).
  */
 
@@ -29,8 +30,8 @@
     // The display name of the app
     @Nullable
     private final String mDisplayName;
-    // The package name of the app
-    private final String mPackageName;
+    // The component name of the app
+    private final ComponentName mComponentName;
     private final Drawable mIcon;
     private final boolean mIsDistractionOptimized;
     private final Intent mMainLaunchIntent;
@@ -38,23 +39,25 @@
 
     /**
      * AppMetaData
-     * @param displayName the name to display in the launcher
-     * @param packageName the application's package
-     * @param icon the application's icon
+     *
+     * @param displayName            the name to display in the launcher
+     * @param componentName          the component name
+     * @param icon                   the application's icon
      * @param isDistractionOptimized whether mainLaunchIntent is safe for driving
-     * @param mainLaunchIntent what to open by default (goes to the media center for media apps)
-     * @param alternateLaunchIntent temporary allowance for media apps that still need to show UI
-     *                              beyond sign in and settings
+     * @param mainLaunchIntent       what to open by default (goes to the media center for media
+     *                               apps)
+     * @param alternateLaunchIntent  temporary allowance for media apps that still need to show UI
+     *                               beyond sign in and settings
      */
     AppMetaData(
             CharSequence displayName,
-            String packageName,
+            ComponentName componentName,
             Drawable icon,
             boolean isDistractionOptimized,
             Intent mainLaunchIntent,
             @Nullable Intent alternateLaunchIntent) {
         mDisplayName = displayName == null ? "" : displayName.toString();
-        mPackageName = packageName == null ? "" : packageName;
+        mComponentName = componentName;
         mIcon = icon;
         mIsDistractionOptimized = isDistractionOptimized;
         mMainLaunchIntent = mainLaunchIntent;
@@ -66,7 +69,11 @@
     }
 
     public String getPackageName() {
-        return mPackageName;
+        return getComponentName().getPackageName();
+    }
+
+    public ComponentName getComponentName() {
+        return mComponentName;
     }
 
     Intent getMainLaunchIntent() {
@@ -86,22 +93,22 @@
     }
 
     /**
-     * The equality of two AppMetaData is determined by whether the package names are the same.
+     * The equality of two AppMetaData is determined by whether the component names are the same.
      *
      * @param o Object that this AppMetaData object is compared against
-     * @return {@code true} when two AppMetaData have the same package name
+     * @return {@code true} when two AppMetaData have the same component name
      */
     @Override
     public boolean equals(Object o) {
         if (!(o instanceof AppMetaData)) {
             return false;
         } else {
-            return ((AppMetaData) o).getPackageName().equals(mPackageName);
+            return ((AppMetaData) o).getComponentName().equals(mComponentName);
         }
     }
 
     @Override
     public int hashCode() {
-        return mPackageName.hashCode();
+        return mComponentName.hashCode();
     }
 }
diff --git a/src/com/android/car/carlauncher/AppSearchActivity.java b/src/com/android/car/carlauncher/AppSearchActivity.java
index 59315db..2947906 100644
--- a/src/com/android/car/carlauncher/AppSearchActivity.java
+++ b/src/com/android/car/carlauncher/AppSearchActivity.java
@@ -170,7 +170,7 @@
         AppLauncherUtils.LauncherAppsInfo appsInfo = AppLauncherUtils.getAllLauncherApps(
                 Collections.emptySet(), getSystemService(LauncherApps.class), mCarPackageManager,
                 mPackageManager);
-        return appsInfo.getApplicationsList();
+        return appsInfo.getLaunchableComponentsList();
     }
 
     public void hideKeyboard() {