Window Manager Flag Migration (4/n)

Wire up the appearance and the transient state of system bars between
WMS and System UI. The derived classes of CommandQueue.Callbacks no
longer listen to setSystemUiVisibility, but listen to showTransient,
abortTransient, and onSystemBarAppearanceChanged instead.

Bug: 118118435
Test: atest InsetsSourceProviderTest InsetsStateControllerTest
            InsetsPolicyTest WindowStateTests CommandQueueTest
            RegisterStatusBarResultTest InsetsFlagsTest
            LightBarControllerTest
Test: build on specific target
Change-Id: Ie35f4b4468bce7ef8c76f091e306610c069fba85
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 35cfe9e..7f717a7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -306,6 +306,11 @@
     oneway void statusBarVisibilityChanged(int displayId, int visibility);
 
     /**
+     * Called by System UI to notify Window Manager to hide transient bars.
+     */
+    oneway void hideTransientBars(int displayId);
+
+    /**
     * When set to {@code true} the system bars will always be shown. This is true even if an app
     * requests to be fullscreen by setting the system ui visibility flags. The
     * functionality was added for the automotive case as a way to guarantee required content stays
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index e4deffa..0fb1c33 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -146,20 +146,11 @@
         }
         final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
         ArrayList<SurfaceParams> params = new ArrayList<>();
-        if (offset.left != 0) {
-            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, mPendingInsets.left, params, state);
-        }
-        if (offset.top != 0) {
-            updateLeashesForSide(INSET_SIDE_TOP, offset.top, mPendingInsets.top, params, state);
-        }
-        if (offset.right != 0) {
-            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, mPendingInsets.right, params,
-                    state);
-        }
-        if (offset.bottom != 0) {
-            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params,
-                    state);
-        }
+        updateLeashesForSide(INSET_SIDE_LEFT, offset.left, mPendingInsets.left, params, state);
+        updateLeashesForSide(INSET_SIDE_TOP, offset.top, mPendingInsets.top, params, state);
+        updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, mPendingInsets.right, params, state);
+        updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params,
+                state);
         SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
         applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
         mCurrentInsets = mPendingInsets;
@@ -224,6 +215,9 @@
     private void updateLeashesForSide(@InsetSide int side, int offset, int inset,
             ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
         ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
+        if (items == null) {
+            return;
+        }
         // TODO: Implement behavior when inset spans over multiple types
         for (int i = items.size() - 1; i >= 0; i--) {
             final InsetsSourceConsumer consumer = items.valueAt(i);
@@ -274,9 +268,15 @@
             SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
             SparseArray<InsetsSourceConsumer> consumers) {
         for (int i = typeSideMap.size() - 1; i >= 0; i--) {
-            int type = typeSideMap.keyAt(i);
-            int side = typeSideMap.valueAt(i);
-            sideSourcesMap.add(side, consumers.get(type));
+            final int type = typeSideMap.keyAt(i);
+            final int side = typeSideMap.valueAt(i);
+            final InsetsSourceConsumer consumer = consumers.get(type);
+            if (consumer == null) {
+                // If the types that we are controlling are less than the types that the system has,
+                // there can be some null consumers.
+                continue;
+            }
+            sideSourcesMap.add(side, consumer);
         }
     }
 }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5bb4f63..eca6dcb 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -67,9 +67,9 @@
      * Translation animation evaluator.
      */
     private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
-            0,
+            (int) (startValue.left + fraction * (endValue.left - startValue.left)),
             (int) (startValue.top + fraction * (endValue.top - startValue.top)),
-            0,
+            (int) (startValue.right + fraction * (endValue.right - startValue.right)),
             (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
 
     /**
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index 276e80a..6e459b2 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -16,10 +16,18 @@
 
 package android.view;
 
+import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
+import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
+import static android.view.View.STATUS_BAR_TRANSLUCENT;
+import static android.view.View.STATUS_BAR_TRANSPARENT;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_SIDE_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_TOP_BAR;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 
@@ -35,9 +43,13 @@
 
     @ViewDebug.ExportedProperty(flagMapping = {
             @ViewDebug.FlagToString(
-                    mask = APPEARANCE_OPAQUE_BARS,
-                    equals = APPEARANCE_OPAQUE_BARS,
-                    name = "OPAQUE_BARS"),
+                    mask = APPEARANCE_OPAQUE_TOP_BAR,
+                    equals = APPEARANCE_OPAQUE_TOP_BAR,
+                    name = "OPAQUE_TOP_BAR"),
+            @ViewDebug.FlagToString(
+                    mask = APPEARANCE_OPAQUE_SIDE_BARS,
+                    equals = APPEARANCE_OPAQUE_SIDE_BARS,
+                    name = "OPAQUE_SIDE_BARS"),
             @ViewDebug.FlagToString(
                     mask = APPEARANCE_LOW_PROFILE_BARS,
                     equals = APPEARANCE_LOW_PROFILE_BARS,
@@ -64,4 +76,44 @@
                     name = "SHOW_TRANSIENT_BARS_BY_SWIPE")
     })
     public @Behavior int behavior;
+
+    /**
+     * Converts system UI visibility to appearance.
+     *
+     * @param systemUiVisibility the system UI visibility to be converted.
+     * @return the outcome {@link Appearance}
+     */
+    public static @Appearance int getAppearance(int systemUiVisibility) {
+        int appearance = 0;
+        appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LOW_PROFILE,
+                APPEARANCE_LOW_PROFILE_BARS);
+        appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+                APPEARANCE_LIGHT_TOP_BAR);
+        appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                APPEARANCE_LIGHT_SIDE_BARS);
+        appearance |= convertNoFlag(systemUiVisibility,
+                STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT, APPEARANCE_OPAQUE_TOP_BAR);
+        appearance |= convertNoFlag(systemUiVisibility,
+                NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT,
+                APPEARANCE_OPAQUE_SIDE_BARS);
+        return appearance;
+    }
+
+    /**
+     * Converts the system UI visibility into an appearance flag if the given visibility contains
+     * the given system UI flag.
+     */
+    private static @Appearance int convertFlag(int systemUiVisibility, int systemUiFlag,
+            @Appearance int appearance) {
+        return (systemUiVisibility & systemUiFlag) != 0 ? appearance : 0;
+    }
+
+    /**
+     * Converts the system UI visibility into an appearance flag if the given visibility doesn't
+     * contains the given system UI flag.
+     */
+    private static @Appearance int convertNoFlag(int systemUiVisibility, int systemUiFlag,
+            @Appearance int appearance) {
+        return (systemUiVisibility & systemUiFlag) == 0 ? appearance : 0;
+    }
 }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index a04c39b..99502a6 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -270,10 +270,23 @@
      *
      * @param type The {@link InternalInsetType} of the source to remove
      */
-    public void removeSource(int type) {
+    public void removeSource(@InternalInsetType int type) {
         mSources.remove(type);
     }
 
+    /**
+     * A shortcut for setting the visibility of the source.
+     *
+     * @param type The {@link InternalInsetType} of the source to set the visibility
+     * @param visible {@code true} for visible
+     */
+    public void setSourceVisible(@InternalInsetType int type, boolean visible) {
+        InsetsSource source = mSources.get(type);
+        if (source != null) {
+            source.setVisible(visible);
+        }
+    }
+
     public void set(InsetsState other) {
         set(other, false /* copySources */);
     }
@@ -357,6 +370,19 @@
         }
     }
 
+    public static boolean containsType(@InternalInsetType int[] types,
+            @InternalInsetType int type) {
+        if (types == null) {
+            return false;
+        }
+        for (int t : types) {
+            if (t == type) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "InsetsState");
         for (int i = mSources.size() - 1; i >= 0; i--) {
@@ -364,7 +390,7 @@
         }
     }
 
-    public static String typeToString(int type) {
+    public static String typeToString(@InternalInsetType int type) {
         switch (type) {
             case TYPE_TOP_BAR:
                 return "TYPE_TOP_BAR";
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 396422e..b415319 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -35,33 +35,39 @@
 public interface WindowInsetsController {
 
     /**
-     * Makes system bars become opaque with solid dark background and light foreground.
+     * Makes the top bars become opaque with solid dark background and light foreground.
      * @hide
      */
-    int APPEARANCE_OPAQUE_BARS = 1;
+    int APPEARANCE_OPAQUE_TOP_BAR = 1;
+
+    /**
+     * Makes the side bars become opaque with solid dark background and light foreground.
+     * @hide
+     */
+    int APPEARANCE_OPAQUE_SIDE_BARS = 1 << 1;
 
     /**
      * Makes items on system bars become less noticeable without changing the layout of the bars.
      * @hide
      */
-    int APPEARANCE_LOW_PROFILE_BARS = 1 << 1;
+    int APPEARANCE_LOW_PROFILE_BARS = 1 << 2;
 
     /**
      * Changes the foreground color for the light top bar so that the items on the bar can be read
      * clearly.
      */
-    int APPEARANCE_LIGHT_TOP_BAR = 1 << 2;
+    int APPEARANCE_LIGHT_TOP_BAR = 1 << 3;
 
     /**
      * Changes the foreground color for the light side bars so that the items on the bar can be read
      * clearly.
      */
-    int APPEARANCE_LIGHT_SIDE_BARS = 1 << 3;
+    int APPEARANCE_LIGHT_SIDE_BARS = 1 << 4;
 
     /** Determines the appearance of system bars. */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {APPEARANCE_OPAQUE_BARS, APPEARANCE_LOW_PROFILE_BARS,
-            APPEARANCE_LIGHT_TOP_BAR, APPEARANCE_LIGHT_SIDE_BARS})
+    @IntDef(flag = true, value = {APPEARANCE_OPAQUE_TOP_BAR, APPEARANCE_OPAQUE_SIDE_BARS,
+            APPEARANCE_LOW_PROFILE_BARS, APPEARANCE_LIGHT_TOP_BAR, APPEARANCE_LIGHT_SIDE_BARS})
     @interface Appearance {
     }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c8ba52a..d9e2ba3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -23,6 +23,7 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.view.AppearanceRegion;
 
 /** @hide */
 oneway interface IStatusBar
@@ -56,7 +57,7 @@
             int mask, in Rect fullscreenBounds, in Rect dockedBounds,
             boolean navbarColorManagedByIme);
 
-    void topAppWindowChanged(int displayId, boolean menuVisible);
+    void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive);
     void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher, boolean isMultiClientImeEnabled);
     void setWindowState(int display, int window, int state);
@@ -172,4 +173,38 @@
      * Notifies System UI whether the recents animation is running or not.
      */
     void onRecentsAnimationStateChanged(boolean running);
+
+    /**
+     * Notifies System UI side of system bar appearance change on the specified display.
+     *
+     * @param displayId the ID of the display to notify
+     * @param appearance the appearance of the focused window. The light top bar appearance is not
+     *                   controlled here, but primaryAppearance and secondaryAppearance.
+     * @param appearanceRegions a set of appearances which will be only applied in their own bounds.
+     *                         This is for system bars which across multiple stack, e.g., status
+     *                         bar, that the bar can have partial appearances in corresponding
+     *                         stacks.
+     * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
+     */
+    void onSystemBarAppearanceChanged(int displayId, int appearance,
+            in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme);
+
+    /**
+     * Notifies System UI to show transient bars. The transient bars are system bars, e.g., status
+     * bar and navigation bar which are temporarily visible to the user.
+     *
+     * @param displayId the ID of the display to notify.
+     * @param types the internal insets types of the bars are about to show transiently.
+     */
+    void showTransient(int displayId, in int[] types);
+
+    /**
+     * Notifies System UI to abort the transient state of system bars, which prevents the bars being
+     * hidden automatically. This is usually called when the app wants to show the permanent system
+     * bars again.
+     *
+     * @param displayId the ID of the display to notify.
+     * @param types the internal insets types of the bars are about to abort the transient state.
+     */
+    void abortTransient(int displayId, in int[] types);
 }
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 6b0f8b2..4c3f04b 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -29,7 +29,6 @@
     public final ArrayMap<String, StatusBarIcon> mIcons;
     public final int mDisabledFlags1;                  // switch[0]
     public final int mSystemUiVisibility;              // switch[1]
-    public final boolean mMenuVisible;                 // switch[2]
     public final int mImeWindowVis;                    // switch[3]
     public final int mImeBackDisposition;              // switch[4]
     public final boolean mShowImeSwitcher;             // switch[5]
@@ -40,16 +39,18 @@
     public final Rect mFullscreenStackBounds;
     public final Rect mDockedStackBounds;
     public final boolean mNavbarColorManagedByIme;
+    public final boolean mAppFullscreen;
+    public final boolean mAppImmersive;
 
     public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
-            int systemUiVisibility, boolean menuVisible, int imeWindowVis, int imeBackDisposition,
+            int systemUiVisibility, int imeWindowVis, int imeBackDisposition,
             boolean showImeSwitcher, int disabledFlags2, int fullscreenStackSysUiVisibility,
             int dockedStackSysUiVisibility, IBinder imeToken, Rect fullscreenStackBounds,
-            Rect dockedStackBounds, boolean navbarColorManagedByIme) {
+            Rect dockedStackBounds, boolean navbarColorManagedByIme, boolean appFullscreen,
+            boolean appImmersive) {
         mIcons = new ArrayMap<>(icons);
         mDisabledFlags1 = disabledFlags1;
         mSystemUiVisibility = systemUiVisibility;
-        mMenuVisible = menuVisible;
         mImeWindowVis = imeWindowVis;
         mImeBackDisposition = imeBackDisposition;
         mShowImeSwitcher = showImeSwitcher;
@@ -60,6 +61,8 @@
         mFullscreenStackBounds = fullscreenStackBounds;
         mDockedStackBounds = dockedStackBounds;
         mNavbarColorManagedByIme = navbarColorManagedByIme;
+        mAppFullscreen = appFullscreen;
+        mAppImmersive = appImmersive;
     }
 
     @Override
@@ -72,7 +75,6 @@
         dest.writeTypedArrayMap(mIcons, flags);
         dest.writeInt(mDisabledFlags1);
         dest.writeInt(mSystemUiVisibility);
-        dest.writeBoolean(mMenuVisible);
         dest.writeInt(mImeWindowVis);
         dest.writeInt(mImeBackDisposition);
         dest.writeBoolean(mShowImeSwitcher);
