Merge "Hook up shell transit to metrics and interleave abort with merge" into sc-v2-dev

This commit is contained in:
Chris Li
2021-07-07 03:18:31 +00:00
committed by Android (Google) Code Review
8 changed files with 130 additions and 17 deletions

View File

@@ -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.");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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