Display Cutout: Dispatch Cutout from WindowManager

Adds the logic to dispatch a DisplayCutout from DisplayFrames
through WindowState to the View hierarchy. Does however not yet
change how windows are laid out in response to a DisplayCutout.

The display cutout is currently never present, the following CL
will add logic to emulate a display cutout on devices that do
not have a physical one.

Bug: 65689439
Test: runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
Change-Id: Ie4cd4b575755b66a7ffead31e28640983ef4894e
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dd0ae33..e5ab3e1 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.IWindowSession;
 import android.view.InputChannel;
@@ -176,6 +177,9 @@
         final Rect mFinalSystemInsets = new Rect();
         final Rect mFinalStableInsets = new Rect();
         final Rect mBackdropFrame = new Rect();
+        final DisplayCutout.ParcelableWrapper mDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+        DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
 
         final WindowManager.LayoutParams mLayout
@@ -302,7 +306,8 @@
             public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                     Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                     MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout,
-                    boolean alwaysConsumeNavBar, int displayId) {
+                    boolean alwaysConsumeNavBar, int displayId,
+                    DisplayCutout.ParcelableWrapper displayCutout) {
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0, outsets);
                 mCaller.sendMessage(msg);
@@ -750,7 +755,7 @@
                         mInputChannel = new InputChannel();
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                             Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets,
-                                mInputChannel) < 0) {
+                                mDisplayCutout, mInputChannel) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -776,7 +781,7 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mMergedConfiguration, mSurfaceHolder.mSurface);
+                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -800,6 +805,8 @@
                         mStableInsets.top += padding.top;
                         mStableInsets.right += padding.right;
                         mStableInsets.bottom += padding.bottom;
+                        mDisplayCutout.set(mDisplayCutout.get().inset(-padding.left, -padding.top,
+                                -padding.right, -padding.bottom));
                     }
 
                     if (mCurWidth != w) {
@@ -819,6 +826,7 @@
                     insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
                     insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
                     insetsChanged |= !mDispatchedOutsets.equals(mOutsets);
+                    insetsChanged |= !mDispatchedDisplayCutout.equals(mDisplayCutout.get());
 
                     mSurfaceHolder.setSurfaceFrameSize(w, h);
                     mSurfaceHolder.mSurfaceLock.unlock();
@@ -885,12 +893,13 @@
                             mDispatchedContentInsets.set(mContentInsets);
                             mDispatchedStableInsets.set(mStableInsets);
                             mDispatchedOutsets.set(mOutsets);
+                            mDispatchedDisplayCutout = mDisplayCutout.get();
                             mFinalSystemInsets.set(mDispatchedOverscanInsets);
                             mFinalStableInsets.set(mDispatchedStableInsets);
                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
                                     null, mFinalStableInsets,
                                     getResources().getConfiguration().isScreenRound(), false,
-                                    null /* displayCutout */);
+                                    mDispatchedDisplayCutout);
                             if (DEBUG) {
                                 Log.v(TAG, "dispatching insets=" + insets);
                             }
diff --git a/core/java/android/view/DisplayCutout.aidl b/core/java/android/view/DisplayCutout.aidl
new file mode 100644
index 0000000..6d13b99
--- /dev/null
+++ b/core/java/android/view/DisplayCutout.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, 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 android.view;
+
+parcelable DisplayCutout.ParcelableWrapper;
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 611cc63..07a57fb 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -23,6 +23,7 @@
 import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.DisplayCutout;
 
 import com.android.internal.os.IResultReceiver;
 import android.util.MergedConfiguration;
@@ -50,7 +51,8 @@
     void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
             in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
             in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
-            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
+            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
+            in DisplayCutout.ParcelableWrapper displayCutout);
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 51d6514..ed167c8 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,6 +22,7 @@
 import android.graphics.Region;
 import android.os.Bundle;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.InputChannel;
 import android.view.IWindow;
 import android.view.IWindowId;
@@ -40,7 +41,8 @@
             out InputChannel outInputChannel);
     int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
-            out Rect outStableInsets, out Rect outOutsets, out InputChannel outInputChannel);
+            out Rect outStableInsets, out Rect outOutsets,
+            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
     int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