@@ -83,6 +85,8 @@
         dest.writeTypedObject(mFullscreenStackBounds, flags);
         dest.writeTypedObject(mDockedStackBounds, flags);
         dest.writeBoolean(mNavbarColorManagedByIme);
+        dest.writeBoolean(mAppFullscreen);
+        dest.writeBoolean(mAppImmersive);
     }
 
     /**
@@ -96,7 +100,6 @@
                             source.createTypedArrayMap(StatusBarIcon.CREATOR);
                     final int disabledFlags1 = source.readInt();
                     final int systemUiVisibility = source.readInt();
-                    final boolean menuVisible = source.readBoolean();
                     final int imeWindowVis = source.readInt();
                     final int imeBackDisposition = source.readInt();
                     final boolean showImeSwitcher = source.readBoolean();
@@ -107,11 +110,13 @@
                     final Rect fullscreenStackBounds = source.readTypedObject(Rect.CREATOR);
                     final Rect dockedStackBounds = source.readTypedObject(Rect.CREATOR);
                     final boolean navbarColorManagedByIme = source.readBoolean();
+                    final boolean appFullscreen = source.readBoolean();
+                    final boolean appImmersive = source.readBoolean();
                     return new RegisterStatusBarResult(icons, disabledFlags1, systemUiVisibility,
-                            menuVisible, imeWindowVis, imeBackDisposition, showImeSwitcher,
-                            disabledFlags2, fullscreenStackSysUiVisibility,
-                            dockedStackSysUiVisibility, imeToken, fullscreenStackBounds,
-                            dockedStackBounds, navbarColorManagedByIme);
+                            imeWindowVis, imeBackDisposition, showImeSwitcher, disabledFlags2,
+                            fullscreenStackSysUiVisibility, dockedStackSysUiVisibility, imeToken,
+                            fullscreenStackBounds, dockedStackBounds, navbarColorManagedByIme,
+                            appFullscreen, appImmersive);
                 }
 
                 @Override
diff --git a/core/java/com/android/internal/view/AppearanceRegion.aidl b/core/java/com/android/internal/view/AppearanceRegion.aidl
new file mode 100644
index 0000000..1638bf5
--- /dev/null
+++ b/core/java/com/android/internal/view/AppearanceRegion.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.internal.view;
+
+parcelable AppearanceRegion;
diff --git a/core/java/com/android/internal/view/AppearanceRegion.java b/core/java/com/android/internal/view/AppearanceRegion.java
new file mode 100644
index 0000000..1a0cb4b
--- /dev/null
+++ b/core/java/com/android/internal/view/AppearanceRegion.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 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.internal.view;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.Parcelable;
+import android.view.InsetsFlags;
+import android.view.ViewDebug;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Specifies which region applies which appearance.
+ */
+@DataClass
+public class AppearanceRegion implements Parcelable {
+
+    private int mAppearance;
+    private @NonNull Rect mBounds;
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final AppearanceRegion sa = (AppearanceRegion) o;
+        return mAppearance == sa.mAppearance && mBounds.equals(sa.mBounds);
+    }
+
+    @Override
+    public String toString() {
+        final String appearanceString =
+                ViewDebug.flagsToString(InsetsFlags.class, "appearance", mAppearance);
+        return "AppearanceRegion{" + appearanceString + " bounds=" + mBounds.toShortString() + "}";
+    }
+
+
+
+    // Code below generated by codegen v1.0.7.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/view/AppearanceRegion.java
+
+
+    @DataClass.Generated.Member
+    public AppearanceRegion(
+            int appearance,
+            @NonNull Rect bounds) {
+        this.mAppearance = appearance;
+        this.mBounds = bounds;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mBounds);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getAppearance() {
+        return mAppearance;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Rect getBounds() {
+        return mBounds;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeInt(mAppearance);
+        dest.writeTypedObject(mBounds, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected AppearanceRegion(android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int appearance = in.readInt();
+        Rect bounds = (Rect) in.readTypedObject(Rect.CREATOR);
+
+        this.mAppearance = appearance;
+        this.mBounds = bounds;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mBounds);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<AppearanceRegion> CREATOR
+            = new Parcelable.Creator<AppearanceRegion>() {
+        @Override
+        public AppearanceRegion[] newArray(int size) {
+            return new AppearanceRegion[size];
+        }
+
+        @Override
+        public AppearanceRegion createFromParcel(android.os.Parcel in) {
+            return new AppearanceRegion(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1570909617357L,
+            codegenVersion = "1.0.7",
+            sourceFile = "frameworks/base/core/java/com/android/internal/view/AppearanceRegion.java",
+            inputSignatures = "private  int mAppearance\nprivate @android.annotation.NonNull android.graphics.Rect mBounds\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override java.lang.String toString()\nclass AppearanceRegion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
+    @Deprecated
+    private void __metadata() {}
+
+}
diff --git a/core/tests/coretests/src/android/view/InsetsFlagsTest.java b/core/tests/coretests/src/android/view/InsetsFlagsTest.java
new file mode 100644
index 0000000..7d4445b
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsFlagsTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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.InsetsFlags.getAppearance;
+import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
+import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
+import static android.view.View.STATUS_BAR_TRANSLUCENT;
+import static android.view.View.STATUS_BAR_TRANSPARENT;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_TOP_BAR;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowInsetsController.Appearance;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link InsetsFlags}.
+ *
+ * <p>Build/Install/Run:
+ *  atest FrameworksCoreTests:InsetsFlagsTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class InsetsFlagsTest {
+
+    @Test
+    public void testGetAppearance() {
+        assertContainsAppearance(APPEARANCE_LOW_PROFILE_BARS, SYSTEM_UI_FLAG_LOW_PROFILE);
+        assertContainsAppearance(APPEARANCE_LIGHT_TOP_BAR, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        assertContainsAppearance(APPEARANCE_LIGHT_SIDE_BARS, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+        assertContainsAppearance(APPEARANCE_OPAQUE_TOP_BAR,
+                0xffffffff & ~(STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT));
+        assertContainsAppearance(APPEARANCE_OPAQUE_SIDE_BARS,
+                0xffffffff & ~(NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT));
+    }
+
+    void assertContainsAppearance(@Appearance int appearance, int systemUiVisibility) {
+        assertTrue((getAppearance(systemUiVisibility) & appearance) == appearance);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index b93c3a7..0be5009 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -48,7 +48,6 @@
         final RegisterStatusBarResult original = new RegisterStatusBarResult(iconMap,
                 0x2 /* disabledFlags1 */,
                 0x4 /* systemUiVisibility */,
-                true /* menuVisible */,
                 0x8 /* imeWindowVis */,
                 0x10 /* imeBackDisposition */,
                 false /* showImeSwitcher */,
@@ -58,7 +57,9 @@
                 new Binder() /* imeToken */,
                 new Rect(0x100, 0x200, 0x400, 0x800) /* fullscreenStackBounds */,
                 new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */,
-                true /* navbarColorManagedByIme */);
+                true /* navbarColorManagedByIme */,
+                true /* appFullscreen */,
+                true /* appImmersive */);
 
         final RegisterStatusBarResult copy = clone(original);
 
@@ -69,7 +70,6 @@
 
         assertThat(copy.mDisabledFlags1).isEqualTo(original.mDisabledFlags1);
         assertThat(copy.mSystemUiVisibility).isEqualTo(original.mSystemUiVisibility);
-        assertThat(copy.mMenuVisible).isEqualTo(original.mMenuVisible);
         assertThat(copy.mImeWindowVis).isEqualTo(original.mImeWindowVis);
         assertThat(copy.mImeBackDisposition).isEqualTo(original.mImeBackDisposition);
         assertThat(copy.mShowImeSwitcher).isEqualTo(original.mShowImeSwitcher);
@@ -82,6 +82,8 @@
         assertThat(copy.mFullscreenStackBounds).isEqualTo(original.mFullscreenStackBounds);
         assertThat(copy.mDockedStackBounds).isEqualTo(original.mDockedStackBounds);
         assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
+        assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
+        assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive);
     }
 
     private RegisterStatusBarResult clone(RegisterStatusBarResult original) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index fe547a0a..b21a9f7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -95,9 +95,12 @@
         default void onDozeAmountChanged(float linear, float eased) {}
 
         /**
-         * Callback to be notified when the sysui visibility changes
+         * Callback to be notified when the fullscreen or immersive state changes.
+         *
+         * @param isFullscreen if any of the system bar is hidden by the focused window.
+         * @param isImmersive if the navigation bar can stay hidden when the display gets tapped.
          */
-        default void onSystemUiVisibilityChanged(int visibility) {}
+        default void onFullscreenStateChanged(boolean isFullscreen, boolean isImmersive) {}
 
         /**
          * Callback to be notified when the pulsing state changes
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d6a8f90..c9dc08e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -22,6 +22,8 @@
 import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 
 import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
 
@@ -43,12 +45,17 @@
 import android.os.Message;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.view.InsetsFlags;
+import android.view.InsetsState.InternalInsetType;
+import android.view.View;
+import android.view.WindowInsetsController.Appearance;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.policy.CallbackController;
@@ -76,7 +83,7 @@
     private static final int MSG_EXPAND_NOTIFICATIONS          = 3 << MSG_SHIFT;
     private static final int MSG_COLLAPSE_PANELS               = 4 << MSG_SHIFT;
     private static final int MSG_EXPAND_SETTINGS               = 5 << MSG_SHIFT;
-    private static final int MSG_SET_SYSTEMUI_VISIBILITY       = 6 << MSG_SHIFT;
+    private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT;
     private static final int MSG_DISPLAY_READY                 = 7 << MSG_SHIFT;
     private static final int MSG_SHOW_IME_BUTTON               = 8 << MSG_SHIFT;
     private static final int MSG_TOGGLE_RECENT_APPS            = 9 << MSG_SHIFT;
@@ -115,6 +122,9 @@
     private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
     private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT;
+    private static final int MSG_SHOW_TRANSIENT                = 48 << MSG_SHIFT;
+    private static final int MSG_ABORT_TRANSIENT               = 49 << MSG_SHIFT;
+    private static final int MSG_TOP_APP_WINDOW_CHANGED        = 50 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -160,28 +170,6 @@
         default void animateExpandSettingsPanel(String obj) { }
 
         /**
-         * Called to notify visibility flag changes.
-         * @see IStatusBar#setSystemUiVisibility(int, int, int, int, int, Rect, Rect).
-         *
-         * @param displayId The id of the display to notify.
-         * @param vis The visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will
-         *            be reported separately in fullscreenStackVis and dockedStackVis.
-         * @param fullscreenStackVis The flags which only apply in the region of the fullscreen
-         *                           stack, which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
-         * @param dockedStackVis The flags that only apply in the region of the docked stack, which
-         *                       is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
-         * @param mask Which flags to change.
-         * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen
-         *                              coordinates.
-         * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates.
-         * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
-         */
-        default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
-                boolean navbarColorManagedByIme) {
-        }
-
-        /**
          * Called to notify IME window status changes.
          *
          * @param displayId The id of the display to notify.
@@ -292,6 +280,28 @@
          * @see IStatusBar#onRecentsAnimationStateChanged(boolean)
          */
         default void onRecentsAnimationStateChanged(boolean running) { }
+
+        /**
+         * @see IStatusBar#onSystemBarAppearanceChanged(int, int, AppearanceRegion[], boolean).
+         */
+        default void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+                AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { }
+
+        /**
+         * @see IStatusBar#showTransient(int, int[]).
+         */
+        default void showTransient(int displayId, @InternalInsetType int[] types) { }
+
+        /**
+         * @see IStatusBar#abortTransient(int, int[]).
+         */
+        default void abortTransient(int displayId, @InternalInsetType int[] types) { }
+
+        /**
+         * @see IStatusBar#topAppWindowChanged(int, boolean, boolean).
+         */
+        default void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+        }
     }
 
     @VisibleForTesting
@@ -456,28 +466,53 @@
         }
     }
 
+    // TODO(b/118118435): Remove this function after migration
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
             int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
             boolean navbarColorManagedByIme) {
         synchronized (mLock) {
-            // Don't coalesce these, since it might have one time flags set such as
-            // STATUS_BAR_UNHIDE which might get lost.
+            final boolean hasDockedStack = !dockedStackBounds.isEmpty();
+            final boolean transientStatus = (vis & View.STATUS_BAR_TRANSIENT) != 0;
+            final boolean transientNavigation = (vis & View.NAVIGATION_BAR_TRANSIENT) != 0;
+            if (transientStatus && transientNavigation) {
+                showTransient(displayId, new int[]{TYPE_TOP_BAR, TYPE_NAVIGATION_BAR});
+            } else if (transientStatus) {
+                showTransient(displayId, new int[]{TYPE_TOP_BAR});
+                abortTransient(displayId, new int[]{TYPE_NAVIGATION_BAR});
+            } else if (transientNavigation) {
+                showTransient(displayId, new int[]{TYPE_NAVIGATION_BAR});
+                abortTransient(displayId, new int[]{TYPE_TOP_BAR});
+            } else {
+                abortTransient(displayId, new int[]{TYPE_TOP_BAR, TYPE_NAVIGATION_BAR});
+            }
             SomeArgs args = SomeArgs.obtain();
             args.argi1 = displayId;
-            args.argi2 = vis;
-            args.argi3 = fullscreenStackVis;
-            args.argi4 = dockedStackVis;
-            args.argi5 = mask;
-            args.argi6 = navbarColorManagedByIme ? 1 : 0;
-            args.arg1 = fullscreenStackBounds;
-            args.arg2 = dockedStackBounds;
-            mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
+            args.argi2 = InsetsFlags.getAppearance(vis);
+            args.argi3 = navbarColorManagedByIme ? 1 : 0;
+            final int fullscreenAppearance = InsetsFlags.getAppearance(fullscreenStackVis);
+            final int dockedAppearance = InsetsFlags.getAppearance(dockedStackVis);
+            args.arg1 = hasDockedStack
+                    ? new AppearanceRegion[]{
+                            new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds),
+                            new AppearanceRegion(dockedAppearance, dockedStackBounds)}
+                    : new AppearanceRegion[]{
+                            new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds)};
+            mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget();
         }
     }
 
     @Override
