Use last focused window to control SysUI while IMC dialog is shown

This change fixes a bug that if a child window hides the system UI
but its parent activity window doesn't, the system UI may keep
showing and hiding infinitely.

Bug: b/111490047
Test: 1. atest WindowManagerSmokeTest
      2. atest CtsWindowManagerDeviceTestCases:LayoutTests
Change-Id: Id8bd6e80fe19051b97918089a05f2a12fc1a673d
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e6195b4..883e939 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -631,8 +631,10 @@
 
     int mPointerLocationMode = 0; // guarded by mLock
 
-    // The last window we were told about in focusChanged.
+    // The windows we were told about in focusChanged.
     WindowState mFocusedWindow;
+    WindowState mLastFocusedWindow;
+
     IApplicationToken mFocusedApp;
 
     PointerLocationView mPointerLocationView;
@@ -3334,6 +3336,9 @@
             mNavigationBar = null;
             mNavigationBarController.setWindow(null);
         }
+        if (mLastFocusedWindow == win) {
+            mLastFocusedWindow = null;
+        }
         mScreenDecorWindows.remove(win);
     }
 
@@ -5879,7 +5884,8 @@
     @Override
     public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
         mFocusedWindow = newFocus;
-        if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+        mLastFocusedWindow = lastFocus;
+        if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
             // If the navigation bar has been hidden or shown, we need to do another
             // layout pass to update that window.
             return FINISH_LAYOUT_REDO_LAYOUT;
@@ -8105,10 +8111,19 @@
         if (winCandidate == null) {
             return 0;
         }
+
+        // The immersive mode confirmation should never affect the system bar visibility, otherwise
+        // it will unhide the navigation bar and hide itself.
         if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
-            // The immersive mode confirmation should never affect the system bar visibility,
-            // otherwise it will unhide the navigation bar and hide itself.
-            winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
+
+            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
+            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
+            // keys, we let it keep controlling the visibility.
+            final boolean lastFocusCanReceiveKeys =
+                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
+            winCandidate = isStatusBarKeyguard() ? mStatusBar
+                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
+                    : mTopFullscreenOpaqueWindowState;
             if (winCandidate == null) {
                 return 0;
             }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 1ebbe3ac..7ea6200 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -491,6 +491,9 @@
          */
         boolean canAcquireSleepToken();
 
+        /** @return true if this window desires key events. */
+        boolean canReceiveKeys();
+
         /**
          * Writes {@link com.android.server.wm.IdentifierProto} to stream.
          */
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c8c4b58..fb0c3bc 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2493,8 +2493,8 @@
         return getWindowConfiguration().keepVisibleDeadAppWindowOnScreen();
     }
 
-    /** @return true if this window desires key events. */
-    boolean canReceiveKeys() {
+    @Override
+    public boolean canReceiveKeys() {
         return isVisibleOrAdding()
                 && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)