Merge "Improve first opaque activity candidate detection" into udc-dev
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1717d24..cb7c7ae 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1640,7 +1640,7 @@
             if (isState(RESUMED)) {
                 newParent.setResumedActivity(this, "onParentChanged");
             }
-            mLetterboxUiController.onActivityParentChanged(newParent);
+            mLetterboxUiController.updateInheritedLetterbox();
         }
 
         if (rootTask != null && rootTask.topRunningActivity() == this) {
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 6ef6fa7..fc99f4c 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -113,6 +113,8 @@
 import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Optional;
 import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
@@ -127,8 +129,7 @@
 final class LetterboxUiController {
 
     private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE =
-            activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing()
-                    && activityRecord.nowVisible;
+            activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing();
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
 
@@ -180,6 +181,10 @@
     // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS
     private final boolean mIsOverrideEnableCompatFakeFocusEnabled;
 
+    // The list of observers for the destroy event of candidate opaque activities
+    // when dealing with translucent activities.
+    private final List<LetterboxUiController> mDestroyListeners = new ArrayList<>();
+
     @Nullable
     private final Boolean mBooleanPropertyAllowOrientationOverride;
     @Nullable
@@ -193,6 +198,10 @@
     @Nullable
     private WindowContainerListener mLetterboxConfigListener;
 
+    @Nullable
+    @VisibleForTesting
+    ActivityRecord mFirstOpaqueActivityBeneath;
+
     private boolean mShowWallpaperForLetterboxBackground;
 
     // In case of transparent activities we might need to access the aspectRatio of the
@@ -353,6 +362,10 @@
             mLetterbox.destroy();
             mLetterbox = null;
         }
+        for (int i = mDestroyListeners.size() - 1; i >= 0; i--) {
+            mDestroyListeners.get(i).updateInheritedLetterbox();
+        }
+        mDestroyListeners.clear();
         if (mLetterboxConfigListener != null) {
             mLetterboxConfigListener.onRemoved();
             mLetterboxConfigListener = null;
@@ -1571,7 +1584,11 @@
      * first opaque activity beneath.
      * @param parent The parent container.
      */
-    void onActivityParentChanged(WindowContainer<?> parent) {
+    void updateInheritedLetterbox() {
+        final WindowContainer<?> parent = mActivityRecord.getParent();
+        if (parent == null) {
+            return;
+        }
         if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
             return;
         }
@@ -1581,22 +1598,24 @@
         }
         // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the
         // opaque activity constraints because we're expecting the activity is already letterboxed.
+        mFirstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
+                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
+                mActivityRecord /* boundary */, false /* includeBoundary */,
+                true /* traverseTopToBottom */);
+        if (mFirstOpaqueActivityBeneath == null || mFirstOpaqueActivityBeneath.isEmbedded()) {
+            // We skip letterboxing if the translucent activity doesn't have any opaque
+            // activities beneath or the activity below is embedded which never has letterbox.
+            mActivityRecord.recomputeConfiguration();
+            return;
+        }
         if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
                 || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
             return;
         }
-        final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
-                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
-                mActivityRecord /* boundary */, false /* includeBoundary */,
-                true /* traverseTopToBottom */);
-        if (firstOpaqueActivityBeneath == null || firstOpaqueActivityBeneath.isEmbedded()) {
-            // We skip letterboxing if the translucent activity doesn't have any opaque
-            // activities beneath or the activity below is embedded which never has letterbox.
-            return;
-        }
-        inheritConfiguration(firstOpaqueActivityBeneath);
+        mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.add(this);
+        inheritConfiguration(mFirstOpaqueActivityBeneath);
         mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
-                mActivityRecord, firstOpaqueActivityBeneath,
+                mActivityRecord, mFirstOpaqueActivityBeneath,
                 (opaqueConfig, transparentOverrideConfig) -> {
                     resetTranslucentOverrideConfig(transparentOverrideConfig);
                     final Rect parentBounds = parent.getWindowConfiguration().getBounds();
@@ -1610,7 +1629,7 @@
                     // We need to initialize appBounds to avoid NPE. The actual value will
                     // be set ahead when resolving the Configuration for the activity.
                     transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect());
-                    inheritConfiguration(firstOpaqueActivityBeneath);
+                    inheritConfiguration(mFirstOpaqueActivityBeneath);
                     return transparentOverrideConfig;
                 });
     }
@@ -1684,10 +1703,7 @@
         if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) {
             return Optional.empty();
         }
-        return Optional.ofNullable(mActivityRecord.getTask().getActivity(
-                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
-                mActivityRecord /* boundary */, false /* includeBoundary */,
-                true /* traverseTopToBottom */));
+        return Optional.ofNullable(mFirstOpaqueActivityBeneath);
     }
 
     /** Resets the screen size related fields so they can be resolved by requested bounds later. */
