From 5b5f97ac72ca326393c7a4795823c5cccf7e2bc1 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Mon, 24 Apr 2023 19:43:25 +0800 Subject: [PATCH] Set transform hint before rotation transaction is applied Unlike legacy transition applies setDisplayProjection immediately when applying rotation change, shell transition applies the display projection in shell side when the animation is ready. So if uses the surface when receiving configuration change but the transaction is not applied yet, it may get an intermediate inconsistent transform hint. With this change: Rotation changed -> send config -> apply pending transaction with fixed transform hint -> transition ready -> shell starts animation and applies display projection and unsets fixed transform hint. Bug: 270282500 Test: atest SurfaceControlTests#testSurfaceChangedOnRotation Change-Id: Id4ad45adb9719b078f46040a09f786092c325a8a --- .../com/android/server/wm/DisplayContent.java | 8 +++ .../com/android/server/wm/Transition.java | 7 +++ .../server/wm/TransitionController.java | 1 + services/tests/wmtests/AndroidManifest.xml | 1 + .../server/wm/SurfaceControlTests.java | 59 +++++++++++++++++++ 5 files changed, 76 insertions(+) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c2bc4591ce0de..4fb7e8b72f644 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2181,6 +2181,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } mWmService.mDisplayManagerInternal.performTraversal(transaction); + if (shellTransitions) { + // Before setDisplayProjection is applied by the start transaction of transition, + // set the transform hint to avoid using surface in old rotation. + getPendingTransaction().setFixedTransformHint(mSurfaceControl, rotation); + // The sync transaction should already contains setDisplayProjection, so unset the + // hint to restore the natural state when the transaction is applied. + transaction.unsetFixedTransformHint(mSurfaceControl); + } scheduleAnimation(); mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 76b0e7b82ba6e..e13429eb2ac14 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -806,6 +806,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (c.getSnapshot() != null) { t.reparent(c.getSnapshot(), null); } + // The fixed transform hint was set in DisplayContent#applyRotation(). Make sure to + // clear the hint in case the start transaction is not applied. + if (c.hasFlags(FLAG_IS_DISPLAY) && c.getStartRotation() != c.getEndRotation() + && c.getContainer() != null) { + t.unsetFixedTransformHint(WindowContainer.fromBinder(c.getContainer().asBinder()) + .asDisplayContent().mSurfaceControl); + } } for (int i = info.getRootCount() - 1; i >= 0; --i) { final SurfaceControl leash = info.getRoot(i).getLeash(); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index cbb4fe2eaa211..2bc785f4680fd 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -725,6 +725,7 @@ class TransitionController { // Run state-validation checks when no transitions are active anymore. if (!inTransition()) { validateStates(); + mAtm.mWindowManager.onAnimationFinished(); } } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index f12b53acd06bd..3f924414b248d 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -79,6 +79,7 @@ android:turnScreenOn="true" android:showWhenLocked="true" /> activity.setContentView(sv)); + sv.getHolder().addCallback(new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(@NonNull SurfaceHolder holder) { + } + @Override + public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, + int height) { + surfaceChangedCount.getAndIncrement(); + Log.i("surfaceChanged", "width=" + width + " height=" + height + + " getTransformHint=" + + sv.getViewRootImpl().getSurfaceControl().getTransformHint()); + } + @Override + public void surfaceDestroyed(@NonNull SurfaceHolder holder) { + } + }); + final int rotation = activity.getResources().getConfiguration() + .windowConfiguration.getRotation(); + activity.setRequestedOrientation(activity.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT + ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + instrumentation.getUiAutomation().syncInputTransactions(); + instrumentation.waitForIdleSync(); + final int newRotation = activity.getResources().getConfiguration() + .windowConfiguration.getRotation(); + final int count = surfaceChangedCount.get(); + activity.finishAndRemoveTask(); + // The first count is triggered from creation, so the target number is 2. + if (rotation != newRotation && count > 2) { + fail("More than once surfaceChanged for rotation change: " + count); + } + } + private SurfaceControl buildTestSurface() { return new SurfaceControl.Builder() .setContainerLayer()