WM: Consolidate seamless rotation logic
Replaces existing seamless rotation logic with the new forced seamless
rotator.
Bug: b/110763772, b/111504081
Test: go/wm-smoke
Test: atest FrameworksServicesTests:com.android.server.wm.WindowStateTests
Test: Build winscope with new proto changes, open new and old traces and verify data can be read correctly
Change-Id: I818d4ce66d8ff3ed268b5d43c6012398672bbfee
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d15271c..e46ad2c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7862,15 +7862,6 @@
if (!displayRotation.getDisplayPolicy().navigationBarCanMove()) {
return false;
}
- int delta = newRotation - oldRotation;
- if (delta < 0) delta += 4;
- // Likewise we don't rotate seamlessly for 180 degree rotations
- // in this case the surfaces never resize, and our logic to
- // revert the transformations on size change will fail. We could
- // fix this in the future with the "tagged" frames idea.
- if (delta == Surface.ROTATION_180) {
- return false;
- }
final WindowState w = mTopFullscreenOpaqueWindowState;
if (w != mFocusedWindow) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 49638a9..a2dd679 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1199,14 +1199,10 @@
}
forAllWindows(w -> {
- w.forceSeamlesslyRotateIfAllowed(oldRotation, rotation);
+ w.seamlesslyRotateIfAllowed(getPendingTransaction(), oldRotation, rotation,
+ rotateSeamlessly);
}, true /* traverseTopToBottom */);
- // TODO(b/111504081): Consolidate seamless rotation logic.
- if (rotateSeamlessly) {
- seamlesslyRotate(getPendingTransaction(), oldRotation, rotation);
- }
-
mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
scheduleAnimation();
diff --git a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
similarity index 84%
rename from services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
rename to services/core/java/com/android/server/wm/SeamlessRotator.java
index f5e6e72..6f597277 100644
--- a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -16,12 +16,14 @@
package com.android.server.wm;
+import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
import android.view.DisplayInfo;
import android.view.Surface.Rotation;
+import android.view.SurfaceControl.Transaction;
import com.android.server.wm.utils.CoordinateTransforms;
@@ -29,22 +31,21 @@
import java.io.StringWriter;
/**
- * Helper class for forced seamless rotation.
+ * Helper class for seamless rotation.
*
* Works by transforming the window token back into the old display rotation.
*
* Uses deferTransactionUntil instead of latching on the buffer size to allow for seamless 180
* degree rotations.
- * TODO(b/111504081): Consolidate seamless rotation logic.
*/
-public class ForcedSeamlessRotator {
+public class SeamlessRotator {
private final Matrix mTransform = new Matrix();
private final float[] mFloat9 = new float[9];
private final int mOldRotation;
private final int mNewRotation;
- public ForcedSeamlessRotator(int oldRotation, int newRotation, DisplayInfo info) {
+ public SeamlessRotator(int oldRotation, int newRotation, DisplayInfo info) {
mOldRotation = oldRotation;
mNewRotation = newRotation;
@@ -62,8 +63,8 @@
* Applies a transform to the window token's surface that undoes the effect of the global
* display rotation.
*/
- public void unrotate(WindowToken token) {
- token.getPendingTransaction().setMatrix(token.getSurfaceControl(), mTransform, mFloat9);
+ public void unrotate(Transaction transaction, WindowToken token) {
+ transaction.setMatrix(token.getSurfaceControl(), mTransform, mFloat9);
}
/**
@@ -94,6 +95,10 @@
win.getPendingTransaction().deferTransactionUntil(win.mSurfaceControl,
win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
win.getFrameNumber());
+ win.getPendingTransaction().deferTransactionUntil(
+ win.mWinAnimator.mSurfaceController.mSurfaceControl,
+ win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
+ win.getFrameNumber());
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fee0fcb..8e704a8 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -122,7 +122,7 @@
private int mTreeWeight = 1;
/**
- * Indicates whether we are animating and have committed the transaction to reparent our
+ * Indicates whether we are animating and have committed the transaction to reparent our
* surface to the animation leash
*/
private boolean mCommittedReparentToAnimationLeash;
@@ -749,20 +749,6 @@
}
/**
- * Seamlessly rotates the container, by recomputing the location in the new
- * rotation, and rotating buffers until they are updated for the new rotation.
- *
- * @param t the transaction to perform the seamless rotation in
- * @param oldRotation the rotation we are rotating from
- * @param newRotation the rotation we are rotating to
- */
- void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- mChildren.get(i).seamlesslyRotate(t, oldRotation, newRotation);
- }
- }
-
- /**
* Returns true if this container is opaque and fills all the space made available by its parent
* container.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8a57143..fa17001 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1896,11 +1896,8 @@
win.setFrameNumber(frameNumber);
- // TODO(b/111504081): Consolidate seamless rotation logic.
- if (win.mPendingForcedSeamlessRotate != null && !mWaitingForConfig) {
- win.mPendingForcedSeamlessRotate.finish(win.mToken, win);
- win.mFinishForcedSeamlessRotateFrameNumber = win.getFrameNumber();
- win.mPendingForcedSeamlessRotate = null;
+ if (!mWaitingForConfig) {
+ win.finishSeamlessRotation();
}
int attrChanges = 0;
@@ -7108,7 +7105,7 @@
}
void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
- if (seamlesslyRotated == w.mSeamlesslyRotated) {
+ if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
return;
}
w.mSeamlesslyRotated = seamlesslyRotated;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 58fb7a0..97313f2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -71,12 +71,20 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+import static com.android.server.wm.AnimationSpecProto.MOVE;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.MoveAnimationSpecProto.FROM;
+import static com.android.server.wm.MoveAnimationSpecProto.TO;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -108,13 +116,6 @@
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
-import static com.android.server.wm.IdentifierProto.HASH_CODE;
-import static com.android.server.wm.IdentifierProto.TITLE;
-import static com.android.server.wm.IdentifierProto.USER_ID;
-import static com.android.server.wm.AnimationSpecProto.MOVE;
-import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS;
-import static com.android.server.wm.MoveAnimationSpecProto.FROM;
-import static com.android.server.wm.MoveAnimationSpecProto.TO;
import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT;
import static com.android.server.wm.WindowStateProto.ANIMATOR;
import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
@@ -122,7 +123,8 @@
import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.DESTROYING;
import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
-import static com.android.server.wm.WindowStateProto.FINISHED_FORCED_SEAMLESS_ROTATION_FRAME;
+import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
+import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
import static com.android.server.wm.WindowStateProto.IDENTIFIER;
@@ -131,7 +133,7 @@
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
import static com.android.server.wm.WindowStateProto.OUTSETS;
import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
-import static com.android.server.wm.WindowStateProto.PENDING_FORCED_SEAMLESS_ROTATION;
+import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -145,8 +147,6 @@
import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
-import static com.android.server.wm.utils.CoordinateTransforms.transformRect;
-import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
import android.annotation.CallSuper;
import android.app.AppOpsManager;
@@ -279,11 +279,10 @@
* Special mode that is intended only for the rounded corner overlay: during rotation
* transition, we un-rotate the window token such that the window appears as it did before the
* rotation.
- * TODO(b/111504081): Consolidate seamless rotation logic.
*/
final boolean mForceSeamlesslyRotate;
- ForcedSeamlessRotator mPendingForcedSeamlessRotate;
- long mFinishForcedSeamlessRotateFrameNumber;
+ SeamlessRotator mPendingSeamlessRotate;
+ long mFinishSeamlessRotateFrameNumber;
private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -632,15 +631,30 @@
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
- void forceSeamlesslyRotateIfAllowed(int oldRotation, int rotation) {
- if (mForceSeamlesslyRotate) {
- if (mPendingForcedSeamlessRotate != null) {
- oldRotation = mPendingForcedSeamlessRotate.getOldRotation();
- }
+ void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
+ boolean requested) {
+ // Invisible windows and the wallpaper do not participate in the seamless rotation animation
+ if (!isVisibleNow() || mIsWallpaper) {
+ return;
+ }
- mPendingForcedSeamlessRotate = new ForcedSeamlessRotator(
- oldRotation, rotation, getDisplayInfo());
- mPendingForcedSeamlessRotate.unrotate(this.mToken);
+ if (mPendingSeamlessRotate != null) {
+ oldRotation = mPendingSeamlessRotate.getOldRotation();
+ }
+
+ if (mForceSeamlesslyRotate || requested) {
+ mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
+ mPendingSeamlessRotate.unrotate(transaction, this.mToken);
+ mService.markForSeamlessRotation(this, true);
+ }
+ }
+
+ void finishSeamlessRotation() {
+ if (mPendingSeamlessRotate != null) {
+ mPendingSeamlessRotate.finish(this.mToken, this);
+ mFinishSeamlessRotateFrameNumber = getFrameNumber();
+ mPendingSeamlessRotate = null;
+ mService.markForSeamlessRotation(this, false);
}
}
@@ -3301,11 +3315,9 @@
proto.write(REMOVED, mRemoved);
proto.write(IS_ON_SCREEN, isOnScreen());
proto.write(IS_VISIBLE, isVisible());
- if (mForceSeamlesslyRotate) {
- proto.write(PENDING_FORCED_SEAMLESS_ROTATION, mPendingForcedSeamlessRotate != null);
- proto.write(FINISHED_FORCED_SEAMLESS_ROTATION_FRAME,
- mFinishForcedSeamlessRotateFrameNumber);
- }
+ proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
+ proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber);
+ proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
proto.end(token);
}
@@ -3461,16 +3473,16 @@
pw.print(prefix); pw.print("mLastFreezeDuration=");
TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println();
}
- if (mForceSeamlesslyRotate) {
- pw.print(prefix); pw.print("forceSeamlesslyRotate: pending=");
- if (mPendingForcedSeamlessRotate != null) {
- mPendingForcedSeamlessRotate.dump(pw);
- } else {
- pw.print("null");
- }
- pw.print(" finishedFrameNumber="); pw.print(mFinishForcedSeamlessRotateFrameNumber);
- pw.println();
+ pw.print(prefix); pw.print("mForceSeamlesslyRotate="); pw.print(mForceSeamlesslyRotate);
+ pw.print(" seamlesslyRotate: pending=");
+ if (mPendingSeamlessRotate != null) {
+ mPendingSeamlessRotate.dump(pw);
+ } else {
+ pw.print("null");
}
+ pw.print(" finishedFrameNumber="); pw.print(mFinishSeamlessRotateFrameNumber);
+ pw.println();
+
if (mHScale != 1 || mVScale != 1) {
pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
pw.print(" mVScale="); pw.println(mVScale);
@@ -4716,8 +4728,8 @@
// Freeze position while we're unrotated, so the surface remains at the position it was
// prior to the rotation.
- if (!mSurfaceAnimator.hasLeash() && mPendingForcedSeamlessRotate == null &&
- !mLastSurfacePosition.equals(mSurfacePosition)) {
+ if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
+ && !mLastSurfacePosition.equals(mSurfacePosition)) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
@@ -4870,31 +4882,6 @@
mFrameNumber = frameNumber;
}
- @Override
- void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
- // Invisible windows, the wallpaper, and force seamlessly rotated windows do not participate
- // in the regular seamless rotation animation.
- if (!isVisibleNow() || mIsWallpaper || mForceSeamlesslyRotate) {
- return;
- }
- final Matrix transform = mTmpMatrix;
-
- mService.markForSeamlessRotation(this, true);
-
- // We rotated the screen, but have not performed a new layout pass yet. In the mean time,
- // we recompute the coordinates of mFrame in the new orientation, so the surface can be
- // properly placed.
- transformToRotation(oldRotation, newRotation, getDisplayInfo(), transform);
- transformRect(transform, mWindowFrames.mFrame, null /* tmpRectF */);
-
- updateSurfacePosition(t);
- mWinAnimator.seamlesslyRotate(t, oldRotation, newRotation);
-
- // Dispatch to children only after mFrame has been updated, as it's needed in the
- // child's updateSurfacePosition.
- super.seamlesslyRotate(t, oldRotation, newRotation);
- }
-
public void getMaxVisibleBounds(Rect out) {
if (out.isEmpty()) {
out.set(mWindowFrames.mVisibleFrame);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 7966d5b..b158ae2 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -686,7 +686,6 @@
final int displayId = mWin.getDisplayId();
final ScreenRotationAnimation screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(displayId);
- // TODO(b/111504081): Consolidate seamless rotation logic.
final boolean windowParticipatesInScreenRotationAnimation =
!mWin.mForceSeamlesslyRotate;
final boolean screenAnimation = screenRotationAnimation != null
@@ -878,7 +877,6 @@
mExtraVScale = (float) 1.0;
boolean wasForceScaled = mForceScaleUntilResize;
- boolean wasSeamlesslyRotated = w.mSeamlesslyRotated;
// Once relayout has been called at least once, we need to make sure
// we only resize the client surface during calls to relayout. For
@@ -898,7 +896,6 @@
// If we are undergoing seamless rotation, the surface has already
// been set up to persist at it's old location. We need to freeze
// updates until a resize occurs.
- mService.markForSeamlessRotation(w, w.mSeamlesslyRotated && !mSurfaceResized);
Rect clipRect = null;
if (calculateCrop(mTmpClipRect)) {
@@ -1069,15 +1066,15 @@
// If we are ending the scaling mode. We switch to SCALING_MODE_FREEZE
// to prevent further updates until buffer latch.
- // When ending both force scaling, and seamless rotation, we need to freeze
- // the Surface geometry until a buffer comes in at the new size (normally position and crop
- // are unfrozen). setGeometryAppliesWithResizeInTransaction accomplishes this for us.
- if ((wasForceScaled && !mForceScaleUntilResize) ||
- (wasSeamlesslyRotated && !w.mSeamlesslyRotated)) {
+ // We also need to freeze the Surface geometry until a buffer
+ // comes in at the new size (normally position and crop are unfrozen).
+ // setGeometryAppliesWithResizeInTransaction accomplishes this for us.
+ if (wasForceScaled && !mForceScaleUntilResize) {
mSurfaceController.setGeometryAppliesWithResizeInTransaction(true);
mSurfaceController.forceScaleableInTransaction(false);
}
+
if (!w.mSeamlesslyRotated) {
applyCrop(clipRect, recoveringMemory);
mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
@@ -1509,29 +1506,6 @@
}
}
- // TODO(b/111504081): Consolidate seamless rotation logic.
- @Deprecated
- void seamlesslyRotate(SurfaceControl.Transaction t, int oldRotation, int newRotation) {
- final WindowState w = mWin;
-
- // We rotated the screen, but have not received a new buffer with the correct size yet. In
- // the mean time, we rotate the buffer we have to the new orientation.
- final Matrix transform = mService.mTmpTransform;
- transformToRotation(oldRotation, newRotation, w.getFrameLw().width(),
- w.getFrameLw().height(), transform);
- transform.getValues(mService.mTmpFloats);
-
- float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
- float DtDx = mService.mTmpFloats[Matrix.MSKEW_Y];
- float DtDy = mService.mTmpFloats[Matrix.MSKEW_X];
- float DsDy = mService.mTmpFloats[Matrix.MSCALE_Y];
- float nx = mService.mTmpFloats[Matrix.MTRANS_X];
- float ny = mService.mTmpFloats[Matrix.MTRANS_Y];
- mSurfaceController.setPosition(t, nx, ny, false);
- mSurfaceController.setMatrix(t, DsDx * w.mHScale, DtDx * w.mVScale, DtDy
- * w.mHScale, DsDy * w.mVScale, false);
- }
-
/** The force-scaled state for a given window can persist past
* the state for it's stack as the windows complete resizing
* independently of one another.