Merge "Initial support for display rotation in shell transitions" into sc-dev

This commit is contained in:
Evan Rosky
2021-03-18 23:24:48 +00:00
committed by Android (Google) Code Review
15 changed files with 393 additions and 25 deletions

View File

@@ -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 + "}";
}
}
}

View File

@@ -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",

View File

@@ -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),

View File

@@ -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;

View File

@@ -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"

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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);
@@ -503,6 +527,19 @@ public class DisplayRotation {
return true;
}
/**
* 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
@@ -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();

View File

@@ -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.
*/

View File

@@ -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)) {

View File

@@ -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}

View File

@@ -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();
}
}
}

View File

@@ -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();

View File

@@ -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;
@@ -1610,6 +1611,54 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(mWm.mDisplayFrozen);
}
@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();

View File

@@ -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);
}
}
}