Simplified ActivityStack.shouldBeVisible()

The method can now be simplified since we have a stack per task so
if a stack should be visible is now a question of if it is occluded
by another stack.

Also,
- Fixed an issue where the windowing mode of the primary split-screen
stack was changing to split-screen-secondary instead of fullscreen
when we are exiting split-screen mode because we are not allowed to
to create fullscreen stack when there is a primary split-screen stack. We
now clear the reference to the primary split-screen stack when exiting
split-screen mode.
- Re-worked windowing mode resolution to be inside ActivityDisplay
object since the determination of the windowing mode is dependant on the
display.

Test: bit FrameworksServicesTests:com.android.server.am.ActivityStackTests
Test: Existing tests pass.
Test: go/wm-smoke
Bug: 64146578
Fixes: 67914671
Change-Id: I7e8cfe49fbf6a5836ded022bb11adcde58ae689c
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 679be1d..9e4a9e9 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -57,7 +57,7 @@
             ComponentName.unflattenFromString("com.foo/.BarActivity2");
 
     private ActivityManagerService mService;
-    private ActivityStack mStack;
+    private TestActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivity;
 
@@ -76,13 +76,13 @@
     @Test
     public void testStackCleanupOnClearingTask() throws Exception {
         mActivity.setTask(null);
-        assertEquals(getActivityRemovedFromStackCount(), 1);
+        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
     }
 
     @Test
     public void testStackCleanupOnActivityRemoval() throws Exception {
         mTask.removeActivity(mActivity);
-        assertEquals(getActivityRemovedFromStackCount(),  1);
+        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(),  1);
     }
 
     @Test
@@ -97,7 +97,7 @@
         final TaskRecord newTask =
                 createTask(mService.mStackSupervisor, testActivityComponent, mStack);
         mActivity.reparent(newTask, 0, null /*reason*/);
-        assertEquals(getActivityRemovedFromStackCount(), 0);
+        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
     }
 
     @Test
@@ -129,15 +129,6 @@
         assertEquals(expectedActivityBounds, mActivity.getBounds());
     }
 
-    private int getActivityRemovedFromStackCount() {
-        if (mStack instanceof ActivityStackReporter) {
-            return ((ActivityStackReporter) mStack).onActivityRemovedFromStackInvocationCount();
-        }
-
-        return -1;
-    }
-
-
     @Test
     public void testCanBeLaunchedOnDisplay() throws Exception {
         testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 4ee1f47..e17e51b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -16,13 +16,19 @@
 
 package com.android.server.am;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
@@ -45,7 +51,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackTests extends ActivityTestsBase {
-    private static final int TEST_STACK_ID = 100;
     private static final ComponentName testActivityComponent =
             ComponentName.unflattenFromString("com.foo/.BarActivity");
     private static final ComponentName testOverlayComponent =
@@ -127,4 +132,122 @@
         assertEquals(mTask.getTopActivity(true /* includeOverlays */), taskOverlay);
         assertNotNull(result.r);
     }
+
+    @Test
+    public void testShouldBeVisible_Fullscreen() throws Exception {
+        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+
+        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
+        // should be visible since it is always on-top.
+        fullscreenStack.setIsTranslucent(false);
+        assertFalse(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+        assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
+
+        // Home stack should be visible behind a translucent fullscreen stack.
+        fullscreenStack.setIsTranslucent(true);
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+    }
+
+    @Test
+    public void testShouldBeVisible_SplitScreen() throws Exception {
+        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Home stack shouldn't be visible if both halves of split-screen are opaque.
+        splitScreenPrimary.setIsTranslucent(false);
+        splitScreenSecondary.setIsTranslucent(false);
+        assertFalse(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+        // Home stack should be visible if one of the halves of split-screen is translucent.
+        splitScreenPrimary.setIsTranslucent(true);
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+        final TestActivityStack splitScreenSecondary2 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        // First split-screen secondary shouldn't be visible behind another opaque split-split
+        // secondary.
+        splitScreenSecondary2.setIsTranslucent(false);
+        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+        // First split-screen secondary should be visible behind another translucent split-split
+        // secondary.
+        splitScreenSecondary2.setIsTranslucent(true);
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+
+        // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
+        assistantStack.setIsTranslucent(false);
+        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+        // Split-screen stacks should be visible behind a translucent fullscreen stack.
+        assistantStack.setIsTranslucent(true);
+        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+    }
+
+    @Test
+    public void testShouldBeVisible_Finishing() throws Exception {
+        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        translucentStack.setIsTranslucent(true);
+
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+
+        final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        topRunningHomeActivity.finishing = true;
+        final ActivityRecord topRunningTranslucentActivity =
+                translucentStack.topRunningActivityLocked();
+        topRunningTranslucentActivity.finishing = true;
+
+        // Home shouldn't be visible since its activity is marked as finishing and it isn't the top
+        // of the stack list.
+        assertFalse(homeStack.shouldBeVisible(null /* starting */));
+        // Home should be visible if we are starting an activity within it.
+        assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
+        // The translucent stack should be visible since it is the top of the stack list even though
+        // it has its activity marked as finishing.
+        assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+    }
+
+    private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
+            ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
+        final T stack = display.createStack(windowingMode, activityType, onTop);
+        // Create a task and activity in the stack so that it has a top running activity.
+        final TaskRecord task = createTask(mSupervisor, testActivityComponent, stack);
+        final ActivityRecord r = createActivity(mService, testActivityComponent, task, 0);
+        return stack;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index d36f9d3..f5cdf21 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -276,20 +276,21 @@
         return service;
     }
 
-    protected interface ActivityStackReporter {
-        int onActivityRemovedFromStackInvocationCount();
-    }
-
     /**
      * Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
      * method is called. Note that its functionality depends on the implementations of the
      * construction arguments.
      */
     protected static class TestActivityStack<T extends StackWindowController>
-            extends ActivityStack<T> implements ActivityStackReporter {
+            extends ActivityStack<T> {
         private int mOnActivityRemovedFromStackCount = 0;
         private T mContainerController;
 
+        static final int IS_TRANSLUCENT_UNSET = 0;
+        static final int IS_TRANSLUCENT_FALSE = 1;
+        static final int IS_TRANSLUCENT_TRUE = 2;
+        private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
+
         TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
                 int windowingMode, int activityType, boolean onTop) {
             super(display, stackId, supervisor, windowingMode, activityType, onTop);
@@ -302,8 +303,7 @@
         }
 
         // Returns the number of times {@link #onActivityRemovedFromStack} has been called
-        @Override
-        public int onActivityRemovedFromStackInvocationCount() {
+        int onActivityRemovedFromStackInvocationCount() {
             return mOnActivityRemovedFromStackCount;
         }
 
@@ -317,5 +317,22 @@
         T getWindowContainerController() {
             return mContainerController;
         }
+
+        void setIsTranslucent(boolean isTranslucent) {
+            mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
+        }
+
+        @Override
+        boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
+            switch (mIsTranslucent) {
+                case IS_TRANSLUCENT_TRUE:
+                    return true;
+                case IS_TRANSLUCENT_FALSE:
+                    return false;
+                case IS_TRANSLUCENT_UNSET:
+                default:
+                    return super.isStackTranslucent(starting, stackBehind);
+            }
+        }
     }
 }