@@ -96,6 +98,7 @@
             int flags, out Rect outFrame, out Rect outOverscanInsets,
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
+            out DisplayCutout.ParcelableWrapper displayCutout,
             out MergedConfiguration outMergedConfiguration, out Surface outSurface);
 
     /*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0525ab1..f27989f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25721,6 +25721,9 @@
          */
         final Rect mStableInsets = new Rect();
 
+        final DisplayCutout.ParcelableWrapper mDisplayCutout =
+                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
+
         /**
          * For windows that include areas that are not covered by real surface these are the outsets
          * for real surface.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1c9d863..0bae36b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -384,12 +384,15 @@
     final Rect mPendingContentInsets = new Rect();
     final Rect mPendingOutsets = new Rect();
     final Rect mPendingBackDropFrame = new Rect();
+    final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
+            new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeNavBar;
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
     final Rect mDispatchContentInsets = new Rect();
     final Rect mDispatchStableInsets = new Rect();
+    DisplayCutout mDispatchDisplayCutout = DisplayCutout.NO_CUTOUT;
 
     private WindowInsets mLastWindowInsets;
 
@@ -730,7 +733,7 @@
                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                             getHostVisibility(), mDisplay.getDisplayId(),
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
-                            mAttachInfo.mOutsets, mInputChannel);
+                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
@@ -752,6 +755,7 @@
                 mPendingOverscanInsets.set(0, 0, 0, 0);
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingStableInsets.set(mAttachInfo.mStableInsets);
+                mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
                 mAttachInfo.mAlwaysConsumeNavBar =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
@@ -1544,15 +1548,20 @@
         if (mLastWindowInsets == null || forceConstruct) {
             mDispatchContentInsets.set(mAttachInfo.mContentInsets);
             mDispatchStableInsets.set(mAttachInfo.mStableInsets);
+            mDispatchDisplayCutout = mAttachInfo.mDisplayCutout.get();
+
             Rect contentInsets = mDispatchContentInsets;
             Rect stableInsets = mDispatchStableInsets;
+            DisplayCutout displayCutout = mDispatchDisplayCutout;
             // For dispatch we preserve old logic, but for direct requests from Views we allow to
             // immediately use pending insets.
             if (!forceConstruct
                     && (!mPendingContentInsets.equals(contentInsets) ||
-                        !mPendingStableInsets.equals(stableInsets))) {
+                        !mPendingStableInsets.equals(stableInsets) ||
+                        !mPendingDisplayCutout.get().equals(displayCutout))) {
                 contentInsets = mPendingContentInsets;
                 stableInsets = mPendingStableInsets;
+                displayCutout = mPendingDisplayCutout.get();
             }
             Rect outsets = mAttachInfo.mOutsets;
             if (outsets.left > 0 || outsets.top > 0 || outsets.right > 0 || outsets.bottom > 0) {
@@ -1563,7 +1572,7 @@
             mLastWindowInsets = new WindowInsets(contentInsets,
                     null /* windowDecorInsets */, stableInsets,
                     mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeNavBar, null /* displayCutout */);
+                    mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
         }
         return mLastWindowInsets;
     }
@@ -1730,6 +1739,9 @@
                 if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
                     insetsChanged = true;
                 }
+                if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) {
+                    insetsChanged = true;
+                }
                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                     if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
@@ -1906,7 +1918,8 @@
                         + " overscan=" + mPendingOverscanInsets.toShortString()
                         + " content=" + mPendingContentInsets.toShortString()
                         + " visible=" + mPendingVisibleInsets.toShortString()
-                        + " visible=" + mPendingStableInsets.toShortString()
+                        + " stable=" + mPendingStableInsets.toShortString()
+                        + " cutout=" + mPendingDisplayCutout.get().toString()
                         + " outsets=" + mPendingOutsets.toShortString()
                         + " surface=" + mSurface);
 
@@ -1931,6 +1944,8 @@
                         mAttachInfo.mVisibleInsets);
                 final boolean stableInsetsChanged = !mPendingStableInsets.equals(
                         mAttachInfo.mStableInsets);
+                final boolean cutoutChanged = !mPendingDisplayCutout.equals(
+                        mAttachInfo.mDisplayCutout);
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -1955,6 +1970,14 @@
                     // Need to relayout with content insets.
                     contentInsetsChanged = true;
                 }