-    public void topAppWindowChanged(int displayId, boolean menuVisible) { }
+    public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+        synchronized (mLock) {
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = isFullscreen ? 1 : 0;
+            args.argi3 = isImmersive ? 1 : 0;
+            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, args).sendToTarget();
+        }
+
+    }
 
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
@@ -827,6 +862,33 @@
         }
     }
 
+    @Override
+    public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
+        synchronized (mLock) {
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = appearance;
+            args.argi3 = navbarColorManagedByIme ? 1 : 0;
+            args.arg1 = appearanceRegions;
+            mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget();
+        }
+    }
+
+    @Override
+    public void showTransient(int displayId, int[] types) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, 0, types).sendToTarget();
+        }
+    }
+
+    @Override
+    public void abortTransient(int displayId, int[] types) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_ABORT_TRANSIENT, displayId, 0, types).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         private H(Looper l) {
             super(l);
@@ -879,15 +941,6 @@
                         mCallbacks.get(i).animateExpandSettingsPanel((String) msg.obj);
                     }
                     break;
-                case MSG_SET_SYSTEMUI_VISIBILITY:
-                    args = (SomeArgs) msg.obj;
-                    for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
-                                args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2,
-                                args.argi6 == 1);
-                    }
-                    args.recycle();
-                    break;
                 case MSG_SHOW_IME_BUTTON:
                     args = (SomeArgs) msg.obj;
                     handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
@@ -1094,6 +1147,39 @@
                         mCallbacks.get(i).onRecentsAnimationStateChanged(msg.arg1 > 0);
                     }
                     break;
+                case MSG_SYSTEM_BAR_APPEARANCE_CHANGED:
+                    args = (SomeArgs) msg.obj;
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).onSystemBarAppearanceChanged(args.argi1, args.argi2,
+                                (AppearanceRegion[]) args.arg1, args.argi3 == 1);
+                    }
+                    args.recycle();
+                    break;
+                case MSG_SHOW_TRANSIENT: {
+                    final int displayId = msg.arg1;
+                    final int[] types = (int[]) msg.obj;
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).showTransient(displayId, types);
+                    }
+                    break;
+                }
+                case MSG_ABORT_TRANSIENT: {
+                    final int displayId = msg.arg1;
+                    final int[] types = (int[]) msg.obj;
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).abortTransient(displayId, types);
+                    }
+                    break;
+                }
+                case MSG_TOP_APP_WINDOW_CHANGED: {
+                    args = (SomeArgs) msg.obj;
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).topAppWindowChanged(
+                                args.argi1, args.argi2 != 0, args.argi3 != 0);
+                    }
+                    args.recycle();
+                    break;
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 5144a95..275475d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -156,7 +156,7 @@
                             Dependency.get(NotificationRemoteInputManager.class),
                             Dependency.get(IWindowManager.class));
             navBar.setAutoHideController(autoHideController);
-            navBar.restoreSystemUiVisibilityState();
+            navBar.restoreAppearanceAndTransientState();
             mNavigationBars.append(displayId, navBar);
 
             if (result != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 8b9268e..e81e5ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -21,7 +21,6 @@
 import android.text.format.DateFormat;
 import android.util.FloatProperty;
 import android.util.Log;
-import android.view.View;
 import android.view.animation.Interpolator;
 
 import com.android.internal.annotations.GuardedBy;
@@ -80,9 +79,14 @@
     private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
 
     /**
-     * Current SystemUiVisibility
+     * If any of the system bars is hidden.
      */
-    private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+    private boolean mIsFullscreen = false;
+
+    /**
+     * If the navigation bar can stay hidden when the display gets tapped.
+     */
+    private boolean mIsImmersive = false;
 
     /**
      * If the device is currently pulsing (AOD2).
@@ -320,12 +324,13 @@
     }
 
     @Override
-    public void setSystemUiVisibility(int visibility) {
-        if (mSystemUiVisibility != visibility) {
-            mSystemUiVisibility = visibility;
+    public void setFullscreenState(boolean isFullscreen, boolean isImmersive) {
+        if (mIsFullscreen != isFullscreen || mIsImmersive != isImmersive) {
+            mIsFullscreen = isFullscreen;
+            mIsImmersive = isImmersive;
             synchronized (mListeners) {
                 for (RankedListener rl : new ArrayList<>(mListeners)) {
-                    rl.mListener.onSystemUiVisibilityChanged(mSystemUiVisibility);
+                    rl.mListener.onFullscreenStateChanged(isFullscreen, isImmersive);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 2ad979ab..07b3550 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -111,9 +111,9 @@
     boolean isKeyguardRequested();
 
     /**
-     * Set systemui visibility
+     * Set the fullscreen state
      */
-    void setSystemUiVisibility(int visibility);
+    void setFullscreenState(boolean isFullscreen, boolean isImmersive);
 
     /**
      * Set pulsing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index 175d072..008464e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -17,52 +17,39 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
-import android.view.View;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 
 /** A controller to control all auto-hide things. */
-public class AutoHideController implements CommandQueue.Callbacks {
+public class AutoHideController {
     private static final String TAG = "AutoHideController";
 
     private final IWindowManager mWindowManagerService;
 
     private final Handler mHandler;
     private final NotificationRemoteInputManager mRemoteInputManager;
-    private final CommandQueue mCommandQueue;
     private StatusBar mStatusBar;
     private NavigationBarFragment mNavigationBar;
 
-    @VisibleForTesting
-    int mDisplayId;
-    @VisibleForTesting
-    int mSystemUiVisibility;
-    // last value sent to window manager
-    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
+    private int mDisplayId;
 
     private boolean mAutoHideSuspended;
 
-    private static final long AUTOHIDE_TIMEOUT_MS = 2250;
+    private static final long AUTO_HIDE_TIMEOUT_MS = 2250;
 
     private final Runnable mAutoHide = () -> {
-        int requested = mSystemUiVisibility & ~getTransientMask();
-        if (mSystemUiVisibility != requested) {
-            notifySystemUiVisibilityChanged(requested);
+        if (isAnyTransientBarShown()) {
+            hideTransientBars();
         }
     };
 
@@ -70,8 +57,6 @@
     public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler,
             NotificationRemoteInputManager notificationRemoteInputManager,
             IWindowManager iWindowManager) {
-        mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
-        mCommandQueue.addCallback(this);
         mHandler = handler;
         mRemoteInputManager = notificationRemoteInputManager;
         mWindowManagerService = iWindowManager;
@@ -79,13 +64,6 @@
         mDisplayId = context.getDisplayId();
     }
 
-    @Override
-    public void onDisplayRemoved(int displayId) {
-        if (displayId == mDisplayId) {
-            mCommandQueue.removeCallback(this);
-        }
-    }
-
     void setStatusBar(StatusBar statusBar) {
         mStatusBar = statusBar;
     }
@@ -94,50 +72,18 @@
         mNavigationBar = navigationBar;
     }
 
-    @Override
-    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
-            boolean navbarColorManagedByIme) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        int oldVal = mSystemUiVisibility;
-        int newVal = (oldVal & ~mask) | (vis & mask);
-        int diff = newVal ^ oldVal;
-
-        if (diff != 0) {
-            mSystemUiVisibility = newVal;
-
-            // ready to unhide
-            if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) {
-                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
-            }
-
-            if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
-                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
-            }
-
-            // Re-send setSystemUiVisibility to update un-hide status.
-            if (mSystemUiVisibility != newVal) {
-                mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
-                        fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
-                        dockedStackBounds, navbarColorManagedByIme);
-            }
-
-            notifySystemUiVisibilityChanged(mSystemUiVisibility);
-        }
-    }
-
-    @VisibleForTesting
-    void notifySystemUiVisibilityChanged(int vis) {
+    private void hideTransientBars() {
         try {
-            if (mLastDispatchedSystemUiVisibility != vis) {
-                mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
-                mLastDispatchedSystemUiVisibility = vis;
-            }
+            mWindowManagerService.hideTransientBars(mDisplayId);
         } catch (RemoteException ex) {
             Log.w(TAG, "Cannot get WindowManager");
         }
+        if (mStatusBar != null) {
+            mStatusBar.clearTransient();
+        }
+        if (mNavigationBar != null) {
+            mNavigationBar.clearTransient();
+        }
     }
 
     void resumeSuspendedAutoHide() {
@@ -156,13 +102,12 @@
         if (checkBarModesRunnable != null) {
             mHandler.removeCallbacks(checkBarModesRunnable);
         }
-        mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0;
+        mAutoHideSuspended = isAnyTransientBarShown();
     }
 
     void touchAutoHide() {
         // update transient bar auto hide
-        if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT)
-                || hasNavigationBar() && mNavigationBar.isSemiTransparent()) {
+        if (isAnyTransientBarShown()) {
             scheduleAutoHide();
         } else {
             cancelAutoHide();
@@ -170,9 +115,9 @@
     }
 
     private Runnable getCheckBarModesRunnable() {
-        if (hasStatusBar()) {
+        if (mStatusBar != null) {
             return () -> mStatusBar.checkBarModes();
-        } else if (hasNavigationBar()) {
+        } else if (mNavigationBar != null) {
             return () -> mNavigationBar.checkNavBarModes();
         } else {
             return null;
@@ -186,15 +131,14 @@
 
     private void scheduleAutoHide() {
         cancelAutoHide();
-        mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
+        mHandler.postDelayed(mAutoHide, AUTO_HIDE_TIMEOUT_MS);
     }
 
     void checkUserAutoHide(MotionEvent event) {
-        boolean shouldAutoHide =
-                (mSystemUiVisibility & getTransientMask()) != 0  // a transient bar is revealed.
+        boolean shouldAutoHide = isAnyTransientBarShown()
                 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
                 && event.getX() == 0 && event.getY() == 0;
-        if (hasStatusBar()) {
+        if (mStatusBar != null) {
             // a touch outside both bars
             shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
         }
@@ -208,23 +152,8 @@
         mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
     }
 
-    private int getTransientMask() {
-        int mask = 0;
-        if (hasStatusBar()) {
-            mask |= View.STATUS_BAR_TRANSIENT;
-        }
-        if (hasNavigationBar()) {
-            mask |= View.NAVIGATION_BAR_TRANSIENT;
-        }
-        return mask;
-    }
-
-    boolean hasNavigationBar() {
-        return mNavigationBar != null;
-    }
-
-    @VisibleForTesting
-    boolean hasStatusBar() {
-        return mStatusBar != null;
+    private boolean isAnyTransientBarShown() {
+        return (mStatusBar != null && mStatusBar.isTransientShown())
+                || mNavigationBar != null && mNavigationBar.isTransientShown();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 211a40a..e6731e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -44,11 +44,11 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_COLORS = false;
 
-    public static final int MODE_OPAQUE = 0;
+    public static final int MODE_TRANSPARENT = 0;
     public static final int MODE_SEMI_TRANSPARENT = 1;
     public static final int MODE_TRANSLUCENT = 2;
     public static final int MODE_LIGHTS_OUT = 3;
-    public static final int MODE_TRANSPARENT = 4;
+    public static final int MODE_OPAQUE = 4;
     public static final int MODE_WARNING = 5;
     public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6;
 
@@ -72,7 +72,7 @@
     private final View mView;
     protected final BarBackgroundDrawable mBarBackground;
 
-    private int mMode;
+    private @TransitionMode int mMode;
     private boolean mAlwaysOpaque = false;
 
     public BarTransitions(View view, int gradientResourceId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 442c089..c9c38a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -16,8 +16,6 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-import static android.view.View.NAVIGATION_BAR_TRANSIENT;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -133,7 +131,7 @@
     private boolean mIsAttached;
     private boolean mIsGesturalModeEnabled;
     private boolean mIsEnabled;
-    private boolean mIsInTransientImmersiveStickyState;
+    private boolean mIsNavBarShownTransiently;
 
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
@@ -198,10 +196,8 @@
         updateCurrentUserResources(currentUserContext.getResources());
     }
 
-    public void onSystemUiVisibilityChanged(int systemUiVisibility) {
-        mIsInTransientImmersiveStickyState =
-                (systemUiVisibility & SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0
-                && (systemUiVisibility & NAVIGATION_BAR_TRANSIENT) != 0;
+    public void onNavBarTransientStateChanged(boolean isTransient) {
+        mIsNavBarShownTransiently = isTransient;
     }
 
     private void disposeInputChannel() {
@@ -316,7 +312,7 @@
         }
 
         // Always allow if the user is in a transient sticky immersive state
-        if (mIsInTransientImmersiveStickyState) {
+        if (mIsNavBarShownTransiently) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index b0b656a..2e776e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -16,15 +16,20 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
 
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.Rect;
-import android.view.View;
+import android.view.InsetsFlags;
+import android.view.ViewDebug;
+import android.view.WindowInsetsController.Appearance;
 
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
+import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
@@ -49,13 +54,10 @@
     private BiometricUnlockController mBiometricUnlockController;
 
     private LightBarTransitionsController mNavigationBarController;
-    private int mSystemUiVisibility;
-    private int mFullscreenStackVisibility;
-    private int mDockedStackVisibility;
-    private boolean mFullscreenLight;
-    private boolean mDockedLight;
-    private int mLastStatusBarMode;
-    private int mLastNavigationBarMode;
+    private @Appearance int mAppearance;
+    private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
+    private int mStatusBarMode;
+    private int mNavigationBarMode;
     private final Color mDarkModeColor;
 
     /**
@@ -75,8 +77,6 @@
      */
     private boolean mForceDarkForScrim;
 
-    private final Rect mLastFullscreenBounds = new Rect();
-    private final Rect mLastDockedBounds = new Rect();
     private boolean mQsCustomizing;
 
     private boolean mDirectReplying;
@@ -101,45 +101,32 @@
         mBiometricUnlockController = biometricUnlockController;
     }
 
-    public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+    void onStatusBarAppearanceChanged(AppearanceRegion[] appearanceRegions, boolean sbModeChanged,
             int statusBarMode, boolean navbarColorManagedByIme) {
-        int oldFullscreen = mFullscreenStackVisibility;
-        int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
-        int diffFullscreen = newFullscreen ^ oldFullscreen;
-        int oldDocked = mDockedStackVisibility;
-        int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
-        int diffDocked = newDocked ^ oldDocked;
-        if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
-                || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
-                || sbModeChanged
-                || !mLastFullscreenBounds.equals(fullscreenStackBounds)
-                || !mLastDockedBounds.equals(dockedStackBounds)) {
-
-            mFullscreenLight = isLight(newFullscreen, statusBarMode,
-                    View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-            mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-            updateStatus(fullscreenStackBounds, dockedStackBounds);
+        final int numStacks = appearanceRegions.length;
+        boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks;
+        for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) {
+            stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]);
         }
-
-        mFullscreenStackVisibility = newFullscreen;
-        mDockedStackVisibility = newDocked;
-        mLastStatusBarMode = statusBarMode;
+        if (stackAppearancesChanged || sbModeChanged) {
+            mAppearanceRegions = appearanceRegions;
+            onStatusBarModeChanged(statusBarMode);
+        }
         mNavbarColorManagedByIme = navbarColorManagedByIme;
-        mLastFullscreenBounds.set(fullscreenStackBounds);
-        mLastDockedBounds.set(dockedStackBounds);
     }
 
-    public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged,
+    void onStatusBarModeChanged(int newBarMode) {
+        mStatusBarMode = newBarMode;
+        updateStatus();
+    }
+
+    void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged,
             int navigationBarMode, boolean navbarColorManagedByIme) {
-        int oldVis = mSystemUiVisibility;
-        int newVis = (oldVis & ~mask) | (vis & mask);
-        int diffVis = newVis ^ oldVis;
-        if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
-                || nbModeChanged) {
-            boolean last = mNavigationLight;
-            mHasLightNavigationBar = isLight(vis, navigationBarMode,
-                    View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+        int diff = appearance ^ mAppearance;
+        if ((diff & APPEARANCE_LIGHT_SIDE_BARS) != 0 || nbModeChanged) {
+            final boolean last = mNavigationLight;
+            mHasLightNavigationBar = isLight(appearance, navigationBarMode,
+                    APPEARANCE_LIGHT_SIDE_BARS);
             mNavigationLight = mHasLightNavigationBar
                     && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
                     && !mQsCustomizing;
@@ -147,17 +134,20 @@
                 updateNavigation();
             }
         }
-        mSystemUiVisibility = newVis;
-        mLastNavigationBarMode = navigationBarMode;
+        mAppearance = appearance;
+        mNavigationBarMode = navigationBarMode;
         mNavbarColorManagedByIme = navbarColorManagedByIme;
     }
 
+    void onNavigationBarModeChanged(int newBarMode) {
+        mHasLightNavigationBar = isLight(mAppearance, newBarMode, APPEARANCE_LIGHT_SIDE_BARS);
+    }
+
     private void reevaluate() {
-        onSystemUiVisibilityChanged(mFullscreenStackVisibility,
-                mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds,
-                true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme);
-        onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */,
-                mLastNavigationBarMode, mNavbarColorManagedByIme);
+        onStatusBarAppearanceChanged(mAppearanceRegions, true /* sbModeChange */, mStatusBarMode,
+                mNavbarColorManagedByIme);
+        onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */,
+                mNavigationBarMode, mNavbarColorManagedByIme);
     }
 
     public void setQsCustomizing(boolean customizing) {
@@ -191,10 +181,10 @@
         }
     }
 
-    private boolean isLight(int vis, int barMode, int flag) {
-        boolean isTransparentBar = (barMode == MODE_TRANSPARENT
+    private static boolean isLight(int appearance, int barMode, int flag) {
+        final boolean isTransparentBar = (barMode == MODE_TRANSPARENT
                 || barMode == MODE_LIGHTS_OUT_TRANSPARENT);
-        boolean light = (vis & flag) != 0;
+        final boolean light = (appearance & flag) != 0;
         return isTransparentBar && light;
     }
 
@@ -207,49 +197,49 @@
                 && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
     }
 
-    private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
-        boolean hasDockedStack = !dockedStackBounds.isEmpty();
+    private void updateStatus() {
+        final int numStacks = mAppearanceRegions.length;
+        int numLightStacks = 0;
 
-        // If both are light or fullscreen is light and there is no docked stack, all icons get
-        // dark.
-        if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+        // We can only have maximum one light stack.
+        int indexLightStack = -1;
+
+        for (int i = 0; i < numStacks; i++) {
+            if (isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode,
+                    APPEARANCE_LIGHT_TOP_BAR)) {
+                numLightStacks++;
+                indexLightStack = i;
+            }
+        }
+
+        // If all stacks are light, all icons get dark.
+        if (numLightStacks == numStacks) {
             mStatusBarIconController.setIconsDarkArea(null);
             mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
 
         }
 
-        // If no one is light or the fullscreen is not light and there is no docked stack,
-        // all icons become white.
-        else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+        // If no one is light, all icons become white.
+        else if (numLightStacks == 0) {
             mStatusBarIconController.getTransitionsController().setIconsDark(
                     false, animateChange());
         }
 
         // Not the same for every stack, magic!
         else {
-            Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
-            if (bounds.isEmpty()) {
-                mStatusBarIconController.setIconsDarkArea(null);
-            } else {
-                mStatusBarIconController.setIconsDarkArea(bounds);
-            }
+            mStatusBarIconController.setIconsDarkArea(
+                    mAppearanceRegions[indexLightStack].getBounds());
             mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
         }
     }
 
     private void updateNavigation() {
         if (mNavigationBarController != null) {
-            mNavigationBarController.setIconsDark(
-                    mNavigationLight, animateChange());
+            mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
         }
     }
 
     @Override
-    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-
-    }
-
-    @Override
     public void onPowerSaveChanged(boolean isPowerSave) {
         reevaluate();
     }
@@ -257,24 +247,21 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("LightBarController: ");
-        pw.print(" mSystemUiVisibility=0x"); pw.print(
-                Integer.toHexString(mSystemUiVisibility));
-        pw.print(" mFullscreenStackVisibility=0x"); pw.print(
-                Integer.toHexString(mFullscreenStackVisibility));
-        pw.print(" mDockedStackVisibility=0x"); pw.println(
-                Integer.toHexString(mDockedStackVisibility));
-
-        pw.print(" mFullscreenLight="); pw.print(mFullscreenLight);
-        pw.print(" mDockedLight="); pw.println(mDockedLight);
-
-        pw.print(" mLastFullscreenBounds="); pw.print(mLastFullscreenBounds);
-        pw.print(" mLastDockedBounds="); pw.println(mLastDockedBounds);
+        pw.print(" mAppearance=0x"); pw.println(ViewDebug.flagsToString(
+                InsetsFlags.class, "appearance", mAppearance));
+        final int numStacks = mAppearanceRegions.length;
+        for (int i = 0; i < numStacks; i++) {
+            final boolean isLight = isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode,
+                    APPEARANCE_LIGHT_TOP_BAR);
+            pw.print(" stack #"); pw.print(i); pw.print(": ");
+            pw.print(mAppearanceRegions[i].toString()); pw.print(" isLight="); pw.println(isLight);
+        }
 
         pw.print(" mNavigationLight="); pw.print(mNavigationLight);
         pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar);
 
