Merge "Hook up shell transit to metrics and interleave abort with merge" into sc-v2-dev
This commit is contained in:
@@ -97,12 +97,13 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
private float mTransitionAnimationScaleSetting = 1.0f;
|
||||
|
||||
private static final class ActiveTransition {
|
||||
IBinder mToken = null;
|
||||
TransitionHandler mHandler = null;
|
||||
boolean mMerged = false;
|
||||
TransitionInfo mInfo = null;
|
||||
SurfaceControl.Transaction mStartT = null;
|
||||
SurfaceControl.Transaction mFinishT = null;
|
||||
IBinder mToken;
|
||||
TransitionHandler mHandler;
|
||||
boolean mMerged;
|
||||
boolean mAborted;
|
||||
TransitionInfo mInfo;
|
||||
SurfaceControl.Transaction mStartT;
|
||||
SurfaceControl.Transaction mFinishT;
|
||||
}
|
||||
|
||||
/** Keeps track of currently playing transitions in the order of receipt. */
|
||||
@@ -422,17 +423,19 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
|
||||
/** Special version of finish just for dealing with no-op/invalid transitions. */
|
||||
private void onAbort(IBinder transition) {
|
||||
final int activeIdx = findActiveTransition(transition);
|
||||
if (activeIdx < 0) return;
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||
"Transition animation aborted due to no-op, notifying core %s", transition);
|
||||
mActiveTransitions.remove(activeIdx);
|
||||
mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
|
||||
onFinish(transition, null /* wct */, null /* wctCB */, true /* abort */);
|
||||
}
|
||||
|
||||
private void onFinish(IBinder transition,
|
||||
@Nullable WindowContainerTransaction wct,
|
||||
@Nullable WindowContainerTransactionCallback wctCB) {
|
||||
onFinish(transition, wct, wctCB, false /* abort */);
|
||||
}
|
||||
|
||||
private void onFinish(IBinder transition,
|
||||
@Nullable WindowContainerTransaction wct,
|
||||
@Nullable WindowContainerTransactionCallback wctCB,
|
||||
boolean abort) {
|
||||
int activeIdx = findActiveTransition(transition);
|
||||
if (activeIdx < 0) {
|
||||
Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
|
||||
@@ -440,28 +443,37 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
return;
|
||||
} else if (activeIdx > 0) {
|
||||
// This transition was merged.
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
|
||||
transition);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged (abort=%b:"
|
||||
+ " %s", abort, transition);
|
||||
final ActiveTransition active = mActiveTransitions.get(activeIdx);
|
||||
active.mMerged = true;
|
||||
active.mAborted = abort;
|
||||
if (active.mHandler != null) {
|
||||
active.mHandler.onTransitionMerged(active.mToken);
|
||||
}
|
||||
return;
|
||||
}
|
||||
mActiveTransitions.get(activeIdx).mAborted = abort;
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||
"Transition animation finished, notifying core %s", transition);
|
||||
"Transition animation finished (abort=%b), notifying core %s", abort, transition);
|
||||
// Merge all relevant transactions together
|
||||
SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
|
||||
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
|
||||
final ActiveTransition toMerge = mActiveTransitions.get(iA);
|
||||
if (!toMerge.mMerged) break;
|
||||
// aborted transitions have no start/finish transactions
|
||||
if (mActiveTransitions.get(iA).mStartT == null) break;
|
||||
if (fullFinish == null) {
|
||||
fullFinish = new SurfaceControl.Transaction();
|
||||
}
|
||||
// Include start. It will be a no-op if it was already applied. Otherwise, we need it
|
||||
// to maintain consistent state.
|
||||
fullFinish.merge(mActiveTransitions.get(iA).mStartT);
|
||||
fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
|
||||
}
|
||||
fullFinish.apply();
|
||||
if (fullFinish != null) {
|
||||
fullFinish.apply();
|
||||
}
|
||||
// Now perform all the finishes.
|
||||
mActiveTransitions.remove(activeIdx);
|
||||
mOrganizer.finishTransition(transition, wct, wctCB);
|
||||
@@ -470,6 +482,12 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
ActiveTransition merged = mActiveTransitions.remove(activeIdx);
|
||||
mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
|
||||
}
|
||||
// sift through aborted transitions
|
||||
while (mActiveTransitions.size() > activeIdx
|
||||
&& mActiveTransitions.get(activeIdx).mAborted) {
|
||||
ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
|
||||
mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
|
||||
}
|
||||
if (mActiveTransitions.size() <= activeIdx) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
|
||||
+ "finished");
|
||||
@@ -500,6 +518,12 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
int mergeIdx = activeIdx + 1;
|
||||
while (mergeIdx < mActiveTransitions.size()) {
|
||||
ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
|
||||
if (mergeCandidate.mAborted) {
|
||||
// transition was aborted, so we can skip for now (still leave it in the list
|
||||
// so that it gets cleaned-up in the right order).
|
||||
++mergeIdx;
|
||||
continue;
|
||||
}
|
||||
if (mergeCandidate.mMerged) {
|
||||
throw new IllegalStateException("Can't merge a transition after not-merging"
|
||||
+ " a preceding one.");
|
||||
|
||||
@@ -665,6 +665,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
boolean allDrawn;
|
||||
private boolean mLastAllDrawn;
|
||||
|
||||
/**
|
||||
* Solely for reporting to ActivityMetricsLogger. Just tracks whether, the last time this
|
||||
* Actiivty was part of a syncset, all windows were ready by the time the sync was ready (vs.
|
||||
* only the top-occluding ones). The assumption here is if some were not ready, they were
|
||||
* covered with starting-window/splash-screen.
|
||||
*/
|
||||
boolean mLastAllReadyAtSync = false;
|
||||
|
||||
private boolean mLastContainsShowWhenLockedWindow;
|
||||
private boolean mLastContainsDismissKeyguardWindow;
|
||||
private boolean mLastContainsTurnScreenOnWindow;
|
||||
@@ -9018,6 +9026,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void finishSync(Transaction outMergedTransaction, boolean cancel) {
|
||||
// This override is just for getting metrics. allFinished needs to be checked before
|
||||
// finish because finish resets all the states.
|
||||
mLastAllReadyAtSync = allSyncFinished();
|
||||
super.finishSync(outMergedTransaction, cancel);
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
private final ActivityTaskManagerService mAtmService;
|
||||
private WindowProcessController mCallerApp;
|
||||
|
||||
@@ -166,6 +166,10 @@ class BLASTSyncEngine {
|
||||
setReady(id, true);
|
||||
}
|
||||
|
||||
boolean isReady(int id) {
|
||||
return mActiveSyncs.get(id).mReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts the sync (ie. it doesn't wait for ready or anything to finish)
|
||||
*/
|
||||
|
||||
@@ -3100,7 +3100,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
|
||||
}
|
||||
mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
|
||||
mAppTransition.dumpDebug(proto, APP_TRANSITION);
|
||||
if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
|
||||
mAtmService.getTransitionController().dumpDebugLegacy(proto, APP_TRANSITION);
|
||||
} else {
|
||||
mAppTransition.dumpDebug(proto, APP_TRANSITION);
|
||||
}
|
||||
if (mFocusedApp != null) {
|
||||
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
|
||||
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
|
||||
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
|
||||
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
@@ -395,6 +398,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
|
||||
handleNonAppWindowsInTransition(displayId, mType, mFlags);
|
||||
|
||||
reportStartReasonsToLogger();
|
||||
|
||||
// Manually show any activities that are visibleRequested. This is needed to properly
|
||||
// support simultaneous animation queueing/merging. Specifically, if transition A makes
|
||||
// an activity invisible, it's finishTransaction (which is applied *after* the animation)
|
||||
@@ -575,6 +580,23 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
}
|
||||
}
|
||||
|
||||
private void reportStartReasonsToLogger() {
|
||||
// Record transition start in metrics logger. We just assume everything is "DRAWN"
|
||||
// at this point since splash-screen is a presentation (shell) detail.
|
||||
ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
|
||||
for (int i = mParticipants.size() - 1; i >= 0; --i) {
|
||||
ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
|
||||
if (r == null) continue;
|
||||
// At this point, r is "ready", but if it's not "ALL ready" then it is probably only
|
||||
// ready due to starting-window.
|
||||
reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
|
||||
&& !r.mLastAllReadyAtSync)
|
||||
? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
|
||||
}
|
||||
mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
|
||||
reasons);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(64);
|
||||
@@ -953,6 +975,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
return out;
|
||||
}
|
||||
|
||||
boolean getLegacyIsReady() {
|
||||
return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
|
||||
}
|
||||
|
||||
static Transition fromBinder(IBinder binder) {
|
||||
return (Transition) binder;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.WindowManager;
|
||||
import android.window.IRemoteTransition;
|
||||
import android.window.ITransitionPlayer;
|
||||
@@ -51,6 +52,11 @@ import java.util.ArrayList;
|
||||
class TransitionController {
|
||||
private static final String TAG = "TransitionController";
|
||||
|
||||
// State constants to line-up with legacy app-transition proto expectations.
|
||||
private static final int LEGACY_STATE_IDLE = 0;
|
||||
private static final int LEGACY_STATE_READY = 1;
|
||||
private static final int LEGACY_STATE_RUNNING = 2;
|
||||
|
||||
private ITransitionPlayer mTransitionPlayer;
|
||||
final ActivityTaskManagerService mAtm;
|
||||
|
||||
@@ -362,6 +368,18 @@ class TransitionController {
|
||||
}
|
||||
}
|
||||
|
||||
void dumpDebugLegacy(ProtoOutputStream proto, long fieldId) {
|
||||
final long token = proto.start(fieldId);
|
||||
int state = LEGACY_STATE_IDLE;
|
||||
if (!mPlayingTransitions.isEmpty()) {
|
||||
state = LEGACY_STATE_RUNNING;
|
||||
} else if (mCollectingTransition != null && mCollectingTransition.getLegacyIsReady()) {
|
||||
state = LEGACY_STATE_READY;
|
||||
}
|
||||
proto.write(AppTransitionProto.APP_TRANSITION_STATE, state);
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
class Lock {
|
||||
private int mTransitionWaiters = 0;
|
||||
void runWhenIdle(long timeout, Runnable r) {
|
||||
|
||||
@@ -3330,6 +3330,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special helper to check that all windows are synced (vs just top one). This is only
|
||||
* used to differentiate between starting-window vs full-drawn in activity-metrics reporting.
|
||||
*/
|
||||
boolean allSyncFinished() {
|
||||
if (!isVisibleRequested()) return true;
|
||||
if (mSyncState != SYNC_STATE_READY) return false;
|
||||
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
||||
final WindowContainer child = mChildren.get(i);
|
||||
if (!child.allSyncFinished()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during reparent to handle sync state when the hierarchy changes.
|
||||
* If this is in a sync group and gets reparented out, it will cancel syncing.
|
||||
|
||||
@@ -5984,6 +5984,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
|
||||
}
|
||||
|
||||
if (mActivityRecord != null
|
||||
&& mWmService.mAtmService.getTransitionController().isShellTransitionsEnabled()
|
||||
&& mAttrs.type == TYPE_APPLICATION_STARTING) {
|
||||
mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
|
||||
.notifyStartingWindowDrawn(mActivityRecord);
|
||||
}
|
||||
|
||||
if (postDrawTransaction != null) {
|
||||
mSyncTransaction.merge(postDrawTransaction);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user