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

Commit 7abbedfd authored by Jiaming Liu's avatar Jiaming Liu
Browse files

Add TaskFragment OP_TYPE_REORDER_TO_BOTTOM and OP_TYPE_REORDER_TO_BOTTOM

Bug: 284050041
Test: atest TaskFragmentOrganizerControllerTest
Change-Id: I99d854ab784305b8e1a3b43a37fe2f3c6be48b3b
parent def158c9
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -88,6 +88,26 @@ public final class TaskFragmentOperation implements Parcelable {
     */
    public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;

    /**
     * Reorders the TaskFragment to be the bottom-most in the Task. Note that this op will bring the
     * TaskFragment to the bottom of the Task below all the other Activities and TaskFragments.
     *
     * This is only allowed for system organizers. See
     * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
     * ITaskFragmentOrganizer, boolean)}
     */
    public static final int OP_TYPE_REORDER_TO_BOTTOM_OF_TASK = 12;

    /**
     * Reorders the TaskFragment to be the top-most in the Task. Note that this op will bring the
     * TaskFragment to the top of the Task above all the other Activities and TaskFragments.
     *
     * This is only allowed for system organizers. See
     * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
     * ITaskFragmentOrganizer, boolean)}
     */
    public static final int OP_TYPE_REORDER_TO_TOP_OF_TASK = 13;

    @IntDef(prefix = { "OP_TYPE_" }, value = {
            OP_TYPE_UNKNOWN,
            OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -101,7 +121,9 @@ public final class TaskFragmentOperation implements Parcelable {
            OP_TYPE_SET_ANIMATION_PARAMS,
            OP_TYPE_SET_RELATIVE_BOUNDS,
            OP_TYPE_REORDER_TO_FRONT,
            OP_TYPE_SET_ISOLATED_NAVIGATION
            OP_TYPE_SET_ISOLATED_NAVIGATION,
            OP_TYPE_REORDER_TO_BOTTOM_OF_TASK,
            OP_TYPE_REORDER_TO_TOP_OF_TASK,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface OperationType {}
+32 −0
Original line number Diff line number Diff line
@@ -24,7 +24,9 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
@@ -1404,6 +1406,24 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                taskFragment.setIsolatedNav(isolatedNav);
                break;
            }
            case OP_TYPE_REORDER_TO_BOTTOM_OF_TASK: {
                final Task task = taskFragment.getTask();
                if (task != null) {
                    task.mChildren.remove(taskFragment);
                    task.mChildren.add(0, taskFragment);
                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
                }
                break;
            }
            case OP_TYPE_REORDER_TO_TOP_OF_TASK: {
                final Task task = taskFragment.getTask();
                if (task != null) {
                    task.mChildren.remove(taskFragment);
                    task.mChildren.add(taskFragment);
                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
                }
                break;
            }
        }
        return effects;
    }
@@ -1431,6 +1451,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
            return false;
        }

        if ((opType == OP_TYPE_REORDER_TO_BOTTOM_OF_TASK
                || opType == OP_TYPE_REORDER_TO_TOP_OF_TASK)
                && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
            final Throwable exception = new SecurityException(
                    "Only a system organizer can perform OP_TYPE_REORDER_TO_BOTTOM_OF_TASK or "
                            + "OP_TYPE_REORDER_TO_TOP_OF_TASK."
            );
            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
                    opType, exception);
            return false;
        }

        final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken();
        return secondaryFragmentToken == null
                || validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType,
