From 29988a3c813438b667c061919b9e8d8e42d33218 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Wed, 7 Jun 2023 14:33:08 -0700 Subject: [PATCH] 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 --- .../taskview/TaskViewTaskController.java | 55 +++++++++++++++++++ .../shell/taskview/TaskViewTransitions.java | 12 ++-- .../wm/shell/taskview/TaskViewTest.java | 23 +++++++- .../taskview/TaskViewTransitionsTest.java | 35 ++++++++++++ 4 files changed, 118 insertions(+), 7 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index 1bbd3679948b5..163cf501734c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -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; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index 2e7fca3f2b469..5baf2e3202272 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -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; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 81fc8438eec0b..1b389565c066b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -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(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java index 71ad0d79eaca3..03ed18c866082 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java @@ -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 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(); + } }