From d1d8402a74b8080732620418e3894fac0ac6335a Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Wed, 14 Jun 2023 01:43:05 +0800 Subject: [PATCH] Consider multiple show-when-locked activities for wallpaper 2 problems: - ActivityRecord#mShowWhenLocked was not involved, i.e. if the app uses Activity#setShowWhenLocked rather than FLAG_SHOW_WHEN_LOCKED, the condition needsShowWhenLockedWallpaper is never satisfied. - The useTopWallpaperAsTarget was always true if there exists one non fullscreen show-when-locked activity even if the wallpaper can be occluded by the opaque show-when-locked activity behind. The first one is fixed by checking WindowState#canShowWhenLocked. The second one is fixed by updating mNeedsShowWhenLockedWallpaper when going through all windows from top to bottom. Note that with shell transition, if the wallpaper becomes visible unexpectedly, it will be collected into transition. And because the wallpaper surface will be put on transition root surface that may have higher z-order, which will cause the wallpaper to be flickering when switching show-when-locked activities with scene transition (covert to translucent). Fix: 286172268 Test: WallpaperControllerTests#testShowWhenLockedWallpaperTarge Change-Id: Ic029c9c42472f7061020e4ca47e325be1ee53787 --- .../server/wm/WallpaperController.java | 44 +++++++++---------- .../com/android/server/wm/WindowState.java | 9 ++-- .../server/wm/WallpaperControllerTests.java | 26 +++++++++++ 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 7f41ff1a3d9b7..eb7b8c2555fbd 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -21,7 +21,6 @@ import static android.app.WallpaperManager.COMMAND_UNFREEZE; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; @@ -137,7 +136,8 @@ class WallpaperController { }; private final ToBooleanFunction mFindWallpaperTargetFunction = w -> { - if (!w.mTransitionController.isShellTransitionsEnabled()) { + final boolean useShellTransition = w.mTransitionController.isShellTransitionsEnabled(); + if (!useShellTransition) { if (w.mActivityRecord != null && !w.mActivityRecord.isVisible() && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) { // If this window's app token is hidden and not animating, it is of no interest. @@ -159,30 +159,23 @@ class WallpaperController { final WindowContainer animatingContainer = w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null; - final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null + if (!useShellTransition && animatingContainer != null && animatingContainer.isAnimating(TRANSITION | PARENTS) && AppTransition.isKeyguardGoingAwayTransitOld(animatingContainer.mTransit) && (animatingContainer.mTransitFlags - & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); - - boolean needsShowWhenLockedWallpaper = false; - if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && mService.mPolicy.isKeyguardLocked()) { - final TransitionController tc = w.mTransitionController; - final boolean isInTransition = tc.isShellTransitionsEnabled() - && tc.inTransition(w); - if (mService.mPolicy.isKeyguardOccluded() || mService.mPolicy.isKeyguardUnoccluding() - || isInTransition) { - // The lowest show when locked window decides whether we need to put the wallpaper - // behind. - needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs) - || (w.mActivityRecord != null && !w.mActivityRecord.fillsParent()); - } + & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0) { + // Keep the wallpaper visible when Keyguard is going away. + mFindResults.setUseTopWallpaperAsTarget(true); } - if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) { - // Keep the wallpaper during Keyguard exit but also when it's needed for a - // non-fullscreen show when locked activity. - mFindResults.setUseTopWallpaperAsTarget(true); + if (mService.mPolicy.isKeyguardLocked() && w.canShowWhenLocked()) { + if (mService.mPolicy.isKeyguardOccluded() || (useShellTransition + ? w.inTransition() : mService.mPolicy.isKeyguardUnoccluding())) { + // The lowest show when locked window decides whether we need to put the wallpaper + // behind. + mFindResults.mNeedsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs) + || (w.mActivityRecord != null && !w.mActivityRecord.fillsParent()); + } } final boolean animationWallpaper = animatingContainer != null @@ -691,7 +684,8 @@ class WallpaperController { private void findWallpaperTarget() { mFindResults.reset(); - if (mDisplayContent.getDefaultTaskDisplayArea() + if (mService.mAtmService.mSupportsFreeformWindowManagement + && mDisplayContent.getDefaultTaskDisplayArea() .isRootTaskVisible(WINDOWING_MODE_FREEFORM)) { // In freeform mode we set the wallpaper as its own target, so we don't need an // additional window to make it visible. @@ -700,6 +694,10 @@ class WallpaperController { mDisplayContent.forAllWindows(mFindWallpapers, true /* traverseTopToBottom */); mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */); + if (mFindResults.mNeedsShowWhenLockedWallpaper) { + // Keep wallpaper visible if the show-when-locked activities doesn't fill screen. + mFindResults.setUseTopWallpaperAsTarget(true); + } if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) { mFindResults.setWallpaperTarget( @@ -1084,6 +1082,7 @@ class WallpaperController { } TopWallpaper mTopWallpaper = new TopWallpaper(); + boolean mNeedsShowWhenLockedWallpaper; boolean useTopWallpaperAsTarget = false; WindowState wallpaperTarget = null; boolean isWallpaperTargetForLetterbox = false; @@ -1132,6 +1131,7 @@ class WallpaperController { void reset() { mTopWallpaper.reset(); + mNeedsShowWhenLockedWallpaper = false; wallpaperTarget = null; useTopWallpaperAsTarget = false; isWallpaperTargetForLetterbox = false; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8f49384f6101c..6773a2d9b10a4 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2983,10 +2983,11 @@ class WindowState extends WindowContainer implements WindowManagerP @Override public boolean canShowWhenLocked() { - final boolean showBecauseOfActivity = - mActivityRecord != null && mActivityRecord.canShowWhenLocked(); - final boolean showBecauseOfWindow = (getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0; - return showBecauseOfActivity || showBecauseOfWindow; + if (mActivityRecord != null) { + // It will also check if its windows contain FLAG_SHOW_WHEN_LOCKED. + return mActivityRecord.canShowWhenLocked(); + } + return (mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0; } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index f3d8114b9e942..4afcd0539b94a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -288,6 +288,32 @@ public class WallpaperControllerTests extends WindowTestsBase { assertEquals(homeWin, dc.mWallpaperController.getWallpaperTarget()); } + @Test + public void testShowWhenLockedWallpaperTarget() { + final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent); + wallpaperWindow.mToken.asWallpaperToken().setShowWhenLocked(true); + final WindowState behind = createWindow(null, TYPE_BASE_APPLICATION, "behind"); + final WindowState topTranslucent = createWindow(null, TYPE_BASE_APPLICATION, + "topTranslucent"); + behind.mAttrs.width = behind.mAttrs.height = topTranslucent.mAttrs.width = + topTranslucent.mAttrs.height = WindowManager.LayoutParams.MATCH_PARENT; + topTranslucent.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; + doReturn(true).when(behind.mActivityRecord).fillsParent(); + doReturn(false).when(topTranslucent.mActivityRecord).fillsParent(); + + spyOn(mWm.mPolicy); + doReturn(true).when(mWm.mPolicy).isKeyguardLocked(); + doReturn(true).when(mWm.mPolicy).isKeyguardOccluded(); + mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + // Wallpaper is visible because the show-when-locked activity is translucent. + assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(wallpaperWindow)); + + behind.mActivityRecord.setShowWhenLocked(true); + mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + // Wallpaper is invisible because the lowest show-when-locked activity is opaque. + assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(null)); + } + /** * Tests that the windowing mode of the wallpaper window must always be fullscreen. */