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

Commit d8fa3054 authored by Marzia Favaro's avatar Marzia Favaro
Browse files

TransitionDispatchState for TaskViewTransitions

Bug: 402454136
Test: TaskViewTransitionAnimationTest
Flag: com.android.window.flags.enable_handlers_debugging_mode
Change-Id: I6fe7b455d982fc39b658cfc4156e2496afd99841
parent 6e52e22a
Loading
Loading
Loading
Loading
+31 −3
Original line number Diff line number Diff line
@@ -26,8 +26,12 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;

import static com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS;
import static com.android.window.flags.Flags.enableHandlersDebuggingMode;
import static com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY;
import static com.android.wm.shell.transition.TransitionDispatchState.CAPTURED_CHANGE_IN_WRONG_TRANSITION;
import static com.android.wm.shell.transition.TransitionDispatchState.CAPTURED_UNRELATED_CHANGE;
import static com.android.wm.shell.transition.TransitionDispatchState.LOST_RELEVANT_CHANGE;
import static com.android.wm.shell.transition.Transitions.transitTypeToString;

import android.annotation.NonNull;
@@ -59,6 +63,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.transition.TransitionDispatchState;
import com.android.wm.shell.transition.Transitions;

import java.util.ArrayList;
@@ -707,11 +712,26 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
                                  @NonNull SurfaceControl.Transaction startTransaction,
                                  @NonNull SurfaceControl.Transaction finishTransaction,
                                  @NonNull Transitions.TransitionFinishCallback finishCallback) {
        if (!Flags.taskViewTransitionsRefactor()) {
            return startAnimationLegacy(transition, info, startTransaction, finishTransaction,
                    finishCallback);
        return startAnimation(transition, info, TransitionDispatchState.getDummyInstance(),
                startTransaction, finishTransaction, finishCallback);
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition,
                                  @Nullable TransitionInfo transitionInfo,
                                  @NonNull TransitionDispatchState dispatchState,
                                  @NonNull SurfaceControl.Transaction startTransaction,
                                  @NonNull SurfaceControl.Transaction finishTransaction,
                                  @NonNull Transitions.TransitionFinishCallback finishCallback) {
        if (!Flags.taskViewTransitionsRefactor() && !enableHandlersDebuggingMode()) {
            return startAnimationLegacy(transition, transitionInfo, startTransaction,
                    finishTransaction, finishCallback);
        }
        final boolean inDataCollectionModeOnly =
                enableHandlersDebuggingMode() && transitionInfo == null;
        final boolean inAnimationMode = !inDataCollectionModeOnly;
        final TransitionInfo info = inDataCollectionModeOnly ? dispatchState.mInfo : transitionInfo;

        final PendingTransition pending = findPending(transition);
        ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.startAnimation(): taskView=%d "
                    + "type=%s transition=%s", pending != null ? pending.mTaskView.hashCode() : -1,
@@ -735,10 +755,16 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
            final TransitionInfo.Change chg = info.getChanges().get(i);
            if (isValidTaskView(chg, pending)) {
                taskViews.add(chg);
                if (inDataCollectionModeOnly) {
                    dispatchState.addError(this, chg, LOST_RELEVANT_CHANGE);
                }
            } else {
                alienChanges.add(chg);
            }
        }
        if (inDataCollectionModeOnly) {
            return false;
        }

        // Prepare taskViews for animation
        for (int i = 0; i < taskViews.size(); ++i) {
@@ -813,10 +839,12 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
                Slog.e(TAG, "Found a launching TaskView in the wrong transition. All "
                        + "TaskView launches should be initiated by shell and in their "
                        + "own transition: " + taskInfo.taskId);
                dispatchState.addError(this, change, CAPTURED_CHANGE_IN_WRONG_TRANSITION);
            } else {
                Slog.w(TAG, "Found a non-TaskView task in a TaskView Transition. This "
                        + "shouldn't happen, so there may be a visual artifact: "
                        + taskInfo.taskId);
                dispatchState.addError(this, change, CAPTURED_UNRELATED_CHANGE);
            }
        }