-        pw.print(" mLastStatusBarMode="); pw.print(mLastStatusBarMode);
-        pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode);
+        pw.print(" mStatusBarMode="); pw.print(mStatusBarMode);
+        pw.print(" mNavigationBarMode="); pw.println(mNavigationBarMode);
 
         pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim);
         pw.print(" mQsCustomizing="); pw.print(mQsCustomizing);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 38dc5ea..7030dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -21,6 +21,10 @@
 import static android.app.StatusBarManager.WindowType;
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.containsType;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_SIDE_BARS;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
@@ -31,7 +35,6 @@
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
@@ -52,7 +55,6 @@
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Binder;
@@ -67,12 +69,14 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
+import android.view.InsetsState.InternalInsetType;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsetsController.Appearance;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
@@ -84,6 +88,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
+import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
@@ -128,7 +133,8 @@
     private static final boolean DEBUG = false;
     private static final String EXTRA_DISABLE_STATE = "disabled_state";
     private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
-    private static final String EXTRA_SYSTEM_UI_VISIBILITY = "system_ui_visibility";
+    private static final String EXTRA_APPEARANCE = "appearance";
+    private static final String EXTRA_TRANSIENT_STATE = "transient_state";
 
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
@@ -165,7 +171,10 @@
     private Locale mLocale;
     private int mLayoutDirection;
 
-    private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+    /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+    private @Appearance int mAppearance;
+
+    private boolean mTransientShown;
     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
     private LightBarController mLightBarController;
     private AutoHideController mAutoHideController;
@@ -295,7 +304,8 @@
         if (savedInstanceState != null) {
             mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
             mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
-            mSystemUiVisibility = savedInstanceState.getInt(EXTRA_SYSTEM_UI_VISIBILITY, 0);
+            mAppearance = savedInstanceState.getInt(EXTRA_APPEARANCE, 0);
+            mTransientShown = savedInstanceState.getBoolean(EXTRA_TRANSIENT_STATE, false);
         }
         mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
 
@@ -397,7 +407,8 @@
         super.onSaveInstanceState(outState);
         outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
         outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2);
-        outState.putInt(EXTRA_SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
+        outState.putInt(EXTRA_APPEARANCE, mAppearance);
+        outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
         if (mNavigationBarView != null) {
             mNavigationBarView.getLightTransitionsController().saveState(outState);
         }
@@ -518,80 +529,107 @@
         rotationButtonController.onRotationProposal(rotation, winRotation, isValid);
     }
 
-    /** Restores the System UI flags saved state to {@link NavigationBarFragment}. */
-    public void restoreSystemUiVisibilityState() {
-        final int barMode = computeBarMode(0, mSystemUiVisibility);
-        if (barMode != -1) {
-            mNavigationBarMode = barMode;
-        }
+    /** Restores the appearance and the transient saved state to {@link NavigationBarFragment}. */
+    public void restoreAppearanceAndTransientState() {
+        final int barMode = barMode(mTransientShown, mAppearance);
+        mNavigationBarMode = barMode;
         checkNavBarModes();
         mAutoHideController.touchAutoHide();
 
-        mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
-                true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */);
+        mLightBarController.onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */,
+                barMode, false /* navbarColorManagedByIme */);
     }
 
     @Override
-    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
-            boolean navbarColorManagedByIme) {
+    public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
-        final int oldVal = mSystemUiVisibility;
-        final int newVal = (oldVal & ~mask) | (vis & mask);
-        final int diff = newVal ^ oldVal;
         boolean nbModeChanged = false;
-        if (diff != 0) {
-            mSystemUiVisibility = newVal;
-
-            // update navigation bar mode
-            final int nbMode = getView() == null
-                    ? -1 : computeBarMode(oldVal, newVal);
-            nbModeChanged = nbMode != -1;
-            if (nbModeChanged) {
-                if (mNavigationBarMode != nbMode) {
-                    if (mNavigationBarMode == MODE_TRANSPARENT
-                            || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
-                        mNavigationBarView.hideRecentsOnboarding();
-                    }
-                    mNavigationBarMode = nbMode;
-                    checkNavBarModes();
-                }
-                mAutoHideController.touchAutoHide();
+        if (mAppearance != appearance) {
+            mAppearance = appearance;
+            if (getView() == null) {
+                return;
             }
-            if (mNavigationBarView != null) {
-                mNavigationBarView.onSystemUiVisibilityChanged(mSystemUiVisibility);
-            }
+            nbModeChanged = updateBarMode(barMode(mTransientShown, appearance));
         }
-        mLightBarController.onNavigationVisibilityChanged(
-                vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme);
+        mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
+                mNavigationBarMode, navbarColorManagedByIme);
     }
 
-    private @TransitionMode int computeBarMode(int oldVis, int newVis) {
-        final int oldMode = barMode(oldVis);
-        final int newMode = barMode(newVis);
-        if (oldMode == newMode) {
-            return -1; // no mode change
+    @Override
+    public void showTransient(int displayId, @InternalInsetType int[] types) {
+        if (displayId != mDisplayId) {
+            return;
         }
-        return newMode;
+        if (!containsType(types, TYPE_NAVIGATION_BAR)) {
+            return;
+        }
+        if (!mTransientShown) {
+            mTransientShown = true;
+            handleTransientChanged();
+        }
     }
 
-    private @TransitionMode int barMode(int vis) {
-        final int lightsOutTransparent =
-                View.SYSTEM_UI_FLAG_LOW_PROFILE | View.NAVIGATION_BAR_TRANSIENT;
-        if ((vis & View.NAVIGATION_BAR_TRANSIENT) != 0) {
+    @Override
+    public void abortTransient(int displayId, @InternalInsetType int[] types) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        if (!containsType(types, TYPE_NAVIGATION_BAR)) {
+            return;
+        }
+        clearTransient();
+    }
+
+    void clearTransient() {
+        if (mTransientShown) {
+            mTransientShown = false;
+            handleTransientChanged();
+        }
+    }
+
+    private void handleTransientChanged() {
+        if (getView() == null) {
+            return;
+        }
+        if (mNavigationBarView != null) {
+            mNavigationBarView.onTransientStateChanged(mTransientShown);
+        }
+        final int barMode = barMode(mTransientShown, mAppearance);
+        if (updateBarMode(barMode)) {
+            mLightBarController.onNavigationBarModeChanged(barMode);
+        }
+    }
+
+    // Returns true if the bar mode is changed.
+    private boolean updateBarMode(int barMode) {
+        if (mNavigationBarMode != barMode) {
+            if (mNavigationBarMode == MODE_TRANSPARENT
+                    || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
+                mNavigationBarView.hideRecentsOnboarding();
+            }
+            mNavigationBarMode = barMode;
+            checkNavBarModes();
+            mAutoHideController.touchAutoHide();
+            return true;
+        }
+        return false;
+    }
+
+    private static @TransitionMode int barMode(boolean isTransient, int appearance) {
+        final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_SIDE_BARS;
+        if (isTransient) {
             return MODE_SEMI_TRANSPARENT;
-        } else if ((vis & View.NAVIGATION_BAR_TRANSLUCENT) != 0) {
-            return MODE_TRANSLUCENT;
-        } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
-            return MODE_LIGHTS_OUT_TRANSPARENT;
-        } else if ((vis & View.NAVIGATION_BAR_TRANSPARENT) != 0) {
-            return MODE_TRANSPARENT;
-        } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+        } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
             return MODE_LIGHTS_OUT;
-        } else {
+        } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
+            return MODE_LIGHTS_OUT_TRANSPARENT;
+        } else if ((appearance & APPEARANCE_OPAQUE_SIDE_BARS) != 0) {
             return MODE_OPAQUE;
+        } else {
+            return MODE_TRANSPARENT;
         }
     }
 
