From 09e0a6e823a77a6fd0d1b5a1e0d16880f8b46e56 Mon Sep 17 00:00:00 2001 From: Shivam Agrawal Date: Wed, 12 Jan 2022 15:09:58 -0500 Subject: [PATCH] Send Task Fragment Info Update When Task Invisible... ...only when the Task has no more running activities. Send a TaskFragment info changed callback if the callback is for the last activities to finish in a Task so that the TaskFragmentOrganizer can delete this TaskFragment. Otherwise, the Task may be removed before it becomes visible again to send this callback because it no longer has activities. As a result, the organizer will never get this info changed event and will not delete the TaskFragment because the organizer thinks the TaskFragment still has running activities. Bug: b/214090984 Test: atest TaskFragmentOrganizerControllerTest Test: atest ActivityEmbeddingLaunchTests Change-Id: Idb58975f4bfb6c67ed6cda3dc6b7c33abd960bbd --- .../wm/TaskFragmentOrganizerController.java | 21 +++++++++- .../TaskFragmentOrganizerControllerTest.java | 38 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index c7fdefc412ccd..123ca889c73e9 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -22,6 +22,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Rect; @@ -497,6 +498,23 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr return null; } + private boolean shouldSendEventWhenTaskInvisible(@NonNull Task task, + @NonNull PendingTaskFragmentEvent event) { + final TaskFragmentOrganizerState state = + mTaskFragmentOrganizerState.get(event.mTaskFragmentOrg.asBinder()); + final TaskFragmentInfo lastInfo = state.mLastSentTaskFragmentInfos.get(event.mTaskFragment); + final TaskFragmentInfo info = event.mTaskFragment.getTaskFragmentInfo(); + // Send an info changed callback if this event is for the last activities to finish in a + // Task so that the {@link TaskFragmentOrganizer} can delete this TaskFragment. Otherwise, + // the Task may be removed before it becomes visible again to send this event because it no + // longer has activities. As a result, the organizer will never get this info changed event + // and will not delete the TaskFragment because the organizer thinks the TaskFragment still + // has running activities. + return event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED + && task.topRunningActivity() == null && lastInfo != null + && lastInfo.getRunningActivityCount() > 0 && info.getRunningActivityCount() == 0; + } + void dispatchPendingEvents() { if (mAtmService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred() || mPendingTaskFragmentEvents.isEmpty()) { @@ -510,7 +528,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i); final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null; if (task != null && (task.lastActiveTime <= event.mDeferTime - || !isTaskVisible(task, visibleTasks, invisibleTasks))) { + || !(isTaskVisible(task, visibleTasks, invisibleTasks) + || shouldSendEventWhenTaskInvisible(task, event)))) { // Defer sending events to the TaskFragment until the host task is active again. event.mDeferTime = task.lastActiveTime; continue; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index dcaf9d7ae4342..f8c7207cefa70 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -21,14 +21,17 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.testing.Assert.assertThrows; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import android.content.Intent; @@ -471,6 +474,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) .setParentTask(task) .setOrganizer(mOrganizer) + .setFragmentToken(mFragmentToken) .build(); // Mock the task to invisible @@ -485,4 +489,38 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // Verifies that event was not sent verify(mOrganizer, never()).onTaskFragmentInfoChanged(any()); } + + /** + * Tests that a task fragment info changed event is still sent if the task is invisible only + * when the info changed event is because of the last activity in a task finishing. + */ + @Test + public void testLastPendingTaskFragmentInfoChangedEventOfInvisibleTaskSent() { + // Create a TaskFragment with an activity, all within a parent task + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setOrganizer(mOrganizer) + .setFragmentToken(mFragmentToken) + .setCreateParentTask() + .createActivityCount(1) + .build(); + final Task parentTask = taskFragment.getTask(); + final ActivityRecord activity = taskFragment.getTopNonFinishingActivity(); + assertTrue(parentTask.shouldBeVisible(null)); + + // Dispatch pending info changed event from creating the activity + mController.registerOrganizer(mIOrganizer); + taskFragment.mTaskFragmentAppearedSent = true; + mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); + mController.dispatchPendingEvents(); + + // Finish the activity and verify that the task is invisible + activity.finishing = true; + assertFalse(parentTask.shouldBeVisible(null)); + + // Verify the info changed callback still occurred despite the task being invisible + reset(mOrganizer); + mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); + mController.dispatchPendingEvents(); + verify(mOrganizer).onTaskFragmentInfoChanged(any()); + } }