+                if (cutoutChanged) {
+                    mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
+                    if (DEBUG_LAYOUT) {
+                        Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout);
+                    }
+                    // Need to relayout with content insets.
+                    contentInsetsChanged = true;
+                }
                 if (alwaysConsumeNavBarChanged) {
                     mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
                     contentInsetsChanged = true;
@@ -2056,6 +2079,7 @@
                         mResizeMode = freeformResizing
                                 ? RESIZE_MODE_FREEFORM
                                 : RESIZE_MODE_DOCKED_DIVIDER;
+                        // TODO: Need cutout?
                         startDragResizing(mPendingBackDropFrame,
                                 mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
                                 mPendingStableInsets, mResizeMode);
@@ -3776,6 +3800,7 @@
                             && mPendingOverscanInsets.equals(args.arg5)
                             && mPendingContentInsets.equals(args.arg2)
                             && mPendingStableInsets.equals(args.arg6)
+                            && mPendingDisplayCutout.get().equals(args.arg9)
                             && mPendingVisibleInsets.equals(args.arg3)
                             && mPendingOutsets.equals(args.arg7)
                             && mPendingBackDropFrame.equals(args.arg8)
@@ -3808,6 +3833,7 @@
                                 || !mPendingOverscanInsets.equals(args.arg5)
                                 || !mPendingContentInsets.equals(args.arg2)
                                 || !mPendingStableInsets.equals(args.arg6)
+                                || !mPendingDisplayCutout.get().equals(args.arg9)
                                 || !mPendingVisibleInsets.equals(args.arg3)
                                 || !mPendingOutsets.equals(args.arg7);
 
@@ -3815,6 +3841,7 @@
                         mPendingOverscanInsets.set((Rect) args.arg5);
                         mPendingContentInsets.set((Rect) args.arg2);
                         mPendingStableInsets.set((Rect) args.arg6);
+                        mPendingDisplayCutout.set((DisplayCutout) args.arg9);
                         mPendingVisibleInsets.set((Rect) args.arg3);
                         mPendingOutsets.set((Rect) args.arg7);
                         mPendingBackDropFrame.set((Rect) args.arg8);
@@ -6258,7 +6285,7 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
+                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                 mPendingMergedConfiguration, mSurface);
 
         mPendingAlwaysConsumeNavBar =
@@ -6541,7 +6568,8 @@
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
             MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar, int displayId) {
+            boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
@@ -6578,6 +6606,7 @@
         args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
         args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
         args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
+        args.arg9 = displayCutout.get(); // DisplayCutout is immutable.
         args.argi1 = forceLayout ? 1 : 0;
         args.argi2 = alwaysConsumeNavBar ? 1 : 0;
         args.argi3 = displayId;
@@ -7610,12 +7639,13 @@
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeNavBar, int displayId) {
+                boolean alwaysConsumeNavBar, int displayId,
+                DisplayCutout.ParcelableWrapper displayCutout) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                         visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
-                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
+                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId, displayCutout);
             }
         }
 
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 8fb56d4..d9aa325 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -48,6 +48,7 @@
     public Object arg6;
     public Object arg7;
     public Object arg8;
+    public Object arg9;
     public int argi1;
     public int argi2;
     public int argi3;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 361fd3da..7178a0d 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -22,6 +22,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
@@ -41,7 +42,8 @@
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw,
             MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar, int displayId) {
+            boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7415ec3..5b059a0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -211,6 +211,7 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -4335,7 +4336,7 @@
     // TODO: Should probably be moved into DisplayFrames.
     public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
         final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
         final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
@@ -4361,12 +4362,15 @@
 
         if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
                 == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+            Rect frame;
             int availRight, availBottom;
             if (canHideNavigationBar() &&
                     (systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
+                frame = displayFrames.mUnrestricted;
                 availRight = displayFrames.mUnrestricted.right;
                 availBottom = displayFrames.mUnrestricted.bottom;
             } else {
+                frame = displayFrames.mRestricted;
                 availRight = displayFrames.mRestricted.right;
                 availBottom = displayFrames.mRestricted.bottom;
             }
@@ -4397,10 +4401,12 @@
                 calculateRelevantTaskInsets(taskBounds, outStableInsets,
                         displayWidth, displayHeight);
             }
+            outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(frame));
             return mForceShowSystemBars;
         }
         outContentInsets.setEmpty();
         outStableInsets.setEmpty();
+        outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
         return mForceShowSystemBars;
     }
 