@@ -990,8 +1028,8 @@
         mAutoHideController.setNavigationBar(this);
     }
 
-    public boolean isSemiTransparent() {
-        return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
+    boolean isTransientShown() {
+        return mTransientShown;
     }
 
     private void checkBarModes() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index ae18833..4b4a35b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -366,8 +366,8 @@
         return super.onTouchEvent(event);
     }
 
-    void onSystemUiVisibilityChanged(int systemUiVisibility) {
-        mEdgeBackGestureHandler.onSystemUiVisibilityChanged(systemUiVisibility);
+    void onTransientStateChanged(boolean isTransient) {
+        mEdgeBackGestureHandler.onNavBarTransientStateChanged(isTransient);
     }
 
     void onBarTransition(int newMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 8e70d082..145be48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -24,6 +24,11 @@
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.view.InsetsFlags.getAppearance;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.InsetsState.containsType;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_TOP_BAR;
 
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.Dependency.BG_HANDLER;
@@ -75,7 +80,6 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.media.AudioAttributes;
 import android.metrics.LogMaker;
 import android.net.Uri;
@@ -104,6 +108,7 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.IWindowManager;
+import android.view.InsetsState.InternalInsetType;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -112,6 +117,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsetsController.Appearance;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
@@ -124,6 +130,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.internal.view.AppearanceRegion;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -428,10 +435,13 @@
     private int mDisabled1 = 0;
     private int mDisabled2 = 0;
 
-    // tracking calls to View.setSystemUiVisibility()
-    private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
-    private final Rect mLastFullscreenStackBounds = new Rect();
-    private final Rect mLastDockedStackBounds = new Rect();
+    /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+    private @Appearance int mAppearance;
+
+    private boolean mTransientShown;
+
+    private boolean mAppFullscreen;
+    private boolean mAppImmersive;
 
     private final DisplayMetrics mDisplayMetrics;
 
@@ -826,10 +836,22 @@
         // Set up the initial notification state. This needs to happen before CommandQueue.disable()
         setUpPresenter();
 
-        setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility,
-                result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility,
-                0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds,
-                result.mNavbarColorManagedByIme);
+        if ((result.mSystemUiVisibility & View.STATUS_BAR_TRANSIENT) != 0) {
+            showTransientUnchecked();
+        }
+        final int fullscreenAppearance = getAppearance(result.mFullscreenStackSysUiVisibility);
+        final int dockedAppearance = getAppearance(result.mDockedStackSysUiVisibility);
+        final AppearanceRegion[] appearanceRegions = result.mDockedStackBounds.isEmpty()
+                ? new AppearanceRegion[]{
+                        new AppearanceRegion(fullscreenAppearance, result.mFullscreenStackBounds)}
+                : new AppearanceRegion[]{
+                        new AppearanceRegion(fullscreenAppearance, result.mFullscreenStackBounds),
+                        new AppearanceRegion(dockedAppearance, result.mDockedStackBounds)};
+        onSystemBarAppearanceChanged(mDisplayId, getAppearance(result.mSystemUiVisibility),
+                appearanceRegions, result.mNavbarColorManagedByIme);
+        mAppFullscreen = result.mAppFullscreen;
+        mAppImmersive = result.mAppImmersive;
+
         // StatusBarManagerService has a back up of IME token and it's restored here.
         setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
                 result.mImeBackDisposition, result.mShowImeSwitcher);
@@ -2230,49 +2252,104 @@
         }
     }
 
-    @Override // CommandQueue
-    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
-            boolean navbarColorManagedByIme) {
+    @Override
+    public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
-        final int oldVal = mSystemUiVisibility;
-        final int newVal = (oldVal&~mask) | (vis&mask);
-        final int diff = newVal ^ oldVal;
-        if (DEBUG) Log.d(TAG, String.format(
-                "setSystemUiVisibility displayId=%d vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
-                displayId, Integer.toHexString(vis), Integer.toHexString(mask),
-                Integer.toHexString(oldVal), Integer.toHexString(newVal),
-                Integer.toHexString(diff)));
-        boolean sbModeChanged = false;
-        if (diff != 0) {
-            mSystemUiVisibility = newVal;
+        boolean barModeChanged = false;
+        final int diff = mAppearance ^ appearance;
+        if (mAppearance != appearance) {
+            mAppearance = appearance;
 
             // update low profile
-            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+            if ((diff & APPEARANCE_LOW_PROFILE_BARS) != 0) {
                 updateAreThereNotifications();
             }
-
-            // ready to unhide
-            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
-                mNoAnimationOnNextBarModeChange = true;
-            }
-
-            // update status bar mode
-            final int sbMode = computeStatusBarMode(oldVal, newVal);
-
-            sbModeChanged = sbMode != -1;
-            if (sbModeChanged && sbMode != mStatusBarMode) {
-                mStatusBarMode = sbMode;
-                checkBarModes();
-                mAutoHideController.touchAutoHide();
-            }
-            mStatusBarStateController.setSystemUiVisibility(mSystemUiVisibility);
+            barModeChanged = updateBarMode(barMode(mTransientShown, appearance));
         }
-        mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
-                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode,
-                navbarColorManagedByIme);
+        mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
+                mStatusBarMode, navbarColorManagedByIme);
+    }
+
+    @Override
+    public void showTransient(int displayId, @InternalInsetType int[] types) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        if (!containsType(types, TYPE_TOP_BAR)) {
+            return;
+        }
+        showTransientUnchecked();
+    }
+
+    private void showTransientUnchecked() {
+        if (!mTransientShown) {
+            mTransientShown = true;
+            mNoAnimationOnNextBarModeChange = true;
+            handleTransientChanged();
+        }
+    }
+
+    @Override
+    public void abortTransient(int displayId, @InternalInsetType int[] types) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        if (!containsType(types, TYPE_TOP_BAR)) {
+            return;
+        }
+        clearTransient();
+    }
+
+    void clearTransient() {
+        if (mTransientShown) {
+            mTransientShown = false;
+            handleTransientChanged();
+        }
+    }
+
+    private void handleTransientChanged() {
+        final int barMode = barMode(mTransientShown, mAppearance);
+        if (updateBarMode(barMode)) {
+            mLightBarController.onStatusBarModeChanged(barMode);
+        }
+    }
+
+    private boolean updateBarMode(int barMode) {
+        if (mStatusBarMode != barMode) {
+            mStatusBarMode = barMode;
+            checkBarModes();
+            mAutoHideController.touchAutoHide();
+            return true;
+        }
+        return false;
+    }
+
+    private static @TransitionMode int barMode(boolean isTransient, int appearance) {
+        final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_TOP_BAR;
+        if (isTransient) {
+            return MODE_SEMI_TRANSPARENT;
+        } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
+            return MODE_LIGHTS_OUT;
+        } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
+            return MODE_LIGHTS_OUT_TRANSPARENT;
+        } else if ((appearance & APPEARANCE_OPAQUE_TOP_BAR) != 0) {
+            return MODE_OPAQUE;
+        } else {
+            return MODE_TRANSPARENT;
+        }
+    }
+
+    @Override
+    public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        mAppFullscreen = isFullscreen;
+        mAppImmersive = isImmersive;
+        mStatusBarStateController.setFullscreenState(isFullscreen, isImmersive);
     }
 
     @Override
@@ -2303,40 +2380,10 @@
         setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
     }
 
-    protected @TransitionMode int computeStatusBarMode(int oldVal, int newVal) {
-        return computeBarMode(oldVal, newVal);
-    }
-
     protected BarTransitions getStatusBarTransitions() {
         return mStatusBarView.getBarTransitions();
     }
 
-    protected @TransitionMode int computeBarMode(int oldVis, int newVis) {
-        final int oldMode = barMode(oldVis);
-        final int newMode = barMode(newVis);
-        if (oldMode == newMode) {
-            return -1; // no mode change
-        }
-        return newMode;
-    }
-
-    private @TransitionMode int barMode(int vis) {
-        int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.STATUS_BAR_TRANSPARENT;
-        if ((vis & View.STATUS_BAR_TRANSIENT) != 0) {
-            return MODE_SEMI_TRANSPARENT;
-        } else if ((vis & View.STATUS_BAR_TRANSLUCENT) != 0) {
-            return MODE_TRANSLUCENT;
-        } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
-            return MODE_LIGHTS_OUT_TRANSPARENT;
-        } else if ((vis & View.STATUS_BAR_TRANSPARENT) != 0) {
-            return MODE_TRANSPARENT;
-        } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
-            return MODE_LIGHTS_OUT;
-        } else {
-            return MODE_OPAQUE;
-        }
-    }
-
     void checkBarModes() {
         if (mDemoMode) return;
         if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
@@ -2392,20 +2439,16 @@
 
     /** Returns whether the top activity is in fullscreen mode. */
     public boolean inFullscreenMode() {
-        return 0
-                != (mSystemUiVisibility
-                        & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION));
+        return mAppFullscreen;
     }
 
     /** Returns whether the top activity is in immersive mode. */
     public boolean inImmersiveMode() {
-        return 0
-                != (mSystemUiVisibility
-                        & (View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY));
+        return mAppImmersive;
     }
 
     private boolean areLightsOn() {
-        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
+        return 0 == (mAppearance & APPEARANCE_LOW_PROFILE_BARS);
     }
 
     public static String viewInfo(View v) {
@@ -4729,8 +4772,8 @@
         void createStatusBar(StatusBar statusbar);
     }
 
-    public @TransitionMode int getStatusBarMode() {
-        return mStatusBarMode;
+    boolean isTransientShown() {
+        return mTransientShown;
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index d04c7bd..6b51391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -42,6 +43,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.Window;
+import android.view.WindowInsets;
 import android.view.WindowInsetsController;
 import android.widget.FrameLayout;
 
@@ -74,7 +76,8 @@
     }
 
     @Override
-    protected boolean fitSystemWindows(Rect insets) {
+    public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+        final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars());
         if (getFitsSystemWindows()) {
             boolean paddingChanged = insets.top != getPaddingTop()
                     || insets.bottom != getPaddingBottom();
@@ -100,9 +103,6 @@
             if (paddingChanged) {
                 setPadding(0, 0, 0, 0);
             }
-            insets.left = 0;
-            insets.top = 0;
-            insets.right = 0;
         } else {
             if (mRightInset != 0 || mLeftInset != 0) {
                 mRightInset = 0;
@@ -116,9 +116,8 @@
             if (changed) {
                 setPadding(0, 0, 0, 0);
             }
-            insets.top = 0;
         }
-        return false;
+        return windowInsets;
     }
 
     private void applyMargins() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 1bd01e1..eb198c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -15,6 +15,8 @@
 package com.android.systemui.statusbar;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -25,10 +27,12 @@
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.view.WindowInsetsController.Appearance;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 
@@ -110,21 +114,56 @@
     }
 
     @Test
-    public void testSetSystemUiVisibility() {
-        Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r, false);
-        waitForIdleSync();
-        verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4),
-                eq(null), eq(r), eq(false));
+    public void testOnSystemBarAppearanceChanged() {
+        doTestOnSystemBarAppearanceChanged(DEFAULT_DISPLAY, 1,
+                new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false);
     }
 
     @Test
-    public void testSetSystemUiVisibilityForSecondaryDisplay() {
-        Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r, false);
+    public void testOnSystemBarAppearanceChangedForSecondaryDisplay() {
+        doTestOnSystemBarAppearanceChanged(SECONDARY_DISPLAY, 1,
+                new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false);
+    }
+
+    private void doTestOnSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
+        mCommandQueue.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions,
+                navbarColorManagedByIme);
         waitForIdleSync();
