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

Commit cbc82b9d authored by Evan Rosky's avatar Evan Rosky
Browse files

Try to recover when receiving unexpected transitionReady

Basically, since everywhere else in the system tends to
propagate bad states, we have to also do the same. This
basically tries to just handle a transition even if it
wasn't told about it beforehand.

Bug: 278781290
Test: This is replacing a crash, so N/A
Change-Id: Ib367976ae21b97fe726abd9edd399cc18da0742d
parent 66c0d06e
Loading
Loading
Loading
Loading
+35 −11
Original line number Diff line number Diff line
@@ -654,12 +654,27 @@ public class Transitions implements RemoteCallable<Transitions>,
        info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
                info.getDebugId(), transitionToken, info);
        final int activeIdx = findByToken(mPendingTransitions, transitionToken);
        int activeIdx = findByToken(mPendingTransitions, transitionToken);
        if (activeIdx < 0) {
            throw new IllegalStateException("Got transitionReady for non-pending transition "
            final ActiveTransition existing = getKnownTransition(transitionToken);
            if (existing != null) {
                Log.e(TAG, "Got duplicate transitionReady for " + transitionToken);
                // The transition is already somewhere else in the pipeline, so just return here.
                t.apply();
                existing.mFinishT.merge(finishT);
                return;
            }
            // This usually means the system is in a bad state and may not recover; however,
            // there's an incentive to propagate bad states rather than crash, so we're kinda
            // required to do the same thing I guess.
            Log.wtf(TAG, "Got transitionReady for non-pending transition "
                    + transitionToken + ". expecting one of "
                    + Arrays.toString(mPendingTransitions.stream().map(
                            activeTransition -> activeTransition.mToken).toArray()));
            final ActiveTransition fallback = new ActiveTransition();
            fallback.mToken = transitionToken;
            mPendingTransitions.add(fallback);
            activeIdx = mPendingTransitions.size() - 1;
        }
        // Move from pending to ready
        final ActiveTransition active = mPendingTransitions.remove(activeIdx);
@@ -1050,34 +1065,43 @@ public class Transitions implements RemoteCallable<Transitions>,
        processReadyQueue(track);
    }

    private boolean isTransitionKnown(IBinder token) {
    /**
     * Checks to see if the transition specified by `token` is already known. If so, it will be
     * returned.
     */
    @Nullable
    private ActiveTransition getKnownTransition(IBinder token) {
        for (int i = 0; i < mPendingTransitions.size(); ++i) {
            if (mPendingTransitions.get(i).mToken == token) return true;
            final ActiveTransition active = mPendingTransitions.get(i);
            if (active.mToken == token) return active;
        }
        for (int i = 0; i < mReadyDuringSync.size(); ++i) {
            if (mReadyDuringSync.get(i).mToken == token) return true;
            final ActiveTransition active = mReadyDuringSync.get(i);
            if (active.mToken == token) return active;
        }
        for (int t = 0; t < mTracks.size(); ++t) {
            final Track tr = mTracks.get(t);
            for (int i = 0; i < tr.mReadyTransitions.size(); ++i) {
                if (tr.mReadyTransitions.get(i).mToken == token) return true;
                final ActiveTransition active = tr.mReadyTransitions.get(i);
                if (active.mToken == token) return active;
            }
            final ActiveTransition active = tr.mActiveTransition;
            if (active == null) continue;
            if (active.mToken == token) return true;
            if (active.mToken == token) return active;
            if (active.mMerged == null) continue;
            for (int m = 0; m < active.mMerged.size(); ++m) {
                if (active.mMerged.get(m).mToken == token) return true;
                final ActiveTransition merged = active.mMerged.get(m);
                if (merged.mToken == token) return merged;
            }
        }
        return false;
        return null;
    }

    void requestStartTransition(@NonNull IBinder transitionToken,
            @Nullable TransitionRequestInfo request) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
                request.getDebugId(), transitionToken, request);
        if (isTransitionKnown(transitionToken)) {
        if (getKnownTransition(transitionToken) != null) {
            throw new RuntimeException("Transition already started " + transitionToken);
        }
        final ActiveTransition active = new ActiveTransition();
@@ -1161,7 +1185,7 @@ public class Transitions implements RemoteCallable<Transitions>,
     */
    private void finishForSync(ActiveTransition reason,
            int trackIdx, @Nullable ActiveTransition forceFinish) {
        if (!isTransitionKnown(reason.mToken)) {
        if (getKnownTransition(reason.mToken) == null) {
            Log.d(TAG, "finishForSleep: already played sync transition " + reason);
            return;
        }