Add secondary launcher mechanism (1/3)

Add a new config value for recording the component name of secondary
launcher.
This secondary launcher with corresponding launch mode set in
AndroidManifest could be used on secondary displays that support system
decorations.
OEMs can easily replace their own secondary launcher by overlay it.

Bug: 118206886
Bug: 111363427
Test: atest RootActivityContainerTests
Test: atest ActivityManagerMultiDisplayTests
Change-Id: Iceab096fd369127231f2085313ee617c7cdea226
diff --git a/api/current.txt b/api/current.txt
index 4db12ea..a22fa59 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10268,6 +10268,7 @@
     field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
     field public static final java.lang.String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE";
     field public static final java.lang.String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
+    field public static final java.lang.String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
     field public static final java.lang.String CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE";
     field public static final java.lang.String CATEGORY_TAB = "android.intent.category.TAB";
     field public static final java.lang.String CATEGORY_TEST = "android.intent.category.TEST";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2f0618c..d5c6c63 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4222,6 +4222,11 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN";
     /**
+     * The home activity shown on secondary displays that support showing home activities.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
+    /**
      * This is the setup wizard activity, that is the first activity that is displayed
      * when the user sets up the device for the first time.
      * @hide
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dd0b1ee..7e56718 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3646,4 +3646,11 @@
 
     <!-- Component name for the default module metadata provider on this device -->
     <string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string>
+
+    <!-- This is the default launcher component to use on secondary displays that support system
+         decorations.
+         This launcher activity must support multiple instances and have corresponding launch mode
+         set in AndroidManifest.
+         {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
+    <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 87fdc1f..907895b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3522,4 +3522,7 @@
   <java-symbol type="dimen" name="rounded_corner_radius_bottom" />
 
   <java-symbol type="string" name="config_defaultModuleMetadataProvider" />
+
+  <!-- For Secondary Launcher -->
+  <java-symbol type="string" name="config_secondaryHomeComponent" />
 </resources>
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5f00bcc..4d8440a8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -42,6 +42,7 @@
 import static android.content.Intent.ACTION_MAIN;
 import static android.content.Intent.CATEGORY_HOME;
 import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.CATEGORY_SECONDARY_HOME;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
@@ -1178,7 +1179,8 @@
 
     private boolean isHomeIntent(Intent intent) {
         return ACTION_MAIN.equals(intent.getAction())
-                && intent.hasCategory(CATEGORY_HOME)
+                && (intent.hasCategory(CATEGORY_HOME)
+                || intent.hasCategory(CATEGORY_SECONDARY_HOME))
                 && intent.getCategories().size() == 1
                 && intent.getData() == null
                 && intent.getType() == null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9861157..f662d0c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5475,6 +5475,31 @@
         return intent;
     }
 
+    /**
+     * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home
+     * activities.
+     *
+     * @param preferredPackage Specify a preferred package name, otherwise use secondary home
+     *                        component defined in config_secondaryHomeComponent.
+     * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME}
+     */
+    Intent getSecondaryHomeIntent(String preferredPackage) {
+        final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
+        if (preferredPackage == null) {
+            // Using the component stored in config if no package name.
+            final String secondaryHomeComponent = mContext.getResources().getString(
+                    com.android.internal.R.string.config_secondaryHomeComponent);
+            intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
+        } else {
+            intent.setPackage(preferredPackage);
+        }
+        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
+        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+            intent.addCategory(Intent.CATEGORY_SECONDARY_HOME);
+        }
+        return intent;
+    }
+
     ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
         if (info == null) return null;
         ApplicationInfo newInfo = new ApplicationInfo(info);
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d0144fd..8ec97c5 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -90,17 +90,18 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.power.V1_0.PowerHint;
-import android.os.Build;
 import android.os.FactoryTest;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.IntArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -110,6 +111,7 @@
 import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ResolverActivity;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.AppTimeTracker;
@@ -346,35 +348,53 @@
     }
 
     /**
-     * This starts home activity on displays that can have system decorations and only if the
-     * home activity can have multiple instances.
+     * This starts home activity on displays that can have system decorations based on displayId -
+     * Default display always use primary home component.
+     * For Secondary displays, the home activity must have category SECONDARY_HOME and then resolves
+     * according to the priorities listed below.
+     *  - If default home is not set, always use the secondary home defined in the config.
+     *  - Use currently selected primary home activity.
+     *  - Use the activity in the same package as currently selected primary home activity.
+     *    If there are multiple activities matched, use first one.
+     *  - Use the secondary home defined in the config.
      */
     boolean startHomeOnDisplay(int userId, String reason, int displayId) {
-        final Intent homeIntent = mService.getHomeIntent();
-        final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+        Intent homeIntent;
+        ActivityInfo aInfo;
+        if (displayId == DEFAULT_DISPLAY) {
+            homeIntent = mService.getHomeIntent();
+            aInfo = resolveHomeActivity(userId, homeIntent);
+        } else {
+            Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId);
+            aInfo = info.first;
+            homeIntent = info.second;
+        }
         if (aInfo == null) {
             return false;
         }
 
