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

Commit 5924f837 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Skip merging non-ready transition

Otherwise TransitionHandler#mergeAnimation will receive
null transition info and null transaction. And if it is
passed to remote, the remote may meet NPE on binder thread
and never report transition finish. Which will lead to
a permanent animating state and affect activity life cycle,
e.g. unable to be stopped.

Bug: 269607576
Test: ShellTransitionTests#testTransitionMergingOnFinish
Change-Id: I391ce78c6dcb7ed7bfef41d33d1e633dba3d680a
parent dd218cba
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -795,6 +795,12 @@ public class Transitions implements RemoteCallable<Transitions> {
                ++mergeIdx;
                continue;
            }
            if (mergeCandidate.mInfo == null) {
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition merge candidate"
                        + " %s is not ready yet", mergeCandidate.mToken);
                // The later transition should not be merged if the prior one is not ready.
                return;
            }
            if (mergeCandidate.mMerged) {
                throw new IllegalStateException("Can't merge a transition after not-merging"
                        + " a preceding one.");
+42 −0
Original line number Diff line number Diff line
@@ -563,6 +563,33 @@ public class ShellTransitionTests extends ShellTestCase {
        assertEquals(0, mDefaultHandler.activeCount());
    }


    @Test
    public void testTransitionMergingOnFinish() {
        final Transitions transitions = createTestTransitions();
        transitions.replaceDefaultHandlerForTest(mDefaultHandler);

        // The current transition.
        final IBinder transitToken1 = new Binder();
        requestStartTransition(transitions, transitToken1);
        onTransitionReady(transitions, transitToken1);

        // The next ready transition.
        final IBinder transitToken2 = new Binder();
        requestStartTransition(transitions, transitToken2);
        onTransitionReady(transitions, transitToken2);

        // The non-ready merge candidate.
        final IBinder transitTokenNotReady = new Binder();
        requestStartTransition(transitions, transitTokenNotReady);

        mDefaultHandler.setSimulateMerge(true);
        mDefaultHandler.mFinishes.get(0).onTransitionFinished(null /* wct */, null /* wctCB */);

        // Make sure that the non-ready transition is not merged.
        assertEquals(0, mDefaultHandler.mergeCount());
    }

    @Test
    public void testTransitionOrderMatchesCore() {
        Transitions transitions = createTestTransitions();
@@ -1036,6 +1063,21 @@ public class ShellTransitionTests extends ShellTestCase {
        }
    }

    private static void requestStartTransition(Transitions transitions, IBinder token) {
        transitions.requestStartTransition(token,
                new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
    }

    private static void onTransitionReady(Transitions transitions, IBinder token) {
        transitions.onTransitionReady(token, createTransitionInfo(),
                mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class));
    }

    private static TransitionInfo createTransitionInfo() {
        return new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
    }

    private static SurfaceControl createMockSurface(boolean valid) {
        SurfaceControl sc = mock(SurfaceControl.class);
        doReturn(valid).when(sc).isValid();