Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d7c8abe authored by Robert Carr's avatar Robert Carr
Browse files

TaskOrganizer: Implement lifetime changes for testing.

Here we implement three new lifetime semantics:
1. If there is a TaskOrganizer registered for a given windowing mode but
   a second organizer is registered, the first organizer will no longer be
   totally deregistered. It will no longer receive new tasks but will continue
   to manage its old Tasks. This lets us run CTS tests that test TaskOrganizer
   without totally breaking SysUI.
2. Implement unregisterTaskOrganizer, if you unregister the organizer it's
   just finished. SysUI probably wont use but CTS will.
3. Implement a sort of registration chaining semantic. When unregistering
   an organizer, the controller will attempt to restore the previous
   organizer for the given windowing mode. This means that when CTS tests
   unregister themselves SysUI can return to control.

Test: TaskOrganizerTests
Bug: 139371701
Change-Id: Ide240c707755b279ce6316f50ca8e023e38b7d66
parent 0e33c62e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
+73 −52
Original line number Diff line number Diff line
@@ -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<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();

@@ -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);
    }

+46 −0
Original line number Diff line number Diff line
@@ -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);