summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Evan Rosky <erosky@google.com> 2021-03-18 23:24:48 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-03-18 23:24:48 +0000
commit557bab6f877b2dd3f6de48527be2fd8f342efa5d (patch)
treebe9573a3c477f7c466b404e75e0191fe6a8dcb66
parentde4d7421d796fd6df303cfe8d9471ba067fa84f7 (diff)
parent1551cb73075900e5d71a174ca4e51652b0d1713e (diff)
Merge "Initial support for display rotation in shell transitions" into sc-dev
-rw-r--r--core/java/android/window/TransitionInfo.java25
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java95
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java38
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java55
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java10
-rw-r--r--services/core/java/com/android/server/wm/Transition.java35
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java49
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java48
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);
+ }
+ }
}