+3 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ public class TransitionDispatchState {
    // Change-related errors
    public static final int LOST_RELEVANT_CHANGE = 3;
    public static final int CAPTURED_UNRELATED_CHANGE = 4;
    public static final int CAPTURED_CHANGE_IN_WRONG_TRANSITION = 5;

    @IntDef(
            value = {
@@ -44,6 +45,7 @@ public class TransitionDispatchState {
                CAPTURED_UNRELATED_FLAG,
                LOST_RELEVANT_CHANGE,
                CAPTURED_UNRELATED_CHANGE,
                CAPTURED_CHANGE_IN_WRONG_TRANSITION,
            })
    public @interface ErrorCode {}

@@ -54,6 +56,7 @@ public class TransitionDispatchState {
            case CAPTURED_UNRELATED_FLAG -> "CAPTURED_UNRELATED_FLAG";
            case LOST_RELEVANT_CHANGE -> "LOST_RELEVANT_CHANGE";
            case CAPTURED_UNRELATED_CHANGE -> "CAPTURED_UNRELATED_CHANGE";
            case CAPTURED_CHANGE_IN_WRONG_TRANSITION -> "CAPTURED_CHANGE_IN_WRONG_TRANSITION";
            default -> "UNKNOWN";
        };
    }
+93 −4
Original line number Diff line number Diff line
@@ -22,7 +22,11 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;

import static com.android.window.flags.Flags.enableHandlersDebuggingMode;
import static com.android.wm.shell.Flags.FLAG_TASK_VIEW_TRANSITIONS_REFACTOR;
import static com.android.wm.shell.Flags.taskViewTransitionsRefactor;
import static com.android.wm.shell.transition.TransitionDispatchState.CAPTURED_UNRELATED_CHANGE;
import static com.android.wm.shell.transition.TransitionDispatchState.LOST_RELEVANT_CHANGE;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -44,6 +48,7 @@ import android.os.IBinder;
import android.platform.test.annotations.UsesFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
@@ -55,6 +60,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.transition.TransitionDispatchState;
import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.StubTransaction;
@@ -73,9 +79,10 @@ import java.util.List;

/**
 * Class to verify the behavior of startAnimation.
 * Verifies that changes behind FLAG_ENABLE_HANDLERS_DEBUGGING_MODE don't change the behavior
 * of startAnimation. The refactor tests' life span matches the flag's. Permanent tests are meant
 * to be added to TaskViewTransitionsTest.
 * 1. Verifies that startAnimation populates TransitionDispatchState correctly.
 * 2. Verifies that changes behind FLAG_ENABLE_HANDLERS_DEBUGGING_MODE don't change the behavior
 *    of startAnimation. Refactor test's life span matches the flag's. Permanent tests are meant to
 *    be added to TaskViewTransitionsTest.
 *    Test failures that manifest only when the flag is on mean that the behavior diverged.
 */
@SmallTest
@@ -123,6 +130,8 @@ public class TaskViewTransitionStartAnimationTest extends ShellTestCase {
    TaskViewTransitions.PendingTransition mPendingFront;
    TaskViewTransitions.PendingTransition mPendingBack;

    static final String TAG = "TVstartAnimTest";

    public TaskViewTransitionStartAnimationTest(FlagsParameterization flags) {
        mSetFlagsRule.setFlagsParameterization(flags);
    }
@@ -147,6 +156,7 @@ public class TaskViewTransitionStartAnimationTest extends ShellTestCase {
        mUnregisteredTaskInfo.token = mUnregisteredToken;
        // Same id as the other to match pending info id
        mUnregisteredTaskInfo.taskId = 314;
        mUnregisteredTaskInfo.launchCookies.add(mock(IBinder.class));
        mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);

        mBounds = new Rect(0, 0, 100, 100);
@@ -208,6 +218,85 @@ public class TaskViewTransitionStartAnimationTest extends ShellTestCase {
        return pending;
    }

    /**
     * Tests on TransitionDispatchState
     */
    @Test
    public void taskView_dispatchStateFindsIncompatible_animationMode() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
        assumeTrue(enableHandlersDebuggingMode());
        assumeTrue(taskViewTransitionsRefactor()); // To avoid running twice

        TransitionInfo.Change showingTV = getTaskView(TRANSIT_TO_FRONT);
        TransitionInfo.Change nonTV = getTask(TRANSIT_TO_BACK, false /* registered */);
        TaskViewTransitions.PendingTransition pending =
                setPendingTransaction(true /* visible*/, false /* opening */);
        // Showing taskView + normal task.
        // TaskView is accepted, but normal task is detected as error
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT)
                .addChange(showingTV)
                .addChange(nonTV)
                .build();

        TransitionDispatchState dispatchState =
                spy(new TransitionDispatchState(pending.mClaimed, info));

        boolean handled = mTaskViewTransitions.startAnimation(pending.mClaimed, info,
                dispatchState, mStartTransaction, mFinishTransaction, mFinishCallback);

        Slog.v(TAG, "DispatchState:\n" + dispatchState.getDebugInfo());
        // Has animated the taskView
        assertWithMessage("Handler should play the transition")
                .that(handled).isTrue();
        ArgumentCaptor<WindowContainerTransaction> wctCaptor =
                ArgumentCaptor.forClass(WindowContainerTransaction.class);
        verify(mFinishCallback).onTransitionFinished(wctCaptor.capture());
        assertWithMessage("Expected wct to be created and sent to callback")
                .that(wctCaptor.getValue()).isNotNull();
        verify(pending.mTaskView).notifyAppeared(eq(false));

        // Non task-view spotted as intruder
        verify(dispatchState).addError(eq(mTaskViewTransitions), eq(nonTV),
                eq(CAPTURED_UNRELATED_CHANGE));
        assertThat(dispatchState.hasErrors(mTaskViewTransitions)).isTrue();
    }

    @Test
    public void taskView_dispatchStateFindsCompatible_dataCollectionMode() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
        assumeTrue(enableHandlersDebuggingMode());
        assumeTrue(taskViewTransitionsRefactor()); // To avoid running twice

        TransitionInfo.Change showingTV = getTaskView(TRANSIT_TO_FRONT);
        TransitionInfo.Change nonTV = getTask(TRANSIT_TO_BACK, false /* registered */);
        TaskViewTransitions.PendingTransition pending =
                setPendingTransaction(true /* visible*/, false /* opening */);
        // Showing taskView + normal task.
        // TaskView is detected as change that could have played
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT)
                .addChange(showingTV)
                .addChange(nonTV)
                .build();

        TransitionDispatchState dispatchState =
                spy(new TransitionDispatchState(pending.mClaimed, info));

        boolean handled = mTaskViewTransitions.startAnimation(pending.mClaimed, null,
                dispatchState, mStartTransaction, mFinishTransaction, mFinishCallback);

        Slog.v(TAG, "DispatchState:\n" + dispatchState.getDebugInfo());
        // Has not animated the taskView
        assertWithMessage("Handler should not play the transition")
                .that(handled).isFalse();
        verify(mFinishCallback, never()).onTransitionFinished(any());
        verify(pending.mTaskView, never()).notifyAppeared(anyBoolean());

        // Non task-view spotted as intruder
        verify(dispatchState)
                .addError(eq(mTaskViewTransitions), eq(showingTV), eq(LOST_RELEVANT_CHANGE));
        assertThat(dispatchState.hasErrors(mTaskViewTransitions)).isTrue();
    }

    /**
     * Refactor tests on taskViews
     */