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

Commit 4c0f7be6 authored by Louis Chang's avatar Louis Chang
Browse files

Allows running TaskFragment related hierarchy op in lock task mode

The placeholder activity was removed while device folded, but
the TaskFragment was failed to be removed because the hierarchy op
was ignored in lock task mode.

Most of the TaskFragment related hierarchy ops operate in the
same task and it won't change the task order. Therefore, it should
still works in lock task mode. However, deleting a TaskFragment
may cleanup all activities in the Task, which needed to be handled
separately.

Bug: 204945438
Test: atest TaskFragmentOrganizerControllerTest
Change-Id: If1d35ef81754b53c5f214672000cc0c066628a29
parent 3695613b
Loading
Loading
Loading
Loading
+125 −99
Original line number Diff line number Diff line
@@ -581,27 +581,130 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
                break;
            }
            case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
            case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: {
                effects |= setAdjacentRootsHierarchyOp(hop);
                break;
            }
        // The following operations may change task order so they are skipped while in lock task
        // mode. The above operations are still allowed because they don't move tasks. And it may
        // be necessary such as clearing launch root after entering lock task mode.
            case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
                final TaskFragmentCreationParams taskFragmentCreationOptions =
                        hop.getTaskFragmentCreationOptions();
                createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
                break;
            }
            case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
                final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
                if (wc == null || !wc.isAttached()) {
                    Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
                    break;
                }
                final TaskFragment taskFragment = wc.asTaskFragment();
                if (taskFragment == null || taskFragment.asTask() != null) {
                    throw new IllegalArgumentException(
                            "Can only delete organized TaskFragment, but not Task.");
                }
                if (isInLockTaskMode) {
                    final ActivityRecord bottomActivity = taskFragment.getActivity(
                            a -> !a.finishing, false /* traverseTopToBottom */);
                    if (bottomActivity != null
                            && mService.getLockTaskController().activityBlockedFromFinish(
                                    bottomActivity)) {
                        Slog.w(TAG, "Skip removing TaskFragment due in lock task mode.");
                        sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
                                new IllegalStateException(
                                        "Not allow to delete task fragment in lock task mode."));
                        break;
                    }
                }
                effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
                break;
            }
            case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
                final IBinder fragmentToken = hop.getContainer();
                if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
                    final Throwable exception = new IllegalArgumentException(
                            "Not allowed to operate with invalid fragment token");
                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                    break;
                }
                final Intent activityIntent = hop.getActivityIntent();
                final Bundle activityOptions = hop.getLaunchOptions();
                final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
                final int result = mService.getActivityStartController()
                        .startActivityInTaskFragment(tf, activityIntent, activityOptions,
                                hop.getCallingActivity());
                if (!isStartResultSuccessful(result)) {
                    sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
                            errorCallbackToken,
                            convertStartFailureToThrowable(result, activityIntent));
                }
                break;
            }
            case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
                final IBinder fragmentToken = hop.getNewParent();
                final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
                if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
                    final Throwable exception = new IllegalArgumentException(
                            "Not allowed to operate with invalid fragment token or activity.");
                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                    break;
                }
                activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
                effects |= TRANSACT_EFFECTS_LIFECYCLE;
                break;
            }
            case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: {
                final IBinder fragmentToken = hop.getContainer();
                final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
                final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
                final TaskFragment tf2 = adjacentFragmentToken != null
                        ? mLaunchTaskFragments.get(adjacentFragmentToken)
                        : null;
                if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
                    final Throwable exception = new IllegalArgumentException(
                            "Not allowed to set adjacent on invalid fragment tokens");
                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                    break;
                }
                tf1.setAdjacentTaskFragment(tf2);
                effects |= TRANSACT_EFFECTS_LIFECYCLE;

                final Bundle bundle = hop.getLaunchOptions();
                final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
                        bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams(
                                bundle) : null;
                if (adjacentParams == null) {
                    break;
                }

                tf1.setDelayLastActivityRemoval(
                        adjacentParams.shouldDelayPrimaryLastActivityRemoval());
                if (tf2 != null) {
                    tf2.setDelayLastActivityRemoval(
                            adjacentParams.shouldDelaySecondaryLastActivityRemoval());
                }
                break;
            }
            default: {
                // The other operations may change task order so they are skipped while in lock
                // task mode. The above operations are still allowed because they don't move
                // tasks. And it may be necessary such as clearing launch root after entering
                // lock task mode.
                if (isInLockTaskMode) {
            Slog.w(TAG, "Skip applying hierarchy operation " + hop + " while in lock task mode");
                    Slog.w(TAG, "Skip applying hierarchy operation " + hop
                            + " while in lock task mode");
                    return effects;
                }
            }
        }

        final WindowContainer wc;
        final IBinder fragmentToken;
        switch (type) {
            case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
            case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
                effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
                break;
            }
            case HIERARCHY_OP_TYPE_REORDER:
            case HIERARCHY_OP_TYPE_REPARENT:
                wc = WindowContainer.fromBinder(hop.getContainer());
            case HIERARCHY_OP_TYPE_REPARENT: {
                final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
                if (wc == null || !wc.isAttached()) {
                    Slog.e(TAG, "Attempt to operate on detached container: " + wc);
                    break;
@@ -630,7 +733,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                }
                effects |= sanitizeAndApplyHierarchyOp(wc, hop);
                break;
            case HIERARCHY_OP_TYPE_LAUNCH_TASK:
            }
            case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
                mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
                        "launchTask HierarchyOp");
                final Bundle launchOpts = hop.getLaunchOptions();
