diff options
3 files changed, 111 insertions, 9 deletions
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 8ff2e6aebdd0..d866b0e2f89e 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -106,3 +106,15 @@ flag { is_exported: true bug: "293949943" } + +flag { + namespace: "window_surfaces" + name: "fix_hide_overlay_api" + description: "Application that calls setHideOverlayWindows() shouldn't hide its own windows, this flag gate the fix of this issue." + is_fixed_read_only: true + is_exported: true + bug: "359424300" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9d499e60737c..8f530a374261 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -326,7 +326,6 @@ import android.window.WindowContainerToken; import android.window.WindowContextInfo; import com.android.internal.R; -import com.android.internal.util.ToBooleanFunction; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; @@ -343,6 +342,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.LatencyTracker; +import com.android.internal.util.ToBooleanFunction; import com.android.internal.view.WindowManagerPolicyThread; import com.android.server.AnimationThread; import com.android.server.DisplayThread; @@ -388,7 +388,6 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -673,6 +672,14 @@ public class WindowManagerService extends IWindowManager.Stub private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>(); /** + * A map that tracks uid/count of windows that cause non-system overlay windows to be hidden. + * The key is the window's uid and the value is the number of windows with that uid that are + * requesting hiding non-system overlay + */ + private final ArrayMap<Integer, Integer> mHidingNonSystemOverlayWindowsCountPerUid = + new ArrayMap<>(); + + /** * In some cases (e.g. when {@link R.bool.config_reverseDefaultRotation} has value * {@value true}) we need to map some orientation to others. This {@link SparseIntArray} * contains the relation between the source orientation and the one to use. @@ -1808,7 +1815,7 @@ public class WindowManagerService extends IWindowManager.Stub UserHandle.getUserId(win.getOwningUid())); win.setHiddenWhileSuspended(suspended); - final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); + final boolean hideSystemAlertWindows = shouldHideNonSystemOverlayWindow(win); win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); boolean imMayMove = true; @@ -2032,6 +2039,22 @@ public class WindowManagerService extends IWindowManager.Stub } } + private boolean shouldHideNonSystemOverlayWindow(WindowState win) { + if (!Flags.fixHideOverlayApi()) { + return !mHidingNonSystemOverlayWindows.isEmpty(); + } + + if (mHidingNonSystemOverlayWindows.isEmpty()) { + return false; + } + + if (mHidingNonSystemOverlayWindowsCountPerUid.size() == 1 + && mHidingNonSystemOverlayWindowsCountPerUid.containsKey(win.getOwningUid())) { + return false; + } + return true; + } + /** * Set whether screen capture is disabled for all windows of a specific user from * the device policy cache, or specific windows based on sensitive content protections. @@ -8733,22 +8756,42 @@ public class WindowManagerService extends IWindowManager.Stub return; } final boolean systemAlertWindowsHidden = !mHidingNonSystemOverlayWindows.isEmpty(); + final int numUIDsRequestHidingPreUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size(); if (surfaceShown && win.hideNonSystemOverlayWindowsWhenVisible()) { if (!mHidingNonSystemOverlayWindows.contains(win)) { mHidingNonSystemOverlayWindows.add(win); + int uid = win.getOwningUid(); + int count = mHidingNonSystemOverlayWindowsCountPerUid.getOrDefault(uid, 0); + mHidingNonSystemOverlayWindowsCountPerUid.put(uid, count + 1); } } else { mHidingNonSystemOverlayWindows.remove(win); + int uid = win.getOwningUid(); + int count = mHidingNonSystemOverlayWindowsCountPerUid.getOrDefault(uid, 0); + if (count <= 1) { + mHidingNonSystemOverlayWindowsCountPerUid.remove(win.getOwningUid()); + } else { + mHidingNonSystemOverlayWindowsCountPerUid.put(uid, count - 1); + } } - final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); - - if (systemAlertWindowsHidden == hideSystemAlertWindows) { - return; + final int numUIDSRequestHidingPostUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size(); + if (Flags.fixHideOverlayApi()) { + if (numUIDSRequestHidingPostUpdate == numUIDsRequestHidingPreUpdate) { + return; + } + // The visibility of SAWs needs to be refreshed only when the number of uids that + // request hiding SAWs changes 0->1, 1->0, 1->2 or 2->1. + if (numUIDSRequestHidingPostUpdate != 1 && numUIDsRequestHidingPreUpdate != 1) { + return; + } + } else { + if (systemAlertWindowsHidden == hideSystemAlertWindows) { + return; + } } - mRoot.forAllWindows((w) -> { - w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); + w.setForceHideNonSystemOverlayWindowIfNeeded(shouldHideNonSystemOverlayWindow(w)); }, false /* traverseTopToBottom */); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 7bc2c477f144..1323d8a59cef 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -37,6 +37,7 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; @@ -1471,6 +1472,52 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test + @EnableFlags(Flags.FLAG_FIX_HIDE_OVERLAY_API) + public void testUpdateOverlayWindows_singleWindowRequestsHiding_doNotHideOverlayWithSameUid() { + WindowState overlayWindow = newWindowBuilder("overlay_window", + TYPE_APPLICATION_OVERLAY).build(); + WindowState appWindow = newWindowBuilder("app_window", TYPE_APPLICATION).build(); + makeWindowVisible(appWindow, overlayWindow); + + int uid = 100000; + spyOn(appWindow); + spyOn(overlayWindow); + doReturn(true).when(appWindow).hideNonSystemOverlayWindowsWhenVisible(); + doReturn(uid).when(appWindow).getOwningUid(); + doReturn(uid).when(overlayWindow).getOwningUid(); + + mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(appWindow, true); + + verify(overlayWindow).setForceHideNonSystemOverlayWindowIfNeeded(false); + } + + @Test + @EnableFlags(Flags.FLAG_FIX_HIDE_OVERLAY_API) + public void testUpdateOverlayWindows_multipleWindowsRequestHiding_hideOverlaysWithAnyUids() { + WindowState overlayWindow = newWindowBuilder("overlay_window", + TYPE_APPLICATION_OVERLAY).build(); + WindowState appWindow1 = newWindowBuilder("app_window_1", TYPE_APPLICATION).build(); + WindowState appWindow2 = newWindowBuilder("app_window_2", TYPE_APPLICATION).build(); + makeWindowVisible(appWindow1, appWindow2, overlayWindow); + + int uid1 = 100000; + int uid2 = 100001; + spyOn(appWindow1); + spyOn(appWindow2); + spyOn(overlayWindow); + doReturn(true).when(appWindow1).hideNonSystemOverlayWindowsWhenVisible(); + doReturn(true).when(appWindow2).hideNonSystemOverlayWindowsWhenVisible(); + doReturn(uid1).when(appWindow1).getOwningUid(); + doReturn(uid1).when(overlayWindow).getOwningUid(); + doReturn(uid2).when(appWindow2).getOwningUid(); + + mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(appWindow1, true); + mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(appWindow2, true); + + verify(overlayWindow).setForceHideNonSystemOverlayWindowIfNeeded(true); + } + + @Test @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API) public void reparentWindowContextToDisplayArea_newDisplay_reparented() { final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE, |