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

Commit 29988a3c authored by Mady Mellor's avatar Mady Mellor
Browse files

Track pending task info & if the task doesn't launch clean it up

If a task fails to launch, we need to tell clients of TaskView that
the task didn't launch. To do this, we need to have a reference to
TaskInfo so that we can notify clients and actually remove the task.

We know the task failed to launch if it isn't found in the transition
animation. We don't have a reference of the TaskInfo there so we need
to rely on onTaskAppeared to get the info. The order of onTaskAppeared
and the transition animation isn't guaranteed so we set a bit from
the transition if it failed & check that in onTaskAppeared (and vice
versa).

Bug: 283461350
Test: TaskViewTransitionsTest TaskViewTest
Test: manual - must be on tablet with lock, enable home controls to
               show on lockscreen, install CtsDeviceControlsApp and
               add a control (note this only repros because currently
               CtsDeviceControlsApp sets a flag to hide itself on
               lockscreen which manifests the bug)
             - lock the device
             - longpress on homecontrols shortcut icon
             => observe the logs and nothing crashes (although the
                device flickers a lot between lockscreen & controls)
Change-Id: I28e0b2c60889d38f422831935564ef739fd3ef3c
Merged-In: I28e0b2c60889d38f422831935564ef739fd3ef3c
parent 264de2e9
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -61,6 +61,16 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
    private TaskViewBase mTaskViewBase;
    private final Context mContext;

    /**
     * There could be a situation where we have task info and receive
     * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}, however, the
     * activity might fail to open, and in this case we need to clean up the task view / notify
     * listeners of a task removal. This requires task info, so we save the info from onTaskAppeared
     * in this situation to allow us to notify listeners correctly if the task failed to open.
     */
    private ActivityManager.RunningTaskInfo mPendingInfo;
    /* Indicates that the task we attempted to launch in the task view failed to launch. */
    private boolean mTaskNotFound;
    protected ActivityManager.RunningTaskInfo mTaskInfo;
    private WindowContainerToken mTaskToken;
    private SurfaceControl mTaskLeash;
@@ -236,6 +246,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
        mTaskInfo = null;
        mTaskToken = null;
        mTaskLeash = null;
        mPendingInfo = null;
        mTaskNotFound = false;
    }

    private void updateTaskVisibility() {
@@ -257,6 +269,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
            SurfaceControl leash) {
        if (isUsingShellTransitions()) {
            mPendingInfo = taskInfo;
            if (mTaskNotFound) {
                // If we were already notified by shell transit that we don't have the
                // the task, clean it up now.
                cleanUpPendingTask();
            }
            // Everything else handled by enter transition.
            return;
        }
@@ -455,6 +473,42 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
        return mTaskInfo;
    }

    /**
     * Indicates that the task was not found in the start animation for the transition.
     * In this case we should clean up the task if we have the pending info. If we don't
     * have the pending info, we'll do it when we receive it in
     * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}.
     */
    void setTaskNotFound() {
        mTaskNotFound = true;
        if (mPendingInfo != null) {
            cleanUpPendingTask();
        }
    }

    /**
     * Called when a task failed to open and we need to clean up task view /
     * notify users of task view.
     */
    void cleanUpPendingTask() {
        if (mPendingInfo != null) {
            if (mListener != null) {
                final int taskId = mPendingInfo.taskId;
                mListenerExecutor.execute(() -> {
                    mListener.onTaskRemovalStarted(taskId);
                });
            }
            mTaskViewBase.onTaskVanished(mPendingInfo);
            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mPendingInfo.token, false);

            // Make sure the task is removed
            WindowContainerTransaction wct = new WindowContainerTransaction();
            wct.removeTask(mPendingInfo.token);
            mTaskViewTransitions.closeTaskView(wct, this);
        }
        resetTaskInfo();
    }

    void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
        if (mTaskToken == null) {
            // Nothing to update, task is not yet available
@@ -492,6 +546,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
            @NonNull SurfaceControl.Transaction finishTransaction,
            ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
            WindowContainerTransaction wct) {
        mPendingInfo = null;
        mTaskInfo = taskInfo;
        mTaskToken = mTaskInfo.token;
        mTaskLeash = leash;
+7 −5
Original line number Diff line number Diff line
@@ -139,7 +139,8 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
     * `taskView`.
     * @param taskView the pending transition should be for this.
     */
    private PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) {
    @VisibleForTesting
    PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) {
        for (int i = mPending.size() - 1; i >= 0; --i) {
            if (mPending.get(i).mTaskView != taskView) continue;
            if (TransitionUtil.isOpeningType(mPending.get(i).mType)) {
@@ -398,10 +399,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
            }
        }
        if (stillNeedsMatchingLaunch) {
            throw new IllegalStateException("Expected a TaskView launch in this transition but"
                    + " didn't get one.");
        }
        if (wct == null && pending == null && changesHandled != info.getChanges().size()) {
            Slog.w(TAG, "Expected a TaskView launch in this transition but didn't get one, "
                    + "cleaning up the task view");
            // Didn't find a task so the task must have never launched
            pending.mTaskView.setTaskNotFound();
        } else if (wct == null && pending == null && changesHandled != info.getChanges().size()) {
            // Just some house-keeping, let another handler animate.
            return false;
        }
+21 −2
Original line number Diff line number Diff line
@@ -128,8 +128,8 @@ public class TaskViewTest extends ShellTestCase {
            doReturn(true).when(mTransitions).isRegistered();
        }
        mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
        mTaskViewTaskController = new TaskViewTaskController(mContext, mOrganizer,
                mTaskViewTransitions, mSyncQueue);
        mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer,
                mTaskViewTransitions, mSyncQueue));
        mTaskView = new TaskView(mContext, mTaskViewTaskController);
        mTaskView.setListener(mExecutor, mViewListener);
    }
@@ -544,4 +544,23 @@ public class TaskViewTest extends ShellTestCase {
        mTaskView.removeTask();
        verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController));
    }

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

        mTaskViewTaskController.setTaskNotFound();
        mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);

        verify(mTaskViewTaskController).cleanUpPendingTask();
        verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController));
    }

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

        mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
        verify(mTaskViewTaskController, never()).cleanUpPendingTask();
    }
}
+35 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.taskview;

import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;

import static com.google.common.truth.Truth.assertThat;
@@ -25,16 +26,19 @@ import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.IBinder;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.transition.Transitions;
@@ -45,6 +49,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.List;

@SmallTest
@@ -295,4 +300,34 @@ public class TaskViewTransitionsTest extends ShellTestCase {
        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
                new Rect(0, 0, 100, 100));
    }

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

        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
        when(change.getTaskInfo()).thenReturn(mTaskInfo);
        when(change.getMode()).thenReturn(TRANSIT_OPEN);

        List<TransitionInfo.Change> changes = new ArrayList<>();
        changes.add(change);

        TransitionInfo info = mock(TransitionInfo.class);
        when(info.getChanges()).thenReturn(changes);

        mTaskViewTransitions.startTaskView(new WindowContainerTransaction(),
                mTaskViewTaskController,
                mock(IBinder.class));

        TaskViewTransitions.PendingTransition pending =
                mTaskViewTransitions.findPendingOpeningTransition(mTaskViewTaskController);

        mTaskViewTransitions.startAnimation(pending.mClaimed,
                info,
                new SurfaceControl.Transaction(),
                new SurfaceControl.Transaction(),
                mock(Transitions.TransitionFinishCallback.class));

        verify(mTaskViewTaskController).setTaskNotFound();
    }
}