From dfe1fc5554c21ec9d489c038dd1eae625dbc9213 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Thu, 10 Jun 2021 15:02:21 -0700 Subject: [PATCH] Hook up shell transit to metrics and interleave abort with merge Hooks up shell transitions to the activitymetricslogger and proto state for app-transitions. Also fixes interleaving of transition abort and merge. Bug: 183993924 Test: atest ActivityMetricsLoggerTests Change-Id: I0ecee0cc23093d00a83e0c8b0ce2c0b9a8ce7aff --- .../wm/shell/transition/Transitions.java | 56 +++++++++++++------ .../com/android/server/wm/ActivityRecord.java | 16 ++++++ .../android/server/wm/BLASTSyncEngine.java | 4 ++ .../com/android/server/wm/DisplayContent.java | 6 +- .../com/android/server/wm/Transition.java | 26 +++++++++ .../server/wm/TransitionController.java | 18 ++++++ .../android/server/wm/WindowContainer.java | 14 +++++ .../com/android/server/wm/WindowState.java | 7 +++ 8 files changed, 130 insertions(+), 17 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 8ed92dfb39c67..1ca71af4d0179 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -97,12 +97,13 @@ public class Transitions implements RemoteCallable { 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 { /** 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 { 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 { 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 { 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."); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 71e047972ed61..5eba87dff5f73 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -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; diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index faeb4ba367486..4355b3855bb73 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -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) */ diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8da5f08386c08..053c3bf07c891 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -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); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 8e18da14d3b31..ff9cc2cacd589 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -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 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; } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 3186aeac0df84..e480ff0c6a5b2 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -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) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 8b8123e399856..913c5e56109b2 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3330,6 +3330,20 @@ class 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. diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8ec6ed2908f86..ee8426eb9d25c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5984,6 +5984,13 @@ class WindowState extends WindowContainer 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); }