@@ -1718,6 +1734,10 @@
     }
 
     private void clearInheritedConfig() {
+        if (mFirstOpaqueActivityBeneath != null) {
+            mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.remove(this);
+        }
+        mFirstOpaqueActivityBeneath = null;
         mLetterboxConfigListener = null;
         mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
         mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index de943d2..06bcbf3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -179,44 +179,6 @@
     }
 
     @Test
-    public void testActivityInHistoryAndNotVisibleIsNotUsedAsOpaqueForTranslucentActivities() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.nowVisible = false;
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        doReturn(false).when(translucentActivity).fillsParent();
-
-        mTask.addChild(translucentActivity);
-
-        assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-    }
-
-    @Test
-    public void testActivityInHistoryAndVisibleIsUsedAsOpaqueForTranslucentActivities() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.nowVisible = true;
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        doReturn(false).when(translucentActivity).fillsParent();
-
-        mTask.addChild(translucentActivity);
-
-        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-    }
-
-    @Test
     public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
         setUpDisplaySizeWithApp(2000, 1000);
@@ -240,7 +202,6 @@
     public void testHorizontalReachabilityEnabledForTranslucentActivities() {
         setUpDisplaySizeWithApp(2500, 1000);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.nowVisible = true;
         final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
         config.setTranslucentLetterboxingOverrideEnabled(true);
         config.setLetterboxHorizontalPositionMultiplier(0.5f);
@@ -316,7 +277,6 @@
     public void testVerticalReachabilityEnabledForTranslucentActivities() {
         setUpDisplaySizeWithApp(1000, 2500);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.nowVisible = true;
         final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
         config.setTranslucentLetterboxingOverrideEnabled(true);
         config.setLetterboxVerticalPositionMultiplier(0.5f);
@@ -389,13 +349,110 @@
     }
 
     @Test
+    public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
+        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+        setUpDisplaySizeWithApp(2000, 1000);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Launch another opaque activity
+        final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        mTask.addChild(opaqueActivity);
+        // Transparent activity strategy not applied
+        assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+
+        // Launch translucent Activity
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        doReturn(false).when(translucentActivity).fillsParent();
+        mTask.addChild(translucentActivity);
+        // Transparent strategy applied
+        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+
+        spyOn(translucentActivity.mLetterboxUiController);
+        clearInvocations(translucentActivity.mLetterboxUiController);
+
+        // We destroy the first opaque activity
+        opaqueActivity.setState(DESTROYED, "testing");
+        opaqueActivity.removeImmediately();
+
+        // Check that updateInheritedLetterbox() is invoked again
+        verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
+    }
+
+    @Test
+    public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
+        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+        setUpDisplaySizeWithApp(2000, 1000);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Launch translucent Activity
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        doReturn(false).when(translucentActivity).fillsParent();
+        mTask.addChild(translucentActivity);
+        // Transparent strategy applied
+        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+        assertNotNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
+
+        spyOn(translucentActivity.mLetterboxUiController);
+        clearInvocations(translucentActivity.mLetterboxUiController);
+
+        // We destroy the first opaque activity
+        mActivity.setState(DESTROYED, "testing");
+        mActivity.removeImmediately();
+
+        // Check that updateInheritedLetterbox() is invoked again
+        verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
+        assertNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
+    }
+
+    @Test
+    public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
+        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+        setUpDisplaySizeWithApp(2000, 1000);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Launch another opaque activity
+        final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        mTask.addChild(opaqueActivity);
+        // Transparent activity strategy not applied
+        assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+
+        // Launch translucent Activity
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        doReturn(false).when(translucentActivity).fillsParent();
+        mTask.addChild(translucentActivity);
+        // Transparent strategy applied
+        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+
+        spyOn(translucentActivity.mLetterboxUiController);
+        clearInvocations(translucentActivity.mLetterboxUiController);
+
+        // Check that updateInheritedLetterbox() is invoked again
+        verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox();
+    }
+
+    @Test
     public void testApplyStrategyToTranslucentActivities() {
         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
         setUpDisplaySizeWithApp(2000, 1000);
         prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         mActivity.info.setMinAspectRatio(1.2f);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.nowVisible = true;
         // Translucent Activity
         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                 .setLaunchedFromUid(mActivity.getUid())
@@ -456,7 +513,6 @@
         prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         mActivity.info.setMinAspectRatio(1.2f);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.nowVisible = true;
         // Translucent Activity
         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                 .setLaunchedFromUid(mActivity.getUid())
@@ -550,7 +606,6 @@
                 true /* ignoreOrientationRequest */);
         mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
                 1.0f /*letterboxVerticalPositionMultiplier*/);
-        mActivity.nowVisible = true;
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
         // We launch a transparent activity
         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
@@ -583,7 +638,6 @@
         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
         setUpDisplaySizeWithApp(2800, 1400);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.nowVisible = true;
         prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         // Rotate to put activity in size compat mode.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);