Merge "Generalize change transition into WindowContainer" into rvc-dev am: 66279fe163

Change-Id: I18bd10524fe03ada89d179d3de9a087dc22f5112
This commit is contained in:
Automerger Merge Worker
2020-02-26 01:59:25 +00:00
16 changed files with 550 additions and 288 deletions

View File

@@ -697,6 +697,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
"-668956537": {
"message": " THUMBNAIL %s: CREATE",
"level": "INFO",
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/SurfaceFreezer.java"
},
"-666510420": {
"message": "With display frozen, orientationChangeComplete=%b",
"level": "VERBOSE",

View File

@@ -275,8 +275,9 @@ class ActivityMetricsLogger {
}
/** @return {@code true} if the activity matches a launched activity in this transition. */
boolean contains(ActivityRecord r) {
return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r);
boolean contains(WindowContainer wc) {
final ActivityRecord r = AppTransitionController.getAppFromContainer(wc);
return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r));
}
/** Called when the activity is drawn or won't be drawn. */
@@ -435,10 +436,10 @@ class ActivityMetricsLogger {
/** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
@Nullable
private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
private TransitionInfo getActiveTransitionInfo(WindowContainer wc) {
for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
final TransitionInfo info = mTransitionInfoList.get(i);
if (info.contains(r)) {
if (info.contains(wc)) {
return info;
}
}
@@ -623,19 +624,19 @@ class ActivityMetricsLogger {
* @param activityToReason A map from activity to a reason integer, which must be on of
* ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) {
void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
final long timestampNs = SystemClock.elapsedRealtimeNanos();
for (int index = activityToReason.size() - 1; index >= 0; index--) {
final ActivityRecord r = activityToReason.keyAt(index);
final TransitionInfo info = getActiveTransitionInfo(r);
final WindowContainer wc = activityToReason.keyAt(index);
final TransitionInfo info = getActiveTransitionInfo(wc);
if (info == null || info.mLoggedTransitionStarting) {
// Ignore any subsequent notifyTransitionStarting.
continue;
}
if (DEBUG_METRICS) {
Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info);
Slog.i(TAG, "notifyTransitionStarting activity=" + wc + " info=" + info);
}
info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);

View File

@@ -40,7 +40,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.activityTypeToString;
@@ -106,7 +105,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_UNSET;
@@ -284,7 +282,6 @@ import android.view.DisplayInfo;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.InputApplicationHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -577,12 +574,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private boolean mCurrentLaunchCanTurnScreenOn = true;
/**
* This leash is used to "freeze" the app surface in place after the state change, but before
* the animation is ready to start.
*/
private SurfaceControl mTransitChangeLeash = null;
/** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
private boolean mLastSurfaceShowing = true;
@@ -1329,15 +1320,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mDisplayContent.executeAppTransition();
}
if (prevDc.mChangingApps.remove(this)) {
// This gets called *after* the ActivityRecord has been reparented to the new display.
// That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
// so this token is now "frozen" while waiting for the animation to start on prevDc
// (which will be cancelled since the window is no-longer a child). However, since this
// is no longer a child of prevDc, this won't be notified of the cancelled animation,
// so we need to cancel the change transition here.
clearChangeLeash(getPendingTransaction(), true /* cancel */);
}
prevDc.mClosingApps.remove(this);
if (prevDc.mFocusedApp == this) {
@@ -3092,7 +3074,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
commitVisibility(false /* visible */, true /* performLayout */);
getDisplayContent().mOpeningApps.remove(this);
getDisplayContent().mChangingApps.remove(this);
getDisplayContent().mChangingContainers.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
@@ -3995,13 +3977,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
appToken, visible, appTransition, isVisible(), mVisibleRequested,
Debug.getCallers(6));
onChildVisibilityRequested(visible);
final DisplayContent displayContent = getDisplayContent();
displayContent.mOpeningApps.remove(this);
displayContent.mClosingApps.remove(this);
if (isInChangeTransition()) {
clearChangeLeash(getPendingTransaction(), true /* cancel */);
}
displayContent.mChangingApps.remove(this);
waitingToShow = false;
mVisibleRequested = visible;
mLastDeferHidingClient = deferHidingClient;
@@ -5805,11 +5785,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
}
@Override
boolean isChangingAppTransition() {
return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
}
/**
* Creates a layer to apply crop to an animation.
*/
@@ -5830,84 +5805,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
this, endDeferFinishCallback);
}
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
if (mWmService.mDisableTransitionAnimation
|| !isVisible()
|| getDisplayContent().mAppTransition.isTransitionSet()
|| getSurfaceControl() == null) {
return false;
}
// Only do an animation into and out-of freeform mode for now. Other mode
// transition animations are currently handled by system-ui.
return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
}
@Override
boolean isWaitingForTransitionStart() {
final DisplayContent dc = getDisplayContent();
return dc != null && dc.mAppTransition.isTransitionSet()
&& (dc.mOpeningApps.contains(this)
|| dc.mClosingApps.contains(this)
|| dc.mChangingApps.contains(this));
|| dc.mChangingContainers.contains(this));
}
/**
* Initializes a change transition. Because the app is visible already, there is a small period
* of time where the user can see the app content/window update before the transition starts.
* To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
* "freezes" the location/crop until the transition starts.
* <p>
* Here's a walk-through of the process:
* 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
* 2. Set the temporary leash's position/crop to the current state.
* 3. Create a snapshot and place that at the top of the leash to cover up content changes.
* 4. Once the transition is ready, it will reparent the app to the animation leash.
* 5. Detach the interim-change-leash.
*/
private void initializeChangeTransition(Rect startBounds) {
mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
mDisplayContent.mChangingApps.add(this);
mTransitStartRect.set(startBounds);
final SurfaceControl.Builder builder = makeAnimationLeash()
.setParent(getAnimationLeashParent())
.setName(getSurfaceControl() + " - interim-change-leash");
mTransitChangeLeash = builder.build();
Transaction t = getPendingTransaction();
t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
t.show(mTransitChangeLeash);
t.reparent(getSurfaceControl(), mTransitChangeLeash);
onAnimationLeashCreated(t, mTransitChangeLeash);
// Skip creating snapshot if this transition is controlled by a remote animator which
// doesn't need it.
ArraySet<Integer> activityTypes = new ArraySet<>();
activityTypes.add(getActivityType());
RemoteAnimationAdapter adapter =
mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
return;
}
if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) {
SurfaceControl.ScreenshotGraphicBuffer snapshot =
mWmService.mTaskSnapshotController.createTaskSnapshot(
task, 1 /* scaleFraction */);
if (snapshot != null) {
mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, t, this,
snapshot.getGraphicBuffer(), true /* relative */);
}
}
}
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
private int getAnimationLayer() {
// The leash is parented to the animation layer. We need to preserve the z-order by using
// the prefix order index, but we boost if necessary.
int layer = 0;
int layer;
if (!inPinnedWindowingMode()) {
layer = getPrefixOrderIndex();
} else {
@@ -5920,21 +5830,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mNeedsZBoost) {
layer += Z_BOOST_BASE;
}
if (!mNeedsAnimationBoundsLayer) {
t.setLayer(leash, layer);
}
return layer;
}
final DisplayContent dc = getDisplayContent();
dc.assignStackOrdering();
if (leash == mTransitChangeLeash) {
// This is a temporary state so skip any animation notifications
return;
} else if (mTransitChangeLeash != null) {
// unparent mTransitChangeLeash for clean-up
clearChangeLeash(t, false /* cancel */);
}
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
t.setLayer(leash, getAnimationLayer());
getDisplayContent().assignStackOrdering();
}
@Override
public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
if (mAnimatingActivityRegistry != null) {
mAnimatingActivityRegistry.notifyStarting(this);
}
@@ -5962,7 +5868,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// surface size has already same as the animating container.
t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
}
t.setLayer(mAnimationBoundsLayer, layer);
t.setLayer(leash, 0);
t.setLayer(mAnimationBoundsLayer, getAnimationLayer());
// Reparent leash to animation bounds layer.
t.reparent(leash, mAnimationBoundsLayer);
@@ -5994,10 +5901,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mLastSurfaceShowing;
}
boolean isInChangeTransition() {
return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit);
}
void attachThumbnailAnimation() {
if (!isAnimating(PARENTS)) {
return;
@@ -6134,31 +6037,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
/**
* @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
* to another leash.
*/
private void clearChangeLeash(Transaction t, boolean cancel) {
if (mTransitChangeLeash == null) {
return;
}
if (cancel) {
clearThumbnail();
SurfaceControl sc = getSurfaceControl();
SurfaceControl parentSc = getParentSurfaceControl();
// Don't reparent if surface is getting destroyed
if (parentSc != null && sc != null) {
t.reparent(sc, getParentSurfaceControl());
}
}
t.hide(mTransitChangeLeash);
t.remove(mTransitChangeLeash);
mTransitChangeLeash = null;
if (cancel) {
onAnimationLeashLost(t);
}
}
void clearAnimatingFlags() {
boolean wallpaperMightChange = false;
for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -6174,7 +6052,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void cancelAnimation() {
cancelAnimationOnly();
clearThumbnail();
clearChangeLeash(getPendingTransaction(), true /* cancel */);
mSurfaceFreezer.unfreeze(getPendingTransaction());
}
/**
@@ -6219,6 +6097,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mRemoteAnimationDefinition = null;
}
@Override
RemoteAnimationDefinition getRemoteAnimationDefinition() {
return mRemoteAnimationDefinition;
}
@@ -6679,8 +6558,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
}
final int prevWinMode = getWindowingMode();
mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
if (shouldUseSizeCompatMode()) {
@@ -6705,12 +6582,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
final int newWinMode = getWindowingMode();
if ((prevWinMode != newWinMode) && (mDisplayContent != null)
&& shouldStartChangeTransition(prevWinMode, newWinMode)) {
initializeChangeTransition(mTmpPrevBounds);
}
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
// configuration, but it's used to push configuration changes so explicitly update that.
@@ -7595,6 +7466,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
@Override
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));

View File

@@ -2308,14 +2308,14 @@ public class AppTransition implements Dump {
}
notifyAppTransitionTimeoutLocked();
if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
|| !dc.mChangingApps.isEmpty()) {
|| !dc.mChangingContainers.isEmpty()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
+ "mOpeningApps.size()=%d mClosingApps.size()=%d "
+ "mChangingApps.size()=%d",
dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
dc.mOpeningApps.size(), dc.mClosingApps.size(),
dc.mChangingApps.size());
dc.mChangingContainers.size());
setTimeout();
mService.mWindowPlacerLocked.performSurfacePlacement();

View File

@@ -55,6 +55,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -86,7 +87,7 @@ public class AppTransitionController {
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
private final ArrayMap<ActivityRecord, Integer> mTempTransitionReasons = new ArrayMap<>();
private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -104,7 +105,8 @@ public class AppTransitionController {
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
|| !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) {
|| !transitionGoodToGo(mDisplayContent.mChangingContainers,
mTempTransitionReasons)) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -130,17 +132,21 @@ public class AppTransitionController {
// transition selection depends on wallpaper target visibility.
mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
}
appCount = mDisplayContent.mChangingApps.size();
appCount = mDisplayContent.mChangingContainers.size();
for (int i = 0; i < appCount; ++i) {
// Clearing for same reason as above.
mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags();
final ActivityRecord activity = getAppFromContainer(
mDisplayContent.mChangingContainers.valueAtUnchecked(i));
if (activity != null) {
activity.clearAnimatingFlags();
}
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps);
mDisplayContent.mOpeningApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
@@ -159,7 +165,7 @@ public class AppTransitionController {
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingApps);
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
final ActivityRecord animLpActivity = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
@@ -171,14 +177,13 @@ public class AppTransitionController {
? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
: null;
final ActivityRecord topChangingApp = allowAnimations
? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */)
? getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */)
: null;
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
|| containsVoiceInteraction(mDisplayContent.mOpeningApps)
|| containsVoiceInteraction(mDisplayContent.mChangingApps);
|| containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
@@ -206,7 +211,7 @@ public class AppTransitionController {
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
mDisplayContent.mChangingApps.clear();
mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
// This has changed the visibility of windows, so perform
@@ -235,9 +240,9 @@ public class AppTransitionController {
return mainWindow != null ? mainWindow.mAttrs : null;
}
RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity,
RemoteAnimationAdapter getRemoteAnimationOverride(@NonNull WindowContainer container,
@TransitionType int transit, ArraySet<Integer> activityTypes) {
final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition();
final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition();
if (definition != null) {
final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
if (adapter != null) {
@@ -271,6 +276,11 @@ public class AppTransitionController {
}
}
static ActivityRecord getAppFromContainer(WindowContainer wc) {
return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity()
: wc.asActivityRecord();
}
/**
* @return The window token that determines the animation theme.
*/
@@ -279,14 +289,14 @@ public class AppTransitionController {
ActivityRecord result;
final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
final ArraySet<ActivityRecord> changingApps = mDisplayContent.mChangingApps;
final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers;
// Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.getRemoteAnimationDefinition() != null
&& w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
return result;
return getAppFromContainer(result);
}
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.fillsParent() && w.findMainWindow() != null);
@@ -302,7 +312,7 @@ public class AppTransitionController {
* of apps in {@code array1}, {@code array2}, and {@code array3}.
*/
private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1,
ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3) {
ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) {
final ArraySet<Integer> result = new ArraySet<>();
for (int i = array1.size() - 1; i >= 0; i--) {
result.add(array1.valueAt(i).getActivityType());
@@ -317,7 +327,7 @@ public class AppTransitionController {
}
private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1,
ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3,
ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3,
Predicate<ActivityRecord> filter) {
final int array2base = array1.size();
final int array3base = array2.size() + array2base;
@@ -325,15 +335,16 @@ public class AppTransitionController {
int bestPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord bestToken = null;
for (int i = 0; i < count; i++) {
final ActivityRecord wtoken = i < array2base
final WindowContainer wtoken = i < array2base
? array1.valueAt(i)
: (i < array3base
? array2.valueAt(i - array2base)
: array3.valueAt(i - array3base));
final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
final ActivityRecord r = getAppFromContainer(wtoken);
if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) {
bestPrefixOrderIndex = prefixOrderIndex;
bestToken = wtoken;
bestToken = r;
}
}
return bestToken;
@@ -589,21 +600,13 @@ public class AppTransitionController {
}
private void handleChangingApps(@TransitionType int transit) {
final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps;
final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
final int appsCount = apps.size();
for (int i = 0; i < appsCount; i++) {
ActivityRecord activity = apps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity);
activity.cancelAnimationOnly();
activity.applyAnimation(null, transit, true, false,
WindowContainer wc = apps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc);
wc.applyAnimation(null, transit, true, false,
null /* animationFinishedCallback */);
activity.updateReportedVisibilityLocked();
mService.openSurfaceTransaction();
try {
activity.showAllWindowsLocked();
} finally {
mService.closeSurfaceTransaction("handleChangingApps");
}
}
}
@@ -628,8 +631,8 @@ public class AppTransitionController {
}
}
private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps,
ArrayMap<ActivityRecord, Integer> outReasons) {
private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
ArrayMap<WindowContainer, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
@@ -652,13 +655,17 @@ public class AppTransitionController {
return false;
}
for (int i = 0; i < apps.size(); i++) {
ActivityRecord activity = apps.valueAt(i);
WindowContainer wc = apps.valueAt(i);
final ActivityRecord activity = getAppFromContainer(wc);
if (activity == null) {
continue;
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ "startingMoved=%b isRelaunching()=%b startingWindow=%s",
activity, activity.allDrawn, activity.startingDisplayed,
activity.startingMoved, activity.isRelaunching(),
activity.startingWindow);
"Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ "startingMoved=%b isRelaunching()=%b startingWindow=%s",
activity, activity.allDrawn, activity.startingDisplayed,
activity.startingMoved, activity.isRelaunching(),
activity.startingWindow);
final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
@@ -838,7 +845,7 @@ public class AppTransitionController {
@VisibleForTesting
boolean isTransitWithinTask(@TransitionType int transit, Task task) {
if (task == null
|| !mDisplayContent.mChangingApps.isEmpty()) {
|| !mDisplayContent.mChangingContainers.isEmpty()) {
// if there is no task, then we can't constrain to the task.
// if anything is changing, it can animate outside its task.
return false;
@@ -882,12 +889,13 @@ public class AppTransitionController {
* {@link ActivityRecord#isVisible}.
* @return The top {@link ActivityRecord}.
*/
private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) {
private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
boolean ignoreInvisible) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord topApp = null;
for (int i = apps.size() - 1; i >= 0; i--) {
final ActivityRecord app = apps.valueAt(i);
if (ignoreInvisible && !app.isVisible()) {
final ActivityRecord app = getAppFromContainer(apps.valueAt(i));
if (app == null || ignoreInvisible && !app.isVisible()) {
continue;
}
final int prefixOrderIndex = app.getPrefixOrderIndex();

View File

@@ -91,7 +91,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
@@ -314,7 +313,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>();
final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
private MetricsLogger mMetricsLogger;
@@ -2695,7 +2694,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Clear all transitions & screen frozen states when removing display.
mOpeningApps.clear();
mClosingApps.clear();
mChangingApps.clear();
mChangingContainers.clear();
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
handleAnimatingStoppedAndTransition();
@@ -2916,9 +2915,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
for (int i = mClosingApps.size() - 1; i >= 0; i--) {
mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
}
for (int i = mChangingApps.size() - 1; i >= 0; i--) {
mChangingApps.valueAt(i).writeIdentifierToProto(proto, CHANGING_APPS);
}
proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
final ActivityStack focusedStack = getFocusedStack();
@@ -3670,7 +3666,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingApps.isEmpty()) {
if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingContainers.isEmpty()) {
pw.println();
if (mOpeningApps.size() > 0) {
pw.print(" mOpeningApps="); pw.println(mOpeningApps);
@@ -3678,8 +3674,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (mClosingApps.size() > 0) {
pw.print(" mClosingApps="); pw.println(mClosingApps);
}
if (mChangingApps.size() > 0) {
pw.print(" mChangingApps="); pw.println(mChangingApps);
if (mChangingContainers.size() > 0) {
pw.print(" mChangingApps="); pw.println(mChangingContainers);
}
}

View File

@@ -500,7 +500,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
if (mTargetActivityRecord != null) {
final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(1);
final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1);
reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
mService.mAtmService.mStackSupervisor.getActivityMetricsLogger()
.notifyTransitionStarting(reasons);

View File

@@ -387,7 +387,7 @@ class RemoteAnimationController implements DeathRecipient {
final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
if (dc.mOpeningApps.contains(topActivity)) {
return RemoteAnimationTarget.MODE_OPENING;
} else if (dc.mChangingApps.contains(topActivity)) {
} else if (dc.mChangingContainers.contains(topActivity)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;

View File

@@ -128,7 +128,8 @@ class SurfaceAnimator {
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
@@ -139,9 +140,14 @@ class SurfaceAnimator {
cancelAnimation();
return;
}
mLeash = createAnimationLeash(surface, t,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
mAnimatable.onAnimationLeashCreated(t, mLeash);
mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
if (mLeash == null) {
mLeash = createAnimationLeash(mAnimatable, surface, t,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
0 /* y */, hidden);
mAnimatable.onAnimationLeashCreated(t, mLeash);
}
mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
@@ -149,6 +155,12 @@ class SurfaceAnimator {
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
@@ -311,15 +323,31 @@ class SurfaceAnimator {
}
private void reset(Transaction t, boolean destroyLeash) {
final SurfaceControl surface = mAnimatable.getSurfaceControl();
final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
mService.mAnimationTransferMap.remove(mAnimation);
mAnimation = null;
mAnimationFinishedCallback = null;
mAnimationType = ANIMATION_TYPE_NONE;
if (mLeash == null) {
return;
}
SurfaceControl leash = mLeash;
mLeash = null;
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
if (scheduleAnim) {
mService.scheduleAnimationLocked();
}
}
static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
boolean destroy) {
boolean scheduleAnim = false;
final SurfaceControl surface = animatable.getSurfaceControl();
final SurfaceControl parent = animatable.getParentSurfaceControl();
// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
// Note that we also set this variable to true even if the parent isn't valid anymore, in
// order to ensure onAnimationLeashLost still gets called in this case.
final boolean reparent = mLeash != null && surface != null;
final boolean reparent = surface != null;
if (reparent) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
// We shouldn't really need these isValid checks but we do
@@ -329,40 +357,30 @@ class SurfaceAnimator {
scheduleAnim = true;
}
}
mService.mAnimationTransferMap.remove(mAnimation);
if (mLeash != null && destroyLeash) {
t.remove(mLeash);
if (destroy) {
t.remove(leash);
scheduleAnim = true;
}
mLeash = null;
mAnimation = null;
mAnimationFinishedCallback = null;
mAnimationType = ANIMATION_TYPE_NONE;
if (reparent) {
// Make sure to inform the animatable after the surface was reparented (or reparent
// wasn't possible, but we still need to invoke the callback)
mAnimatable.onAnimationLeashLost(t);
animatable.onAnimationLeashLost(t);
scheduleAnim = true;
}
if (scheduleAnim) {
mService.scheduleAnimationLocked();
}
return scheduleAnim;
}
private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
int height, boolean hidden) {
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, int width, int height, int x, int y, boolean hidden) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
.setParent(mAnimatable.getAnimationLeashParent())
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
.setHidden(hidden)
.setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
// TODO: rely on builder.setHidden(hidden) instead of show and setAlpha when b/138459974 is
// fixed.
t.setPosition(leash, x, y);
t.show(leash);
t.setAlpha(leash, hidden ? 0 : 1);
@@ -489,13 +507,22 @@ class SurfaceAnimator {
void commitPendingTransaction();
/**
* Called when the was created.
* Called when the animation leash is created. Note that this is also called by
* {@link SurfaceFreezer}, so this doesn't mean we're about to start animating.
*
* @param t The transaction to use to apply any necessary changes.
* @param leash The leash that was created.
*/
void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
/**
* Called when the animator is about to start animating the leash.
*
* @param t The transaction to use to apply any necessary changes.
* @param leash The leash that was created.
*/
default void onLeashAnimationStarting(Transaction t, SurfaceControl leash) { }
/**
* Called when the leash is being destroyed, or when the leash is being transferred to
* another SurfaceAnimator.

View File

@@ -0,0 +1,245 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.Surface;
import android.view.SurfaceControl;
import com.android.server.protolog.common.ProtoLog;
import java.util.function.Supplier;
/**
* This class handles "freezing" of an Animatable. The Animatable in question should implement
* Freezable.
*
* The point of this is to enable WindowContainers to each be capable of freezing themselves.
* Freezing means taking a snapshot and placing it above everything in the sub-hierarchy.
* The "placing above" requires that a parent surface be inserted above the target surface so that
* the target surface and the snapshot are siblings.
*
* The overall flow for a transition using this would be:
* 1. Set transition and record animatable in mChangingApps
* 2. Call {@link #freeze} to set-up the leashes and cover with a snapshot.
* 3. When transition participants are ready, start SurfaceAnimator with this as a parameter
* 4. SurfaceAnimator will then {@link #takeLeashForAnimation} instead of creating another leash.
* 5. The animation system should eventually clean this up via {@link #unfreeze}.
*/
class SurfaceFreezer {
private final Freezable mAnimatable;
private final WindowManagerService mWmService;
private SurfaceControl mLeash;
Snapshot mSnapshot = null;
final Rect mFreezeBounds = new Rect();
/**
* @param animatable The object to animate.
*/
SurfaceFreezer(Freezable animatable, WindowManagerService service) {
mAnimatable = animatable;
mWmService = service;
}
/**
* Freeze the target surface. This is done by creating a leash (inserting a parent surface
* above the target surface) and then taking a snapshot and placing it over the target surface.
*
* @param startBounds The original bounds (on screen) of the surface we are snapshotting.
*/
void freeze(SurfaceControl.Transaction t, Rect startBounds) {
mFreezeBounds.set(startBounds);
mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
t, startBounds.width(), startBounds.height(), startBounds.left, startBounds.top,
false /* hidden */);
mAnimatable.onAnimationLeashCreated(t, mLeash);
SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
if (freezeTarget != null) {
GraphicBuffer snapshot = createSnapshotBuffer(freezeTarget, startBounds);
if (snapshot != null) {
mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, snapshot, mLeash);
}
}
}
/**
* Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation.
* By transferring the leash, this will no longer try to clean-up the leash when finished.
*/
SurfaceControl takeLeashForAnimation() {
SurfaceControl out = mLeash;
mLeash = null;
return out;
}
/**
* Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the
* snapshot.
*/
void unfreeze(SurfaceControl.Transaction t) {
if (mSnapshot != null) {
mSnapshot.destroy(t);
}
if (mLeash == null) {
return;
}
SurfaceControl leash = mLeash;
mLeash = null;
final boolean scheduleAnim = SurfaceAnimator.removeLeash(t, mAnimatable, leash,
false /* destroy */);
if (scheduleAnim) {
mWmService.scheduleAnimationLocked();
}
}
boolean hasLeash() {
return mLeash != null;
}
private static GraphicBuffer createSnapshotBuffer(@NonNull SurfaceControl target,
@Nullable Rect bounds) {
Rect cropBounds = null;
if (bounds != null) {
cropBounds = new Rect(bounds);
cropBounds.offsetTo(0, 0);
}
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
SurfaceControl.captureLayers(
target, cropBounds, 1.f /* frameScale */, PixelFormat.RGBA_8888);
final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
: null;
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
return null;
}
return buffer;
}
class Snapshot {
private SurfaceControl mSurfaceControl;
private AnimationAdapter mAnimation;
private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback;
/**
* @param t Transaction to create the thumbnail in.
* @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
*/
Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
GraphicBuffer thumbnailHeader, SurfaceControl parent) {
Surface drawSurface = surfaceFactory.get();
// We can't use a delegating constructor since we need to
// reference this::onAnimationFinished
final int width = thumbnailHeader.getWidth();
final int height = thumbnailHeader.getHeight();
mSurfaceControl = mAnimatable.makeAnimationLeash()
.setName("snapshot anim: " + mAnimatable.toString())
.setBufferSize(width, height)
.setFormat(PixelFormat.TRANSLUCENT)
.setParent(parent)
.build();
ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
// Transfer the thumbnail to the surface
drawSurface.copyFrom(mSurfaceControl);
drawSurface.attachAndQueueBuffer(thumbnailHeader);
drawSurface.release();
t.show(mSurfaceControl);
// We parent the thumbnail to the container, and just place it on top of anything else
// in the container.
t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
}
void destroy(SurfaceControl.Transaction t) {
if (mSurfaceControl == null) {
return;
}
t.remove(mSurfaceControl);
mSurfaceControl = null;
}
/**
* Starts an animation.
*
* @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
* component responsible for running the animation. It runs the animation with
* {@link AnimationAdapter#startAnimation} once the hierarchy with
* the Leash has been set up.
* @param animationFinishedCallback The callback being triggered when the animation
* finishes.
*/
void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type,
@Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) {
cancelAnimation(t, true /* restarting */);
mAnimation = anim;
mFinishedCallback = animationFinishedCallback;
if (mSurfaceControl == null) {
cancelAnimation(t, false /* restarting */);
return;
}
mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback);
}
/**
* Cancels the animation, and resets the leash.
*
* @param t The transaction to use for all cancelling surface operations.
* @param restarting Whether we are restarting the animation.
*/
private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
final SurfaceControl leash = mSurfaceControl;
final AnimationAdapter animation = mAnimation;
final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
mFinishedCallback;
mAnimation = null;
mFinishedCallback = null;
if (animation != null) {
animation.onAnimationCancelled(leash);
if (!restarting) {
if (animationFinishedCallback != null) {
animationFinishedCallback.onAnimationFinished(
ANIMATION_TYPE_APP_TRANSITION, animation);
}
}
}
if (!restarting) {
// TODO: do we need to destroy?
destroy(t);
}
}
}
/** freezable */
public interface Freezable extends SurfaceAnimator.Animatable {
/**
* @return The surface to take a snapshot of. If this returns {@code null}, no snapshot
* will be generated (but the rest of the freezing logic will still happen).
*/
@Nullable SurfaceControl getFreezeSnapshotTarget();
}
}

View File

@@ -58,6 +58,7 @@ import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
@@ -78,6 +79,9 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -119,10 +123,13 @@ import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -1884,6 +1891,8 @@ class Task extends WindowContainer<WindowContainer> {
.setBounds(mLastNonFullscreenBounds);
}
final int prevWinMode = getWindowingMode();
mTmpPrevBounds.set(getBounds());
final boolean wasInMultiWindowMode = inMultiWindowMode();
super.onConfigurationChanged(newParentConfig);
if (wasInMultiWindowMode != inMultiWindowMode()) {
@@ -1891,6 +1900,12 @@ class Task extends WindowContainer<WindowContainer> {
updateShadowsRadius(isFocused(), getPendingTransaction());
}
final int newWinMode = getWindowingMode();
if ((prevWinMode != newWinMode) && (mDisplayContent != null)
&& shouldStartChangeTransition(prevWinMode, newWinMode)) {
initializeChangeTransition(mTmpPrevBounds);
}
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
// current (non-fullscreen) bounds for persistence.
if (getWindowConfiguration().persistTaskBounds()) {
@@ -1904,6 +1919,63 @@ class Task extends WindowContainer<WindowContainer> {
saveLaunchingStateIfNeeded();
}
/**
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*/
private void initializeChangeTransition(Rect startBounds) {
mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
mDisplayContent.mChangingContainers.add(this);
mSurfaceFreezer.freeze(getPendingTransaction(), startBounds);
}
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
if (mWmService.mDisableTransitionAnimation
|| !isVisible()
|| getDisplayContent().mAppTransition.isTransitionSet()
|| getSurfaceControl() == null) {
return false;
}
// Only do an animation into and out-of freeform mode for now. Other mode
// transition animations are currently handled by system-ui.
return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
}
@VisibleForTesting
boolean isInChangeTransition() {
return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit);
}
@Override
public SurfaceControl getFreezeSnapshotTarget() {
final int transit = mDisplayContent.mAppTransition.getAppTransition();
if (!AppTransition.isChangeTransit(transit)) {
return null;
}
// Skip creating snapshot if this transition is controlled by a remote animator which
// doesn't need it.
final ArraySet<Integer> activityTypes = new ArraySet<>();
activityTypes.add(getActivityType());
final RemoteAnimationAdapter adapter =
mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
this, transit, activityTypes);
if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
return null;
}
return getSurfaceControl();
}
@Override
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
proto.write(USER_ID, mUserId);
proto.write(TITLE, intent != null && intent.getComponent() != null
? intent.getComponent().flattenToShortString() : "Task");
proto.end(token);
}
/**
* Saves launching state if necessary so that we can launch the activity to its latest state.
* It only saves state if this task has been shown to user and it's in fullscreen or freeform
@@ -2614,12 +2686,22 @@ class Task extends WindowContainer<WindowContainer> {
if (!isRootTask) {
adjustBoundsForDisplayChangeIfNeeded(dc);
}
final DisplayContent prevDc = mDisplayContent;
super.onDisplayChanged(dc);
if (!isRootTask) {
final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY;
mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
mTaskId, displayId);
}
if (prevDc != null && prevDc.mChangingContainers.remove(this)) {
// This gets called *after* this has been reparented to the new display.
// That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
// so this token is now "frozen" while waiting for the animation to start on prevDc
// (which will be cancelled since the window is no-longer a child). However, since this
// is no longer a child of prevDc, this won't be notified of the cancelled animation,
// so we need to cancel the change transition here.
mSurfaceFreezer.unfreeze(getPendingTransaction());
}
}
/**
@@ -3010,15 +3092,6 @@ class Task extends WindowContainer<WindowContainer> {
return forAllTasks((t) -> { return t != this && t.isTaskAnimating(); });
}
/**
* @return {@code true} if changing app transition is running.
*/
@Override
boolean isChangingAppTransition() {
final ActivityRecord activity = getTopVisibleActivity();
return activity != null && getDisplayContent().mChangingApps.contains(activity);
}
@Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {

View File

@@ -669,8 +669,7 @@ class WallpaperController {
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps,
ArraySet<ActivityRecord> changingApps) {
void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) {
boolean adjust = false;
if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
@@ -682,15 +681,6 @@ class WallpaperController {
break;
}
}
if (!adjust) {
for (int i = changingApps.size() - 1; i >= 0; --i) {
final ActivityRecord activity = changingApps.valueAt(i);
if (activity.windowsCanBeWallpaperTarget()) {
adjust = true;
break;
}
}
}
}
if (adjust) {

View File

@@ -25,9 +25,13 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -64,6 +68,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.IWindowContainer;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
@@ -94,7 +99,7 @@ import java.util.function.Predicate;
* changes are made to this class.
*/
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable,
implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
BLASTSyncEngine.TransactionReadyListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
@@ -169,6 +174,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* Applied as part of the animation pass in "prepareSurfaces".
*/
protected final SurfaceAnimator mSurfaceAnimator;
final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
private final Point mTmpPos = new Point();
@@ -252,7 +258,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* where it represents the starting-state snapshot.
*/
WindowContainerThumbnail mThumbnail;
final Rect mTransitStartRect = new Rect();
final Point mTmpPoint = new Point();
protected final Rect mTmpRect = new Rect();
final Rect mTmpPrevBounds = new Rect();
@@ -277,6 +282,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
@Override
@@ -834,7 +840,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @return {@code true} if the container is in changing app transition.
*/
boolean isChangingAppTransition() {
return false;
return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this);
}
void sendAppVisibilityToClients() {
@@ -885,6 +891,31 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return false;
}
/**
* Called when the visibility of a child is asked to change. This is before visibility actually
* changes (eg. a transition animation might play out first).
*/
void onChildVisibilityRequested(boolean visible) {
// If we are changing visibility, then a snapshot isn't necessary and we are no-longer
// part of a change transition.
mSurfaceFreezer.unfreeze(getPendingTransaction());
if (mDisplayContent != null) {
mDisplayContent.mChangingContainers.remove(this);
}
WindowContainer parent = getParent();
if (parent != null) {
parent.onChildVisibilityRequested(visible);
}
}
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
proto.write(USER_ID, USER_NULL);
proto.write(TITLE, "WindowContainer");
proto.end(token);
}
/**
* Returns {@code true} if this container is focusable. Generally, if a parent is not focusable,
* this will not be focusable either.
@@ -1917,7 +1948,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback);
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
mSurfaceFreezer);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -1933,6 +1965,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mSurfaceAnimator.cancelAnimation();
}
@Override
public SurfaceControl getFreezeSnapshotTarget() {
return null;
}
@Override
public Builder makeAnimationLeash() {
return makeSurface().setContainerLayer();
@@ -2002,8 +2039,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (thumbnailAdapter != null) {
mThumbnail.startAnimation(
getPendingTransaction(), thumbnailAdapter, !isVisible());
mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION,
(type, anim) -> { });
}
}
} else {
@@ -2049,7 +2087,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
final RemoteAnimationController.RemoteAnimationRecord adapters =
controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
(isChanging ? mTransitStartRect : null));
(isChanging ? mSurfaceFreezer.mFreezeBounds : null));
resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
} else if (isChanging) {
final float durationScale = mWmService.getTransitionAnimationScaleLocked();
@@ -2057,14 +2095,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo,
durationScale, true /* isAppAnimation */, false /* isThumbnail */),
new WindowChangeAnimationSpec(mSurfaceFreezer.mFreezeBounds, mTmpRect,
displayInfo, durationScale, true /* isAppAnimation */,
false /* isThumbnail */),
getSurfaceAnimationRunner());
final AnimationAdapter thumbnailAdapter = mThumbnail != null
? new LocalAnimationAdapter(new WindowChangeAnimationSpec(mTransitStartRect,
mTmpRect, displayInfo, durationScale, true /* isAppAnimation */,
true /* isThumbnail */), getSurfaceAnimationRunner())
final AnimationAdapter thumbnailAdapter = mSurfaceFreezer.mSnapshot != null
? new LocalAnimationAdapter(new WindowChangeAnimationSpec(
mSurfaceFreezer.mFreezeBounds, mTmpRect, displayInfo, durationScale,
true /* isAppAnimation */, true /* isThumbnail */), getSurfaceAnimationRunner())
: null;
resultAdapters = new Pair<>(adapter, thumbnailAdapter);
mTransit = transit;
@@ -2182,6 +2221,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Override
public void onAnimationLeashLost(Transaction t) {
mLastLayer = -1;
mSurfaceFreezer.unfreeze(t);
reassignLayer(t);
}
@@ -2322,6 +2362,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mSurfaceControl = sc;
}
RemoteAnimationDefinition getRemoteAnimationDefinition() {
return null;
}
/** Cheap way of doing cast and instanceof. */
Task asTask() {
return null;

View File

@@ -255,7 +255,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
}
private void notifyTransitionStarting(ActivityRecord activity) {
final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>();
final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
mActivityMetricsLogger.notifyTransitionStarting(reasons);
}

