Dispatch DisplayCutout from DisplayAdapter

Adds a config resource to configure the main display's cutout and
dispatches it from the LocalDisplayAdapter.

WindowManager's DisplayContent then transforms it to match the current
rotation.

Also fixes an issue in EmulatedDisplayCutout where the Path was never
reset and changes the color to black. Also fixes the RoundedCorners
overlay such that it can layout within the cutout area.

Test: atest CoordinateTransformsTest DisplayContentTests
Bug: 65689439
Change-Id: If39c8ea3cb55c761517f270dcca292682c0918ad
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2cc2a0e..4c5520f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -62,6 +62,7 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -121,6 +122,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
+import android.graphics.Path;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -137,6 +139,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.InputDevice;
 import android.view.MagnificationSpec;
@@ -158,6 +161,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -208,6 +212,9 @@
     int mInitialDisplayHeight = 0;
     int mInitialDisplayDensity = 0;
 
+    DisplayCutout mInitialDisplayCutout;
+    DisplayCutout mDisplayCutoutOverride;
+
     /**
      * Overridden display size. Initialized with {@link #mInitialDisplayWidth}
      * and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size".
@@ -1160,6 +1167,7 @@
             mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
         }
+        mDisplayInfo.displayCutout = calculateDisplayCutoutForCurrentRotation();
         mDisplayInfo.getAppMetrics(mDisplayMetrics);
         if (mDisplayScalingDisabled) {
             mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED;
@@ -1181,6 +1189,18 @@
         return mDisplayInfo;
     }
 
+    DisplayCutout calculateDisplayCutoutForCurrentRotation() {
+        final DisplayCutout cutout = mInitialDisplayCutout;
+        if (cutout == null || cutout == DisplayCutout.NO_CUTOUT || mRotation == ROTATION_0) {
+            return cutout;
+        }
+        final Path bounds = cutout.getBounds().getBoundaryPath();
+        transformPhysicalToLogicalCoordinates(mRotation, mInitialDisplayWidth,
+                mInitialDisplayHeight, mTmpMatrix);
+        bounds.transform(mTmpMatrix);
+        return DisplayCutout.fromBounds(bounds);
+    }
+
     /**
      * Compute display configuration based on display properties and policy settings.
      * Do not call if mDisplayReady == false.
@@ -1643,6 +1663,7 @@
         mInitialDisplayWidth = mDisplayInfo.logicalWidth;
         mInitialDisplayHeight = mDisplayInfo.logicalHeight;
         mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
+        mInitialDisplayCutout = mDisplayInfo.displayCutout;
     }
 
     /**
@@ -1657,10 +1678,12 @@
         final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
         final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight;
         final int newDensity = mDisplayInfo.logicalDensityDpi;
+        final DisplayCutout newCutout = mDisplayInfo.displayCutout;
 
         final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
                 || mInitialDisplayHeight != newHeight
-                || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi;
+                || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
+                || !Objects.equals(mInitialDisplayCutout, newCutout);
 
         if (displayMetricsChanged) {
             // Check if display size or density is forced.
@@ -1677,6 +1700,7 @@
             mInitialDisplayWidth = newWidth;
             mInitialDisplayHeight = newHeight;
             mInitialDisplayDensity = newDensity;
+            mInitialDisplayCutout = newCutout;
             mService.reconfigureDisplayLocked(this);
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 0155712..bd06192 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -22,6 +22,7 @@
 import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
 
 import android.annotation.NonNull;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
@@ -100,9 +101,12 @@
     /** During layout, the current screen borders along which input method windows are placed. */
     public final Rect mDock = new Rect();
 
-    /** Definition of the cutout */
+    /** The display cutout used for layout (after rotation and emulation) */
     @NonNull public DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
 
+    /** The cutout as supplied by display info */
+    @NonNull private DisplayCutout mDisplayInfoCutout = DisplayCutout.NO_CUTOUT;
+
     /**
      * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
      */
@@ -126,6 +130,8 @@
         mRotation = info.rotation;
         mDisplayInfoOverscan.set(
                 info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom);
