Merge "A brave new world for window insets (1/n)"
diff --git a/api/current.txt b/api/current.txt
index d1560edf2..9d46863 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13945,6 +13945,7 @@
   }
 
   public final class Insets {
+    method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
     method public static android.graphics.Insets of(int, int, int, int);
     method public static android.graphics.Insets of(android.graphics.Rect);
     field public static final android.graphics.Insets NONE;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 0277b7e..25bd033 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1461,7 +1461,6 @@
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
 Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
 Landroid/view/IWindowManager;->thawRotation()V
-Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession;
 Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
 Landroid/view/IWindowSession;->getInTouchMode()Z
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f6bb762..45d53f3 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -56,6 +56,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -184,6 +185,7 @@
         final DisplayCutout.ParcelableWrapper mDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
         DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
+        final InsetsState mInsetsState = new InsetsState();
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
 
         final WindowManager.LayoutParams mLayout
@@ -803,9 +805,11 @@
                         mLayout.windowAnimations =
                                 com.android.internal.R.style.Animation_Wallpaper;
                         mInputChannel = new InputChannel();
+
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                                 mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
-                                mOutsets, mDisplayCutout, mInputChannel) < 0) {
+                                mOutsets, mDisplayCutout, mInputChannel,
+                                mInsetsState) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -831,7 +835,8 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
+                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+                            mInsetsState);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b8b7f3..af41b69 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -24,6 +24,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.DisplayCutout;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 import android.util.MergedConfiguration;
@@ -53,6 +54,12 @@
             in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
             boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
             in DisplayCutout.ParcelableWrapper displayCutout);
+
+    /**
+     * Called when the window insets configuration has changed.
+     */
+    void insetsChanged(in InsetsState insetsState);
+
     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 bedfa9f..9762586 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -28,6 +28,7 @@
 import android.view.IWindowId;
 import android.view.MotionEvent;
 import android.view.WindowManager;
+import android.view.InsetsState;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -40,10 +41,11 @@
     int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outFrame,
             out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