-        verify(mCallbacks).setSystemUiVisibility(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(3), eq(4),
-                eq(null), eq(r), eq(false));
+        verify(mCallbacks).onSystemBarAppearanceChanged(eq(displayId), eq(appearance),
+                eq(appearanceRegions), eq(navbarColorManagedByIme));
+    }
+
+    @Test
+    public void testShowTransient() {
+        int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+        mCommandQueue.showTransient(DEFAULT_DISPLAY, types);
+        waitForIdleSync();
+        verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types));
+    }
+
+    @Test
+    public void testShowTransientForSecondaryDisplay() {
+        int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+        mCommandQueue.showTransient(SECONDARY_DISPLAY, types);
+        waitForIdleSync();
+        verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types));
+    }
+
+    @Test
+    public void testAbortTransient() {
+        int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+        mCommandQueue.abortTransient(DEFAULT_DISPLAY, types);
+        waitForIdleSync();
+        verify(mCallbacks).abortTransient(eq(DEFAULT_DISPLAY), eq(types));
+    }
+
+    @Test
+    public void testAbortTransientForSecondaryDisplay() {
+        int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+        mCommandQueue.abortTransient(SECONDARY_DISPLAY, types);
+        waitForIdleSync();
+        verify(mCallbacks).abortTransient(eq(SECONDARY_DISPLAY), eq(types));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
deleted file mode 100644
index 16f02d9..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui.statusbar.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.IWindowManager;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/** atest AutoHideControllerTest */
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class AutoHideControllerTest extends SysuiTestCase {
-
-    private AutoHideController mAutoHideController;
-
-    private static final int FULL_MASK = 0xffffffff;
-
-    @Before
-    public void setUp() {
-        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mAutoHideController =
-                spy(new AutoHideController(mContext, Dependency.get(Dependency.MAIN_HANDLER),
-                        mock(NotificationRemoteInputManager.class), mock(IWindowManager.class)));
-        mAutoHideController.mDisplayId = DEFAULT_DISPLAY;
-        mAutoHideController.mSystemUiVisibility = View.VISIBLE;
-    }
-
-    @After
-    public void tearDown() {
-        mAutoHideController = null;
-    }
-
-    @Test
-    public void testSetSystemUiVisibilityEarlyReturnWithDifferentDisplay() {
-        mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect(), false);
-
-        verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
-    }
-
-    @Test
-    public void testSetSystemUiVisibilityEarlyReturnWithSameVisibility() {
-        mAutoHideController
-                .setSystemUiVisibility(
-                        DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect(), false);
-
-        verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
-    }
-
-    // Test if status bar unhide status doesn't change without status bar.
-    @Test
-    public void testSetSystemUiVisibilityWithoutStatusBar() {
-        doReturn(false).when(mAutoHideController).hasStatusBar();
-        int expectedStatus = View.STATUS_BAR_UNHIDE;
-        mAutoHideController.mSystemUiVisibility =
-                View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_UNHIDE;
-
-        mAutoHideController.setSystemUiVisibility(
-                DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect(), false);
-
-        assertEquals("System UI visibility should not be changed",
-                expectedStatus, mAutoHideController.mSystemUiVisibility);
-        verify(mAutoHideController, times(1)).notifySystemUiVisibilityChanged(eq(expectedStatus));
-    }
-
-    @Test
-    public void testSetSystemUiVisibilityWithVisChanged() {
-        doReturn(true).when(mAutoHideController).hasStatusBar();
-        doReturn(true).when(mAutoHideController).hasNavigationBar();
-        mAutoHideController.mSystemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
-                | View.STATUS_BAR_UNHIDE
-                | View.NAVIGATION_BAR_UNHIDE;
-
-        mAutoHideController.setSystemUiVisibility(
-                DEFAULT_DISPLAY, View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE,
-                2, 3, FULL_MASK, null, new Rect(), false);
-
-        int expectedStatus = View.VISIBLE;
-        assertEquals(expectedStatus, mAutoHideController.mSystemUiVisibility);
-        verify(mAutoHideController).notifySystemUiVisibilityChanged(eq(expectedStatus));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
new file mode 100644
index 0000000..6260d53
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 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.systemui.statusbar.phone;
+
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class LightBarControllerTest extends SysuiTestCase {
+
+    private LightBarTransitionsController mLightBarTransitionsController;
+    private SysuiDarkIconDispatcher mStatusBarIconController;
+    private LightBarController mLightBarController;
+
+    @Before
+    public void setup() {
+        mStatusBarIconController = mock(SysuiDarkIconDispatcher.class);
+        mLightBarTransitionsController = mock(LightBarTransitionsController.class);
+        when(mStatusBarIconController.getTransitionsController()).thenReturn(
+                mLightBarTransitionsController);
+        mLightBarController = new LightBarController(mContext, mStatusBarIconController,
+                mock(BatteryController.class));
+    }
+
+    @Test
+    public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksLight() {
+        final Rect firstBounds = new Rect(0, 0, 1, 1);
+        final Rect secondBounds = new Rect(1, 0, 2, 1);
+        final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+                new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, firstBounds),
+                new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, secondBounds)
+        };
+        mLightBarController.onStatusBarAppearanceChanged(
+                appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+                false /* navbarColorManagedByIme */);
+        verify(mStatusBarIconController).setIconsDarkArea(eq(null));
+        verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+    }
+
+    @Test
+    public void testOnStatusBarAppearanceChanged_multipleStacks_oneStackLightOneStackDark() {
+        final Rect firstBounds = new Rect(0, 0, 1, 1);
+        final Rect secondBounds = new Rect(1, 0, 2, 1);
+        final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+                new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, firstBounds),
+                new AppearanceRegion(0 /* appearance */, secondBounds)
+        };
+        mLightBarController.onStatusBarAppearanceChanged(
+                appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+                false /* navbarColorManagedByIme */);
+        verify(mStatusBarIconController).setIconsDarkArea(eq(firstBounds));
+        verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+    }
+
+    @Test
+    public void testOnStatusBarAppearanceChanged_multipleStacks_oneStackDarkOneStackLight() {
+        final Rect firstBounds = new Rect(0, 0, 1, 1);
+        final Rect secondBounds = new Rect(1, 0, 2, 1);
+        final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+                new AppearanceRegion(0 /* appearance */, firstBounds),
+                new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, secondBounds)
+        };
+        mLightBarController.onStatusBarAppearanceChanged(
+                appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+                false /* navbarColorManagedByIme */);
+        verify(mStatusBarIconController).setIconsDarkArea(eq(secondBounds));
+        verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+    }
+
+    @Test
+    public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksDark() {
+        final Rect firstBounds = new Rect(0, 0, 1, 1);
+        final Rect secondBounds = new Rect(1, 0, 2, 1);
+        final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+                new AppearanceRegion(0 /* appearance */, firstBounds),
+                new AppearanceRegion(0 /* appearance */, secondBounds)
+        };
+        mLightBarController.onStatusBarAppearanceChanged(
+                appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+                false /* navbarColorManagedByIme */);
+        verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
+    }
+
+    @Test
+    public void testOnStatusBarAppearanceChanged_singleStack_light() {
+        final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+                new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, new Rect(0, 0, 1, 1))
+        };
+        mLightBarController.onStatusBarAppearanceChanged(
+                appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+                false /* navbarColorManagedByIme */);
+        verify(mStatusBarIconController).setIconsDarkArea(eq(null));
+        verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+    }
+
+    @Test
+    public void testOnStatusBarAppearanceChanged_singleStack_dark() {
+        final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+                new AppearanceRegion(0, new Rect(0, 0, 1, 1))
+        };
+        mLightBarController.onStatusBarAppearanceChanged(
+                appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+                false /* navbarColorManagedByIme */);
+        verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
+    }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a93d2b8..ec64ee6 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -18,7 +18,10 @@
 
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.view.InsetsState.InternalInsetType;
+import android.view.WindowInsetsController.Appearance;
 
+import com.android.internal.view.AppearanceRegion;
 import com.android.server.notification.NotificationDelegate;
 
 public interface StatusBarManagerInternal {
@@ -75,7 +78,7 @@
 
     void startAssist(Bundle args);
     void onCameraLaunchGestureDetected(int source);
-    void topAppWindowChanged(int displayId, boolean menuVisible);
+    void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive);
     void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
             int mask, Rect fullscreenBounds, Rect dockedBounds, boolean isNavbarColorManagedByIme,
             String cause);
@@ -113,4 +116,14 @@
      * Notifies System UI whether the recents animation is running.
      */
     void onRecentsAnimationStateChanged(boolean running);
+
+    /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAppearanceChanged */
+    void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme);
+
+    /** @see com.android.internal.statusbar.IStatusBar#showTransient */
+    void showTransient(int displayId, @InternalInsetType int[] types);
+
+    /** @see com.android.internal.statusbar.IStatusBar#abortTransient */
+    void abortTransient(int displayId, @InternalInsetType int[] types);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 65bb2342..3c1a6af 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -47,6 +47,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.WindowInsetsController.Appearance;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -56,6 +57,7 @@
 import com.android.internal.statusbar.RegisterStatusBarResult;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.view.AppearanceRegion;
 import com.android.server.LocalServices;
 import com.android.server.notification.NotificationDelegate;
 import com.android.server.policy.GlobalActionsProvider;
@@ -256,8 +258,8 @@
         }
 
         @Override
-        public void topAppWindowChanged(int displayId, boolean menuVisible) {
-            StatusBarManagerService.this.topAppWindowChanged(displayId, menuVisible);
+        public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+            StatusBarManagerService.this.topAppWindowChanged(displayId, isFullscreen, isImmersive);
         }
 
         @Override
@@ -467,6 +469,36 @@
             }
 
         }
+
+        @Override
+        public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+                AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
+            // TODO (b/118118435): save the information to UiState
+            if (mBar != null) {
+                try {
+                    mBar.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions,
+                            navbarColorManagedByIme);
+                } catch (RemoteException ex) { }
+            }
+        }
+
+        @Override
+        public void showTransient(int displayId, int[] types) {
+            if (mBar != null) {
+                try {
+                    mBar.showTransient(displayId, types);
+                } catch (RemoteException ex) { }
+            }
+        }
+
+        @Override
+        public void abortTransient(int displayId, int[] types) {
+            if (mBar != null) {
+                try {
+                    mBar.abortTransient(displayId, types);
+                } catch (RemoteException ex) { }
+            }
+        }
     };
 
     private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
@@ -817,23 +849,19 @@
     }
 
     /**
-     * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
-     * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
-     * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
+     * Enables System UI to know whether the top app is fullscreen or not, and whether this app is
+     * in immersive mode or not.
      */
-    private void topAppWindowChanged(int displayId, final boolean menuVisible) {
+    private void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
         enforceStatusBar();
 
-        if (SPEW) {
-            Slog.d(TAG, "display#" + displayId + ": "
-                    + (menuVisible ? "showing" : "hiding") + " MENU key");
-        }
         synchronized(mLock) {
-            getUiState(displayId).setMenuVisible(menuVisible);
+            getUiState(displayId).setFullscreen(isFullscreen);
+            getUiState(displayId).setImmersive(isImmersive);
             mHandler.post(() -> {
                 if (mBar != null) {
                     try {
-                        mBar.topAppWindowChanged(displayId, menuVisible);
+                        mBar.topAppWindowChanged(displayId, isFullscreen, isImmersive);
                     } catch (RemoteException ex) {
                     }
                 }
@@ -942,7 +970,8 @@
         private int mDockedStackSysUiVisibility = 0;
         private final Rect mFullscreenStackBounds = new Rect();
         private final Rect mDockedStackBounds = new Rect();
-        private boolean mMenuVisible = false;
+        private boolean mFullscreen = false;
+        private boolean mImmersive = false;
         private int mDisabled1 = 0;
         private int mDisabled2 = 0;
         private int mImeWindowVis = 0;
@@ -964,12 +993,12 @@
             mDisabled2 = disabled2;
         }
 
-        private boolean isMenuVisible() {
-            return mMenuVisible;
+        private void setFullscreen(boolean isFullscreen) {
+            mFullscreen = isFullscreen;
         }
 
-        private void setMenuVisible(boolean menuVisible) {
-            mMenuVisible = menuVisible;
+        private void setImmersive(boolean immersive) {
+            mImmersive = immersive;
         }
 
         private boolean disableEquals(int disabled1, int disabled2) {
@@ -1056,12 +1085,12 @@
             // Make it aware of multi-display if needed.
             final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
             return new RegisterStatusBarResult(icons, gatherDisableActionsLocked(mCurrentUserId, 1),
-                    state.mSystemUiVisibility, state.mMenuVisible, state.mImeWindowVis,
+                    state.mSystemUiVisibility, state.mImeWindowVis,
                     state.mImeBackDisposition, state.mShowImeSwitcher,
                     gatherDisableActionsLocked(mCurrentUserId, 2),
                     state.mFullscreenStackSysUiVisibility, state.mDockedStackSysUiVisibility,
                     state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds,
-                    state.mNavbarColorManagedByIme);
+                    state.mNavbarColorManagedByIme, state.mFullscreen, state.mImmersive);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 940e814..aa0b68b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3450,6 +3450,14 @@
         return win != null;
     }
 
+    void hideTransientBars() {
+        // TODO(b/118118435): Remove this after migration
+        final int transientFlags = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
+        statusBarVisibilityChanged(mLastStatusBarVisibility & ~transientFlags);
+
+        getInsetsPolicy().hideTransient();
+    }
+
     void statusBarVisibilityChanged(int visibility) {
         mLastStatusBarVisibility = visibility;
         visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 150e26e..8877e4c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -30,7 +30,11 @@
 import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
@@ -129,6 +133,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -145,6 +150,7 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewRootImpl;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerGlobal;
@@ -158,6 +164,7 @@
 import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.function.TriConsumer;
+import com.android.internal.view.AppearanceRegion;
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.LocalServices;
 import com.android.server.UiThread;
@@ -235,7 +242,7 @@
     @Px
     private int mSideGestureInset;
 
-    private StatusBarManagerInternal getStatusBarManagerInternal() {
+    StatusBarManagerInternal getStatusBarManagerInternal() {
         synchronized (mServiceAcquireLock) {
             if (mStatusBarManagerInternal == null) {
                 mStatusBarManagerInternal =
@@ -327,14 +334,18 @@
     private int mForceClearedSystemUiFlags = 0;
     private int mLastFullscreenStackSysUiFlags;
     private int mLastDockedStackSysUiFlags;
+    private int mLastAppearance;
+    private int mLastFullscreenAppearance;
+    private int mLastDockedAppearance;
     private final Rect mNonDockedStackBounds = new Rect();
     private final Rect mDockedStackBounds = new Rect();
     private final Rect mLastNonDockedStackBounds = new Rect();
     private final Rect mLastDockedStackBounds = new Rect();
 
-    // What we last reported to system UI about whether the compatibility
-    // menu needs to be displayed.
-    private boolean mLastFocusNeedsMenu = false;
+    // What we last reported to system UI about whether the focused window is fullscreen/immersive.
+    private boolean mLastFocusIsFullscreen = false;
+    private boolean mLastFocusIsImmersive = false;
+
     // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
     private long mPendingPanicGestureUptime;
 
@@ -3045,19 +3056,38 @@
                 // Swipe-up for navigation bar is disabled during setup
                 return;
             }
-            boolean sb = mStatusBarController.checkShowTransientBarLw();
-            boolean nb = mNavigationBarController.checkShowTransientBarLw()
-                    && !isNavBarEmpty(mLastSystemUiFlags);
-            if (sb || nb) {
-                // Don't show status bar when swiping on already visible navigation bar
-                if (!nb && swipeTarget == mNavigationBar) {
-                    if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
+            if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+                if (swipeTarget == mNavigationBar
+                        && !getInsetsPolicy().isHidden(InsetsState.TYPE_NAVIGATION_BAR)) {
+                    // Don't show status bar when swiping on already visible navigation bar
                     return;
                 }
-                if (sb) mStatusBarController.showTransient();
-                if (nb) mNavigationBarController.showTransient();
+                final InsetsControlTarget controlTarget =
+                        swipeTarget.getControllableInsetProvider().getControlTarget();
+                if (controlTarget == null) {
+                    return;
+                }
+                if (controlTarget.canShowTransient()) {
+                    mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+                            new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+                } else {
+                    controlTarget.showInsets(WindowInsets.Type.systemBars(), false);
+                }
+            } else {
+                boolean sb = mStatusBarController.checkShowTransientBarLw();
+                boolean nb = mNavigationBarController.checkShowTransientBarLw()
+                        && !isNavBarEmpty(mLastSystemUiFlags);
+                if (sb || nb) {
+                    // Don't show status bar when swiping on already visible navigation bar
+                    if (!nb && swipeTarget == mNavigationBar) {
+                        if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
+                        return;
+                    }
+                    if (sb) mStatusBarController.showTransient();
+                    if (nb) mNavigationBarController.showTransient();
+                    updateSystemUiVisibilityLw();
+                }
                 mImmersiveModeConfirmation.confirmCurrentPrompt();
-                updateSystemUiVisibilityLw();
             }
         }
     }
@@ -3078,6 +3108,10 @@
         return mService.mPolicy.isKeyguardOccluded();
     }
 
+    InsetsPolicy getInsetsPolicy() {
+        return mDisplayContent.getInsetsPolicy();
+    }
+
     void resetSystemUiVisibilityLw() {
         mLastSystemUiFlags = 0;
         updateSystemUiVisibilityLw();
@@ -3129,14 +3163,20 @@
                     &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
         }
 
+        final int appearance = win.mAttrs.insetsFlags.appearance;
         final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
                 mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
         final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
                 mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
+        final int fullscreenAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+        final int dockedAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+                mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
         mService.getStackBounds(
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
-        mService.getStackBounds(mDockedStackBounds.isEmpty()
-                        ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+        final boolean inSplitScreen = !mDockedStackBounds.isEmpty();
+        mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                        : WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
         final Pair<Integer, Boolean> result =
                 updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
@@ -3144,8 +3184,24 @@
         final int diff = visibility ^ mLastSystemUiFlags;
         final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
         final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
-        final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
-        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+        final InsetsPolicy insetsPolicy = getInsetsPolicy();
+        final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
+                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) != 0
+                || (PolicyControl.getWindowFlags(win, win.mAttrs) & FLAG_FULLSCREEN) != 0
+                || (mStatusBar != null && insetsPolicy.isHidden(TYPE_TOP_BAR))
+                || (mNavigationBar != null && insetsPolicy.isHidden(
+                        InsetsState.TYPE_NAVIGATION_BAR));
+        final int behavior = win.mAttrs.insetsFlags.behavior;
+        final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE
+                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)) != 0
+                || behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
+                || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0
+                && mLastAppearance == appearance
+                && mLastFullscreenAppearance == fullscreenAppearance
+                && mLastDockedAppearance == dockedAppearance
+                && mLastFocusIsFullscreen == isFullscreen
+                && mLastFocusIsImmersive == isImmersive
                 && mFocusedApp == win.getAppToken()
                 && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
                 && mLastDockedStackBounds.equals(mDockedStackBounds)) {
@@ -3154,21 +3210,39 @@
         mLastSystemUiFlags = visibility;
         mLastFullscreenStackSysUiFlags = fullscreenVisibility;
         mLastDockedStackSysUiFlags = dockedVisibility;
-        mLastFocusNeedsMenu = needsMenu;
+        mLastAppearance = appearance;
+        mLastFullscreenAppearance = fullscreenAppearance;
+        mLastDockedAppearance = dockedAppearance;
+        mLastFocusIsFullscreen = isFullscreen;
+        mLastFocusIsImmersive = isImmersive;
         mFocusedApp = win.getAppToken();
         mLastNonDockedStackBounds.set(mNonDockedStackBounds);
         mLastDockedStackBounds.set(mDockedStackBounds);
         final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
         final Rect dockedStackBounds = new Rect(mDockedStackBounds);
+        final AppearanceRegion[] appearanceRegions = inSplitScreen
+                ? new AppearanceRegion[]{
+                        new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds),
+                        new AppearanceRegion(dockedAppearance, dockedStackBounds)}
+                : new AppearanceRegion[]{
+                        new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds)};
         final boolean isNavbarColorManagedByIme = result.second;
         mHandler.post(() -> {
             StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
             if (statusBar != null) {
                 final int displayId = getDisplayId();
+                // TODO(b/118118435): disabled flags only
                 statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility,
                         dockedVisibility, 0xffffffff, fullscreenStackBounds,
                         dockedStackBounds, isNavbarColorManagedByIme, win.toString());
-                statusBar.topAppWindowChanged(displayId, needsMenu);
+                if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+                    statusBar.onSystemBarAppearanceChanged(displayId, appearance,
+                            appearanceRegions, isNavbarColorManagedByIme);
+                }
+                statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive);
+
+                // TODO(b/118118435): Remove this after removing system UI visibilities.
+                mDisplayContent.statusBarVisibilityChanged(visibility);
             }
         });
         return diff;