View File

@@ -103,12 +103,12 @@ public class AppChangeTransitionTests extends WindowTestsBase {
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(1, mDisplayContent.mChangingApps.size());
assertEquals(1, mDisplayContent.mChangingContainers.size());
// Verify we are in a change transition, but without a snapshot.
// Though, the test will actually have crashed by now if a snapshot is attempted.
assertNull(mActivity.getThumbnail());
assertTrue(mActivity.isInChangeTransition());
assertNull(mTask.mSurfaceFreezer.mSnapshot);
assertTrue(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -121,14 +121,14 @@ public class AppChangeTransitionTests extends WindowTestsBase {
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(1, mDisplayContent.mChangingApps.size());
assertTrue(mActivity.isInChangeTransition());
assertEquals(1, mDisplayContent.mChangingContainers.size());
assertTrue(mTask.isInChangeTransition());
// Removing the app-token from the display should clean-up the
// the change leash.
mDisplayContent.removeAppToken(mActivity.token);
assertEquals(0, mDisplayContent.mChangingApps.size());
assertFalse(mActivity.isInChangeTransition());
assertEquals(0, mDisplayContent.mChangingContainers.size());
assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -152,8 +152,8 @@ public class AppChangeTransitionTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
// Make sure we're not waiting for a change animation (no leash)
assertFalse(mActivity.isInChangeTransition());
assertNull(mActivity.getThumbnail());
assertFalse(mTask.isInChangeTransition());
assertNull(mActivity.mSurfaceFreezer.mSnapshot);
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -165,13 +165,13 @@ public class AppChangeTransitionTests extends WindowTestsBase {
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(1, mDisplayContent.mChangingApps.size());
assertTrue(mActivity.isInChangeTransition());
assertEquals(1, mDisplayContent.mChangingContainers.size());
assertTrue(mTask.isInChangeTransition());
// Changing visibility should cancel the change transition and become closing
mActivity.setVisibility(false, false);
assertEquals(0, mDisplayContent.mChangingApps.size());
assertFalse(mActivity.isInChangeTransition());
assertEquals(0, mDisplayContent.mChangingContainers.size());
assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();

View File

@@ -247,7 +247,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testChange() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mChangingApps.add(win.mActivityRecord);
mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150),
@@ -290,7 +290,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
verify(mThumbnailFinishedCallback).onAnimationFinished(
eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
} finally {
mDisplayContent.mChangingApps.clear();
mDisplayContent.mChangingContainers.clear();
}
}