Merge "Don't launch ResolverActivity in home stack" into qt-dev
am: c82290fe51

Change-Id: Ibcd852cc5e40b7ec06f115ec4e41f6209cc3d108
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4278860..802683a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -916,8 +916,12 @@
         }
     }
 
+    static boolean isResolverActivity(String className) {
+        return ResolverActivity.class.getName().equals(className);
+    }
+
     boolean isResolverActivity() {
-        return ResolverActivity.class.getName().equals(mActivityComponent.getClassName());
+        return isResolverActivity(mActivityComponent.getClassName());
     }
 
     boolean isResolverOrChildActivity() {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 7eac07c..919141c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -171,7 +171,12 @@
     void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+        if (!ActivityRecord.isResolverActivity(aInfo.name)) {
+            // The resolver activity shouldn't be put in home stack because when the foreground is
+            // standard type activity, the resolver activity should be put on the top of current
+            // foreground instead of bring home stack to front.
+            options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+        }
         options.setLaunchDisplayId(displayId);
         mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                 .setOutActivity(tmpOutRecord)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 45d5219..a9807fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -79,7 +79,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
-import com.android.server.wm.TaskRecord.TaskRecordFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -330,21 +329,14 @@
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
-        // instrument the stack and task used.
-        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor)
-                .setCreateStack(false)
-                .build();
-
-        // use factory that only returns spy task.
-        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-
-        // return task when created.
-        doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
+        // Use factory that only returns spy task.
+        mockTaskRecordFactory();
 
         if (mockGetLaunchStack) {
+            // Instrument the stack and task used.
+            final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
             // Direct starter to use spy stack.
             doReturn(stack).when(mRootActivityContainer)
                     .getLaunchStack(any(), any(), any(), anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 2991dff..09b511a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -72,6 +72,7 @@
 import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.TaskRecord.TaskRecordFactory;
 import com.android.server.wm.utils.MockTracker;
 
 import org.junit.After;
@@ -160,6 +161,19 @@
     }
 
     /**
+     * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
+     * when starting activity in unit tests.
+     */
+    void mockTaskRecordFactory() {
+        final TaskRecord task = new TaskBuilder(mSupervisor).setCreateStack(false).build();
+        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
+        TaskRecord.setTaskRecordFactory(factory);
+        doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
+                any() /* info */, any() /* intent */, any() /* voiceSession */,
+                any() /* voiceInteractor */);
+    }
+
+    /**
      * Builder for creating new activities.
      */
     protected static class ActivityBuilder {
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 bac1ecd..b2084f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -25,7 +25,6 @@
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 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;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -71,10 +70,10 @@
 import java.util.List;
 
 /**
- * Tests for the {@link ActivityStackSupervisor} class.
+ * Tests for the {@link RootActivityContainer} class.
  *
  * Build/Install/Run:
- *  atest WmTests:ActivityStackSupervisorTests
+ *  atest WmTests:RootActivityContainerTests
  */
 @MediumTest
 @Presubmit
@@ -399,17 +398,15 @@
      */
     @Test
     public void testStartHomeOnAllDisplays() {
+        mockResolveHomeActivity();
+
         // Create secondary displays.
         final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
         mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
         doReturn(true).when(secondDisplay).supportsSystemDecorations();
 
         // Create mock tasks and other necessary mocks.
-        TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
-        final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-        doAnswer(i -> taskBuilder.build()).when(factory)
-                .create(any(), anyInt(), any(), any(), any(), any());
+        mockTaskRecordFactory();
         doReturn(true).when(mRootActivityContainer)
                 .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
         doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
@@ -510,6 +507,26 @@
     }
 
     /**
+     * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+     * activity type (in a new stack) so the order of back stack won't be broken.
+     */
+    @Test
+    public void testStartResolverActivityForHome() {
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.packageName = "android";
+        info.name = ResolverActivity.class.getName();
+        doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any());
+        mockTaskRecordFactory();
+
+        mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
+        final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
+
+        assertEquals(info, resolverActivity.info);
+        assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType());
+    }
+
+    /**
      * Tests that secondary home should be selected if default home not set.
      */
     @Test
@@ -542,13 +559,7 @@
      */
     @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));
+        mockResolveHomeActivity();
 
         final List<ResolveInfo> resolutions = new ArrayList<>();
         doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
@@ -575,13 +586,7 @@
      */
     @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 ActivityInfo aInfoDefault = mockResolveHomeActivity();
 
         final List<ResolveInfo> resolutions = new ArrayList<>();
         final ResolveInfo infoFake1 = new ResolveInfo();
@@ -612,13 +617,7 @@
      */
     @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));
+        mockResolveHomeActivity();
 
         final List<ResolveInfo> resolutions = new ArrayList<>();
         final ResolveInfo infoFake1 = new ResolveInfo();
@@ -646,4 +645,19 @@
                 resolvedInfo.first.applicationInfo.packageName);
         assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
     }
+
+    /**
+     * Mock {@link RootActivityContainerTests#resolveHomeActivity} for returning consistent activity
+     * info for test cases (the original implementation will resolve from the real package manager).
+     */
+    private ActivityInfo mockResolveHomeActivity() {
+        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));
+        return aInfoDefault;
+    }
 }