-            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
+            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
+            out InsetsState insetsState);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
-            out Rect outStableInsets);
+            out Rect outStableInsets, out InsetsState insetsState);
     void remove(IWindow window);
 
     /**
@@ -86,6 +88,7 @@
      * config for window, if it is now becoming visible and the merged configuration has changed
      * since it was last displayed.
      * @param outSurface Object in which is placed the new display surface.
+     * @param insetsState The current insets state in the system.
      *
      * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
      * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -96,7 +99,8 @@
             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);
+            out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+            out InsetsState insetsState);
 
     /*
      * Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
new file mode 100644
index 0000000..7841d04
--- /dev/null
+++ b/core/java/android/view/InsetsController.java
@@ -0,0 +1,55 @@
+/*
+ * 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 android.view;
+
+import android.graphics.Rect;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link WindowInsetsController} on the client.
+ */
+class InsetsController {
+
+    private final InsetsState mState = new InsetsState();
+    private final Rect mFrame = new Rect();
+
+    void onFrameChanged(Rect frame) {
+        mFrame.set(frame);
+    }
+
+    public InsetsState getState() {
+        return mState;
+    }
+
+    public void setState(InsetsState state) {
+        mState.set(state);
+    }
+
+    /**
+     * @see InsetsState#calculateInsets
+     */
+    WindowInsets calculateInsets(boolean isScreenRound,
+            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix); pw.println("InsetsController:");
+        mState.dump(prefix + "  ", pw);
+    }
+}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
new file mode 100644
index 0000000..0cb8ad7
--- /dev/null
+++ b/core/java/android/view/InsetsSource.java
@@ -0,0 +1,159 @@
+/*
+ * 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 android.view;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents the state of a single window generating insets for clients.
+ * @hide
+ */
+public class InsetsSource implements Parcelable {
+
+    private final @InternalInsetType int mType;
+
+    /** Frame of the source in screen coordinate space */
+    private final Rect mFrame;
+    private boolean mVisible;
+
+    private final Rect mTmpFrame = new Rect();
+
+    public InsetsSource(@InternalInsetType int type) {
+        mType = type;
+        mFrame = new Rect();
+    }
+
+    public InsetsSource(InsetsSource other) {
+        mType = other.mType;
+        mFrame = new Rect(other.mFrame);
+        mVisible = other.mVisible;
+    }
+
+    public void setFrame(Rect frame) {
+        mFrame.set(frame);
+    }
+
+    public void setVisible(boolean visible) {
+        mVisible = visible;
+    }
+
+    public @InternalInsetType int getType() {
+        return mType;
+    }
+
+    public Rect getFrame() {
+        return mFrame;
+    }
+
+    /**
+     * Calculates the insets this source will cause to a client window.
+     *
+     * @param relativeFrame The frame to calculate the insets relative to.
+     * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
+     * @return The resulting insets.
+     */
+    public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+        if (!ignoreVisibility && !mVisible) {
+            return Insets.NONE;
+        }
+        if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+            return Insets.NONE;
+        }
+
+        // Intersecting at top/bottom
+        if (mTmpFrame.width() == relativeFrame.width()) {
+            if (mTmpFrame.top == relativeFrame.top) {
+                return Insets.of(0, mTmpFrame.height(), 0, 0);
+            } else {
+                return Insets.of(0, 0, 0, mTmpFrame.height());
+            }
+        }
+        // Intersecting at left/right
+        else if (mTmpFrame.height() == relativeFrame.height()) {
+            if (mTmpFrame.left == relativeFrame.left) {
+                return Insets.of(mTmpFrame.width(), 0, 0, 0);
+            } else {
+                return Insets.of(0, 0, mTmpFrame.width(), 0);
+            }
+        } else {
+            return Insets.NONE;
+        }
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix);
+        pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
+        pw.print(" frame="); pw.print(mFrame.toShortString());
+        pw.print(" visible="); pw.print(mVisible);
+        pw.println();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        InsetsSource that = (InsetsSource) o;
+
+        if (mType != that.mType) return false;
+        if (mVisible != that.mVisible) return false;
+        return mFrame.equals(that.mFrame);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mType;
+        result = 31 * result + mFrame.hashCode();
+        result = 31 * result + (mVisible ? 1 : 0);
+        return result;
+    }
+
+    public InsetsSource(Parcel in) {
+        mType = in.readInt();
+        mFrame = in.readParcelable(null /* loader */);
+        mVisible = in.readBoolean();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeParcelable(mFrame, 0 /* flags*/);
+        dest.writeBoolean(mVisible);
+    }
+
+    public static final Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
+
+        public InsetsSource createFromParcel(Parcel in) {
+            return new InsetsSource(in);
+        }
+
+        public InsetsSource[] newArray(int size) {
+            return new InsetsSource[size];
+        }
+    };
+}
diff --git a/core/java/android/view/InsetsState.aidl b/core/java/android/view/InsetsState.aidl
new file mode 100644
index 0000000..d02ddd1
--- /dev/null
+++ b/core/java/android/view/InsetsState.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 InsetsState;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
new file mode 100644
index 0000000..9895adc
--- /dev/null
+++ b/core/java/android/view/InsetsState.java
@@ -0,0 +1,240 @@
+/*
+ * 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 android.view;
+
+import android.annotation.IntDef;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Holder for state of system windows that cause window insets for all other windows in the system.
+ * @hide
+ */
+public class InsetsState implements Parcelable {
+
+    /**
+     * Internal representation of inset source types. This is different from the public API in
+     * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
+     * at the same time.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "TYPE", value = {
+            TYPE_TOP_BAR,
+            TYPE_SIDE_BAR_1,
+            TYPE_SIDE_BAR_2,
+            TYPE_SIDE_BAR_3,
+            TYPE_IME
+    })
+    public @interface InternalInsetType {}
+
+    static final int FIRST_TYPE = 0;
+
+    /** Top bar. Can be status bar or caption in freeform windowing mode. */
+    public static final int TYPE_TOP_BAR = FIRST_TYPE;
+
+    /**
+     * Up to 3 side bars that appear on left/right/bottom. On phones there is only one side bar
+     * (the navigation bar, see {@link #TYPE_NAVIGATION_BAR}), but other form factors might have
+     * multiple, like Android Auto.
+     */
+    public static final int TYPE_SIDE_BAR_1 = 1;
+    public static final int TYPE_SIDE_BAR_2 = 2;
+    public static final int TYPE_SIDE_BAR_3 = 3;
+
+    /** Input method window. */
+    public static final int TYPE_IME = 4;
+    static final int LAST_TYPE = TYPE_IME;
+
+    // Derived types
+
+    /** First side bar is navigation bar. */
+    public static final int TYPE_NAVIGATION_BAR = TYPE_SIDE_BAR_1;
+
+    /** A shelf is the same as the navigation bar. */
+    public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
+
+    private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
+
+    public InsetsState() {
+    }
+
+    /**
+     * Calculates {@link WindowInsets} based on the current source configuration.
+     *
+     * @param frame The frame to calculate the insets relative to.
+     * @return The calculated insets.
+     */
+    public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
+            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+        Insets systemInsets = Insets.NONE;
+        Insets maxInsets = Insets.NONE;
+        final Rect relativeFrame = new Rect(frame);
+        final Rect relativeFrameMax = new Rect(frame);
+        for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+            InsetsSource source = mSources.get(type);
+            if (source == null) {
+                continue;
+            }
+            systemInsets = processSource(source, systemInsets, relativeFrame,
+                    false /* ignoreVisibility */);
+
+            // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
+            // target.
+            if (source.getType() != TYPE_IME) {
+                maxInsets = processSource(source, maxInsets, relativeFrameMax,
+                        true /* ignoreVisibility */);
+            }
+        }
+        return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+                alwaysConsumeNavBar, cutout);
+    }
+
+    private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
+            boolean ignoreVisibility) {
+        Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
+        insets = Insets.add(currentInsets, insets);
+        relativeFrame.inset(insets);
+        return insets;
+    }
+
+    public InsetsSource getSource(@InternalInsetType int type) {
+        return mSources.computeIfAbsent(type, InsetsSource::new);
+    }
+
+    /**
+     * Modifies the state of this class to exclude a certain type to make it ready for dispatching
+     * to the client.
+     *
+     * @param type The {@link InternalInsetType} of the source to remove
+     */
+    public void removeSource(int type) {
+        mSources.remove(type);
+    }
+
+    public void set(InsetsState other) {
+        set(other, false /* copySources */);
+    }
+
+    public void set(InsetsState other, boolean copySources) {
+        mSources.clear();
+        if (copySources) {
+            for (int i = 0; i < other.mSources.size(); i++) {
+                InsetsSource source = other.mSources.valueAt(i);
+                mSources.put(source.getType(), new InsetsSource(source));
+            }
+        } else {
+            mSources.putAll(other.mSources);
+        }
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "InsetsState");
+        for (int i = mSources.size() - 1; i >= 0; i--) {
+            mSources.valueAt(i).dump(prefix + "  ", pw);
+        }
+    }
+
+    static String typeToString(int type) {
+        switch (type) {
+            case TYPE_TOP_BAR:
+                return "TYPE_TOP_BAR";
+            case TYPE_SIDE_BAR_1:
+                return "TYPE_SIDE_BAR_1";
+            case TYPE_SIDE_BAR_2:
+                return "TYPE_SIDE_BAR_2";
+            case TYPE_SIDE_BAR_3:
+                return "TYPE_SIDE_BAR_3";
+            case TYPE_IME:
+                return "TYPE_IME";
+            default:
+                return "TYPE_UNKNOWN";
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) { return true; }
+        if (o == null || getClass() != o.getClass()) { return false; }
+
+        InsetsState state = (InsetsState) o;
+
+        if (mSources.size() != state.mSources.size()) {
+            return false;
+        }
+        for (int i = mSources.size() - 1; i >= 0; i--) {
+            InsetsSource source = mSources.valueAt(i);
+            InsetsSource otherSource = state.mSources.get(source.getType());
+            if (otherSource == null) {
+                return false;
+            }
+            if (!otherSource.equals(source)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return mSources.hashCode();
+    }
+
+    public InsetsState(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSources.size());
+        for (int i = 0; i < mSources.size(); i++) {
+            dest.writeParcelable(mSources.valueAt(i), 0 /* flags */);
+        }
+    }
+
+    public static final Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
+
+        public InsetsState createFromParcel(Parcel in) {
+            return new InsetsState(in);
+        }
+
+        public InsetsState[] newArray(int size) {
+            return new InsetsState[size];
+        }
+    };
+
+    public void readFromParcel(Parcel in) {
+        mSources.clear();
+        final int size = in.readInt();
+        for (int i = 0; i < size; i++) {
+            final InsetsSource source = in.readParcelable(null /* loader */);
+            mSources.put(source.getType(), source);
+        }
+    }
+}
+
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8e9f2d7..d9d52c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -161,6 +161,19 @@
     private static final boolean MT_RENDERER_AVAILABLE = true;
 
     /**
+     * If set to true, the view system will switch from using rectangles retrieved from window to
+     * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
+     * directly from the full configuration, enabling richer information about the insets state, as
+     * well as new APIs to control it frame-by-frame, and synchronize animations with it.
+     * <p>
+     * Only switch this to true once the new insets system is productionized and the old APIs are
+     * fully migrated over.
+     */
+    private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets";
+    private static final boolean USE_NEW_INSETS =
+            SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false);
+
+    /**
      * Set this system property to true to force the view hierarchy to render
      * at 60 Hz. This can be used to measure the potential framerate.
      */
