From 6f049fef5c4025433c13381358099d569596603e Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Sat, 25 Mar 2023 14:38:26 +0000 Subject: [PATCH] Refresh layout parameter for bounds change from relayout The relayout method also assumes the client can get the latest configuration. So if the client only relies on IWindow#resized to compute its layout parameter, there may be a race that resized gets config 1, and then even if relayout gets config 2, the window still uses old layout parameter. So this change simulates similar behavior as ViewRootImpl did: check the config changes both from resized and relayout. Also optimize a case if resized is called many times in a short time from server side, or the ui thread is not able to consume the events in time, then only the last event will be handled. So several relayout invocations for outdated intermediate states can be reduced. Bug: 264829202 Test: Hardcoded to return a wrong bound from IWindow#resized to wallpaper, wallpaper can still show correct size. Change-Id: If4299bc7bba022b12c33343ecc2a45050c159210 --- .../service/wallpaper/WallpaperService.java | 66 ++++++++++++++++--- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 77bbeb59927a3..74ab7095d3924 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -112,6 +112,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; /** @@ -431,6 +432,7 @@ public abstract class WallpaperService extends Service { Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, reportDraw ? 1 : 0, mergedConfiguration); + mIWallpaperEngine.mPendingResizeCount.incrementAndGet(); mCaller.sendMessage(msg); } @@ -510,6 +512,7 @@ public abstract class WallpaperService extends Service { public Engine(Supplier clockFunction, Handler handler) { mClockFunction = clockFunction; mHandler = handler; + mMergedConfiguration.setOverrideConfiguration(getResources().getConfiguration()); } /** @@ -1051,6 +1054,10 @@ public abstract class WallpaperService extends Service { out.print(prefix); out.print("mZoom="); out.println(mZoom); out.print(prefix); out.print("mPreviewSurfacePosition="); out.println(mPreviewSurfacePosition); + final int pendingCount = mIWallpaperEngine.mPendingResizeCount.get(); + if (pendingCount != 0) { + out.print(prefix); out.print("mPendingResizeCount="); out.println(pendingCount); + } synchronized (mLock) { out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); out.print(" mPendingXOffset="); out.println(mPendingXOffset); @@ -1113,10 +1120,6 @@ public abstract class WallpaperService extends Service { } } - private void updateConfiguration(MergedConfiguration mergedConfiguration) { - mMergedConfiguration.setTo(mergedConfiguration); - } - void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { if (mDestroyed) { Log.w(TAG, "Ignoring updateSurface due to destroyed"); @@ -1165,7 +1168,7 @@ public abstract class WallpaperService extends Service { | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; final Configuration config = mMergedConfiguration.getMergedConfiguration(); - final Rect maxBounds = config.windowConfiguration.getMaxBounds(); + final Rect maxBounds = new Rect(config.windowConfiguration.getMaxBounds()); if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT && myHeight == ViewGroup.LayoutParams.MATCH_PARENT) { mLayout.width = myWidth; @@ -1221,6 +1224,17 @@ public abstract class WallpaperService extends Service { final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight, View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration, mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle); + final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration() + .windowConfiguration.getMaxBounds(); + if (!outMaxBounds.equals(maxBounds)) { + Log.i(TAG, "Retry updateSurface because bounds changed from relayout: " + + maxBounds + " -> " + outMaxBounds); + mSurfaceHolder.mSurfaceLock.unlock(); + mDrawingAllowed = false; + mCaller.sendMessage(mCaller.obtainMessageI(MSG_WINDOW_RESIZED, + redrawNeeded ? 1 : 0)); + return; + } final int transformHint = SurfaceControl.rotationToBufferTransform( (mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4); @@ -2324,6 +2338,8 @@ public abstract class WallpaperService extends Service { final IBinder mWindowToken; final int mWindowType; final boolean mIsPreview; + final AtomicInteger mPendingResizeCount = new AtomicInteger(); + boolean mReportDraw; boolean mShownReported; int mReqWidth; int mReqHeight; @@ -2579,11 +2595,7 @@ public abstract class WallpaperService extends Service { mEngine.doCommand(cmd); } break; case MSG_WINDOW_RESIZED: { - final boolean reportDraw = message.arg1 != 0; - mEngine.updateConfiguration(((MergedConfiguration) message.obj)); - mEngine.updateSurface(true, false, reportDraw); - mEngine.doOffsetsChanged(true); - mEngine.scaleAndCropScreenshot(); + handleResized((MergedConfiguration) message.obj, message.arg1 != 0); } break; case MSG_WINDOW_MOVED: { // Do nothing. What does it mean for a Wallpaper to move? @@ -2631,6 +2643,40 @@ public abstract class WallpaperService extends Service { Log.w(TAG, "Unknown message type " + message.what); } } + + /** + * In general this performs relayout for IWindow#resized. If there are several pending + * (in the message queue) MSG_WINDOW_RESIZED from server side, only the last one will be + * handled (ignore intermediate states). Note that this procedure cannot be skipped if the + * configuration is not changed because this is also used to dispatch insets changes. + */ + private void handleResized(MergedConfiguration config, boolean reportDraw) { + // The config can be null when retrying for a changed config from relayout, otherwise + // it is from IWindow#resized which always sends non-null config. + final int pendingCount = config != null ? mPendingResizeCount.decrementAndGet() : -1; + if (reportDraw) { + mReportDraw = true; + } + if (pendingCount > 0) { + if (DEBUG) { + Log.d(TAG, "Skip outdated resize, bounds=" + + config.getMergedConfiguration().windowConfiguration.getMaxBounds() + + " pendingCount=" + pendingCount); + } + return; + } + if (config != null) { + if (DEBUG) { + Log.d(TAG, "Update config from resized, bounds=" + + config.getMergedConfiguration().windowConfiguration.getMaxBounds()); + } + mEngine.mMergedConfiguration.setTo(config); + } + mEngine.updateSurface(true /* forceRelayout */, false /* forceReport */, mReportDraw); + mReportDraw = false; + mEngine.doOffsetsChanged(true); + mEngine.scaleAndCropScreenshot(); + } } /**