diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 6a7873998723c..47e606a5313ee 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -181,7 +181,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s", tf.getName()); try { - mOrganizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo()); + mOrganizer.onTaskFragmentInfoChanged(info); mLastSentTaskFragmentInfos.put(tf, info); } catch (RemoteException e) { Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e); @@ -424,6 +424,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr } // Remove and add for re-ordering. mPendingTaskFragmentEvents.remove(pendingEvent); + // Reset the defer time when TaskFragment is changed, so that it can check again if + // the event should be sent to the organizer, for example the TaskFragment may become + // empty. + pendingEvent.mDeferTime = 0; } mPendingTaskFragmentEvents.add(pendingEvent); } @@ -654,26 +658,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr return null; } - private boolean shouldSendEventWhenTaskInvisible(@NonNull Task task, - @NonNull PendingTaskFragmentEvent event) { + private boolean shouldSendEventWhenTaskInvisible(@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. - // Another case is when an organized TaskFragment became empty because the last running - // activity is reparented to a new Task due to enter PiP. We also want to notify the - // organizer, so it can remove the empty TaskFragment and update the paired TaskFragment - // without causing the extra delay. + // TaskFragment so that the {@link TaskFragmentOrganizer} can delete this TaskFragment. return event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED - && (task.topRunningActivity() == null || info.isTaskFragmentClearedForPip()) - && lastInfo != null - && lastInfo.getRunningActivityCount() > 0 && info.getRunningActivityCount() == 0; + && lastInfo != null && lastInfo.hasRunningActivity() && info.isEmpty(); } void dispatchPendingEvents() { @@ -690,7 +683,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null; if (task != null && (task.lastActiveTime <= event.mDeferTime || !(isTaskVisible(task, visibleTasks, invisibleTasks) - || shouldSendEventWhenTaskInvisible(task, event)))) { + || shouldSendEventWhenTaskInvisible(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 24d04da49e54a..75b5e73a604e4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -778,6 +778,47 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { verify(mOrganizer).onTaskFragmentInfoChanged(any()); } + /** + * Tests that a task fragment info changed event is sent if the TaskFragment becomes empty + * even if the Task is invisible. + */ + @Test + public void testPendingTaskFragmentInfoChangedEvent_emptyTaskFragment() { + // Create a TaskFragment with an activity, all within a parent task + final Task task = createTask(mDisplayContent); + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setOrganizer(mOrganizer) + .setFragmentToken(mFragmentToken) + .createActivityCount(1) + .build(); + final ActivityRecord embeddedActivity = taskFragment.getTopNonFinishingActivity(); + // Add another activity in the Task so that it always contains a non-finishing activitiy. + final ActivityRecord nonEmbeddedActivity = createActivityRecord(task); + assertTrue(task.shouldBeVisible(null)); + + // Dispatch pending info changed event from creating the activity + mController.registerOrganizer(mIOrganizer); + taskFragment.mTaskFragmentAppearedSent = true; + mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); + mController.dispatchPendingEvents(); + verify(mOrganizer).onTaskFragmentInfoChanged(any()); + + // Verify the info changed callback is not called when the task is invisible + reset(mOrganizer); + doReturn(false).when(task).shouldBeVisible(any()); + mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); + mController.dispatchPendingEvents(); + verify(mOrganizer, never()).onTaskFragmentInfoChanged(any()); + + // Finish the embedded activity, and verify the info changed callback is called because the + // TaskFragment is becoming empty. + embeddedActivity.finishing = true; + mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); + mController.dispatchPendingEvents(); + verify(mOrganizer).onTaskFragmentInfoChanged(any()); + } + /** * When an embedded {@link TaskFragment} is removed, we should clean up the reference in the * {@link WindowOrganizerController}.