diff options
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 8ed92dfb39c6..1ca71af4d017 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<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."); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 71e047972ed6..5eba87dff5f7 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 faeb4ba36748..4355b3855bb7 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 8da5f08386c0..053c3bf07c89 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 8e18da14d3b3..ff9cc2cacd58 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<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; } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 3186aeac0df8..e480ff0c6a5b 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 8b8123e39985..913c5e56109b 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3331,6 +3331,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** + * 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. * If this is not in a sync group and gets parented into one, it will prepare itself. diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8ec6ed2908f8..ee8426eb9d25 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<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); } |