@@ -4519,7 +4525,7 @@
 
             w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
                     df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
-                    df /* stableFrame */, df /* outsetFrame */);
+                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout);
             final Rect frame = w.getFrameLw();
 
             if (frame.left <= 0 && frame.top <= 0) {
@@ -4581,7 +4587,8 @@
         // Let the status bar determine its size.
         mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                 vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
-                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
+                displayFrames.mDisplayCutout);
 
         // For layout, the status bar is always at the top with our fixed height.
         displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
@@ -4712,7 +4719,7 @@
         // And compute the final frame.
         mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                 mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
-                mTmpNavigationFrame, mTmpNavigationFrame);
+                mTmpNavigationFrame, mTmpNavigationFrame, displayFrames.mDisplayCutout);
         if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
         return mNavigationBarController.checkHiddenLw();
     }
@@ -5218,7 +5225,7 @@
                 + " sf=" + sf.toShortString()
                 + " osf=" + (osf == null ? "null" : osf.toShortString()));
 
-        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf);
+        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout);
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5f067d4..4a7647b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -77,6 +77,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IWindowManager;
 import android.view.InputEventReceiver;
@@ -210,10 +211,11 @@
          * @param stableFrame The frame around which stable system decoration is positioned.
          * @param outsetFrame The frame that includes areas that aren't part of the surface but we
          * want to treat them as such.
+         * @param displayCutout the display displayCutout
          */
         public void computeFrameLw(Rect parentFrame, Rect displayFrame,
                 Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
-                Rect stableFrame, @Nullable Rect outsetFrame);
+                Rect stableFrame, @Nullable Rect outsetFrame, DisplayCutout displayCutout);
 
         /**
          * Retrieve the current frame of the window that has been assigned by
@@ -1144,12 +1146,13 @@
      * @param outStableInsets The areas covered by stable system windows irrespective of their
      *                        current visibility. Expressed as positive insets.
      * @param outOutsets The areas that are not real display, but we would like to treat as such.
+     * @param outDisplayCutout The area that has been cut away from the display.
      * @return Whether to always consume the navigation bar.
      *         See {@link #isNavBarForcedShownLw(WindowState)}.
      */
     default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         return false;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 0249713..209ce3f 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,8 +21,10 @@
 import static android.view.Surface.ROTATION_90;
 import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
 
+import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 
 import java.io.PrintWriter;
@@ -94,6 +96,14 @@
     /** During layout, the current screen borders along which input method windows are placed. */
     public final Rect mDock = new Rect();
 
+    /** Definition of the cutout */
+    @NonNull public DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
+
+    /**
+     * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
+     */
+    public final Rect mDisplayCutoutSafe = new Rect();
+
     private final Rect mDisplayInfoOverscan = new Rect();
     private final Rect mRotatedDisplayInfoOverscan = new Rect();
     public int mDisplayWidth;
@@ -152,7 +162,9 @@
         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);
     }
 
     public int getInputMethodWindowVisibleHeight() {
@@ -182,6 +194,7 @@
         dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
         dumpFrame(mDisplayInfoOverscan, "mDisplayInfoOverscan", myPrefix, pw);
         dumpFrame(mRotatedDisplayInfoOverscan, "mRotatedDisplayInfoOverscan", myPrefix, pw);
+        pw.println(myPrefix + "mDisplayCutout=" + mDisplayCutout);
     }
 
     private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ad7c300..63eea10 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -23,9 +23,8 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -45,13 +44,13 @@
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowId;
 import android.view.IWindowSession;
 import android.view.IWindowSessionCallback;
 import android.view.InputChannel;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowManager;
 
@@ -192,15 +191,17 @@
             int viewVisibility, Rect outContentInsets, Rect outStableInsets,
             InputChannel outInputChannel) {
         return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
-                outContentInsets, outStableInsets, null /* outOutsets */, outInputChannel);
+                outContentInsets, outStableInsets, null /* outOutsets */, null /* cutout */,
+                outInputChannel);
     }
 
     @Override
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets, InputChannel outInputChannel) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout,
+            InputChannel outInputChannel) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
-                outContentInsets, outStableInsets, outOutsets, outInputChannel);
+                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
     }
 
     @Override
@@ -214,7 +215,7 @@
     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