@@ -432,6 +445,8 @@
     boolean mAdded;
     boolean mAddedTouchMode;
 
+    final Rect mTmpFrame = new Rect();
+
     // These are accessed by multiple threads.
     final Rect mWinFrame; // frame given by window manager.
 
@@ -444,6 +459,7 @@
     final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
             new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeNavBar;
+    private InsetsState mPendingInsets;
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
@@ -531,6 +547,8 @@
             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
                     new InputEventConsistencyVerifier(this, 0) : null;
 
+    private final InsetsController mInsetsController = new InsetsController();
+
     static final class SystemUiVisibilityInfo {
         int seq;
         int globalVisibility;
@@ -797,9 +815,11 @@
                     mAttachInfo.mRecomputeGlobalAttributes = true;
                     collectViewAttributes();
                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
-                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
+                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
-                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
+                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
+                            mInsetsController.getState());
+                    setFrame(mTmpFrame);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
@@ -826,6 +846,7 @@
                 mAttachInfo.mAlwaysConsumeNavBar =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
                 mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
+                mPendingInsets = mInsetsController.getState();
                 if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
                     mAttachInfo.mRootView = null;
@@ -1771,7 +1792,8 @@
             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.
+            // immediately use pending insets. This is such that getRootWindowInsets returns the
+            // result from the layout hint before we ran a traversal shortly after adding a window.
             if (!forceConstruct
                     && (!mPendingContentInsets.equals(contentInsets) ||
                         !mPendingStableInsets.equals(stableInsets) ||
@@ -1788,10 +1810,16 @@
             }
             contentInsets = ensureInsetsNonNegative(contentInsets, "content");
             stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
-            mLastWindowInsets = new WindowInsets(contentInsets,
-                    null /* windowDecorInsets */, stableInsets,
-                    mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            if (USE_NEW_INSETS) {
+                mLastWindowInsets = mInsetsController.calculateInsets(
+                        mContext.getResources().getConfiguration().isScreenRound(),
+                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            } else {
+                mLastWindowInsets = new WindowInsets(contentInsets,
+                        null /* windowDecorInsets */, stableInsets,
+                        mContext.getResources().getConfiguration().isScreenRound(),
+                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            }
         }
         return mLastWindowInsets;
     }
@@ -1991,6 +2019,9 @@
                 if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
                     insetsChanged = true;
                 }
+                if (!mPendingInsets.equals(mInsetsController.getState())) {
+                    insetsChanged = true;
+                }
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
@@ -2184,6 +2215,8 @@
                         mAttachInfo.mStableInsets);
                 final boolean cutoutChanged = !mPendingDisplayCutout.equals(
                         mAttachInfo.mDisplayCutout);
