Loading services/core/java/com/android/server/wm/ActivityStack.java +10 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 Loading @@ -802,6 +811,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final ITaskOrganizer org = mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); setTaskOrganizer(org); mLastTaskOrganizerWindowingMode = windowingMode; } @Override Loading services/core/java/com/android/server/wm/TaskOrganizerController.java +73 −52 Original line number Diff line number Diff line Loading @@ -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); } Loading @@ -89,26 +88,76 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { class TaskOrganizerState { ITaskOrganizer mOrganizer; DeathRecipient mDeathRecipient; int mWindowingMode; ArrayList<Task> 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<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap(); final HashMap<ITaskOrganizer, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap(); Loading @@ -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) { Loading @@ -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) { Loading @@ -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); } Loading services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +46 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading
services/core/java/com/android/server/wm/ActivityStack.java +10 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 Loading @@ -802,6 +811,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final ITaskOrganizer org = mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); setTaskOrganizer(org); mLastTaskOrganizerWindowingMode = windowingMode; } @Override Loading
services/core/java/com/android/server/wm/TaskOrganizerController.java +73 −52 Original line number Diff line number Diff line Loading @@ -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); } Loading @@ -89,26 +88,76 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { class TaskOrganizerState { ITaskOrganizer mOrganizer; DeathRecipient mDeathRecipient; int mWindowingMode; ArrayList<Task> 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<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap(); final HashMap<ITaskOrganizer, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap(); Loading @@ -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) { Loading @@ -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) { Loading @@ -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); } Loading
services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +46 −0 Original line number Diff line number Diff line Loading @@ -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); Loading