diff options
8 files changed, 419 insertions, 82 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index faa4a0ed2294..c9b38d00c0ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -262,12 +262,6 @@ public class ShellTaskOrganizer extends TaskOrganizer { synchronized (mLock) { ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId); final TaskAppearedInfo data = mTasks.get(taskInfo.taskId); - if (data == null) { - // TODO(b/171749427): It means onTaskInfoChanged send before onTaskAppeared or - // after onTaskVanished, it should be fixed in controller side. - return; - } - final TaskListener oldListener = getTaskListener(data.getTaskInfo()); final TaskListener newListener = getTaskListener(taskInfo); mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash())); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java index de3bb2950c0a..f8b4dd9bc621 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java @@ -41,12 +41,12 @@ public class PipUiEventLogger { } public void setTaskInfo(TaskInfo taskInfo) { - if (taskInfo == null) { - mPackageName = null; - mPackageUid = INVALID_PACKAGE_UID; - } else { + if (taskInfo != null && taskInfo.topActivity != null) { mPackageName = taskInfo.topActivity.getPackageName(); mPackageUid = getUid(mPackageName, taskInfo.userId); + } else { + mPackageName = null; + mPackageUid = INVALID_PACKAGE_UID; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index f1b3cc5866a4..fc1811b11d73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -1005,6 +1005,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } private void updateThemeColors() { + if (mScrimBehind == null) return; int background = Utils.getColorAttr(mScrimBehind.getContext(), android.R.attr.colorBackgroundFloating).getDefaultColor(); int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 23acbf75429c..ff349fa637a5 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -862,6 +862,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } } + + // Send any pending task-info changes that were queued-up during a layout deferment + mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); @@ -1014,9 +1017,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.scheduleAnimationLocked(); - // Send any pending task-info changes that were queued-up during a layout deferment - mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges(); - if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit"); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 637240c06de2..af85ec69eeac 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4993,7 +4993,6 @@ class Task extends WindowContainer<WindowContainer> { } } else { // No longer managed by any organizer. - mTaskAppearedSent = false; setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */); if (mCreatedByOrganizer) { removeImmediately("setTaskOrganizer"); @@ -5019,7 +5018,7 @@ class Task extends WindowContainer<WindowContainer> { */ boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) { if (getSurfaceControl() == null) { - // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one + // Can't call onTaskAppeared without a surfacecontrol, so defer this until next one // is created. return false; } @@ -7672,7 +7671,7 @@ class Task extends WindowContainer<WindowContainer> { void dispatchTaskInfoChangedIfNeeded(boolean force) { if (isOrganized()) { - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, force); + mAtmService.mTaskOrganizerController.onTaskInfoChanged(this, force); } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 65247d0b9937..b3e010872d3b 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -151,27 +151,23 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId); final boolean visible = task.isVisible(); final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible, - "TaskOrganizerController.onTaskAppeared")); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskAppeared callback", e); - } - }); + try { + mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible, + "TaskOrganizerController.onTaskAppeared")); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskAppeared callback", e); + } } void onTaskVanished(Task task) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId); final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskVanished(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskVanished callback", e); - } - }); + try { + mTaskOrganizer.onTaskVanished(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskVanished callback", e); + } } void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) { @@ -180,20 +176,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return; } ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - // Purposely notify of task info change immediately instead of deferring (like - // appear and vanish) to allow info changes (such as new PIP params) to flow - // without waiting. - mTaskOrganizer.onTaskInfoChanged(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + // Purposely notify of task info change immediately instead of deferring (like + // appear and vanish) to allow info changes (such as new PIP params) to flow + // without waiting. + mTaskOrganizer.onTaskInfoChanged(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); + } } void onBackPressedOnTaskRoot(Task task) { @@ -203,17 +197,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // Skip if the task has not yet received taskAppeared(). return; } - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); + } } } @@ -258,28 +250,34 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return mOrganizer.prepareLeash(t, t.isVisible(), reason); } - void addTask(Task t) { - if (t.mTaskAppearedSent) return; + private boolean addTask(Task t) { + if (t.mTaskAppearedSent) { + return false; + } if (!mOrganizedTasks.contains(t)) { mOrganizedTasks.add(t); } + if (t.taskAppearedReady()) { t.mTaskAppearedSent = true; - mOrganizer.onTaskAppeared(t); + return true; } + return false; } - void removeTask(Task t) { + private boolean removeTask(Task t) { + mOrganizedTasks.remove(t); + mInterceptBackPressedOnRootTasks.remove(t.mTaskId); + if (t.mTaskAppearedSent) { if (t.getSurfaceControl() != null) { t.migrateToNewSurfaceControl(); } t.mTaskAppearedSent = false; - mOrganizer.onTaskVanished(t); + return true; } - mOrganizedTasks.remove(t); - mInterceptBackPressedOnRootTasks.remove(t.mTaskId); + return false; } void dispose() { @@ -290,8 +288,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // possible. while (!mOrganizedTasks.isEmpty()) { final Task t = mOrganizedTasks.get(0); - if (!t.updateTaskOrganizerState(true /* forceUpdate */)) { - removeTask(t); + t.updateTaskOrganizerState(true /* forceUpdate */); + if (mOrganizedTasks.contains(t)) { + // updateTaskOrganizerState should remove the task from the list, but still + // check it again to avoid while-loop isn't terminate. + if (removeTask(t)) { + TaskOrganizerController.this.onTaskVanishedInternal( + mOrganizer.mTaskOrganizer, t); + } } } @@ -305,6 +309,33 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } + static class PendingTaskEvent { + static final int EVENT_APPEARED = 0; + static final int EVENT_VANISHED = 1; + static final int EVENT_INFO_CHANGED = 2; + static final int EVENT_ROOT_BACK_PRESSED = 3; + + final int mEventType; + final Task mTask; + final ITaskOrganizer mTaskOrg; + boolean mForce; + + PendingTaskEvent(Task task, int event) { + this(task, task.mTaskOrganizer, event); + } + + PendingTaskEvent(Task task, ITaskOrganizer taskOrg, int eventType) { + mTask = task; + mTaskOrg = taskOrg; + mEventType = eventType; + } + + boolean isLifecycleEvent() { + return mEventType == EVENT_APPEARED || mEventType == EVENT_VANISHED + || mEventType == EVENT_INFO_CHANGED; + } + } + private final ActivityTaskManagerService mService; private final WindowManagerGlobalLock mGlobalLock; @@ -312,7 +343,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>(); private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); - private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); + // Pending task events due to layout deferred. + private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>(); // Set of organized tasks (by taskId) that dispatch back pressed to their organizers private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); @@ -337,6 +369,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) { mDeferTaskOrgCallbacksConsumer = consumer; } + + @VisibleForTesting + ArrayList<PendingTaskEvent> getPendingEventList() { + return mPendingTaskEvents; + } + /** * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode. */ @@ -442,14 +480,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onTaskAppeared(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - state.addTask(task); + if (state != null && state.addTask(task)) { + PendingTaskEvent pending = getPendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED); + mPendingTaskEvents.add(pending); + } + } } void onTaskVanished(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - if (state != null) { - state.removeTask(task); + if (state != null && state.removeTask(task)) { + onTaskVanishedInternal(organizer, task); + } + } + + private void onTaskVanishedInternal(ITaskOrganizer organizer, Task task) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId) { + // This task is vanished so remove all pending event of it. + mPendingTaskEvents.remove(i); + if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) { + // If task appeared callback still pend, ignore this callback too. + return; + } + } } + + PendingTaskEvent pending = + new PendingTaskEvent(task, organizer, PendingTaskEvent.EVENT_VANISHED); + mPendingTaskEvents.add(pending); } @Override @@ -518,30 +580,76 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - void dispatchPendingTaskInfoChanges() { - if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { + void dispatchPendingEvents() { + if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred() + || mPendingTaskEvents.isEmpty()) { return; } - for (int i = 0, n = mPendingTaskInfoChanges.size(); i < n; ++i) { - dispatchTaskInfoChanged(mPendingTaskInfoChanges.get(i), false /* force */); + + for (int i = 0, n = mPendingTaskEvents.size(); i < n; i++) { + PendingTaskEvent event = mPendingTaskEvents.get(i); + final Task task = event.mTask; + final TaskOrganizerState state; + switch (event.mEventType) { + case PendingTaskEvent.EVENT_APPEARED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null && task.taskAppearedReady()) { + state.mOrganizer.onTaskAppeared(task); + } + break; + case PendingTaskEvent.EVENT_VANISHED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null) { + state.mOrganizer.onTaskVanished(task); + } + break; + case PendingTaskEvent.EVENT_INFO_CHANGED: + dispatchTaskInfoChanged(event.mTask, event.mForce); + break; + case PendingTaskEvent.EVENT_ROOT_BACK_PRESSED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null) { + state.mOrganizer.onBackPressedOnTaskRoot(task); + } + break; + } } - mPendingTaskInfoChanges.clear(); + mPendingTaskEvents.clear(); } - void dispatchTaskInfoChanged(Task task, boolean force) { - if (!force && mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { - // Defer task info reporting while layout is deferred. This is because layout defer - // blocks tend to do lots of re-ordering which can mess up animations in receivers. - mPendingTaskInfoChanges.remove(task); - mPendingTaskInfoChanges.add(task); + void onTaskInfoChanged(Task task, boolean force) { + if (!task.mTaskAppearedSent) { + // Skip if task still not appeared. return; } + + // Defer task info reporting while layout is deferred. This is because layout defer + // blocks tend to do lots of re-ordering which can mess up animations in receivers. + PendingTaskEvent pending = getPendingLifecycleTaskEvent(task); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_INFO_CHANGED); + } else { + if (pending.mEventType != PendingTaskEvent.EVENT_INFO_CHANGED) { + // If queued event is appeared, it means task still not appeared so ignore + // this info changed. If queued event is vanished, it means task should + // will vanished early so do not need this info changed. + return; + } + // Remove and add for re-ordering. + mPendingTaskEvents.remove(pending); + } + pending.mForce = force; + mPendingTaskEvents.add(pending); + } + + private void dispatchTaskInfoChanged(Task task, boolean force) { RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task); if (mTmpTaskInfo == null) { mTmpTaskInfo = new RunningTaskInfo(); } mTmpTaskInfo.configuration.unset(); task.fillTaskInfo(mTmpTaskInfo); + boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo); if (!changed) { int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration); @@ -705,11 +813,48 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return false; } - final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder()); - state.mOrganizer.onBackPressedOnTaskRoot(task); + PendingTaskEvent pendingVanished = + getPendingTaskEvent(task, PendingTaskEvent.EVENT_VANISHED); + if (pendingVanished != null) { + // This task will vanish before this callback so just ignore. + return false; + } + + PendingTaskEvent pending = getPendingTaskEvent( + task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED); + } else { + // Pending already exist, remove and add for re-ordering. + mPendingTaskEvents.remove(pending); + } + mPendingTaskEvents.add(pending); return true; } + @Nullable + private PendingTaskEvent getPendingTaskEvent(Task task, int type) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId && type == entry.mEventType) { + return entry; + } + } + return null; + } + + @VisibleForTesting + @Nullable + PendingTaskEvent getPendingLifecycleTaskEvent(Task task) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId && entry.isLifecycleEvent()) { + return entry; + } + } + return null; + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.print(prefix); pw.println("TaskOrganizerController:"); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index c4eb63581153..52ed2788d795 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -222,6 +222,7 @@ public class WindowAnimator { mService.destroyPreservedSurfaceLocked(); + mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); executeAfterPrepareSurfacesRunnables(); if (DEBUG_WINDOW_TRACE) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index fe7bdd8f620a..d8be2c1a21a7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -80,6 +80,8 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; +import com.android.server.wm.TaskOrganizerController.PendingTaskEvent; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -150,10 +152,14 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); } @@ -162,15 +168,21 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setHasBeenVisible(true); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertTrue(stack.getHasBeenVisible()); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); } @@ -195,12 +207,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false /* fakeDraw */); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertTaskVanished(organizer, false /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @@ -210,11 +226,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); stack.setTaskOrganizer(null); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); + verify(organizer).onTaskVanished(any()); assertFalse(stack.isOrganized()); } @@ -224,11 +245,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); + assertTaskVanished(organizer, true /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @@ -243,6 +269,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task task3 = createTask(stack3); final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); // verify that tasks are returned and taskAppeared is not called assertContainsTasks(existingTasks, stack, stack2, stack3); @@ -254,6 +282,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Now we replace the registration and verify the new organizer receives existing tasks final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertContainsTasks(existingTasks2, stack, stack2, stack3); verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); @@ -265,6 +295,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Now we unregister the second one, the first one should automatically be reregistered // so we verify that it's now seeing changes. mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(3)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3); @@ -599,6 +631,8 @@ public class WindowOrganizerTests extends WindowTestsBase { Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); RunningTaskInfo info1 = task.getTaskInfo(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); lastReportedTiles.clear(); called[0] = false; @@ -673,6 +707,8 @@ public class WindowOrganizerTests extends WindowTestsBase { Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); RunningTaskInfo info2 = task2.getTaskInfo(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( mDisplayContent.mDisplayId, null /* activityTypes */).size(); @@ -856,6 +892,8 @@ public class WindowOrganizerTests extends WindowTestsBase { .setAspectRatio(new Rational(3, 4)).build(); mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); waitUntilHandlersIdle(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertNotNull(o.mChangedInfo); assertNotNull(o.mChangedInfo.pictureInPictureParams); final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational(); @@ -895,16 +933,24 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.setTaskOrganizer(organizer); // setHasBeenVisible was already called once by the set-up code. stack.setHasBeenVisible(true); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setTaskOrganizer(null); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onTaskVanished(any()); stack.setTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)).onTaskVanished(any()); } @@ -923,6 +969,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify a back pressed does not call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()).onBackPressedOnTaskRoot(any()); // Enable intercepting back @@ -931,6 +979,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify now that the back press does call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); // Disable intercepting back @@ -939,6 +989,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify now that the back press no longer calls the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @@ -1019,6 +1071,151 @@ public class WindowOrganizerTests extends WindowTestsBase { assertTrue(task2.isOrganized()); } + @Test + public void testAppearDeferThenInfoChange() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testAppearDeferThenVanish() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + final Task task = createTask(stack); + + stack.removeImmediately(); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(0, pendingEvents.size()); + } + + @Test + public void testInfoChangeDeferMultiple() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2")); + waitUntilHandlersIdle(); + + pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription2", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testInfoChangDeferThenVanish() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + + stack.removeImmediately(); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testVanishDeferThenInfoChange() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.removeImmediately(); + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + } + + @Test + public void testVanishDeferThenBackOnRoot() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.removeImmediately(); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + } + + private ArrayList<PendingTaskEvent> getTaskPendingEvent(Task task) { + ArrayList<PendingTaskEvent> total = + mWm.mAtmService.mTaskOrganizerController.getPendingEventList(); + ArrayList<PendingTaskEvent> result = new ArrayList(); + + for (int i = 0; i < total.size(); i++) { + PendingTaskEvent entry = total.get(i); + if (entry.mTask.mTaskId == task.mTaskId) { + result.add(entry); + } + } + + return result; + } + /** * Verifies that task vanished is called for a specific task. */ |