+                final boolean insetsStateChanged = !mPendingInsets.equals(
+                        mInsetsController.getState());
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -2221,6 +2254,10 @@
                     mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
                     contentInsetsChanged = true;
                 }
+                if (insetsStateChanged) {
+                    mInsetsController.setState(mPendingInsets);
+                    contentInsetsChanged = true;
+                }
                 if (contentInsetsChanged || mLastSystemUiVisibility !=
                         mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
                         || mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -2666,7 +2703,6 @@
     }
 
     private void maybeHandleWindowMove(Rect frame) {
-
         // TODO: Well, we are checking whether the frame has changed similarly
         // to how this is done for the insets. This is however incorrect since
         // the insets and the frame are translated. For example, the old frame
@@ -4171,6 +4207,7 @@
     private final static int MSG_UPDATE_POINTER_ICON = 27;
     private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
     private final static int MSG_DRAW_FINISHED = 29;
+    private final static int MSG_INSETS_CHANGED = 30;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -4226,6 +4263,8 @@
                     return "MSG_POINTER_CAPTURE_CHANGED";
                 case MSG_DRAW_FINISHED:
                     return "MSG_DRAW_FINISHED";
+                case MSG_INSETS_CHANGED:
+                    return "MSG_INSETS_CHANGED";
             }
             return super.getMessageName(message);
         }
@@ -4306,7 +4345,7 @@
                                 || !mPendingVisibleInsets.equals(args.arg3)
                                 || !mPendingOutsets.equals(args.arg7);
 
-                        mWinFrame.set((Rect) args.arg1);
+                        setFrame((Rect) args.arg1);
                         mPendingOverscanInsets.set((Rect) args.arg5);
                         mPendingContentInsets.set((Rect) args.arg2);
                         mPendingStableInsets.set((Rect) args.arg6);
@@ -4329,16 +4368,25 @@
                         requestLayout();
                     }
                     break;
+                case MSG_INSETS_CHANGED:
+                    mPendingInsets = (InsetsState) msg.obj;
+
+                    // TODO: Full traversal not needed here
+                    if (USE_NEW_INSETS) {
+                        requestLayout();
+                    }
+                    break;
                 case MSG_WINDOW_MOVED:
                     if (mAdded) {
                         final int w = mWinFrame.width();
                         final int h = mWinFrame.height();
                         final int l = msg.arg1;
                         final int t = msg.arg2;
-                        mWinFrame.left = l;
-                        mWinFrame.right = l + w;
-                        mWinFrame.top = t;
-                        mWinFrame.bottom = t + h;
+                        mTmpFrame.left = l;
+                        mTmpFrame.right = l + w;
+                        mTmpFrame.top = t;
+                        mTmpFrame.bottom = t + h;
+                        setFrame(mTmpFrame);
 
                         mPendingBackDropFrame.set(mWinFrame);
                         maybeHandleWindowMove(mWinFrame);
@@ -6724,9 +6772,9 @@
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
-                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
+                mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
-                mPendingMergedConfiguration, mSurface);
+                mPendingMergedConfiguration, mSurface, mPendingInsets);
 
         mPendingAlwaysConsumeNavBar =
                 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6736,15 +6784,22 @@
         }
 
         if (mTranslator != null) {
-            mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+            mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
             mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
         }
