Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c35de32 authored by Evan Rosky's avatar Evan Rosky
Browse files

Add fallback transition player

Adds a fallback transition player which basically acts
a simple "forwarder". This is intended to be used during
startup or during periods of sysui being crashed.

Bug: 365884835
Test: existing tests since they automatically use this
Flag: com.android.window.flags.fallback_transition_player
Change-Id: I57bc45aa2924b402ac89e34a23fea997e498f0e7
parent 56139acd
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -474,3 +474,10 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "fallback_transition_player"
    namespace: "windowing_frontend"
    description: "Use a fallback transition-player while sysui is starting or crashed."
    bug: "365884835"
}
+93 −25
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_DISPLAY_LEVEL_TRANSITION;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.window.DesktopExperienceFlags.ENABLE_PARALLEL_CD_TRANSITIONS_DURING_RECENTS;

import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN;
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;

import android.annotation.ColorInt;
@@ -47,10 +48,12 @@ import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.ITransitionMetricsReporter;
import android.window.ITransitionPlayer;
import android.window.RemoteTransition;
import android.window.StartingWindowRemovalInfo;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
@@ -247,6 +250,10 @@ class TransitionController {
    TransitionController(ActivityTaskManagerService atm) {
        mAtm = atm;
        mRemotePlayer = new RemotePlayer(atm);
        if (Flags.fallbackTransitionPlayer()) {
            mTransitionPlayers.add(
                    new TransitionPlayerRecord(new FallbackPlayer(atm), null /* proc */));
        }
    }

    void setWindowManager(WindowManagerService wms) {
@@ -302,7 +309,7 @@ class TransitionController {
    Transition createTransition(@WindowManager.TransitionType int type,
            @WindowManager.TransitionFlags int flags) {
        if (mTransitionPlayers.isEmpty()) {
            throw new IllegalStateException("Shell Transitions not enabled");
            throw new IllegalStateException("Somehow creating a transition while flushing");
        }
        if (mCollectingTransition != null) {
            throw new IllegalStateException("Trying to directly start transition collection while "
@@ -322,8 +329,7 @@ class TransitionController {
            throw new IllegalStateException("Simultaneous transition collection not supported.");
        }
        if (mTransitionPlayers.isEmpty()) {
            // If sysui has been killed (by a test) or crashed, we can temporarily have no player
            // In this case, abort the transition.
            // This means transitions are being "flushed" due to switching players.
            transition.abort();
            return;
        }
@@ -338,15 +344,15 @@ class TransitionController {

    void registerTransitionPlayer(@Nullable ITransitionPlayer player,
            @Nullable WindowProcessController playerProc) {
        if (!mTransitionPlayers.isEmpty()) {
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Registering transition "
        if (!mTransitionPlayers.isEmpty() || Flags.fallbackTransitionPlayer()) {
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Registering transition "
                    + "player %s over %d other players", player.asBinder(),
                    mTransitionPlayers.size());
            // flush currently running transitions so that the new player doesn't get
            // intermediate state
            flushRunningTransitions();
        } else {
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Registering transition "
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Registering transition "
                    + "player %s ", player.asBinder());
        }
        mTransitionPlayers.add(new TransitionPlayerRecord(player, playerProc));
@@ -359,18 +365,18 @@ class TransitionController {
            if (mTransitionPlayers.get(idx).mPlayer.asBinder() == player.asBinder()) break;
        }
        if (idx < 0) {
            ProtoLog.w(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Attempt to unregister "
            ProtoLog.w(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Attempt to unregister "
                    + "transition player %s but it isn't registered", player.asBinder());
            return;
        }
        final boolean needsFlush = idx == (mTransitionPlayers.size() - 1);
        final TransitionPlayerRecord record = mTransitionPlayers.remove(idx);
        if (needsFlush) {
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Unregistering active "
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Unregistering active "
                    + "transition player %s at index=%d leaving %d in stack", player.asBinder(),
                    idx, mTransitionPlayers.size());
        } else {
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Unregistering transition "
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Unregistering transition "
                    + "player %s at index=%d leaving %d in stack", player.asBinder(), idx,
                    mTransitionPlayers.size());
        }
@@ -383,11 +389,12 @@ class TransitionController {
    }

    @Nullable ITransitionPlayer getTransitionPlayer() {
        return mTransitionPlayers.isEmpty() ? null : mTransitionPlayers.getLast().mPlayer;
        if (!Flags.fallbackTransitionPlayer() && mTransitionPlayers.isEmpty()) return null;
        return mTransitionPlayers.getLast().mPlayer;
    }

    boolean isShellTransitionsEnabled() {
        return !mTransitionPlayers.isEmpty();
        return !mTransitionPlayers.isEmpty() || Flags.fallbackTransitionPlayer();
    }

    /** @return {@code true} if using shell-transitions rotation instead of fixed-rotation. */
@@ -782,6 +789,9 @@ class TransitionController {
            @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
            @NonNull WindowContainer readyGroupRef, @NonNull ActionChain chain) {
        if (mTransitionPlayers.isEmpty()) {
            if (Flags.fallbackTransitionPlayer()) {
                throw new IllegalStateException("Somehow requesting transition while flushing");
            }
            return null;
        }
        Transition newTransition = null;
@@ -851,8 +861,7 @@ class TransitionController {
            return transition;
        }
        if (mTransitionPlayers.isEmpty() || transition.isAborted()) {
            // Apparently, some tests will kill(and restart) systemui, so there is a chance that
            // the player might be transiently null.
            // Either flushing or already flushed, so abort here.
            if (transition.isCollecting()) {
                transition.abort();
            }
@@ -891,7 +900,7 @@ class TransitionController {

            transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
            transition.mLogger.mRequest = request;
            mTransitionPlayers.getLast().mPlayer.requestStartTransition(
            getTransitionPlayer().requestStartTransition(
                    transition.getToken(), request);
            if (remoteTransition != null) {
                transition.setRemoteAnimationApp(remoteTransition.getAppThread());
@@ -908,7 +917,8 @@ class TransitionController {
     * @return the new transition if it was created for this request, `null` otherwise.
     */
    Transition requestCloseTransitionIfNeeded(@NonNull WindowContainer<?> wc) {
        if (mTransitionPlayers.isEmpty() || isCollecting()) return null;
        if (!Flags.fallbackTransitionPlayer() && mTransitionPlayers.isEmpty()) return null;
        if (isCollecting()) return null;
        if (!wc.isVisibleRequested()) return null;
        return requestStartTransition(createTransition(TRANSIT_CLOSE, 0 /* flags */), wc.asTask(),
                null /* remoteTransition */, null /* displayChange */);
@@ -1174,7 +1184,7 @@ 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(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
                    "Moving #%d from collecting to waiting.", mCollectingTransition.getSyncId());
            mWaitingTransitions.add(mCollectingTransition);
            mCollectingTransition = null;
@@ -1202,7 +1212,7 @@ class TransitionController {
            // the incoming transition's changes stale (e.g. display change transition had not
            // been formally started). So send this transition to Shell before applying any changes
            // to report it as a no-op.
            ProtoLog.w(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
            ProtoLog.w(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
                    "Formerly queued #%d force-reported as no-op", queued.mTransition.getSyncId());
            queued.mTransition.playNow();
        } else {
@@ -1566,7 +1576,7 @@ class TransitionController {
        }

        mQueuedTransitions.add(queuedTransition);
        ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
        ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
                "Queueing transition: %s", transit);
    }

@@ -1582,7 +1592,7 @@ class TransitionController {
                // Check if we can run in parallel here.
                if (canStartCollectingNow(transit)) {
                    // start running in parallel.
                    ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from"
                    ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from"
                            + " collecting to waiting.", mCollectingTransition.getSyncId());
                    mWaitingTransitions.add(mCollectingTransition);
                    mCollectingTransition = null;
@@ -1610,6 +1620,9 @@ class TransitionController {
    @Nullable
    Transition createAndStartCollecting(int type) {
        if (mTransitionPlayers.isEmpty()) {
            if (Flags.fallbackTransitionPlayer()) {
                throw new IllegalStateException("Can't create transition while flushing");
            }
            return null;
        }
        if (!mQueuedTransitions.isEmpty()) {
@@ -1621,7 +1634,7 @@ class TransitionController {
                // Check if we can run in parallel here.
                if (canStartCollectingNow(null /* transit */)) {
                    // create and collect in parallel.
                    ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from"
                    ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from"
                            + " collecting to waiting.", mCollectingTransition.getSyncId());
                    mWaitingTransitions.add(mCollectingTransition);
                    mCollectingTransition = null;
@@ -1645,7 +1658,7 @@ class TransitionController {
            // Just add to queue since we already have a queue.
            mQueuedTransitions.add(new QueuedTransition(syncGroup,
                    (deferred) -> applySync.accept(true /* deferred */)));
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
                    "Queueing legacy sync-set: %s", syncGroup.mSyncId);
            return;
        }
@@ -1842,10 +1855,10 @@ class TransitionController {
        }

        void logOnSend() {
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnSendLog());
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnSendLog());
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
                    "    startWCT=%s", mStartWCT);
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "    info=%s",
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "    info=%s",
                    mInfo.toString("    " /* prefix */));
        }

@@ -1871,7 +1884,7 @@ class TransitionController {
        }

        void logOnFinish() {
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnFinishLog());
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnFinishLog());
        }
    }

@@ -1935,4 +1948,59 @@ class TransitionController {
            }
        }
    }

    /** Fallback player used during time periods where a real player is not registered. */
    private static class FallbackPlayer implements ITransitionPlayer {
        final ActivityTaskManagerService mAtm;

        FallbackPlayer(ActivityTaskManagerService atm) {
            mAtm = atm;
        }

        @Override
        public IBinder asBinder() {
            return null;
        }

        @Override
        public void onTransitionReady(IBinder transitionToken, TransitionInfo info,
                SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
                throws RemoteException {
            ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Playing [FALLBACK] "
                    + "animation for #%d @%d", info.getDebugId(), info.getTrack());
            t.apply();
            finishT.apply();
            mAtm.mH.post(() -> {
                try {
                    ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Transition animation [FALLBACK] "
                            + "finished #%d @%d", info.getDebugId(), info.getTrack());
                    mAtm.getWindowOrganizerController().finishTransition(transitionToken,
                            null /* wct */);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error finishing transition from fallback", e);
                }
            });
        }

        @Override
        public void requestStartTransition(IBinder transitionToken, TransitionRequestInfo request)
                throws RemoteException {
            mAtm.mH.post(() -> {
                try {
                    ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
                            "Transition requested [FALLBACK] (#%d): %s %s", request.getDebugId(),
                            transitionToken, request);
                    mAtm.getWindowOrganizerController().startTransition(transitionToken,
                            null /* wct */);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error starting transition from fallback", e);
                }
            });
        }

        @Override
        public void removeStartingWindow(StartingWindowRemovalInfo removalInfo)
                throws RemoteException {
        }
    }
}