@@ -660,7 +764,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                    }
                }
                break;
            case HIERARCHY_OP_TYPE_PENDING_INTENT:
            }
            case HIERARCHY_OP_TYPE_PENDING_INTENT: {
                String resolvedType = hop.getActivityIntent() != null
                        ? hop.getActivityIntent().resolveTypeIfNeeded(
                        mService.mContext.getContentResolver())
@@ -683,57 +788,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                        hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
                        null /* requiredPermission */, options);
                break;
            case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
                final TaskFragmentCreationParams taskFragmentCreationOptions =
                        hop.getTaskFragmentCreationOptions();
                createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
                break;
            case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
                wc = WindowContainer.fromBinder(hop.getContainer());
                if (wc == null || !wc.isAttached()) {
                    Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
                    break;
                }
                final TaskFragment taskFragment = wc.asTaskFragment();
                if (taskFragment == null || taskFragment.asTask() != null) {
                    throw new IllegalArgumentException(
                            "Can only delete organized TaskFragment, but not Task.");
                }
                effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
                break;
            case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
                fragmentToken = hop.getContainer();
                if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
                    final Throwable exception = new IllegalArgumentException(
                            "Not allowed to operate with invalid fragment token");
                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                    break;
                }
                final Intent activityIntent = hop.getActivityIntent();
                final Bundle activityOptions = hop.getLaunchOptions();
                final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
                final int result = mService.getActivityStartController()
                        .startActivityInTaskFragment(tf, activityIntent, activityOptions,
                                hop.getCallingActivity());
                if (!isStartResultSuccessful(result)) {
                    sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
                            errorCallbackToken,
                            convertStartFailureToThrowable(result, activityIntent));
                }
                break;
            case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
                fragmentToken = hop.getNewParent();
                final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
                if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
                    final Throwable exception = new IllegalArgumentException(
                            "Not allowed to operate with invalid fragment token or activity.");
                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                    break;
            }
                activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
                effects |= TRANSACT_EFFECTS_LIFECYCLE;
                break;
            case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
            case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
                final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
                final WindowContainer newParent = hop.getNewParent() != null
                        ? WindowContainer.fromBinder(hop.getNewParent())
@@ -746,37 +802,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                reparentTaskFragment(oldParent, newParent, errorCallbackToken);
                effects |= TRANSACT_EFFECTS_LIFECYCLE;
                break;
            case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
                fragmentToken = hop.getContainer();
                final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
                final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
                final TaskFragment tf2 = adjacentFragmentToken != null
                        ? mLaunchTaskFragments.get(adjacentFragmentToken)
                        : null;
                if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
                    final Throwable exception = new IllegalArgumentException(
                            "Not allowed to set adjacent on invalid fragment tokens");
                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                    break;
                }
                tf1.setAdjacentTaskFragment(tf2);
                effects |= TRANSACT_EFFECTS_LIFECYCLE;

                final Bundle bundle = hop.getLaunchOptions();
                final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
                        bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams(
                                bundle) : null;
                if (adjacentParams == null) {
                    break;
                }

                tf1.setDelayLastActivityRemoval(
                        adjacentParams.shouldDelayPrimaryLastActivityRemoval());
                if (tf2 != null) {
                    tf2.setDelayLastActivityRemoval(
                            adjacentParams.shouldDelaySecondaryLastActivityRemoval());
            }
                break;
        }
        return effects;
    }