+        setFrame(mTmpFrame);
+
         return relayoutResult;
     }
 
+    private void setFrame(Rect frame) {
+        mWinFrame.set(frame);
+        mInsetsController.onFrameChanged(frame);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -6847,6 +6902,8 @@
 
         mChoreographer.dump(prefix, writer);
 
+        mInsetsController.dump(prefix, writer);
+
         writer.print(prefix); writer.println("View Hierarchy:");
         dumpViewHierarchy(innerPrefix, writer, mView);
     }
@@ -7055,6 +7112,10 @@
         mHandler.sendMessage(msg);
     }
 
+    private void dispatchInsetsChanged(InsetsState insetsState) {
+        mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
+    }
+
     public void dispatchMoved(int newX, int newY) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
         if (mTranslator != null) {
@@ -8118,6 +8179,14 @@
         }
 
         @Override
+        public void insetsChanged(InsetsState insetsState) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchInsetsChanged(insetsState);
+            }
+        }
+
+        @Override
         public void moved(int newX, int newY) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index b9d53c1..d78bfac 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -120,6 +120,8 @@
         arg5 = null;
         arg6 = null;
         arg7 = null;
+        arg8 = null;
+        arg9 = null;
         argi1 = 0;
         argi2 = 0;
         argi3 = 0;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 137ca7f..36fe4fc 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -27,6 +27,7 @@
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.PointerIcon;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -53,6 +54,10 @@
     }
 
     @Override
