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/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 {