-            outContentInsets, outStableInsets, null /* outOutsets */, null);
+            outContentInsets, outStableInsets, null /* outOutsets */, null /* cutout */, null);
     }
 
     @Override
@@ -232,6 +233,7 @@
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
+            DisplayCutout.ParcelableWrapper cutout,
             MergedConfiguration mergedConfiguration, Surface outSurface) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
@@ -239,7 +241,8 @@
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
-                outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
+                outStableInsets, outsets, outBackdropFrame, cutout,
+                mergedConfiguration, outSurface);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 41915a3..3c1806d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -57,6 +57,7 @@
 import android.os.SystemClock;
 import android.util.MergedConfiguration;
 import android.util.Slog;
+import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -134,6 +135,7 @@
         window.setSession(session);
         final Surface surface = new Surface();
         final Rect tmpRect = new Rect();
+        final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
         final Rect tmpFrame = new Rect();
         final Rect taskBounds;
         final Rect tmpContentInsets = new Rect();
@@ -196,7 +198,7 @@
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                     View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect,
-                    tmpRect, null);
+                    tmpRect, tmpCutout, null);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -212,7 +214,7 @@
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
                     tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpMergedConfiguration, surface);
+                    tmpCutout, tmpMergedConfiguration, surface);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -437,7 +439,8 @@
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
                 Rect stableInsets, Rect outsets, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeNavBar, int displayId) {
+                boolean alwaysConsumeNavBar, int displayId,
+                DisplayCutout.ParcelableWrapper displayCutout) {
             if (mergedConfiguration != null && mOuter != null
                     && mOuter.mOrientationOnCreation
                             != mergedConfiguration.getMergedConfiguration().orientation) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 82f842c..1dc8a16 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -189,6 +189,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -1129,9 +1130,9 @@
     }
 
     public int addWindow(Session session, IWindow client, int seq,
-            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
+            LayoutParams attrs, int viewVisibility, int displayId,
             Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
-            InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
         int[] appOp = new int[1];
         int res = mPolicy.checkAddPermission(attrs, appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1466,7 +1467,7 @@
                 taskBounds = null;
             }
             if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayFrames, outContentInsets,
-                    outStableInsets, outOutsets)) {
+                    outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
 
@@ -1846,11 +1847,12 @@
     }
 
     public int relayoutWindow(Session session, IWindow client, int seq,
-            WindowManager.LayoutParams attrs, int requestedWidth,
+            LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
-            MergedConfiguration mergedConfiguration, Surface outSurface) {
+            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
+            Surface outSurface) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -2135,6 +2137,7 @@
             win.mLastRelayoutContentInsets.set(win.mContentInsets);
             outVisibleInsets.set(win.mVisibleInsets);
             outStableInsets.set(win.mStableInsets);