+    public void insetsChanged(InsetsState insetsState) {
+    }
+
+    @Override
     public void moved(int newX, int newY) {
     }
 
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
new file mode 100644
index 0000000..ed472d2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 android.view;
+
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceTest {
+
+    private InsetsSource mSource = new InsetsSource(TYPE_NAVIGATION_BAR);
+
+    @Before
+    public void setUp() {
+        mSource.setVisible(true);
+    }
+
+    @Test
+    public void testCalculateInsetsTop() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsBottom() {
+        mSource.setFrame(new Rect(0, 400, 500, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 0, 100), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsLeft() {
+        mSource.setFrame(new Rect(0, 0, 100, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(100, 0, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsRight() {
+        mSource.setFrame(new Rect(400, 0, 500, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 100, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_overextend() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_invisible() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        mSource.setVisible(false);
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_ignoreVisibility() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        mSource.setVisible(false);
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                true /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    // Parcel and equals already tested via InsetsStateTest
+}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
new file mode 100644
index 0000000..6bb9539
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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 android.view;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsStateTest {
+
+    private InsetsState mState = new InsetsState();
+    private InsetsState mState2 = new InsetsState();
+
+    @Test
+    public void testCalculateInsets() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testCalculateInsets_imeAndNav() {
+        mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(100, insets.getStableInsetBottom());
+        assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testCalculateInsets_navRightStatusTop() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+        mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testStripForDispatch() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState.removeSource(TYPE_IME);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(0, insets.getSystemWindowInsetBottom());
+    }
+
+    @Test
+    public void testEquals_differentRect() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_differentSource() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_sameButDifferentInsertOrder() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        assertEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_visibility() {
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        Parcel p = Parcel.obtain();
+        mState.writeToParcel(p, 0 /* flags */);
+        mState2.readFromParcel(p);
+        p.recycle();
+        assertEquals(mState, mState2);
+    }
+}
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index de110c8..d9da27c 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -82,6 +82,17 @@
     }
 
     /**
+     * Add two Insets.
+     *
+     * @param a The first Insets to add.
+     * @param b The second Insets to add.
+     * @return a + b, i. e. all insets on every side are added together.
+     */
+    public static @NonNull Insets add(@NonNull Insets a, @NonNull Insets b) {
+        return Insets.of(a.left + b.left, a.top + b.top, a.right + b.right, a.bottom + b.bottom);
+    }
+
+    /**
      * Two Insets instances are equal iff they belong to the same class and their fields are
      * pairwise equal.
      *
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index c4dc0ad..40a32f3 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -106,6 +106,20 @@
     }
 
     /**
+     * @hide
+     */
+    public Rect(@Nullable Insets r) {
+        if (r == null) {
+            left = top = right = bottom = 0;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
+    }
+
+    /**
      * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
      *
      * @hide
@@ -418,6 +432,18 @@
     }
 
     /**
+     * Insets the rectangle on all sides specified by the dimensions of {@code insets}.
+     * @hide
+     * @param insets The insets to inset the rect by.
+     */
+    public void inset(Insets insets) {
+        left += insets.left;
+        top += insets.top;
+        right -= insets.right;
+        bottom -= insets.bottom;
+    }
+
+    /**
      * Insets the rectangle on all sides specified by the insets.
      * @hide
      * @param left The amount to add from the rectangle's left
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a834ef1..c0e9836 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -34,6 +34,7 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.GONE;
+import static android.view.InsetsState.TYPE_IME;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_TOP;
@@ -123,6 +124,7 @@
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -150,6 +152,7 @@
 import android.view.Gravity;
 import android.view.InputChannel;
 import android.view.InputDevice;
+import android.view.InsetsState.InternalInsetType;
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -161,6 +164,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.TriConsumer;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.DisplayRotationUtil;
 import com.android.server.wm.utils.RotationCache;
@@ -506,6 +510,8 @@
 
     private final PointerEventDispatcher mPointerEventDispatcher;
 
+    private final InsetsStateController mInsetsStateController;
+
     // Last systemUiVisibility we received from status bar.
     private int mLastStatusBarVisibility = 0;
     // Last systemUiVisibility we dispatched to windows.
@@ -902,6 +908,7 @@
 
         mService.mAnimator.addDisplayLocked(mDisplayId);
         mInputMonitor = new InputMonitor(service, mDisplayId);
+        mInsetsStateController = new InsetsStateController(this);
     }
 
     boolean isReady() {
@@ -1038,6 +1045,23 @@
         return mDisplayRotation;
     }
 
+    /**
+     * Marks a window as providing insets for the rest of the windows in the system.
+     *
+     * @param type The type of inset this window provides.
+     * @param win The window.
+     * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of
+     *                      the window should be taken.
+     */
+    void setInsetProvider(@InternalInsetType int type, WindowState win,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+        mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
+    }
+
+    InsetsStateController getInsetsStateController() {
+        return mInsetsStateController;
+    }
+
     @VisibleForTesting
     void setDisplayRotation(DisplayRotation displayRotation) {
         mDisplayRotation = displayRotation;
@@ -2712,6 +2736,8 @@
         mDisplayRotation.dump(prefix, pw);
         pw.println();
         mInputMonitor.dump(pw, "  ");
+        pw.println();
+        mInsetsStateController.dump(prefix, pw);
     }
 
     @Override
@@ -2994,6 +3020,8 @@
                     mInputMethodWindow.getDisplayId());
         }
         computeImeTarget(true /* updateImeTarget */);
+        mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win,
+                null /* frameProvider */);
     }
 
     /**
@@ -3449,6 +3477,7 @@
             pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                     "after finishPostLayoutPolicyLw", pendingLayoutChanges);
+                mInsetsStateController.onPostLayout();
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c16f95e..0e5947a 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -128,6 +129,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.Surface;
@@ -804,6 +806,11 @@
                 if (mDisplayContent.isDefaultDisplay) {
                     mService.mPolicy.setKeyguardCandidateLw(win);
                 }
+                mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win,
+                        (displayFrames, windowState, rect) -> {
+                            rect.top = 0;
+                            rect.bottom = getStatusBarHeight(displayFrames);
+                        });
                 break;
             case TYPE_NAVIGATION_BAR:
                 mContext.enforceCallingOrSelfPermission(
@@ -818,6 +825,8 @@
                 mNavigationBarController.setWindow(win);
                 mNavigationBarController.setOnBarVisibilityChangedListener(
                         mNavBarVisibilityListener, true);
+                mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR,
+                        win, null /* frameProvider */);
                 if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
                 break;
             case TYPE_NAVIGATION_BAR_PANEL:
@@ -845,9 +854,11 @@
             if (mDisplayContent.isDefaultDisplay) {
                 mService.mPolicy.setKeyguardCandidateLw(null);
             }
+            mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null);
         } else if (mNavigationBar == win) {
             mNavigationBar = null;
             mNavigationBarController.setWindow(null);
+            mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null);
         }
         if (mLastFocusedWindow == win) {
             mLastFocusedWindow = null;
@@ -855,6 +866,11 @@
         mScreenDecorWindows.remove(win);
     }
 
+    private int getStatusBarHeight(DisplayFrames displayFrames) {
+        return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
+                displayFrames.mDisplayCutoutSafe.top);
+    }
+
     /**
      * Control the animation to run when a window's state changes.  Return a
      * non-0 number to force the animation to a specific resource ID, or 0
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
new file mode 100644
index 0000000..e96f0b1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.InsetsSource;
+
+import com.android.internal.util.function.TriConsumer;
+import com.android.server.policy.WindowManagerPolicy;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
+ */
+class InsetsSourceProvider {
+
+    private final Rect mTmpRect = new Rect();
+    private final @NonNull InsetsSource mSource;
+    private WindowState mWin;
+    private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+
+    InsetsSourceProvider(InsetsSource source) {
+        mSource = source;
+    }
+
+    InsetsSource getSource() {
+        return mSource;
+    }
+
+    /**
+     * Updates the window that currently backs this source.
+     *
+     * @param win The window that links to this source.
+     * @param frameProvider Based on display frame state and the window, calculates the resulting
+     *                      frame that should be reported to clients.
+     */
+    void setWindow(@Nullable WindowState win,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+        if (mWin != null) {
+            mWin.setInsetProvider(null);
+        }
+        mWin = win;
+        mFrameProvider = frameProvider;
+        if (win == null) {
+            mSource.setVisible(false);
+            mSource.setFrame(new Rect());
+        } else {
+            mSource.setVisible(true);
+            mWin.setInsetProvider(this);
+        }
+    }
+
+    /**
+     * Called when a layout pass has occurred.
+     */
+    void onPostLayout() {
+        if (mWin == null) {
+            return;
+        }
+
+        mTmpRect.set(mWin.getFrameLw());
+        if (mFrameProvider != null) {
+            mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+        } else {
+            mTmpRect.inset(mWin.mGivenContentInsets);
+        }
+        mSource.setFrame(mTmpRect);
+        mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
+
+    }
+}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
new file mode 100644
index 0000000..1189ee6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+
+import android.util.ArrayMap;
+import android.view.InsetsState;
+
+import java.io.PrintWriter;
+import java.util.function.Consumer;
+
+/**
+ * Manages global window inset state in the system represented by {@link InsetsState}.
+ */
+class InsetsStateController {
+
+    private final InsetsState mLastState = new InsetsState();
+    private final InsetsState mState = new InsetsState();
+    private final DisplayContent mDisplayContent;
+    private ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
+
+    private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
+        if (w.isVisible()) {
+            w.notifyInsetsChanged();
+        }
+    };
+
+    InsetsStateController(DisplayContent displayContent) {
+        mDisplayContent = displayContent;
+    }
+
+    /**
+     * When dispatching window state to the client, we'll need to exclude the source that represents
+     * the window that is being dispatched.
+     *
+     * @param target The client we dispatch the state to.
+     * @return The state stripped of the necessary information.
+     */
+    InsetsState getInsetsForDispatch(WindowState target) {
+        final InsetsSourceProvider provider = target.getInsetProvider();
+        if (provider == null) {
+            return mState;
+        }
+
+        final InsetsState state = new InsetsState();
+        state.set(mState);
+        final int type = provider.getSource().getType();
+        state.removeSource(type);
+
+        // Navigation bar doesn't get influenced by anything else
+        if (type == TYPE_NAVIGATION_BAR) {
+            state.removeSource(TYPE_IME);
+            state.removeSource(TYPE_TOP_BAR);
+        }
+        return state;
+    }
+
+    /**
+     * @return The provider of a specific type.
+     */
+    InsetsSourceProvider getSourceProvider(int type) {
+        return mControllers.computeIfAbsent(type,
+                key -> new InsetsSourceProvider(mState.getSource(key)));
+    }
+
+    /**
+     * Called when a layout pass has occurred.
+     */
+    void onPostLayout() {
+        for (int i = mControllers.size() - 1; i>= 0; i--) {
+            mControllers.valueAt(i).onPostLayout();
+        }
+        if (!mLastState.equals(mState)) {
+            mLastState.set(mState, true /* copySources */);
+            notifyInsetsChanged();
+        }
+    }
+
+    private void notifyInsetsChanged() {
+        mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "WindowInsetsStateController");
+        mState.dump(prefix + "  ", pw);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6838c55..37b5a7c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -50,6 +50,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.InsetsState;
 import android.view.WindowManager;
 
 import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -153,17 +154,21 @@
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets, Rect outOutsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+            InsetsState outInsetsState) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
-                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
+                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
+                outInsetsState);
     }
 
     @Override
     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
