From b46f9233eaaa32b2930298a4dead518c702855c5 Mon Sep 17 00:00:00 2001 From: Vadim Caen Date: Fri, 14 Feb 2020 17:54:39 +0100 Subject: [PATCH] Compute Wallpaper scaling on the server Image wallpaper uses DisplayInfo to compute the wallpaper scaling. When the wallpaper has a fixed rotation transform, its DisplayInfo do not correspond to its transformed bounds. We can't use the Configuration's bounds either because the wallpaper token is created on the server side and we do not have a way to propagate the changed configuration to a token created from the server. Bug: 148000485 Test: atest WallpaperControllerTests#testWallpaperSizeWithFixedTransform Change-Id: Ie80592ae868c980faddece17b94cd34178b1de0e (cherry picked from commit f79baeaa200d0a6a6d0774b1e9261d2ae97e2d99) --- .../service/wallpaper/WallpaperService.java | 19 +----- .../server/wm/WallpaperWindowToken.java | 19 ++++++ .../server/wm/WindowManagerService.java | 1 + .../com/android/server/wm/WindowToken.java | 9 +++ .../server/wm/WallpaperControllerTests.java | 67 +++++++++++++++++++ 5 files changed, 98 insertions(+), 17 deletions(-) diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index e9285cc7931d3..bf3c08870c630 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -49,7 +49,6 @@ import android.util.Log; import android.util.MergedConfiguration; import android.view.Display; import android.view.DisplayCutout; -import android.view.DisplayInfo; import android.view.Gravity; import android.view.IWindowSession; import android.view.InputChannel; @@ -833,22 +832,8 @@ public abstract class WallpaperService extends Service { mLayout.x = 0; mLayout.y = 0; - if (!fixedSize) { - mLayout.width = myWidth; - mLayout.height = myHeight; - } else { - // Force the wallpaper to cover the screen in both dimensions - // only internal implementations like ImageWallpaper - DisplayInfo displayInfo = new DisplayInfo(); - mDisplay.getDisplayInfo(displayInfo); - final float layoutScale = Math.max( - (float) displayInfo.logicalHeight / (float) myHeight, - (float) displayInfo.logicalWidth / (float) myWidth); - mLayout.height = (int) (myHeight * layoutScale); - mLayout.width = (int) (myWidth * layoutScale); - mWindowFlags |= WindowManager.LayoutParams.FLAG_SCALED; - } - + mLayout.width = myWidth; + mLayout.height = myHeight; mLayout.format = mFormat; mCurWindowFlags = mWindowFlags; diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 1e22141f232a4..e0419e4b344a4 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -28,6 +28,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.view.DisplayInfo; +import android.view.ViewGroup; +import android.view.WindowManager; import android.view.animation.Animation; import java.util.function.Consumer; @@ -168,6 +170,23 @@ class WallpaperWindowToken extends WindowToken { } } + @Override + void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) { + if (attrs.height == ViewGroup.LayoutParams.MATCH_PARENT + || attrs.width == ViewGroup.LayoutParams.MATCH_PARENT) { + return; + } + + final DisplayInfo displayInfo = win.getDisplayInfo(); + + final float layoutScale = Math.max( + (float) displayInfo.logicalHeight / (float) attrs.height, + (float) displayInfo.logicalWidth / (float) attrs.width); + attrs.height = (int) (attrs.height * layoutScale); + attrs.width = (int) (attrs.width * layoutScale); + attrs.flags |= WindowManager.LayoutParams.FLAG_SCALED; + } + boolean hasVisibleNotDrawnWallpaper() { for (int j = mChildren.size() - 1; j >= 0; --j) { final WindowState wallpaper = mChildren.get(j); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 673e7e0d241ed..3f4f629b52924 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2122,6 +2122,7 @@ public class WindowManagerService extends IWindowManager.Stub int privateFlagChanges = 0; if (attrs != null) { displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid); + win.mToken.adjustWindowParams(win, attrs); // if they don't have the permission, mask out the status bar bits if (seq == win.mSeq) { int systemUiVisibility = attrs.systemUiVisibility diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 76a03150c8df5..7eea5cb8b3ad9 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -48,6 +48,7 @@ import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.InsetsState; import android.view.SurfaceControl; +import android.view.WindowManager; import com.android.server.policy.WindowManagerPolicy; import com.android.server.protolog.common.ProtoLog; @@ -500,6 +501,14 @@ class WindowToken extends WindowContainer { rotator.unrotateInsets(outSurfaceInsets); } + /** + * Gives a chance to this {@link WindowToken} to adjust the {@link + * android.view.WindowManager.LayoutParams} of its windows. + */ + void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) { + } + + @CallSuper @Override public void dumpDebug(ProtoOutputStream proto, long fieldId, 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 14d8a9ddab67a..aa665241c50b4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -16,19 +16,30 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.graphics.Rect; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.Surface; +import android.view.WindowManager; import androidx.test.filters.SmallTest; +import com.android.server.wm.utils.WmDisplayCutout; + import org.junit.Test; import org.junit.runner.RunWith; @@ -72,4 +83,60 @@ public class WallpaperControllerTests extends WindowTestsBase { wallpaperWindow.mWinAnimator.mLastAlpha = 1; assertTrue(dc.mWallpaperController.canScreenshotWallpaper()); } + + @Test + public void testWallpaperSizeWithFixedTransform() { + // No wallpaper + final DisplayContent dc = createNewDisplay(); + dc.mWmService.mIsFixedRotationTransformEnabled = true; + + // No wallpaper WSA Surface + WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), + true, dc, true /* ownerCanManageAppTokens */); + WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER, + wallpaperWindowToken, "wallpaperWindow"); + + WindowManager.LayoutParams attrs = wallpaperWindow.getAttrs(); + Rect bounds = dc.getBounds(); + int displayHeight = dc.getBounds().height(); + + // Use a wallpaper with a different ratio than the display + int wallpaperWidth = bounds.width() * 2; + int wallpaperHeight = (int) (bounds.height() * 1.10); + + // Simulate what would be done on the client's side + attrs.width = wallpaperWidth; + attrs.height = wallpaperHeight; + attrs.flags |= FLAG_LAYOUT_NO_LIMITS; + attrs.gravity = Gravity.TOP | Gravity.LEFT; + wallpaperWindow.getWindowFrames().mParentFrame.set(dc.getBounds()); + + // Calling layoutWindowLw a first time, so adjustWindowParams gets the correct data + dc.getDisplayPolicy().layoutWindowLw(wallpaperWindow, null, dc.mDisplayFrames); + + wallpaperWindowToken.adjustWindowParams(wallpaperWindow, attrs); + dc.getDisplayPolicy().layoutWindowLw(wallpaperWindow, null, dc.mDisplayFrames); + + assertEquals(Configuration.ORIENTATION_PORTRAIT, dc.getConfiguration().orientation); + int expectedWidth = (int) (wallpaperWidth * (displayHeight / (double) wallpaperHeight)); + + // Check that the wallpaper is correctly scaled + assertEquals(new Rect(0, 0, expectedWidth, displayHeight), wallpaperWindow.getFrameLw()); + Rect portraitFrame = wallpaperWindow.getFrameLw(); + + // Rotate the display + dc.getDisplayRotation().updateOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, true); + dc.sendNewConfiguration(); + + // Apply the fixed transform + Configuration config = new Configuration(); + final DisplayInfo info = dc.computeScreenConfiguration(config, Surface.ROTATION_0); + final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0); + final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), info, cutout); + wallpaperWindowToken.applyFixedRotationTransform(info, displayFrames, config); + + // Check that the wallpaper has the same frame in landscape than in portrait + assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation); + assertEquals(portraitFrame, wallpaperWindow.getFrameLw()); + } }