-        if (!canStartHomeOnDisplay(aInfo, displayId,
-                false /* allowInstrumenting */)) {
+        if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
             return false;
         }
 
+        // Updates the home component of the intent.
+        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
         // Update the reason for ANR debugging to verify if the user activity is the one that
         // actually launched.
         final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
-                aInfo.applicationInfo.uid);
+                aInfo.applicationInfo.uid) + ":" + displayId;
         mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                 displayId);
         return true;
     }
 
     /**
-     * This resolves the home activity info and updates the home component of the given intent.
+     * This resolves the home activity info.
      * @return the home activity info if any.
      */
-    private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+    @VisibleForTesting
+    ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
         final int flags = ActivityManagerService.STOCK_PM_FLAGS;
         final ComponentName comp = homeIntent.getComponent();
         ActivityInfo aInfo = null;
@@ -400,13 +420,82 @@
             return null;
         }
 
-        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
         aInfo = new ActivityInfo(aInfo);
         aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
-        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
         return aInfo;
     }
 
+    @VisibleForTesting
+    Pair<ActivityInfo, Intent> resolveSecondaryHomeActivity(int userId, int displayId) {
+        if (displayId == DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException(
+                    "resolveSecondaryHomeActivity: Should not be DEFAULT_DISPLAY");
+        }
+        // Resolve activities in the same package as currently selected primary home activity.
+        Intent homeIntent = mService.getHomeIntent();
+        ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+        if (aInfo != null) {
+            if (ResolverActivity.class.getName().equals(aInfo.name)) {
+                // Always fallback to secondary home component if default home is not set.
+                aInfo = null;
+            } else {
+                // Look for secondary home activities in the currently selected default home
+                // package.
+                homeIntent = mService.getSecondaryHomeIntent(aInfo.applicationInfo.packageName);
+                final List<ResolveInfo> resolutions = resolveActivities(userId, homeIntent);
+                final int size = resolutions.size();
+                final String targetName = aInfo.name;
+                aInfo = null;
+                for (int i = 0; i < size; i++) {
+                    ResolveInfo resolveInfo = resolutions.get(i);
+                    // We need to traverse all resolutions to check if the currently selected
+                    // default home activity is present.
+                    if (resolveInfo.activityInfo.name.equals(targetName)) {
+                        aInfo = resolveInfo.activityInfo;
+                        break;
+                    }
+                }
+                if (aInfo == null && size > 0) {
+                    // First one is the best.
+                    aInfo = resolutions.get(0).activityInfo;
+                }
+            }
+        }
+
+        if (aInfo != null) {
+            if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
+                aInfo = null;
+            }
+        }
+
+        // Fallback to secondary home component.
+        if (aInfo == null) {
+            homeIntent = mService.getSecondaryHomeIntent(null);
+            aInfo = resolveHomeActivity(userId, homeIntent);
+        }
+        return Pair.create(aInfo, homeIntent);
+    }
+
+    /**
+     * Retrieve all activities that match the given intent.
+     * The list should already ordered from best to worst matched.
+     * {@link android.content.pm.PackageManager#queryIntentActivities}
+     */
+    @VisibleForTesting
+    List<ResolveInfo> resolveActivities(int userId, Intent homeIntent) {
+        List<ResolveInfo> resolutions;
+        try {
+            final String resolvedType =
+                    homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+            resolutions = AppGlobals.getPackageManager().queryIntentActivities(homeIntent,
+                    resolvedType, ActivityManagerService.STOCK_PM_FLAGS, userId).getList();
+
+        } catch (RemoteException e) {
+            resolutions = new ArrayList<>();
+        }
+        return resolutions;
+    }
+
     boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
         if (!mService.isBooting() && !mService.isBooted()) {
             // Not ready yet!
@@ -457,6 +546,14 @@
             return true;
         }
 
+        final boolean deviceProvisioned = Settings.Global.getInt(
+                mService.mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+        if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY && !deviceProvisioned) {
+            // Can't launch home on secondary display before device is provisioned.
+            return false;
+        }
+
         final ActivityDisplay display = getActivityDisplay(displayId);
         if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
             // Can't launch home on display that doesn't support system decorations.
@@ -464,13 +561,9 @@
         }
 
         final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
