Letterboxing: ensure bar contrast

Ensures that any bars that draw over a letterbox draw their own
background such that they always have contrast.

This fixes an issue, where if a light status / navigation bar app was
letterboxed, the bar would still draw dark icons over the now black
letterbox, making the icons unreadable.

To do so, splits the letterbox into a layout and an apply surface
changes phase.

Change-Id: Ia8afa3386d75d9a72434d701b867c3ebc35cc36f
Fixes: 72696928
Test: Open a letterboxed app with a white navigation bar, verify it is visible
Test: ApiDemos > App > Activity > Max Aspect Ratio > 1:1, verify navbar is visible
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 1f71b8f..c15893b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -24,7 +24,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.SurfaceControl.HIDDEN;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -712,7 +711,7 @@
         if (destroyedSomething) {
             final DisplayContent dc = getDisplayContent();
             dc.assignWindowLayers(true /*setLayoutNeeded*/);
-            updateLetterbox(null);
+            updateLetterboxSurface(null);
         }
     }
 
@@ -979,7 +978,7 @@
     void removeChild(WindowState child) {
         super.removeChild(child);
         checkKeyguardFlagsChanged();
-        updateLetterbox(child);
+        updateLetterboxSurface(child);
     }
 
     private boolean waitingForReplacement() {
@@ -1470,7 +1469,7 @@
         return isInterestingAndDrawn;
     }
 
-    void updateLetterbox(WindowState winHint) {
+    void layoutLetterbox(WindowState winHint) {
         final WindowState w = findMainWindow();
         if (w != winHint && winHint != null && w != null) {
             return;
@@ -1481,19 +1480,20 @@
             if (mLetterbox == null) {
                 mLetterbox = new Letterbox(() -> makeChildSurface(null));
             }
-            mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame);
+            mLetterbox.layout(getParent().getBounds(), w.mFrame);
         } else if (mLetterbox != null) {
-            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-            // Make sure we have a transaction here, in case we're called outside of a transaction.
-            // This does not use mPendingTransaction, because SurfaceAnimator uses a
-            // global transaction in onAnimationEnd.
-            SurfaceControl.openTransaction();
-            try {
-                mLetterbox.hide(t);
-            } finally {
-                SurfaceControl.mergeToGlobalTransaction(t);
-                SurfaceControl.closeTransaction();
-            }
+            mLetterbox.hide();
+        }
+    }
+
+    void updateLetterboxSurface(WindowState winHint) {
+        final WindowState w = findMainWindow();
+        if (w != winHint && winHint != null && w != null) {
+            return;
+        }
+        layoutLetterbox(winHint);
+        if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
+            mLetterbox.applySurfaceChanges(mPendingTransaction);
         }
     }
 
@@ -2156,4 +2156,12 @@
             return new Rect();
         }
     }
+
+    /**
+     * @eturn true if there is a letterbox and any part of that letterbox overlaps with
+     * the given {@code rect}.
+     */
+    boolean isLetterboxOverlappingWith(Rect rect) {
+        return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
+    }
 }