@@ -3190,6 +3264,22 @@
         return vis;
     }
 
+    private int updateLightStatusBarAppearanceLw(int appearance, WindowState opaque,
+            WindowState opaqueOrDimming) {
+        final boolean onKeyguard = isStatusBarKeyguard() && !isKeyguardOccluded();
+        final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;
+        if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
+            // If the top fullscreen-or-dimming window is also the top fullscreen, respect
+            // its light flag.
+            appearance &= ~APPEARANCE_LIGHT_TOP_BAR;
+            appearance |= statusColorWin.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_TOP_BAR;
+        } else if (statusColorWin != null && statusColorWin.isDimming()) {
+            // Otherwise if it's dimming, clear the light flag.
+            appearance &= ~APPEARANCE_LIGHT_TOP_BAR;
+        }
+        return appearance;
+    }
+
     @VisibleForTesting
     @Nullable
     static WindowState chooseNavigationColorWindowLw(WindowState opaque,
@@ -3511,6 +3601,8 @@
                 mPendingPanicGestureUptime = SystemClock.uptimeMillis();
                 if (!isNavBarEmpty(mLastSystemUiFlags)) {
                     mNavigationBarController.showTransient();
+                    mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+                            new int[] {InsetsState.TYPE_NAVIGATION_BAR}));
                 }
             }
         }
@@ -3588,9 +3680,6 @@
             pw.print(" mForceClearedSystemUiFlags=0x");
             pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
         }
-        if (mLastFocusNeedsMenu) {
-            pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu);
-        }
         pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
         pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
         pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 22ba82a..c8ce53d 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -42,4 +42,11 @@
      */
     default void hideInsets(@InsetType int types, boolean fromIme) {
     }
+
+    /**
+     * Returns {@code true} if the control target allows the system to show transient windows.
+     */
+    default boolean canShowTransient() {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 2dc50d8..fc51b46 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -16,13 +16,22 @@
 
 package com.android.server.wm;
 
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
 
 import android.annotation.Nullable;
+import android.app.StatusBarManager;
+import android.util.IntArray;
+import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetType;
+import android.view.ViewRootImpl;
 
 /**
  * Policy that implements who gets control over the windows generating insets.
@@ -32,6 +41,12 @@
     private final InsetsStateController mStateController;
     private final DisplayContent mDisplayContent;
     private final DisplayPolicy mPolicy;
+    private final TransientControlTarget mTransientControlTarget = new TransientControlTarget();
+    private final IntArray mShowingTransientTypes = new IntArray();
+
+    private WindowState mFocusedWin;
+    private BarWindow mTopBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
+    private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
 
     InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
         mStateController = stateController;
@@ -41,11 +56,132 @@
 
     /** Updates the target which can control system bars. */
     void updateBarControlTarget(@Nullable WindowState focusedWin) {
+        mFocusedWin = focusedWin;
         mStateController.onBarControlTargetChanged(getTopControlTarget(focusedWin),
-                getNavControlTarget(focusedWin));
+                getFakeTopControlTarget(focusedWin),
+                getNavControlTarget(focusedWin),
+                getFakeNavControlTarget(focusedWin));
+        if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
+            return;
+        }
+        mTopBar.setVisible(focusedWin == null
+                || focusedWin != getTopControlTarget(focusedWin)
+                || focusedWin.getClientInsetsState().getSource(TYPE_TOP_BAR).isVisible());
+        mNavBar.setVisible(focusedWin == null
+                || focusedWin != getNavControlTarget(focusedWin)
+                || focusedWin.getClientInsetsState().getSource(TYPE_NAVIGATION_BAR).isVisible());
+    }
+
+    boolean isHidden(@InternalInsetType int type) {
+        final InsetsSourceProvider provider =  mStateController.peekSourceProvider(type);
+        return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
+    }
+
+    void showTransient(IntArray types) {
+        boolean changed = false;
+        for (int i = types.size() - 1; i >= 0; i--) {
+            final int type = types.get(i);
+            if (mShowingTransientTypes.indexOf(type) != -1) {
+                continue;
+            }
+            if (!isHidden(type)) {
+                continue;
+            }
+            mShowingTransientTypes.add(type);
+            changed = true;
+        }
+        if (changed) {
+            updateBarControlTarget(mFocusedWin);
+            mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
+                    mShowingTransientTypes.toArray());
+            mStateController.notifyInsetsChanged();
+            // TODO(b/118118435): Animation
+        }
+    }
+
+    void hideTransient() {
+        if (mShowingTransientTypes.size() == 0) {
+            return;
+        }
+
+        // TODO(b/118118435): Animation
+        mShowingTransientTypes.clear();
+        updateBarControlTarget(mFocusedWin);
+        mStateController.notifyInsetsChanged();
+    }
+
+    /**
+     * @see InsetsStateController#getInsetsForDispatch
+     */
+    InsetsState getInsetsForDispatch(WindowState target) {
+        InsetsState state = mStateController.getInsetsForDispatch(target);
+        if (mShowingTransientTypes.size() == 0) {
+            return state;
+        }
+        for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
+            state.setSourceVisible(mShowingTransientTypes.get(i), false);
+        }
+        return state;
+    }
+
+    void onInsetsModified(WindowState windowState, InsetsState state) {
+        mStateController.onInsetsModified(windowState, state);
+        checkAbortTransient(windowState, state);
+        if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
+            return;
+        }
+        if (windowState == getTopControlTarget(mFocusedWin)) {
+            mTopBar.setVisible(state.getSource(TYPE_TOP_BAR).isVisible());
+        }
+        if (windowState == getNavControlTarget(mFocusedWin)) {
+            mNavBar.setVisible(state.getSource(TYPE_NAVIGATION_BAR).isVisible());
+        }
+    }
+
+    /**
+     * Called when a window modified the insets state. If the window set a insets source to visible
+     * while it is shown transiently, we need to abort the transient state.
+     *
+     * @param windowState who changed the insets state.
+     * @param state the modified insets state.
+     */
+    private void checkAbortTransient(WindowState windowState, InsetsState state) {
+        if (mShowingTransientTypes.size() != 0) {
+            IntArray abortTypes = new IntArray();
+            for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
+                final int type = mShowingTransientTypes.get(i);
+                if (mStateController.isFakeTarget(type, windowState)
+                        && state.getSource(type).isVisible()) {
+                    mShowingTransientTypes.remove(i);
+                    abortTypes.add(type);
+                }
+            }
+            if (abortTypes.size() > 0) {
+                mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
+                        abortTypes.toArray());
+                updateBarControlTarget(mFocusedWin);
+            }
+        }
+    }
+
+    private @Nullable InsetsControlTarget getFakeTopControlTarget(@Nullable WindowState focused) {
+        if (mShowingTransientTypes.indexOf(TYPE_TOP_BAR) != -1) {
+            return focused;
+        }
+        return null;
+    }
+
+    private @Nullable InsetsControlTarget getFakeNavControlTarget(@Nullable WindowState focused) {
+        if (mShowingTransientTypes.indexOf(TYPE_NAVIGATION_BAR) != -1) {
+            return focused;
+        }
+        return null;
     }
 
     private @Nullable InsetsControlTarget getTopControlTarget(@Nullable WindowState focusedWin) {
+        if (mShowingTransientTypes.indexOf(TYPE_TOP_BAR) != -1) {
+            return mTransientControlTarget;
+        }
         if (areSystemBarsForciblyVisible() || isStatusBarForciblyVisible()) {
             return null;
         }
@@ -53,6 +189,9 @@
     }
 
     private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) {
+        if (mShowingTransientTypes.indexOf(TYPE_NAVIGATION_BAR) != -1) {
+            return mTransientControlTarget;
+        }
         if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) {
             return null;
         }
@@ -66,7 +205,7 @@
         }
         final int privateFlags = statusBar.mAttrs.privateFlags;
 
-        // TODO: Pretend to the app that it's still able to control it?
+        // TODO(b/118118435): Pretend to the app that it's still able to control it?
         if ((privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
             return true;
         }
@@ -100,4 +239,31 @@
         return isDockedStackVisible || isFreeformStackVisible || isResizing;
     }
 
