Allow Recents transitions to collect in parallel
We can't fully support parallel transition collection,
but we can for specific cases (like recents). This CL
adds some hard-coded checks for recents specifically and
allows it to collect in parallel with activity-only
transitions. This is basically way of assigning "priority"
to recents since it is a very user-forward operation
and an integral part of how users percieve device
performance.
Bug: 277838915
Bug: 264536014
Test: In the running task, launch a slow-to-draw activity,
then swipe to recents or home. It should not wait
anymore.
Change-Id: Ie09e31bf290847b108578b688f57360536fe7f4e
This commit is contained in:
@@ -403,8 +403,11 @@ public class ActivityOptions extends ComponentOptions {
|
||||
private static final String KEY_SPLASH_SCREEN_STYLE =
|
||||
"android.activity.splashScreenStyle";
|
||||
|
||||
/** See {@link #setTransientLaunch()}. */
|
||||
private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
|
||||
/**
|
||||
* See {@link #setTransientLaunch()}.
|
||||
* @hide
|
||||
*/
|
||||
public static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
|
||||
|
||||
/** see {@link #makeLaunchIntoPip(PictureInPictureParams)}. */
|
||||
private static final String KEY_LAUNCH_INTO_PIP_PARAMS =
|
||||
|
||||
@@ -1183,6 +1183,12 @@
|
||||
"group": "WM_DEBUG_APP_TRANSITIONS",
|
||||
"at": "com\/android\/server\/wm\/AppTransitionController.java"
|
||||
},
|
||||
"-1005167552": {
|
||||
"message": "Playing #%d in parallel on track #%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
|
||||
"at": "com\/android\/server\/wm\/TransitionController.java"
|
||||
},
|
||||
"-1003678883": {
|
||||
"message": "Cleaning splash screen token=%s",
|
||||
"level": "VERBOSE",
|
||||
@@ -1447,6 +1453,12 @@
|
||||
"group": "WM_DEBUG_TASKS",
|
||||
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
|
||||
},
|
||||
"-774908272": {
|
||||
"message": "Marking #%d animation as SYNC.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
|
||||
"at": "com\/android\/server\/wm\/TransitionController.java"
|
||||
},
|
||||
"-771177730": {
|
||||
"message": "Removing focused app token:%s displayId=%d",
|
||||
"level": "VERBOSE",
|
||||
@@ -1495,12 +1507,6 @@
|
||||
"group": "WM_DEBUG_CONFIGURATION",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"-741766551": {
|
||||
"message": "Content Recording: Ignoring session on invalid virtual display",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/ContentRecordingController.java"
|
||||
},
|
||||
"-732715767": {
|
||||
"message": "Unable to retrieve window container to start recording for display %d",
|
||||
"level": "VERBOSE",
|
||||
@@ -2017,6 +2023,12 @@
|
||||
"group": "WM_DEBUG_WALLPAPER",
|
||||
"at": "com\/android\/server\/wm\/WallpaperController.java"
|
||||
},
|
||||
"-266707683": {
|
||||
"message": "Moving #%d from collecting to waiting.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
|
||||
"at": "com\/android\/server\/wm\/TransitionController.java"
|
||||
},
|
||||
"-262984451": {
|
||||
"message": "Relaunch failed %s",
|
||||
"level": "INFO",
|
||||
@@ -2089,6 +2101,12 @@
|
||||
"group": "WM_DEBUG_WINDOW_MOVEMENT",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-186693085": {
|
||||
"message": "Starting a Recents transition which can be parallel.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
|
||||
"at": "com\/android\/server\/wm\/Transition.java"
|
||||
},
|
||||
"-182877285": {
|
||||
"message": "Wallpaper layer changed: assigning layers + relayout",
|
||||
"level": "VERBOSE",
|
||||
|
||||
@@ -55,6 +55,7 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
|
||||
import static android.window.TransitionInfo.FLAG_TASK_LAUNCHING_BEHIND;
|
||||
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
|
||||
import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
|
||||
|
||||
import static com.android.server.wm.ActivityRecord.State.RESUMED;
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
|
||||
@@ -65,12 +66,14 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.IApplicationThread;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IRemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
@@ -85,6 +88,7 @@ import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.ScreenCapture;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.graphics.ColorUtils;
|
||||
@@ -185,7 +189,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
|
||||
final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
|
||||
|
||||
/** The final animation targets derived from participants after promotion. */
|
||||
private ArrayList<ChangeInfo> mTargets;
|
||||
ArrayList<ChangeInfo> mTargets;
|
||||
|
||||
/** The displays that this transition is running on. */
|
||||
private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
|
||||
@@ -271,9 +275,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
|
||||
/** Any 2 transitions of this type can run in parallel with each other. Used for testing. */
|
||||
static final int PARALLEL_TYPE_MUTUAL = 1;
|
||||
|
||||
/** This is a recents transition. */
|
||||
static final int PARALLEL_TYPE_RECENTS = 2;
|
||||
|
||||
|
||||
@IntDef(prefix = { "PARALLEL_TYPE_" }, value = {
|
||||
PARALLEL_TYPE_NONE,
|
||||
PARALLEL_TYPE_MUTUAL
|
||||
PARALLEL_TYPE_MUTUAL,
|
||||
PARALLEL_TYPE_RECENTS
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface ParallelType {}
|
||||
@@ -328,6 +337,21 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
|
||||
mFlags |= flag;
|
||||
}
|
||||
|
||||
void calcParallelCollectType(WindowContainerTransaction wct) {
|
||||
for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
|
||||
final WindowContainerTransaction.HierarchyOp hop = wct.getHierarchyOps().get(i);
|
||||
if (hop.getType() != HIERARCHY_OP_TYPE_PENDING_INTENT) continue;
|
||||
final Bundle b = hop.getLaunchOptions();
|
||||
if (b == null || b.isEmpty()) continue;
|
||||
final boolean transientLaunch = b.getBoolean(ActivityOptions.KEY_TRANSIENT_LAUNCH);
|
||||
if (transientLaunch) {
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
|
||||
"Starting a Recents transition which can be parallel.");
|
||||
mParallelCollectType = PARALLEL_TYPE_RECENTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Records an activity as transient-launch. This activity must be already collected. */
|
||||
void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelow) {
|
||||
if (mTransientLaunches == null) {
|
||||
@@ -380,6 +404,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasTransientLaunch() {
|
||||
return mTransientLaunches != null && !mTransientLaunches.isEmpty();
|
||||
}
|
||||
|
||||
boolean isTransientLaunch(@NonNull ActivityRecord activity) {
|
||||
return mTransientLaunches != null && mTransientLaunches.containsKey(activity);
|
||||
}
|
||||
|
||||
@@ -879,6 +879,8 @@ class TransitionController {
|
||||
// If it's a legacy sync, then it needs to wait until there is no collecting transition.
|
||||
if (queued.mTransition == null) return;
|
||||
if (!canStartCollectingNow(queued.mTransition)) return;
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from collecting"
|
||||
+ " to waiting.", mCollectingTransition.getSyncId());
|
||||
mWaitingTransitions.add(mCollectingTransition);
|
||||
mCollectingTransition = null;
|
||||
} else if (mSyncEngine.hasActiveSync()) {
|
||||
@@ -930,10 +932,37 @@ class TransitionController {
|
||||
* in {@link #getIsIndependent} later.
|
||||
*/
|
||||
boolean getCanBeIndependent(Transition collecting, Transition queued) {
|
||||
// For tests
|
||||
if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL
|
||||
&& collecting.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) {
|
||||
return true;
|
||||
}
|
||||
// For recents
|
||||
if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
|
||||
if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
|
||||
// Must serialize with itself.
|
||||
return false;
|
||||
}
|
||||
// allow this if `collecting` only has activities
|
||||
for (int i = 0; i < collecting.mParticipants.size(); ++i) {
|
||||
final WindowContainer wc = collecting.mParticipants.valueAt(i);
|
||||
final ActivityRecord ar = wc.asActivityRecord();
|
||||
if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) {
|
||||
// Is task or above, so can't be independent
|
||||
return false;
|
||||
}
|
||||
if (ar != null && ar.isActivityTypeHomeOrRecents()) {
|
||||
// It's a recents or home type, so it conflicts.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
|
||||
// We can collect simultaneously with recents if it is populated. This is because
|
||||
// we know that recents will not collect/trampoline any more stuff. If anything in the
|
||||
// queued transition overlaps, it will end up just waiting in sync-queue anyways.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -942,11 +971,47 @@ class TransitionController {
|
||||
* `running` is playing based on its current state.
|
||||
*/
|
||||
static boolean getIsIndependent(Transition running, Transition incoming) {
|
||||
// For tests
|
||||
if (running.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL
|
||||
&& incoming.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// For now there's only one mutually-independent pair: an all activity-level transition and
|
||||
// a transient-launch where none of the activities are part of the transient-launch task,
|
||||
// so the following logic is hard-coded specifically for this.
|
||||
// Also, we currently restrict valid transient-launches to just recents.
|
||||
final Transition recents;
|
||||
final Transition other;
|
||||
if (running.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS
|
||||
&& running.hasTransientLaunch()) {
|
||||
if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
|
||||
// Recents can't be independent from itself.
|
||||
return false;
|
||||
}
|
||||
recents = running;
|
||||
other = incoming;
|
||||
} else if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS
|
||||
&& incoming.hasTransientLaunch()) {
|
||||
recents = incoming;
|
||||
other = running;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// Check against *targets* because that is the post-promotion set of containers that are
|
||||
// actually animating.
|
||||
for (int i = 0; i < other.mTargets.size(); ++i) {
|
||||
final WindowContainer wc = other.mTargets.get(i).mContainer;
|
||||
final ActivityRecord ar = wc.asActivityRecord();
|
||||
if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) {
|
||||
// Is task or above, so for now don't let them be independent.
|
||||
return false;
|
||||
}
|
||||
if (ar != null && recents.isTransientLaunch(ar)) {
|
||||
// Change overlaps with recents, so serialize.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void assignTrack(Transition transition, TransitionInfo info) {
|
||||
@@ -970,9 +1035,15 @@ class TransitionController {
|
||||
if (track < 0) {
|
||||
// Didn't overlap with anything, so give it its own track
|
||||
track = mTrackCount;
|
||||
if (track > 0) {
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Playing #%d in parallel on "
|
||||
+ "track #%d", transition.getSyncId(), track);
|
||||
}
|
||||
}
|
||||
if (sync) {
|
||||
info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC);
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Marking #%d animation as SYNC.",
|
||||
transition.getSyncId());
|
||||
}
|
||||
transition.mAnimationTrack = track;
|
||||
info.setTrack(track);
|
||||
@@ -1145,6 +1216,8 @@ class TransitionController {
|
||||
// Check if we can run in parallel here.
|
||||
if (canStartCollectingNow(transit)) {
|
||||
// start running in parallel.
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from"
|
||||
+ " collecting to waiting.", mCollectingTransition.getSyncId());
|
||||
mWaitingTransitions.add(mCollectingTransition);
|
||||
mCollectingTransition = null;
|
||||
moveToCollecting(transit);
|
||||
|
||||
@@ -299,6 +299,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
final boolean needsSetReady = t != null;
|
||||
final Transition nextTransition = new Transition(type, 0 /* flags */,
|
||||
mTransitionController, mService.mWindowManager.mSyncEngine);
|
||||
nextTransition.calcParallelCollectType(wct);
|
||||
mTransitionController.startCollectOrQueue(nextTransition,
|
||||
(deferred) -> {
|
||||
nextTransition.start();
|
||||
|
||||
Reference in New Issue
Block a user