-                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
-                && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE;
         if (!supportMultipleInstance) {
-            // Can't launch home on other displays if it requested to be single instance. Also we
-            // don't allow home applications that target before Q to have multiple home activity
-            // instances because they may not be expected to have multiple home scenario and
-            // haven't explicitly request for single instance.
+            // Can't launch home on secondary displays if it requested to be single instance.
             return false;
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 9b18388..58302d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -23,8 +23,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -36,7 +36,7 @@
 import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
-import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -47,18 +47,27 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
 
 import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
-import android.os.Build;
 import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
 import androidx.test.filters.MediumTest;
+
+import com.android.internal.app.ResolverActivity;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Tests for the {@link ActivityStackSupervisor} class.
@@ -385,31 +394,10 @@
     }
 
     /**
-     * Tests home activities that targeted sdk before Q cannot start on secondary display.
-     */
-    @Test
-    public void testStartHomeTargetSdkBeforeQ() throws Exception {
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
-        mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
-        doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
-        final ActivityInfo info = new ActivityInfo();
-        info.launchMode = LAUNCH_MULTIPLE;
-        info.applicationInfo = new ApplicationInfo();
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
-        assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-    }
-
-    /**
      * Tests that home activities can be started on the displays that supports system decorations.
      */
-    @Test
-    public void testStartHomeOnAllDisplays() {
+    // TODO (b/118206886): Will add it back once launcher's patch is merged into master.
+    private void testStartHomeOnAllDisplays() {
         // Create secondary displays.
         final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
         mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
@@ -477,4 +465,142 @@
         assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
                 true /* allowInstrumenting*/));
     }
+
+    /**
+     * Tests that secondary home should be selected if default home not set.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
+        final Intent defaultHomeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = ResolverActivity.class.getName();
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(defaultHomeIntent));
+
+        final String secondaryHomeComponent = mService.mContext.getResources().getString(
+                com.android.internal.R.string.config_secondaryHomeComponent);
+        final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+        final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+        final ActivityInfo aInfoSecondary = new ActivityInfo();
+        aInfoSecondary.name = comp.getClassName();
+        doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(secondaryHomeIntent));
+
+        // Should fallback to secondary home if default home not set.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(comp.getClassName(), resolvedInfo.first.name);
+    }
+
+    /**
+     * Tests that secondary home should be selected if default home not support secondary displays
+     * or there is no matched activity in the same package as selected default home.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
+        final Intent defaultHomeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = "fakeHomeActivity";
+        aInfoDefault.applicationInfo = new ApplicationInfo();
+        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(defaultHomeIntent));
+
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+        final String secondaryHomeComponent = mService.mContext.getResources().getString(
+                com.android.internal.R.string.config_secondaryHomeComponent);
+        final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+        final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+        final ActivityInfo aInfoSecondary = new ActivityInfo();
+        aInfoSecondary.name = comp.getClassName();
+        doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(secondaryHomeIntent));
+
+        // Should fallback to secondary home if selected default home not support secondary displays
+        // or there is no matched activity in the same package as selected default home.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(comp.getClassName(), resolvedInfo.first.name);
+    }
+
+    /**
+     * Tests that default home activity should be selected if it already support secondary displays.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = "fakeHomeActivity";
+        aInfoDefault.applicationInfo = new ApplicationInfo();
+        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(homeIntent));
+
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        final ResolveInfo infoFake1 = new ResolveInfo();
+        infoFake1.activityInfo = new ActivityInfo();
+        infoFake1.activityInfo.name = "fakeActivity1";
+        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+        final ResolveInfo infoFake2 = new ResolveInfo();
+        infoFake2.activityInfo = aInfoDefault;
+        resolutions.add(infoFake1);
+        resolutions.add(infoFake2);
+        doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+        doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+                any(), anyInt(), anyBoolean());
+
+        // Use default home activity if it support secondary displays.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(aInfoDefault.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+        assertEquals(aInfoDefault.name, resolvedInfo.first.name);
+    }
+
+    /**
+     * Tests that the first one that matches should be selected if there are multiple activities.
+     */
+    @Test
+    public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfoDefault = new ActivityInfo();
+        aInfoDefault.name = "fakeHomeActivity";
+        aInfoDefault.applicationInfo = new ApplicationInfo();
+        aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+        doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+                refEq(homeIntent));
+
+        final List<ResolveInfo> resolutions = new ArrayList<>();
+        final ResolveInfo infoFake1 = new ResolveInfo();
+        infoFake1.activityInfo = new ActivityInfo();
+        infoFake1.activityInfo.name = "fakeActivity1";
+        infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+        final ResolveInfo infoFake2 = new ResolveInfo();
+        infoFake2.activityInfo = new ActivityInfo();
+        infoFake2.activityInfo.name = "fakeActivity2";
+        infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
+        infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
+        resolutions.add(infoFake1);
+        resolutions.add(infoFake2);
+        doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+        doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+                any(), anyInt(), anyBoolean());
+
+        // Use the first one of matched activities in the same package as selected default home.
+        final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+                .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+        assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
+                resolvedInfo.first.applicationInfo.packageName);
+        assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
+    }
 }