diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index d5961a881c363..d380f8cd7337b 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -356,6 +356,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { // TODO(task-hierarchy): remove when tiles can be actual parents TaskTile mTile = null; + private int mLastTaskOrganizerWindowingMode = -1; + private final Handler mHandler; private class ActivityStackHandler extends Handler { @@ -794,6 +796,13 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } final int windowingMode = getWindowingMode(); + if (windowingMode == mLastTaskOrganizerWindowingMode) { + // If our windowing mode hasn't actually changed, then just stick + // with our old organizer. This lets us implement the semantic + // where SysUI can continue to manage it's old tasks + // while CTS temporarily takes over the registration. + return; + } /* * Different windowing modes may be managed by different task organizers. If * getTaskOrganizer returns null, we still call setTaskOrganizer to @@ -802,6 +811,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final ITaskOrganizer org = mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); setTaskOrganizer(org); + mLastTaskOrganizerWindowingMode = windowingMode; } @Override diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 44a6fc936961d..0733a72446a26 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -74,11 +74,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void binderDied() { synchronized (mGlobalLock) { - final TaskOrganizerState state = mTaskOrganizerStates.get(mTaskOrganizer); - for (int i = 0; i < state.mOrganizedTasks.size(); i++) { - state.mOrganizedTasks.get(i).taskOrganizerDied(); - } - mTaskOrganizerStates.remove(mTaskOrganizer); + final TaskOrganizerState state = + mTaskOrganizerStates.get(mTaskOrganizer.asBinder()); + state.releaseTasks(); + mTaskOrganizerStates.remove(mTaskOrganizer.asBinder()); if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) { mTaskOrganizersForWindowingMode.remove(mWindowingMode); } @@ -89,26 +88,76 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { class TaskOrganizerState { ITaskOrganizer mOrganizer; DeathRecipient mDeathRecipient; + int mWindowingMode; ArrayList mOrganizedTasks = new ArrayList<>(); + // Save the TaskOrganizer which we replaced registration for + // so it can be re-registered if we unregister. + TaskOrganizerState mReplacementFor; + boolean mDisposed = false; + + + TaskOrganizerState(ITaskOrganizer organizer, int windowingMode, + TaskOrganizerState replacing) { + mOrganizer = organizer; + mDeathRecipient = new DeathRecipient(organizer, windowingMode); + try { + organizer.asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + Slog.e(TAG, "TaskOrganizer failed to register death recipient"); + } + mWindowingMode = windowingMode; + mReplacementFor = replacing; + } + void addTask(Task t) { mOrganizedTasks.add(t); + try { + mOrganizer.taskAppeared(t.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskAppeared callback" + e); + } } void removeTask(Task t) { + try { + mOrganizer.taskVanished(t.getRemoteToken()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskVanished callback" + e); + } mOrganizedTasks.remove(t); } - TaskOrganizerState(ITaskOrganizer organizer, DeathRecipient deathRecipient) { - mOrganizer = organizer; - mDeathRecipient = deathRecipient; + void dispose() { + mDisposed = true; + releaseTasks(); + handleReplacement(); + } + + void releaseTasks() { + for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) { + final Task t = mOrganizedTasks.get(i); + t.taskOrganizerDied(); + removeTask(t); + } + } + + void handleReplacement() { + if (mReplacementFor != null && !mReplacementFor.mDisposed) { + mTaskOrganizersForWindowingMode.put(mWindowingMode, mReplacementFor); + } + } + + void unlinkDeath() { + mDisposed = true; + mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0); } }; final HashMap mTaskOrganizersForWindowingMode = new HashMap(); - final HashMap mTaskOrganizerStates = new HashMap(); + final HashMap mTaskOrganizerStates = new HashMap(); final HashMap mTaskOrganizersByPendingSyncId = new HashMap(); @@ -128,17 +177,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func); } - private void clearIfNeeded(int windowingMode) { - final TaskOrganizerState oldState = mTaskOrganizersForWindowingMode.get(windowingMode); - if (oldState != null) { - oldState.mOrganizer.asBinder().unlinkToDeath(oldState.mDeathRecipient, 0); - } - } - /** * Register a TaskOrganizer to manage tasks as they enter the given windowing mode. * If there was already a TaskOrganizer for this windowing mode it will be evicted - * and receive taskVanished callbacks in the process. + * but will continue to organize it's existing tasks. */ @Override public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) { @@ -153,24 +195,25 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - clearIfNeeded(windowingMode); - DeathRecipient dr = new DeathRecipient(organizer, windowingMode); - try { - organizer.asBinder().linkToDeath(dr, 0); - } catch (RemoteException e) { - Slog.e(TAG, "TaskOrganizer failed to register death recipient"); - } - - final TaskOrganizerState state = new TaskOrganizerState(organizer, dr); + final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode, + mTaskOrganizersForWindowingMode.get(windowingMode)); mTaskOrganizersForWindowingMode.put(windowingMode, state); - - mTaskOrganizerStates.put(organizer, state); + mTaskOrganizerStates.put(organizer.asBinder(), state); } } finally { Binder.restoreCallingIdentity(origId); } } + void unregisterTaskOrganizer(ITaskOrganizer organizer) { + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); + state.unlinkDeath(); + if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) { + mTaskOrganizersForWindowingMode.remove(state.mWindowingMode); + } + state.dispose(); + } + ITaskOrganizer getTaskOrganizer(int windowingMode) { final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode); if (state == null) { @@ -179,35 +222,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return state.mOrganizer; } - private void sendTaskAppeared(ITaskOrganizer organizer, Task task) { - try { - organizer.taskAppeared(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending taskAppeared callback" + e); - } - } - - private void sendTaskVanished(ITaskOrganizer organizer, Task task) { - try { - organizer.taskVanished(task.getRemoteToken()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending taskVanished callback" + e); - } - } - void onTaskAppeared(ITaskOrganizer organizer, Task task) { - TaskOrganizerState state = mTaskOrganizerStates.get(organizer); - + TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.addTask(task); - sendTaskAppeared(organizer, task); } void onTaskVanished(ITaskOrganizer organizer, Task task) { - final TaskOrganizerState state = mTaskOrganizerStates.get(organizer); - sendTaskVanished(organizer, task); - - // This could trigger TaskAppeared for other tasks in the same stack so make sure - // we do this AFTER sending taskVanished. + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.removeTask(task); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index 078347e96a07a..a9a20f675f905 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -138,6 +138,52 @@ public class TaskOrganizerTests extends WindowTestsBase { assertFalse(stack.isControlledByTaskOrganizer()); } + @Test + public void testUnregisterOrganizer() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ITaskOrganizer organizer = registerMockOrganizer(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer).taskAppeared(any()); + assertTrue(stack.isControlledByTaskOrganizer()); + + mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + verify(organizer).taskVanished(any()); + assertFalse(stack.isControlledByTaskOrganizer()); + } + + @Test + public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent); + final Task task2 = createTaskInStack(stack2, 0 /* userId */); + final ActivityStack stack3 = createTaskStackOnDisplay(mDisplayContent); + final Task task3 = createTaskInStack(stack3, 0 /* userId */); + final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); + + // First organizer is registered, verify a task appears when changing windowing mode + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer, times(1)).taskAppeared(any()); + assertTrue(stack.isControlledByTaskOrganizer()); + + // Now we replace the registration and1 verify the new organizer receives tasks + // newly entering the windowing mode. + final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); + stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer2).taskAppeared(any()); + assertTrue(stack2.isControlledByTaskOrganizer()); + + // 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); + + stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer, times(2)).taskAppeared(any()); + assertTrue(stack3.isControlledByTaskOrganizer()); + } + @Test public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);