+136 −0
Original line number Diff line number Diff line
@@ -26,7 +26,9 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
@@ -1655,6 +1657,127 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
        assertEquals(frontMostTaskFragment, tf0);
    }

    @Test
    public void testApplyTransaction_reorderToBottomOfTask() {
        mController.unregisterOrganizer(mIOrganizer);
        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
        final Task task = createTask(mDisplayContent);
        // Create a non-embedded Activity at the bottom.
        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
                .setTask(task)
                .build();
        final TaskFragment tf0 = createTaskFragment(task);
        final TaskFragment tf1 = createTaskFragment(task);
        // Create a non-embedded Activity at the top.
        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
                .setTask(task)
                .build();

        // Ensure correct order of the children before the operation
        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());

        // Reorder TaskFragment to bottom
        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
                OP_TYPE_REORDER_TO_BOTTOM_OF_TASK).build();
        mTransaction.addTaskFragmentOperation(tf1.getFragmentToken(), operation);
        assertApplyTransactionAllowed(mTransaction);

        // Ensure correct order of the children after the operation
        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
        assertEquals(tf0, task.getChildAt(2).asTaskFragment());
        assertEquals(bottomActivity, task.getChildAt(1).asActivityRecord());
        assertEquals(tf1, task.getChildAt(0).asTaskFragment());
    }

    @Test
    public void testApplyTransaction_reorderToTopOfTask() {
        mController.unregisterOrganizer(mIOrganizer);
        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
        final Task task = createTask(mDisplayContent);
        // Create a non-embedded Activity at the bottom.
        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
                .setTask(task)
                .build();
        final TaskFragment tf0 = createTaskFragment(task);
        final TaskFragment tf1 = createTaskFragment(task);
        // Create a non-embedded Activity at the top.
        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
                .setTask(task)
                .build();

        // Ensure correct order of the children before the operation
        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());

        // Reorder TaskFragment to top
        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
                OP_TYPE_REORDER_TO_TOP_OF_TASK).build();
        mTransaction.addTaskFragmentOperation(tf0.getFragmentToken(), operation);
        assertApplyTransactionAllowed(mTransaction);

        // Ensure correct order of the children after the operation
        assertEquals(tf0, task.getChildAt(3).asTaskFragment());
        assertEquals(topActivity, task.getChildAt(2).asActivityRecord());
        assertEquals(tf1, task.getChildAt(1).asTaskFragment());
        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
    }

    @Test
    public void testApplyTransaction_reorderToBottomOfTask_failsIfNotSystemOrganizer() {
        testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
                OP_TYPE_REORDER_TO_BOTTOM_OF_TASK);
    }

    @Test
    public void testApplyTransaction_reorderToTopOfTask_failsIfNotSystemOrganizer() {
        testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
                OP_TYPE_REORDER_TO_TOP_OF_TASK);
    }

    private void testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
            @TaskFragmentOperation.OperationType int opType) {
        final Task task = createTask(mDisplayContent);
        // Create a non-embedded Activity at the bottom.
        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
                .setTask(task)
                .build();
        final TaskFragment tf0 = createTaskFragment(task);
        final TaskFragment tf1 = createTaskFragment(task);
        // Create a non-embedded Activity at the top.
        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
                .setTask(task)
                .build();

        // Ensure correct order of the children before the operation
        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());

        // Apply reorder transaction, which is expected to fail for non-system organizer.
        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
                opType).build();
        mTransaction
                .addTaskFragmentOperation(tf0.getFragmentToken(), operation)
                .setErrorCallbackToken(mErrorToken);
        assertApplyTransactionAllowed(mTransaction);
        // The pending event will be dispatched on the handler (from requestTraversal).
        waitHandlerIdle(mWm.mAnimationHandler);

        assertTaskFragmentErrorTransaction(opType, SecurityException.class);

        // Ensure no change to the order of the children after the operation
        assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
        assertEquals(tf1, task.getChildAt(2).asTaskFragment());
        assertEquals(tf0, task.getChildAt(1).asTaskFragment());
        assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
    }

    /**
     * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
     * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the
@@ -1782,6 +1905,19 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
        assertEquals(activityToken, change.getActivityToken());
    }

    /** Setups an embedded TaskFragment. */
    private TaskFragment createTaskFragment(Task task) {
        final IBinder token = new Binder();
        TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                .setParentTask(task)
                .setFragmentToken(token)
                .setOrganizer(mOrganizer)
                .createActivityCount(1)
                .build();
        mWindowOrganizerController.mLaunchTaskFragments.put(token, taskFragment);
        return taskFragment;
    }

    /** Setups an embedded TaskFragment in a PIP Task. */
    private void setupTaskFragmentInPip() {
        mTaskFragment = new TaskFragmentBuilder(mAtm)