+        mDisplayInfoCutout = info.displayCutout != null
+                ? info.displayCutout : DisplayCutout.NO_CUTOUT;
     }
 
     public void onBeginLayout(boolean emulateDisplayCutout, int statusBarHeight) {
@@ -166,12 +172,29 @@
         mStable.set(mUnrestricted);
         mStableFullscreen.set(mUnrestricted);
         mCurrent.set(mUnrestricted);
-        mDisplayCutout = DisplayCutout.NO_CUTOUT;
-        mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
-                Integer.MAX_VALUE, Integer.MAX_VALUE);
+        mDisplayCutout = mDisplayInfoCutout;
         if (emulateDisplayCutout) {
             setEmulatedDisplayCutout((int) (statusBarHeight * 0.8));
         }
+        mDisplayCutout = mDisplayCutout.calculateRelativeTo(mOverscan);
+
+        mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
+                Integer.MAX_VALUE, Integer.MAX_VALUE);
+        if (!mDisplayCutout.isEmpty()) {
+            final DisplayCutout c = mDisplayCutout;
+            if (c.getSafeInsetLeft() > 0) {
+                mDisplayCutoutSafe.left = mRestrictedOverscan.left + c.getSafeInsetLeft();
+            }
+            if (c.getSafeInsetTop() > 0) {
+                mDisplayCutoutSafe.top = mRestrictedOverscan.top + c.getSafeInsetTop();
+            }
+            if (c.getSafeInsetRight() > 0) {
+                mDisplayCutoutSafe.right = mRestrictedOverscan.right - c.getSafeInsetRight();
+            }
+            if (c.getSafeInsetBottom() > 0) {
+                mDisplayCutoutSafe.bottom = mRestrictedOverscan.bottom - c.getSafeInsetBottom();
+            }
+        }
     }
 
     public int getInputMethodWindowVisibleHeight() {
@@ -194,8 +217,7 @@
                         new Point(height, (screenWidth - widthBottom) / 2),
                         new Point(height, (screenWidth + widthBottom) / 2),
                         new Point(0, (screenWidth + widthTop) / 2)
-                )).calculateRelativeTo(mUnrestricted);
-                mDisplayCutoutSafe.left = height;
+                ));
                 break;
             case ROTATION_180:
                 mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
@@ -203,8 +225,7 @@
                         new Point((screenWidth - widthBottom) / 2, screenHeight - height),
                         new Point((screenWidth + widthBottom) / 2, screenHeight - height),
                         new Point((screenWidth + widthTop) / 2, screenHeight)
-                )).calculateRelativeTo(mUnrestricted);
-                mDisplayCutoutSafe.bottom = screenHeight - height;
+                ));
                 break;
             case ROTATION_270:
                 mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
@@ -212,8 +233,7 @@
                         new Point(screenHeight - height, (screenWidth - widthBottom) / 2),
                         new Point(screenHeight - height, (screenWidth + widthBottom) / 2),
                         new Point(screenHeight, (screenWidth + widthTop) / 2)
-                )).calculateRelativeTo(mUnrestricted);
-                mDisplayCutoutSafe.right = screenHeight - height;
+                ));
                 break;
             default:
                 mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
@@ -221,8 +241,7 @@
                         new Point((screenWidth - widthBottom) / 2, height),
                         new Point((screenWidth + widthBottom) / 2, height),
                         new Point((screenWidth + widthTop) / 2, 0)
-                )).calculateRelativeTo(mUnrestricted);
-                mDisplayCutoutSafe.top = height;
+                ));
                 break;
         }
     }
diff --git a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
new file mode 100644
index 0000000..09d7b5d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.annotation.Dimension;
+import android.graphics.Matrix;
+import android.view.Surface.Rotation;
+
+public class CoordinateTransforms {
+
+    private CoordinateTransforms() {
+    }
+
+    /**
+     * Sets a matrix such that given a rotation, it transforms physical display
+     * coordinates to that rotation's logical coordinates.
+     *
+     * @param rotation the rotation to which the matrix should transform
+     * @param out      the matrix to be set
+     */
+    public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
+            @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+        switch (rotation) {
+            case ROTATION_0:
+                out.reset();
+                break;
+            case ROTATION_90:
+                out.setRotate(270);
+                out.postTranslate(0, physicalWidth);
+                break;
+            case ROTATION_180:
+                out.setRotate(180);
+                out.postTranslate(physicalWidth, physicalHeight);
+                break;
+            case ROTATION_270:
+                out.setRotate(90);
+                out.postTranslate(physicalHeight, 0);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown rotation: " + rotation);
+        }
+    }
+}