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

Commit 7a84e808 authored by Eric Lin's avatar Eric Lin
Browse files

Update task info before notifying task removal.

Ensure TaskViewTaskController updates task information before notifying
listeners of task removal. This prevents outdated information (e.g.,
windowing mode) from causing unintended cleanup behavior in consumers
like BubbleTaskView during bubble-to-fullscreen transitions.

Bug: 388630258
Flag: com.android.wm.shell.enable_create_any_bubble
Test: atest WMShellUnitTests:TaskViewTest
Change-Id: Iea8597c629647e874b8ff93e8bd50c093b45492e
parent 9ccc4e8e
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;

import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -292,7 +293,11 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
        if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;

        final SurfaceControl taskLeash = mTaskLeash;
        if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
            handleAndNotifyTaskRemoval(taskInfo);
        } else {
            handleAndNotifyTaskRemoval(mTaskInfo);
        }

        mTransaction.reparent(taskLeash, null).apply();
        resetTaskInfo();
@@ -454,6 +459,20 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
        if (mListener == null) return;
        ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TaskController.notifyTaskRemovalStarted(): taskView=%d "
                + "task=%s", hashCode(), taskInfo);

        if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
            // Update mTaskInfo to reflect the latest task state before notifying the listener, as
            // it may have been changed by ShellTaskOrganizer#onTaskInfoChanged(), which triggers
            // task listener updates via ShellTaskOrganizer#updateTaskListenerIfNeeded() when a
            // task's info changes, resulting in onTaskVanished() being called on the old listener;
            // without updating mTaskInfo here would leave it with outdated information (e.g.,
            // windowing mode), potentially causing incorrect state checks and unintended cleanup
            // actions in consumers of TaskViewTaskController, such as task removal in
            // BubbleTaskView#cleanup.
            mTaskInfo = taskInfo;
            mTaskToken = mTaskInfo.token;
        }

        final int taskId = taskInfo.taskId;
        mListenerExecutor.execute(() -> mListener.onTaskRemovalStarted(taskId));
    }
+54 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.taskview;

import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;

import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +33,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -67,6 +69,7 @@ import com.android.wm.shell.TestHandler;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.transition.Transitions;

import org.junit.After;
@@ -79,11 +82,11 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;

import java.util.List;

import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;

import java.util.List;

@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -91,7 +94,10 @@ public class TaskViewTest extends ShellTestCase {

    @Parameters(name = "{0}")
    public static List<FlagsParameterization> getParams() {
        return FlagsParameterization.allCombinationsOf(Flags.FLAG_TASK_VIEW_REPOSITORY);
        return FlagsParameterization.allCombinationsOf(
                Flags.FLAG_TASK_VIEW_REPOSITORY,
                Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE
        );
    }

    @Mock
@@ -433,6 +439,51 @@ public class TaskViewTest extends ShellTestCase {
        verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
    }

    @Test
    public void testOnTaskVanished_withTaskInfoUpdate_notifiesTaskRemoval() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);

        // Capture task info when onTaskRemovalStarted is triggered on the task view listener.
        final ActivityManager.RunningTaskInfo[] capturedTaskInfo =
                new ActivityManager.RunningTaskInfo[1];
        final int taskId = mTaskInfo.taskId;
        doAnswer(invocation -> {
            capturedTaskInfo[0] = mTaskView.getTaskInfo();
            return null;
        }).when(mViewListener).onTaskRemovalStarted(taskId);

        // Set up a mock TaskViewBase to verify notified task info.
        final TaskViewBase mockTaskViewBase = mock(TaskViewBase.class);
        mTaskViewTaskController.setTaskViewBase(mockTaskViewBase);

        // Prepare and trigger task opening animation with mTaskInfo.
        mTaskViewTransitions.prepareOpenAnimation(mTaskViewTaskController, true /* newTask */,
                new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
                mLeash, new WindowContainerTransaction());
        mTaskView.surfaceCreated(mock(SurfaceHolder.class));

        // Simulate task info change with windowing mode update.
        final ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
        newTaskInfo.token = mTaskInfo.token;
        newTaskInfo.taskId = taskId;
        newTaskInfo.taskDescription = mTaskInfo.taskDescription;
        newTaskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);

        // Invoke onTaskVanished with updated task info.
        mTaskViewTaskController.onTaskVanished(newTaskInfo);

        verify(mViewListener).onTaskRemovalStarted(taskId);
        if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
            // Verify TaskViewBase and listener updates with new task info.
            verify(mockTaskViewBase).onTaskVanished(same(newTaskInfo));
            assertThat(capturedTaskInfo[0]).isSameInstanceAs(newTaskInfo);
        } else {
            // Verify TaskViewBase and listener updates with old task info.
            verify(mockTaskViewBase).onTaskVanished(same(mTaskInfo));
            assertThat(capturedTaskInfo[0]).isSameInstanceAs(mTaskInfo);
        }
    }

    @Test
    public void testOnBackPressedOnTaskRoot() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);