+            outCutout.set(win.mDisplayCutout);
             outOutsets.set(win.mOutsets);
             outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
             if (localLOGV) Slog.v(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c2ac905..18755b1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -42,7 +42,6 @@
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -144,6 +143,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IApplicationToken;
@@ -320,6 +320,11 @@
     private final Rect mLastOutsets = new Rect();
     private boolean mOutsetsChanged = false;
 
+    /** Part of the display that has been cut away. See {@link DisplayCutout}. */
+    DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
+    private DisplayCutout mLastDisplayCutout = DisplayCutout.NO_CUTOUT;
+    private boolean mDisplayCutoutChanged;
+
     /**
      * Set to true if we are waiting for this window to receive its
      * given internal insets before laying out other windows based on it.
@@ -772,7 +777,7 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            Rect outsetFrame) {
+            Rect outsetFrame, DisplayCutout displayCutout) {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -1009,6 +1014,10 @@
         mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
         mStableFrame.offset(-layoutXDiff, -layoutYDiff);
 
+        // TODO(roosa): Figure out what frame exactly this needs to be calculated with.
+        mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+
+
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -1149,8 +1158,9 @@
         mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
         mFrameSizeChanged |= (mLastFrame.width() != mFrame.width()) ||
                 (mLastFrame.height() != mFrame.height());
+        mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
         return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
-                || mOutsetsChanged || mFrameSizeChanged;
+                || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
     }
 
     /**
@@ -1195,6 +1205,7 @@
                 || winAnimator.mSurfaceResized
                 || mOutsetsChanged
                 || mFrameSizeChanged
+                || mDisplayCutoutChanged
                 || configChanged
                 || dragResizingChanged
                 || !isResizedWhileNotDragResizingReported()
@@ -1214,7 +1225,8 @@
                         + " dragResizingChanged=" + dragResizingChanged
                         + " resizedWhileNotDragResizingReported="
                         + isResizedWhileNotDragResizingReported()
-                        + " reportOrientationChanged=" + mReportOrientationChanged);
+                        + " reportOrientationChanged=" + mReportOrientationChanged
+                        + " displayCutoutChanged=" + mDisplayCutoutChanged);
             }
 
             // If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -2849,6 +2861,7 @@
             final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
             final boolean reportOrientation = mReportOrientationChanged;
             final int displayId = getDisplayId();
+            final DisplayCutout displayCutout = mDisplayCutout;
             if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                     && mClient instanceof IWindow.Stub) {
                 // To prevent deadlock simulate one-way call if win.mClient is a local object.
@@ -2858,7 +2871,7 @@
                         try {
                             dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
                                     stableInsets, outsets, reportDraw, mergedConfiguration,
-                                    reportOrientation, displayId);
+                                    reportOrientation, displayId, displayCutout);
                         } catch (RemoteException e) {
                             // Not a remote call, RemoteException won't be raised.
                         }
@@ -2866,7 +2879,8 @@
                 });
             } else {
                 dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
-                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId);
+                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId,
+                        displayCutout);
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
@@ -2880,6 +2894,7 @@
             mStableInsetsChanged = false;
             mOutsetsChanged = false;
             mFrameSizeChanged = false;
+            mDisplayCutoutChanged = false;
             mResizedWhileNotDragResizingReported = true;
             mWinAnimator.mSurfaceResized = false;
             mReportOrientationChanged = false;
@@ -2922,14 +2937,16 @@
 
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId)
+            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId,
+            DisplayCutout displayCutout)
             throws RemoteException {
         final boolean forceRelayout = isDragResizeChanged() || mResizedWhileNotDragResizing
                 || reportOrientation;
 
         mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                 reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
-                mPolicy.isNavBarForcedShownLw(this), displayId);
+                mPolicy.isNavBarForcedShownLw(this), displayId,
+                new DisplayCutout.ParcelableWrapper(displayCutout));
         mDragResizingChangeReported = true;
     }
 
@@ -3246,6 +3263,7 @@
                     pw.print(" stable="); mStableInsets.printShortString(pw);
                     pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw);
                     pw.print(" outsets="); mOutsets.printShortString(pw);
+                    pw.print(" cutout=" + mDisplayCutout);
                     pw.println();
             pw.print(prefix); pw.print("Lst insets: overscan=");
                     mLastOverscanInsets.printShortString(pw);
@@ -3254,6 +3272,7 @@
                     pw.print(" stable="); mLastStableInsets.printShortString(pw);
                     pw.print(" physical="); mLastOutsets.printShortString(pw);
                     pw.print(" outset="); mLastOutsets.printShortString(pw);
+                    pw.print(" cutout=" + mLastDisplayCutout);
                     pw.println();
         }
         pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
@@ -4278,6 +4297,7 @@
         mLastVisibleInsets.set(mVisibleInsets);
         mLastStableInsets.set(mStableInsets);
         mLastOutsets.set(mOutsets);
+        mLastDisplayCutout = mDisplayCutout;
     }
 
     // TODO: Hack to work around the number of states AppWindowToken needs to access without having
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index a70441d..d1e0132 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -20,6 +20,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.WindowManager;
 
@@ -34,6 +35,8 @@
     public final Rect stableFrame = new Rect();
     public Rect outsetFrame = new Rect();
 
+    public DisplayCutout displayCutout;
+
     public WindowManager.LayoutParams attrs;
     public int displayId;
     public boolean isVoiceInteraction;
@@ -56,7 +59,7 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            @Nullable Rect outsetFrame) {
+            @Nullable Rect outsetFrame, DisplayCutout displayCutout) {
         this.parentFrame.set(parentFrame);
         this.displayFrame.set(displayFrame);
         this.overscanFrame.set(overlayFrame);
@@ -65,6 +68,7 @@
         this.decorFrame.set(decorFrame);
         this.stableFrame.set(stableFrame);
         this.outsetFrame = outsetFrame == null ? null : new Rect(outsetFrame);
+        this.displayCutout = displayCutout;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index 0a644b60..353aa7d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -23,6 +23,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
 
@@ -37,7 +38,8 @@
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig,
-            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId)
+            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout)
             throws RemoteException {
 
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index fdcf57b..337fd50 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -22,12 +22,12 @@
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.Debug;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IWindow;
@@ -38,6 +38,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Arrays;
+
 /**
  * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
  *
@@ -158,7 +160,7 @@
         // When mFrame extends past cf, the content insets are
         // the difference between mFrame and ContentFrame. Visible
         // and stable frames work the same way.
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame,0, 0, 1000, 1000);
         assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
         assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -173,7 +175,7 @@
         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 100, 100, 200, 200);
         assertRect(w.mContentInsets, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
@@ -194,7 +196,7 @@
 
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // It can select various widths and heights within the bounds.
@@ -202,14 +204,14 @@
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         // Explicit width and height without requested width/height
         // gets us nothing.
         assertRect(w.mFrame, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
         assertRect(w.mFrame, 0, 0, 300, 300);
@@ -222,14 +224,14 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
@@ -237,7 +239,7 @@
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
@@ -247,16 +249,16 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 600, 600, 900, 900);
     }
 
@@ -277,7 +279,7 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, DisplayCutout.NO_CUTOUT);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -289,7 +291,7 @@
         final int cfRight = logicalWidth / 2;
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         int contentInsetRight = taskRight - cfRight;
         int contentInsetBottom = taskBottom - cfBottom;
@@ -306,7 +308,7 @@
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         contentInsetRight = insetRight - cfRight;
         contentInsetBottom = insetBottom - cfBottom;
@@ -338,13 +340,13 @@
 
         final Rect policyCrop = new Rect();
 
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
 
         dcf.setEmpty();
         // Likewise with no decor frame we would get no crop
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
 
@@ -357,7 +359,7 @@
         w.mAttrs.height = logicalHeight / 2;
         w.mRequestedWidth = logicalWidth / 2;
         w.mRequestedHeight = logicalHeight / 2;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
 
         w.calculatePolicyCrop(policyCrop);
         // Normally the crop is shrunk from the decor frame
@@ -394,7 +396,7 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
-                pf /* stableFrame */, null /* outsetFrame */);
+                pf /* stableFrame */, null /* outsetFrame */, DisplayCutout.NO_CUTOUT);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -413,12 +415,31 @@
 
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
-                cf /* stableFrame */, null /* outsetFrame */);
+                cf /* stableFrame */, null /* outsetFrame */, DisplayCutout.NO_CUTOUT);
         assertEquals(cf, w.mFrame);
         assertEquals(cf, w.getContentFrameLw());
         assertRect(w.mContentInsets, 0, 0, 0, 0);
     }
 