+            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
+            InsetsState outInsetsState) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                 new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
-                new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */);
+                new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
+                outInsetsState);
     }
 
     @Override
@@ -182,7 +187,7 @@
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
             Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface) {
+            Surface outSurface, InsetsState outInsetsState) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -190,7 +195,7 @@
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurface);
+                mergedConfiguration, outSurface, outInsetsState);
         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 8f02f8a..9a56606 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -66,6 +66,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -142,6 +143,7 @@
         final Rect taskBounds;
         final Rect tmpContentInsets = new Rect();
         final Rect tmpStableInsets = new Rect();
+        final InsetsState mTmpInsetsState = new InsetsState();
         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
         int backgroundColor = WHITE;
         int statusBarColor = 0;
@@ -202,7 +204,7 @@
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                     View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
-                    tmpRect, tmpCutout, null);
+                    tmpRect, tmpCutout, null, mTmpInsetsState);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -218,7 +220,7 @@
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpCutout, tmpMergedConfiguration, surface);
+                    tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
         } catch (RemoteException e) {
             // Local call.
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5df3451..b91afcd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -218,6 +218,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.WindowContentFrameStats;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.RemoveContentMode;
@@ -1111,7 +1112,8 @@
     public int addWindow(Session session, IWindow client, int seq,
             LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
             Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+            InsetsState outInsetsState) {
         int[] appOp = new int[1];
         int res = mPolicy.checkAddPermission(attrs, appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1459,6 +1461,7 @@
                     outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
+            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
 
             if (mInTouchMode) {
                 res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -1856,7 +1859,7 @@
             long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface) {
+            Surface outSurface, InsetsState outInsetsState) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -2157,6 +2160,7 @@
                     outStableInsets, outOutsets);
             outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
             outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
+            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
             if (localLOGV) Slog.v(
                 TAG_WM, "Relayout given client " + client.asBinder()
                 + ", requestedWidth=" + requestedWidth
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 91c3288..cfd1f86 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -143,6 +143,7 @@
 import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
 
 import android.annotation.CallSuper;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -194,6 +195,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowManagerPolicy.DisplayContentInfo;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.utils.InsetUtils;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -578,6 +580,8 @@
      */
     private boolean mIsDimming = false;
 
+    private @Nullable InsetsSourceProvider mInsetProvider;
+
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
 
     void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@@ -2955,6 +2959,18 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
+    /**
+     * Called when the insets state changed.
+     */
+    void notifyInsetsChanged() {
+        try {
+            mClient.insetsChanged(
+                    getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to deliver inset state change", e);
+        }
+    }
+
     Rect getBackdropFrame(Rect frame) {
         // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
         // start even if we haven't received the relayout window, so that the client requests
@@ -4776,6 +4792,14 @@
         mWindowFrames.setContentChanged(false);
     }
 
+    void setInsetProvider(InsetsSourceProvider insetProvider) {
+        mInsetProvider = insetProvider;
+    }
+
+    InsetsSourceProvider getInsetProvider() {
+        return mInsetProvider;
+    }
+
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java
new file mode 100644
index 0000000..241b987
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsSourceProviderTest extends WindowTestsBase {
+
+    private InsetsSourceProvider mProvider = new InsetsSourceProvider(
+            new InsetsSource(TYPE_TOP_BAR));
+
+    @Test
+    public void testPostLayout() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        topBar.mHasSurface = true;
+        mProvider.setWindow(topBar, null);
+        mProvider.onPostLayout();
+        assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 100, 0, 0),
+                mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+    }
+
+    @Test
+    public void testPostLayout_invisible() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar, null);
+        mProvider.onPostLayout();
+        assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+    }
+
+    @Test
+    public void testPostLayout_frameProvider() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar,
+                (displayFrames, windowState, rect) -> {
+                    rect.set(10, 10, 20, 20);
+                });
+        mProvider.onPostLayout();
+        assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java
new file mode 100644
index 0000000..7505db1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsState;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsStateControllerTest extends WindowTestsBase {
+
+    @Test
+    public void testStripForDispatch_notOwn() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        topBar.setInsetProvider(
+                mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR));
+        assertNotNull(mDisplayContent.getInsetsStateController().getInsetsForDispatch(app)
+                .getSource(TYPE_TOP_BAR));
+    }
+
+    @Test
+    public void testStripForDispatch_own() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        topBar.setInsetProvider(
+                mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR));
+        assertEquals(new InsetsState(),
+                mDisplayContent.getInsetsStateController().getInsetsForDispatch(topBar));
+    }
+
+    @Test
+    public void testStripForDispatch_navBar() {
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_NAVIGATION_BAR)
+                .setWindow(navBar, null);
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_IME)
+                .setWindow(ime, null);
+        assertEquals(new InsetsState(),
+                mDisplayContent.getInsetsStateController().getInsetsForDispatch(navBar));
+    }
+}
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 99deeb9..432af0d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -24,6 +24,7 @@
 import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -39,6 +40,9 @@
             Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
             DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException {
     }
+    @Override
+    public void insetsChanged(InsetsState insetsState) throws RemoteException {
+    }
 
     @Override
     public void moved(int newX, int newY) throws RemoteException {
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index ae3914e..d5987a5 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -26,6 +26,7 @@
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.IWindowSession;
+import android.view.InsetsState;
 import android.view.Surface;
 import android.view.View;
 import android.view.WindowManager;
@@ -105,7 +106,7 @@
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
                                 new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
-                                new Surface());
+                                new Surface(), new InsetsState());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
@@ -131,8 +132,9 @@
             final IWindowSession session = WindowManagerGlobal.getWindowSession();
             final Rect tmpRect = new Rect();
             try {
-                final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams,
-                        View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect);
+                final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq,
+                        layoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect,
+                        new InsetsState());
             } catch (RemoteException e) {
                 e.printStackTrace();
             }