+    private class BarWindow {
+
+        private final int mId;
+        private  @StatusBarManager.WindowVisibleState int mState =
+                StatusBarManager.WINDOW_STATE_SHOWING;
+
+        BarWindow(int id) {
+            mId = id;
+        }
+
+        private void setVisible(boolean visible) {
+            final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
+            if (mState != state) {
+                mState = state;
+                mPolicy.getStatusBarManagerInternal().setWindowState(
+                        mDisplayContent.getDisplayId(), mId, state);
+            }
+        }
+    }
+
+    // TODO(b/118118435): Implement animations for it (with SurfaceAnimator)
+    private class TransientControlTarget implements InsetsControlTarget {
+
+        @Override
+        public void notifyInsetsControlChanged() {
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 3731d3f..a7724a1 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -111,7 +111,9 @@
     void setWindow(@Nullable WindowState win,
             @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
         if (mWin != null) {
-            mWin.setInsetProvider(null);
+            if (mControllable) {
+                mWin.setControllableInsetProvider(null);
+            }
             // The window may be animating such that we can hand out the leash to the control
             // target. Revoke the leash by cancelling the animation to correct the state.
             // TODO: Ideally, we should wait for the animation to finish so previous window can
@@ -123,8 +125,8 @@
         if (win == null) {
             setServerVisible(false);
             mSource.setFrame(new Rect());
-        } else {
-            mWin.setInsetProvider(this);
+        } else if (mControllable) {
+            mWin.setControllableInsetProvider(this);
             if (mControlTarget != null) {
                 updateControlForTarget(mControlTarget, true /* force */);
             }
@@ -132,6 +134,13 @@
     }
 
     /**
+     * @return Whether there is a window which backs this source.
+     */
+    boolean hasWindow() {
+        return mWin != null;
+    }
+
+    /**
      * Called when a layout pass has occurred.
      */
     void onPostLayout() {
@@ -225,6 +234,10 @@
         return null;
     }
 
+    InsetsControlTarget getControlTarget() {
+        return mControlTarget;
+    }
+
     boolean isClientVisible() {
         return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
     }
@@ -241,9 +254,13 @@
         @Override
         public void startAnimation(SurfaceControl animationLeash, Transaction t,
                 OnAnimationFinishedCallback finishCallback) {
-            // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
-            t.setAlpha(animationLeash, 1 /* alpha */);
-            t.hide(animationLeash);
+            // TODO(b/118118435): We can remove the type check when implementing the transient bar
+            //                    animation.
+            if (mSource.getType() == TYPE_IME) {
+                // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
+                t.setAlpha(animationLeash, 1 /* alpha */);
+                t.hide(animationLeash);
+            }
 
             mCapturedLeash = animationLeash;
             final Rect frame = mWin.getWindowFrames().mFrame;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index b0410335c..e055424 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.InsetsState.InternalInsetType;
 import static android.view.InsetsState.TYPE_IME;
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.TYPE_TOP_BAR;
@@ -31,6 +30,7 @@
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetType;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -73,7 +73,7 @@
      * @return The state stripped of the necessary information.
      */
     InsetsState getInsetsForDispatch(WindowState target) {
-        final InsetsSourceProvider provider = target.getInsetProvider();
+        final InsetsSourceProvider provider = target.getControllableInsetProvider();
         if (provider == null) {
             return mState;
         }
@@ -123,6 +123,13 @@
     }
 
     /**
+     * @return The provider of a specific type or null if we don't have it.
+     */
+    @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetType int type) {
+        return mProviders.get(type);
+    }
+
+    /**
      * Called when a layout pass has occurred.
      */
     void onPostLayout() {
@@ -152,6 +159,10 @@
         }
     }
 
+    boolean isFakeTarget(@InternalInsetType int type, InsetsControlTarget target) {
+        return mTypeFakeControlTargetMap.get(type) == target;
+    }
+
     void onImeTargetChanged(@Nullable InsetsControlTarget imeTarget) {
         onControlChanged(TYPE_IME, imeTarget);
         notifyPendingInsetsControlChanged();
@@ -166,9 +177,13 @@
      *                       and visibility.
      */
     void onBarControlTargetChanged(@Nullable InsetsControlTarget topControlling,
-            @Nullable InsetsControlTarget navControlling) {
+            @Nullable InsetsControlTarget fakeTopControlling,
+            @Nullable InsetsControlTarget navControlling,
+            @Nullable InsetsControlTarget fakeNavControlling) {
         onControlChanged(TYPE_TOP_BAR, topControlling);
         onControlChanged(TYPE_NAVIGATION_BAR, navControlling);
+        onControlFakeTargetChanged(TYPE_TOP_BAR, fakeTopControlling);
+        onControlFakeTargetChanged(TYPE_NAVIGATION_BAR, fakeNavControlling);
         notifyPendingInsetsControlChanged();
     }
 
@@ -279,7 +294,7 @@
         });
     }
 
-    private void notifyInsetsChanged() {
+    void notifyInsetsChanged() {
         mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
     }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b047d8f..06e7d66 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -465,7 +465,8 @@
             final WindowState windowState = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (windowState != null) {
-                windowState.getDisplayContent().getInsetsStateController().onInsetsModified(
+                windowState.setClientInsetsState(state);
+                windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(
                         windowState, state);
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2dd6d0a..e90f3da 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1663,7 +1663,7 @@
                     outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
             }
-            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
+            outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win));
 
             if (mInTouchMode) {
                 res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -2356,7 +2356,7 @@
                     outStableInsets, outOutsets);
             outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
             outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
-            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
+            outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win));
             if (DEBUG) {
                 Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
                         + ", requestedWidth=" + requestedWidth
@@ -5574,6 +5574,20 @@
     }
 
     @Override
+    public void hideTransientBars(int displayId) {
+        mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
+                "hideTransientBars()");
+        synchronized (mGlobalLock) {
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent != null) {
+                displayContent.hideTransientBars();
+            } else {
+                Slog.w(TAG, "hideTransientBars with invalid displayId=" + displayId);
+            }
+        }
+    }
+
+    @Override
     public void setForceShowSystemBars(boolean show) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c25a71d..b9cf29a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -31,6 +31,7 @@
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -191,6 +192,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.InsetsState;
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -633,11 +635,20 @@
      */
     private boolean mIsDimming = false;
 
-    private @Nullable InsetsSourceProvider mInsetProvider;
+    private @Nullable InsetsSourceProvider mControllableInsetProvider;
+    private InsetsState mClientInsetsState = new InsetsState();
 
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
     private KeyInterceptionInfo mKeyInterceptionInfo;
 
+    InsetsState getClientInsetsState() {
+        return mClientInsetsState;
+    }
+
+    void setClientInsetsState(InsetsState state) {
+        mClientInsetsState = state;
+    }
+
     void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
             @Rotation int rotation, boolean requested) {
         // Invisible windows and the wallpaper do not participate in the seamless rotation animation
@@ -1493,7 +1504,8 @@
         return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
                 // If we don't have a provider, this window isn't used as a window generating
                 // insets, so nobody can hide it over the inset APIs.
-                && (mInsetProvider == null || mInsetProvider.isClientVisible());
+                && (mControllableInsetProvider == null
+                        || mControllableInsetProvider.isClientVisible());
     }
 
     /**
@@ -3351,7 +3363,7 @@
     void notifyInsetsChanged() {
         try {
             mClient.insetsChanged(
-                    getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
+                    getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this));
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to deliver inset state change", e);
         }
@@ -3361,8 +3373,9 @@
     public void notifyInsetsControlChanged() {
         final InsetsStateController stateController =
                 getDisplayContent().getInsetsStateController();
+        final InsetsPolicy policy = getDisplayContent().getInsetsPolicy();
         try {
-            mClient.insetsControlChanged(stateController.getInsetsForDispatch(this),
+            mClient.insetsControlChanged(policy.getInsetsForDispatch(this),
                     stateController.getControlsForDispatch(this));
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to deliver inset state change", e);
@@ -3387,6 +3400,11 @@
         }
     }
 
+    @Override
+    public boolean canShowTransient() {
+        return (mAttrs.insetsFlags.behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
+    }
+
     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
@@ -4815,7 +4833,7 @@
     void startAnimation(Animation anim) {
 
         // If we are an inset provider, all our animations are driven by the inset client.
-        if (mInsetProvider != null && mInsetProvider.isControllable()) {
+        if (mControllableInsetProvider != null) {
             return;
         }
 
@@ -4835,7 +4853,7 @@
     private void startMoveAnimation(int left, int top) {
 
         // If we are an inset provider, all our animations are driven by the inset client.
-        if (mInsetProvider != null && mInsetProvider.isControllable()) {
+        if (mControllableInsetProvider != null) {
             return;
         }
 
@@ -5319,12 +5337,22 @@
         mWindowFrames.setContentChanged(false);
     }
 
-    void setInsetProvider(InsetsSourceProvider insetProvider) {
-        mInsetProvider = insetProvider;
+    /**
+     * Set's an {@link InsetsSourceProvider} to be associated with this window, but only if the
+     * provider itself is controllable, as one window can be the provider of more than one inset
+     * type (i.e. gesture insets). If this window is controllable, all its animations must be
+     * controlled by its control target, and the visibility of this window should be taken account
+     * into the state of the control target.
+     *
+     * @param insetProvider the provider which should not be visible to the client.
+     * @see InsetsStateController#getInsetsForDispatch(WindowState)
+     */
+    void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
+        mControllableInsetProvider = insetProvider;
     }
 
-    InsetsSourceProvider getInsetProvider() {
-        return mInsetProvider;
+    InsetsSourceProvider getControllableInsetProvider() {
+        return mControllableInsetProvider;
     }
 
     private final class MoveAnimationSpec implements AnimationSpec {
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index f3a8e1a..8c2ae5a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
@@ -32,7 +33,9 @@
 import static org.junit.Assert.assertNull;
 
 import android.platform.test.annotations.Presubmit;
+import android.util.IntArray;
 import android.view.InsetsSourceControl;
+import android.view.InsetsState;
 import android.view.test.InsetsModeSession;
 
 import androidx.test.filters.FlakyTest;
@@ -151,6 +154,91 @@
         assertEquals(1, controls.length);
     }
 
+    @Test
+    public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
+        addWindow(TYPE_STATUS_BAR, "topBar")
+                .getControllableInsetProvider().getSource().setVisible(false);
+        addWindow(TYPE_NAVIGATION_BAR, "navBar")
+                .getControllableInsetProvider().getSource().setVisible(false);
+        final WindowState app = addWindow(TYPE_APPLICATION, "app");
+
+        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        policy.updateBarControlTarget(app);
+        policy.showTransient(
+                IntArray.wrap(new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+        final InsetsSourceControl[] controls =
+                mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+        // The app must get both fake controls.
+        assertEquals(2, controls.length);
+        for (int i = controls.length - 1; i >= 0; i--) {
+            assertNull(controls[i].getLeash());
+        }
+    }
+
+    @Test
+    public void testShowTransientBars_topCanBeTransient_appGetsTopFakeControl() {
+        addWindow(TYPE_STATUS_BAR, "topBar")
+                .getControllableInsetProvider().getSource().setVisible(false);
+        addWindow(TYPE_NAVIGATION_BAR, "navBar")
+                .getControllableInsetProvider().getSource().setVisible(true);
+        final WindowState app = addWindow(TYPE_APPLICATION, "app");
+
+        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        policy.updateBarControlTarget(app);
+        policy.showTransient(
+                IntArray.wrap(new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+        final InsetsSourceControl[] controls =
+                mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+        // The app must get the fake control of the top bar, and must get the real control of the
+        // navigation bar.
+        assertEquals(2, controls.length);
+        for (int i = controls.length - 1; i >= 0; i--) {
+            final InsetsSourceControl control = controls[i];
+            if (control.getType() == TYPE_TOP_BAR) {
+                assertNull(controls[i].getLeash());
+            } else {
+                assertNotNull(controls[i].getLeash());
+            }
+        }
+    }
+
+    @Test
+    public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
+        addWindow(TYPE_STATUS_BAR, "topBar")
+                .getControllableInsetProvider().getSource().setVisible(false);
+        addWindow(TYPE_NAVIGATION_BAR, "navBar")
+                .getControllableInsetProvider().getSource().setVisible(false);
+        final WindowState app = addWindow(TYPE_APPLICATION, "app");
+
+        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        policy.updateBarControlTarget(app);
+        policy.showTransient(
+                IntArray.wrap(new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+        InsetsSourceControl[] controls =
+                mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+        // The app must get both fake controls.
+        assertEquals(2, controls.length);
+        for (int i = controls.length - 1; i >= 0; i--) {
+            assertNull(controls[i].getLeash());
+        }
+
+        final InsetsState state = policy.getInsetsForDispatch(app);
+        state.setSourceVisible(TYPE_TOP_BAR, true);
+        state.setSourceVisible(InsetsState.TYPE_NAVIGATION_BAR, true);
+        policy.onInsetsModified(app, state);
+
+        controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+        // The app must get both real controls.
+        assertEquals(2, controls.length);
+        for (int i = controls.length - 1; i >= 0; i--) {
+            assertNotNull(controls[i].getLeash());
+        }
+    }
+
     private WindowState addWindow(int type, String name) {
         final WindowState win = createWindow(null, type, name);
         mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 81ea32b..011161b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -66,7 +66,7 @@
         final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
-        topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+        topBar.setControllableInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
         assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
     }
 
@@ -75,7 +75,7 @@
         final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
         mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
                 .setWindow(topBar, null);
-        topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+        topBar.setControllableInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
         final InsetsState state = getController().getInsetsForDispatch(topBar);
         for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
             final InsetsSource source = state.sourceAt(i);
@@ -101,7 +101,7 @@
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
         getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
-        getController().onBarControlTargetChanged(app, app);
+        getController().onBarControlTargetChanged(app, null, app, null);
         InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
         assertEquals(2, controls.length);
     }
@@ -111,9 +111,9 @@
         final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
-        getController().onBarControlTargetChanged(app, null);
+        getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
-        getController().onBarControlTargetChanged(null, null);
+        getController().onBarControlTargetChanged(null, null, null, null);
         assertNull(getController().getControlsForDispatch(app));
     }
 
@@ -123,7 +123,7 @@
         final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
-        getController().onBarControlTargetChanged(app, null);
+        getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
         topBar.cancelAnimation();
         assertNull(getController().getControlsForDispatch(app));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 1a4562b..98d73ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -397,14 +397,15 @@
     }
 
     @Test
-    public void testVisibleWithInsetsProvider() throws Exception {
+    public void testVisibleWithInsetsProvider() {
         final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         topBar.mHasSurface = true;
         assertTrue(topBar.isVisible());
         mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
-                .setWindow(topBar, null);
-        mDisplayContent.getInsetsStateController().onBarControlTargetChanged(app, app);
+                .setWindow(topBar, null /* frameProvider */);
+        mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
+                app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
         mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
                 .onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR));
         waitUntilHandlersIdle();
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index c9e3404..957216e 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -41,6 +41,7 @@
             "android.view.DisplayCutoutTest",
             "android.view.InsetsAnimationControlImplTest",
             "android.view.InsetsControllerTest",
+            "android.view.InsetsFlagsTest",
             "android.view.InsetsSourceTest",
             "android.view.InsetsSourceConsumerTest",
             "android.view.InsetsStateTest",