diff options
| author | 2021-03-18 23:24:48 +0000 | |
|---|---|---|
| committer | 2021-03-18 23:24:48 +0000 | |
| commit | 557bab6f877b2dd3f6de48527be2fd8f342efa5d (patch) | |
| tree | be9573a3c477f7c466b404e75e0191fe6a8dcb66 | |
| parent | de4d7421d796fd6df303cfe8d9471ba067fa84f7 (diff) | |
| parent | 1551cb73075900e5d71a174ca4e51652b0d1713e (diff) | |
Merge "Initial support for display rotation in shell transitions" into sc-dev
15 files changed, 393 insertions, 25 deletions
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 499ce25f8bb9..a0b4e24ca7c2 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -16,6 +16,7 @@ package android.window; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_NONE; @@ -31,6 +32,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; +import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManager; @@ -284,6 +286,8 @@ public final class TransitionInfo implements Parcelable { private final Rect mEndAbsBounds = new Rect(); private final Point mEndRelOffset = new Point(); private ActivityManager.RunningTaskInfo mTaskInfo = null; + private int mStartRotation = ROTATION_UNDEFINED; + private int mEndRotation = ROTATION_UNDEFINED; public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { mContainer = container; @@ -301,6 +305,8 @@ public final class TransitionInfo implements Parcelable { mEndAbsBounds.readFromParcel(in); mEndRelOffset.readFromParcel(in); mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR); + mStartRotation = in.readInt(); + mEndRotation = in.readInt(); } /** Sets the parent of this change's container. The parent must be a participant or null. */ @@ -341,6 +347,12 @@ public final class TransitionInfo implements Parcelable { mTaskInfo = taskInfo; } + /** Sets the start and end rotation of this container. */ + public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) { + mStartRotation = start; + mEndRotation = end; + } + /** @return the container that is changing. May be null if non-remotable (eg. activity) */ @Nullable public WindowContainerToken getContainer() { @@ -404,6 +416,14 @@ public final class TransitionInfo implements Parcelable { return mTaskInfo; } + public int getStartRotation() { + return mStartRotation; + } + + public int getEndRotation() { + return mEndRotation; + } + @Override /** @hide */ public void writeToParcel(@NonNull Parcel dest, int flags) { @@ -416,6 +436,8 @@ public final class TransitionInfo implements Parcelable { mEndAbsBounds.writeToParcel(dest, flags); mEndRelOffset.writeToParcel(dest, flags); dest.writeTypedObject(mTaskInfo, flags); + dest.writeInt(mStartRotation); + dest.writeInt(mEndRotation); } @NonNull @@ -442,7 +464,8 @@ public final class TransitionInfo implements Parcelable { public String toString() { return "{" + mContainer + "(" + mParent + ") leash=" + mLeash + " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb=" - + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}"; + + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r=" + + mStartRotation + "->" + mEndRotation + "}"; } } } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 5b57f1901550..c70d6b0e946b 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1231,6 +1231,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, + "-672355406": { + "message": " Rejecting as no-op: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/Transition.java" + }, "-672228342": { "message": "resumeTopActivityLocked: Top activity resumed %s", "level": "DEBUG", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index 2b0a0cd3de20..963a3dc70262 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -28,7 +28,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { // with those in the framework ProtoLogGroup WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), - WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM_SHELL), WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 629ff0db6b4a..b29b18bec032 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -96,6 +96,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { }; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getMode() == TRANSIT_CHANGE) { + // No default animation for this, so just update bounds/position. + t.setPosition(change.getLeash(), + change.getEndAbsBounds().left - change.getEndRelOffset().x, + change.getEndAbsBounds().top - change.getEndRelOffset().y); + if (change.getTaskInfo() != null) { + // Skip non-tasks since those usually have null bounds. + t.setWindowCrop(change.getLeash(), + change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); + } + } // Don't animate anything that isn't independent. if (!TransitionInfo.isIndependent(change, info)) continue; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java index 400bf15f8c65..2f38f0ab4509 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.TransitionOldType; +import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import android.os.RemoteException; import android.util.Log; @@ -35,6 +36,8 @@ import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionInfo; +import java.util.ArrayList; + /** * @see RemoteAnimationAdapter */ @@ -100,6 +103,52 @@ public class RemoteAnimationAdapterCompat { }; } + private static class CounterRotator { + SurfaceControl mSurface = null; + ArrayList<SurfaceControl> mRotateChildren = null; + + void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta, + float displayW, float displayH) { + if (rotateDelta == 0) return; + mRotateChildren = new ArrayList<>(); + // We want to counter-rotate, so subtract from 4 + rotateDelta = 4 - (rotateDelta + 4) % 4; + mSurface = new SurfaceControl.Builder() + .setName("Transition Unrotate") + .setContainerLayer() + .setParent(parent) + .build(); + // column-major + if (rotateDelta == 1) { + t.setMatrix(mSurface, 0, 1, -1, 0); + t.setPosition(mSurface, displayW, 0); + } else if (rotateDelta == 2) { + t.setMatrix(mSurface, -1, 0, 0, -1); + t.setPosition(mSurface, displayW, displayH); + } else if (rotateDelta == 3) { + t.setMatrix(mSurface, 0, -1, 1, 0); + t.setPosition(mSurface, 0, displayH); + } + t.show(mSurface); + } + + void addChild(SurfaceControl.Transaction t, SurfaceControl child) { + if (mSurface == null) return; + t.reparent(child, mSurface); + mRotateChildren.add(child); + } + + void cleanUp(SurfaceControl rootLeash) { + if (mSurface == null) return; + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (int i = mRotateChildren.size() - 1; i >= 0; --i) { + t.reparent(mRotateChildren.get(i), rootLeash); + } + t.remove(mSurface); + t.apply(); + } + } + private static IRemoteTransition.Stub wrapRemoteTransition( final RemoteAnimationRunnerCompat remoteAnimationAdapter) { return new IRemoteTransition.Stub() { @@ -116,17 +165,46 @@ public class RemoteAnimationAdapterCompat { // TODO(b/177438007): Move this set-up logic into launcher's animation impl. boolean isReturnToHome = false; + TransitionInfo.Change launcherTask = null; + TransitionInfo.Change wallpaper = null; + int launcherLayer = 0; + int rotateDelta = 0; + float displayW = 0; + float displayH = 0; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getTaskInfo() != null && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { isReturnToHome = change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT; - break; + launcherTask = change; + launcherLayer = info.getChanges().size() - i; + } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { + wallpaper = change; + } + if (change.getParent() == null && change.getEndRotation() >= 0 + && change.getEndRotation() != change.getStartRotation()) { + rotateDelta = change.getEndRotation() - change.getStartRotation(); + displayW = change.getEndAbsBounds().width(); + displayH = change.getEndAbsBounds().height(); + } + } + + // Prepare for rotation if there is one + final CounterRotator counterLauncher = new CounterRotator(); + final CounterRotator counterWallpaper = new CounterRotator(); + if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) { + counterLauncher.setup(t, info.getChange(launcherTask.getParent()).getLeash(), + rotateDelta, displayW, displayH); + if (counterLauncher.mSurface != null) { + t.setLayer(counterLauncher.mSurface, launcherLayer); } } if (isReturnToHome) { + if (counterLauncher.mSurface != null) { + t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3); + } // Need to "boost" the closing things since that's what launcher expects. for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -136,6 +214,7 @@ public class RemoteAnimationAdapterCompat { if (!TransitionInfo.isIndependent(change, info)) continue; if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { t.setLayer(leash, info.getChanges().size() * 3 - i); + counterLauncher.addChild(t, leash); } } // Make wallpaper visible immediately since launcher apparently won't do this. @@ -143,6 +222,18 @@ public class RemoteAnimationAdapterCompat { t.show(wallpapersCompat[i].leash.getSurfaceControl()); t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f); } + } else { + if (launcherTask != null) { + counterLauncher.addChild(t, launcherTask.getLeash()); + } + if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) { + counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(), + rotateDelta, displayW, displayH); + if (counterWallpaper.mSurface != null) { + t.setLayer(counterWallpaper.mSurface, -1); + counterWallpaper.addChild(t, wallpaper.getLeash()); + } + } } t.apply(); @@ -150,6 +241,8 @@ public class RemoteAnimationAdapterCompat { @Override public void run() { try { + counterLauncher.cleanUp(info.getRootLeash()); + counterWallpaper.cleanUp(info.getRootLeash()); finishCallback.onTransitionFinished(null /* wct */); } catch (RemoteException e) { Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index 87f6b8202ded..246476abb06d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -56,6 +56,7 @@ public class RemoteAnimationTargetCompat { public final boolean isNotInRecents; public final Rect contentInsets; public final PictureInPictureParams pictureInPictureParams; + public final int rotationChange; private final SurfaceControl mStartLeash; @@ -74,6 +75,7 @@ public class RemoteAnimationTargetCompat { contentInsets = app.contentInsets; activityType = app.windowConfiguration.getActivityType(); pictureInPictureParams = app.pictureInPictureParams; + rotationChange = 0; mStartLeash = app.startLeash; } @@ -102,7 +104,7 @@ public class RemoteAnimationTargetCompat { localBounds = new Rect(change.getEndAbsBounds()); localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y); sourceContainerBounds = null; - screenSpaceBounds = change.getEndAbsBounds(); + screenSpaceBounds = new Rect(change.getEndAbsBounds()); prefixOrderIndex = order; // TODO(shell-transitions): I guess we need to send content insets? evaluate how its used. contentInsets = new Rect(0, 0, 0, 0); @@ -115,6 +117,7 @@ public class RemoteAnimationTargetCompat { } pictureInPictureParams = null; mStartLeash = null; + rotationChange = change.getEndRotation() - change.getStartRotation(); } public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 95b2b5d088a0..6d2410578ced 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1428,7 +1428,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp computeScreenConfiguration(config); } else if (currentConfig != null // If waiting for a remote rotation, don't prematurely update configuration. - && !mDisplayRotation.isWaitingForRemoteRotation()) { + && !(mDisplayRotation.isWaitingForRemoteRotation() + || mAtmService.getTransitionController().isCollecting(this))) { // No obvious action we need to take, but if our current state mismatches the // activity manager's, update it, disregarding font scale, which should remain set // to the value of the previous configuration. @@ -1470,6 +1471,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mDisplayRotation.updateOrientation(orientation, forceUpdate); } + @Override + boolean isSyncFinished() { + if (mDisplayRotation.isWaitingForRemoteRotation()) return false; + return super.isSyncFinished(); + } + /** * Returns a valid rotation if the activity can use different orientation than the display. * Otherwise {@link #ROTATION_UNDEFINED}. @@ -1763,7 +1770,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Update rotation of the display. * * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL - * {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN. + * {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN unless using Shell transitions. */ boolean updateRotationUnchecked() { return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */); @@ -1778,8 +1785,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private void applyRotation(final int oldRotation, final int rotation) { mDisplayRotation.applyCurrentRotation(rotation); - final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly(); - final Transaction transaction = getPendingTransaction(); + final boolean shellTransitions = + mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null; + final boolean rotateSeamlessly = + mDisplayRotation.isRotatingSeamlessly() && !shellTransitions; + final Transaction transaction = + shellTransitions ? getSyncTransaction() : getPendingTransaction(); ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly ? null : getRotationAnimation(); // We need to update our screen size information to match the new rotation. If the rotation @@ -1795,9 +1806,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp screenRotationAnimation.setRotation(transaction, rotation); } - forAllWindows(w -> { - w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly); - }, true /* traverseTopToBottom */); + if (!shellTransitions) { + forAllWindows(w -> { + w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly); + }, true /* traverseTopToBottom */); + } mWmService.mDisplayManagerInternal.performTraversal(transaction); scheduleAnimation(); @@ -4573,6 +4586,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mWmService.makeSurfaceBuilder(mSession).setParent(getOverlayLayer()); } + @Override + public SurfaceControl.Builder makeAnimationLeash() { + return mWmService.makeSurfaceBuilder(mSession).setParent(mSurfaceControl) + .setContainerLayer(); + } + /** * Reparents the given surface to {@link #mOverlayLayer} SurfaceControl. */ @@ -5919,4 +5938,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mDisplayAreaPolicy.getDisplayAreaForWindowToken(windowType, options, ownerCanManageAppToken, roundedCornerOverlay); } + + @Override + DisplayContent asDisplayContent() { + return this; + } } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 5df1355f3460..d0e4c40e3416 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFA import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; +import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; @@ -408,8 +409,11 @@ public class DisplayRotation { * THE SCREEN. */ boolean updateRotationUnchecked(boolean forceUpdate) { + final boolean useShellTransitions = + mService.mAtmService.getTransitionController().getTransitionPlayer() != null; + final int displayId = mDisplayContent.getDisplayId(); - if (!forceUpdate) { + if (!forceUpdate && !useShellTransitions) { if (mDeferredRotationPauseCount > 0) { // Rotation updates have been paused temporarily. Defer the update until updates // have been resumed. @@ -472,6 +476,12 @@ public class DisplayRotation { return false; } + final Transition t = (useShellTransitions + && !mService.mAtmService.getTransitionController().isCollecting()) + ? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE) + : null; + mService.mAtmService.getTransitionController().collect(mDisplayContent); + ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d rotation changed to %d from %d, lastOrientation=%d", displayId, rotation, oldRotation, lastOrientation); @@ -484,6 +494,20 @@ public class DisplayRotation { mDisplayContent.setLayoutNeeded(); + if (useShellTransitions) { + if (t != null) { + // This created its own transition, so send a start request. + mService.mAtmService.getTransitionController().requestStartTransition( + t, null /* trigger */, null /* remote */); + } else { + // Use remote-rotation infra since the transition has already been requested + // TODO(shell-transitions): Remove this once lifecycle management can cover all + // rotation cases. + startRemoteRotation(oldRotation, mRotation); + } + return true; + } + mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT, mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION); @@ -504,6 +528,19 @@ public class DisplayRotation { } /** + * Utility to get a rotating displaycontent from a Transition. + * @return null if the transition doesn't contain a rotating display. + */ + static DisplayContent getDisplayFromTransition(Transition transition) { + for (int i = transition.mParticipants.size() - 1; i >= 0; --i) { + final WindowContainer wc = transition.mParticipants.valueAt(i); + if (!(wc instanceof DisplayContent)) continue; + return (DisplayContent) wc; + } + return null; + } + + /** * A Remote rotation is when we are waiting for some registered (remote) * {@link IDisplayWindowRotationController} to calculate and return some hierarchy operations * to perform in sync with the rotation. @@ -537,6 +574,22 @@ public class DisplayRotation { } mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); mIsWaitingForRemoteRotation = false; + + if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) { + if (!mService.mAtmService.getTransitionController().isCollecting()) { + throw new IllegalStateException("Trying to rotate outside a transition"); + } + mService.mAtmService.getTransitionController().collect(mDisplayContent); + // Go through all tasks and collect them before the rotation + // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper + // handling is synchronized. + mDisplayContent.forAllTasks(task -> { + if (task.isVisible()) { + mService.mAtmService.getTransitionController().collect(task); + } + }); + mDisplayContent.getInsetsStateController().addProvidersToTransition(); + } mService.mAtmService.deferWindowLayout(); try { mDisplayContent.sendNewConfiguration(); diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index a971794dc97d..b6057c610277 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -248,6 +248,16 @@ class InsetsStateController { return result; } + public void addProvidersToTransition() { + for (int i = mProviders.size() - 1; i >= 0; --i) { + final InsetsSourceProvider p = mProviders.valueAt(i); + if (p == null) continue; + final WindowContainer wc = p.mWin; + if (wc == null) continue; + mDisplayContent.mAtmService.getTransitionController().collect(wc); + } + } + /** * @return The provider of a specific type. */ diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index aadb2722a313..75be444567ff 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -17,6 +17,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; @@ -411,8 +412,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private static boolean reportIfNotTop(WindowContainer wc) { // Organized tasks need to be reported anyways because Core won't show() their surfaces // and we can't rely on onTaskAppeared because it isn't in sync. + // Also report wallpaper so it can be handled properly during display change/rotation. // TODO(shell-transitions): switch onTaskAppeared usage over to transitions OPEN. - return wc.isOrganized(); + return wc.isOrganized() || isWallpaper(wc); } /** @return the depth of child within ancestor, 0 if child == ancestor, or -1 if not a child. */ @@ -430,7 +432,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } private static boolean isWallpaper(WindowContainer wc) { - return wc instanceof WallpaperWindowToken; + return wc.asWallpaperToken() != null; } /** @@ -576,7 +578,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final ArrayList<WindowContainer> tmpList = new ArrayList<>(); // Build initial set of top-level participants by removing any participants that are no-ops - // or children of other participants or are otherwise invalid. + // or children of other participants or are otherwise invalid; however, keep around a list + // of participants that should always be reported even if they aren't top. for (WindowContainer wc : participants) { // Don't include detached windows. if (!wc.isAttached()) continue; @@ -584,7 +587,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final ChangeInfo changeInfo = changes.get(wc); // Reject no-ops - if (!changeInfo.hasChanged(wc)) continue; + if (!changeInfo.hasChanged(wc)) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + " Rejecting as no-op: %s", wc); + continue; + } // Search through ancestors to find the top-most participant (if one exists) WindowContainer topParent = null; @@ -651,10 +658,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe /** Gets the leash surface for a window container */ private static SurfaceControl getLeashSurface(WindowContainer wc) { + final DisplayContent asDC = wc.asDisplayContent(); + if (asDC != null) { + // DisplayContent is the "root", so we use the windowing layer instead to avoid + // hardware-screen-level surfaces. + return asDC.getWindowingLayer(); + } return wc.getSurfaceControl(); } private static SurfaceControl getOrigParentSurface(WindowContainer wc) { + if (wc.asDisplayContent() != null) { + // DisplayContent is the "root", so we reinterpret it's wc as the window layer + // making the parent surface the displaycontent's surface. + return wc.getSurfaceControl(); + } return wc.getParent().getSurfaceControl(); } @@ -744,6 +762,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left, target.getBounds().top - target.getParent().getBounds().top); change.setFlags(info.getChangeFlags(target)); + change.setRotation(info.mRotation, target.getWindowConfiguration().getRotation()); final Task task = target.asTask(); if (task != null) { final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo(); @@ -773,12 +792,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe int mWindowingMode; final Rect mAbsoluteBounds = new Rect(); boolean mShowWallpaper; + int mRotation = ROTATION_UNDEFINED; ChangeInfo(@NonNull WindowContainer origState) { mVisible = origState.isVisibleRequested(); mWindowingMode = origState.getWindowingMode(); mAbsoluteBounds.set(origState.getBounds()); mShowWallpaper = origState.showWallpaper(); + mRotation = origState.getWindowConfiguration().getRotation(); } @VisibleForTesting @@ -797,7 +818,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // if mWindowingMode is 0, this container wasn't attached at collect time, so // assume no change in windowing-mode. || (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode) - || !newState.getBounds().equals(mAbsoluteBounds); + || !newState.getBounds().equals(mAbsoluteBounds) + || mRotation != newState.getWindowConfiguration().getRotation(); } @TransitionInfo.TransitionMode @@ -826,8 +848,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // checks to use requested visibility. flags |= FLAG_TRANSLUCENT; } - if (wc instanceof ActivityRecord - && wc.asActivityRecord().mUseTransferredAnimation) { + if (wc.asActivityRecord() != null && wc.asActivityRecord().mUseTransferredAnimation) { flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; } if (isWallpaper(wc)) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 0bb9854d8020..a5843d496add 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3084,6 +3084,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return null; } + /** Cheap way of doing cast and instanceof. */ + DisplayContent asDisplayContent() { + return null; + } + /** * @return {@code true} if window container is manage by a * {@link android.window.WindowOrganizer} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c5e000000eee..57394d6d858e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4148,12 +4148,19 @@ public class WindowManagerService extends IWindowManager.Stub .notifyOnActivityRotation(displayContent.mDisplayId); } - if (!rotationChanged || forceRelayout) { - displayContent.setLayoutNeeded(); - layoutNeeded = true; - } - if (rotationChanged || alwaysSendConfiguration) { - displayContent.sendNewConfiguration(); + final boolean pendingRemoteRotation = rotationChanged + && (displayContent.getDisplayRotation().isWaitingForRemoteRotation() + || mAtmService.getTransitionController().isCollecting()); + // Even if alwaysSend, we are waiting for a transition or remote to provide + // rotated configuration, so we can't update configuration yet. + if (!pendingRemoteRotation) { + if (!rotationChanged || forceRelayout) { + displayContent.setLayoutNeeded(); + layoutNeeded = true; + } + if (rotationChanged || alwaysSendConfiguration) { + displayContent.sendNewConfiguration(); + } } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 9973664346f0..e3d549bee2e7 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -246,6 +246,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId); mService.deferWindowLayout(); try { + if (transition != null) { + // First check if we have a display rotation transition and if so, update it. + final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition); + if (dc != null && transition.mChanges.get(dc).mRotation != dc.getRotation()) { + // Go through all tasks and collect them before the rotation + // TODO(shell-transitions): move collect() to onConfigurationChange once + // wallpaper handling is synchronized. + dc.forAllTasks(task -> { + if (task.isVisible()) transition.collect(task); + }); + dc.getInsetsStateController().addProvidersToTransition(); + dc.sendNewConfiguration(); + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } + } ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = t.getChanges().entrySet().iterator(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 67fe7bf436b7..33bcc5b727df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -119,6 +119,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.WindowManager; +import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; @@ -1611,6 +1612,54 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testShellTransitRotation() { + DisplayContent dc = createNewDisplay(); + + // Set-up mock shell transitions + final TestTransitionPlayer testPlayer = new TestTransitionPlayer( + mAtm.getTransitionController(), mAtm.mWindowOrganizerController); + mAtm.getTransitionController().registerTransitionPlayer(testPlayer); + + final DisplayRotation dr = dc.getDisplayRotation(); + doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean()); + // Rotate 180 degree so the display doesn't have configuration change. This condition is + // used for the later verification of stop-freezing (without setting mWaitingForConfig). + doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt()); + mWm.mDisplayRotationController = + new IDisplayWindowRotationController.Stub() { + @Override + public void onRotateDisplay(int displayId, int fromRotation, int toRotation, + IDisplayWindowRotationCallback callback) { + try { + callback.continueRotateDisplay(toRotation, null); + } catch (RemoteException e) { + assertTrue(false); + } + } + }; + + // kill any existing rotation animation (vestigial from test setup). + dc.setRotationAnimation(null); + + final int origRot = dc.getConfiguration().windowConfiguration.getRotation(); + + mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */); + // Should create a transition request without performing rotation + assertNotNull(testPlayer.mLastRequest); + assertEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation()); + + // Once transition starts, rotation is applied and transition shows DC rotating. + testPlayer.start(); + assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation()); + assertNotNull(testPlayer.mLastReady); + assertEquals(dc, DisplayRotation.getDisplayFromTransition(testPlayer.mLastTransit)); + WindowContainerToken dcToken = dc.mRemoteToken.toWindowContainerToken(); + assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(), + testPlayer.mLastReady.getChange(dcToken).getStartRotation()); + testPlayer.finish(); + } + + @Test public void testGetOrCreateRootHomeTask_defaultDisplay() { TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 779457bb3e2f..b210dfb654ba 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -60,6 +60,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; import android.annotation.IntDef; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.IApplicationThread; @@ -90,7 +91,10 @@ import android.view.View; import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; import android.window.ITaskOrganizer; +import android.window.ITransitionPlayer; import android.window.StartingWindowInfo; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import com.android.internal.policy.AttributeCache; import com.android.internal.util.ArrayUtils; @@ -1359,4 +1363,48 @@ class WindowTestsBase extends SystemServiceTestsBase { mHasSurface = hadSurface; } } + + class TestTransitionPlayer extends ITransitionPlayer.Stub { + final TransitionController mController; + final WindowOrganizerController mOrganizer; + Transition mLastTransit = null; + TransitionRequestInfo mLastRequest = null; + TransitionInfo mLastReady = null; + + TestTransitionPlayer(@NonNull TransitionController controller, + @NonNull WindowOrganizerController organizer) { + mController = controller; + mOrganizer = organizer; + } + + void clear() { + mLastTransit = null; + mLastReady = null; + mLastRequest = null; + } + + @Override + public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo, + SurfaceControl.Transaction transaction) throws RemoteException { + mLastTransit = Transition.fromBinder(transitToken); + mLastReady = transitionInfo; + } + + @Override + public void requestStartTransition(IBinder transitToken, + TransitionRequestInfo request) throws RemoteException { + mLastTransit = Transition.fromBinder(transitToken); + mLastRequest = request; + } + + public void start() { + mOrganizer.startTransition(mLastRequest.getType(), mLastTransit, null); + mLastTransit.onTransactionReady(mLastTransit.getSyncId(), + mock(SurfaceControl.Transaction.class)); + } + + public void finish() { + mController.finishTransition(mLastTransit); + } + } } |