+    @Test
+    public void testDisplayCutout() {
+        // Regular fullscreen task and window
+        Task task = new TaskWithBounds(null);
+        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
+
+        final Rect pf = new Rect(0, 0, 1000, 1000);
+        // Create a display cutout of size 50x50, aligned top-center
+        final DisplayCutout cutout = createDisplayCutoutFromRect(500, 0, 550, 50);
+
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout);
+
+        assertEquals(w.mDisplayCutout.getSafeInsetTop(), 50);
+        assertEquals(w.mDisplayCutout.getSafeInsetBottom(), 0);
+        assertEquals(w.mDisplayCutout.getSafeInsetLeft(), 0);
+        assertEquals(w.mDisplayCutout.getSafeInsetRight(), 0);
+    }
+
     private WindowStateWithTask createWindow(Task task, int width, int height) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
         attrs.width = width;
@@ -426,4 +447,13 @@
 
         return new WindowStateWithTask(attrs, task);
     }
+
+    private DisplayCutout createDisplayCutoutFromRect(int left, int top, int right, int bottom) {
+        return DisplayCutout.fromBoundingPolygon(Arrays.asList(
+                new Point(left, top),
+                new Point (left, bottom),
+                new Point (right, bottom),
+                new Point (left, bottom)
+        ));
+    }
 }
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index 6b9bb31..9174014 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.View;
@@ -103,7 +104,8 @@
                         WindowManagerGlobal.getWindowSession().relayout(window,
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
-                                new MergedConfiguration(), new Surface());
+                                new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
+                                new Surface());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }