From 0399d9c9514d7f1b350d2206dd24dff075a8da89 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Mon, 29 Nov 2021 21:25:59 +0800 Subject: [PATCH] Block split Activity in lock task mode Before this CL, Activity was only blocked from finishing if it is the last Activity in th Task to start lock task mode. However, it may make half of screen black if the finishing Activity is in one of adjacent TaskFragments. This CL considers the scenario that two adjacent TaskFragments side-by-side. If a finishing Activity leads to TaskFragment empty, and also make the adjacent TaskFragment finish, block the Activity from finishing. Bug: 205813709 Test: manual - use Settings in task pinning mode Change-Id: I6116d644aeccc13460d302fe3b40953fa67dca6c --- .../extensions/embedding/SplitController.java | 5 +- .../extensions/embedding/SplitPresenter.java | 24 +++++++--- .../android/server/wm/LockTaskController.java | 48 +++++++++++++++---- .../com/android/server/wm/TaskFragment.java | 2 +- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index fe6c7ba3b24c9..e8d6f31459b7e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -293,11 +293,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) { + SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity, + secondaryContainer, splitRule); + // Remove container later to prevent pinning escaping toast showing in lock task mode. if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) { removeExistingSecondaryContainers(wct, primaryContainer); } - SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity, - secondaryContainer, splitRule); mSplitContainers.add(splitContainer); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 81be21cbd7aa9..ade573132eef7 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -112,8 +112,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryContainer.setLastRequestedBounds(secondaryRectBounds); // Set adjacent to each other so that the containers below will be invisible. - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), - secondaryContainer.getTaskFragmentToken(), rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -149,8 +148,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryActivity, secondaryRectBounds, primaryContainer); // Set adjacent to each other so that the containers below will be invisible. - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), - secondaryContainer.getTaskFragmentToken(), rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -269,8 +267,22 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds); - setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), - secondaryContainer.getTaskFragmentToken(), rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); + } + + private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) { + final Rect parentBounds = getParentContainerBounds(primaryContainer); + // Clear adjacent TaskFragments if the container is shown in fullscreen, or the + // secondaryContainer could not be finished. + if (!shouldShowSideBySide(parentBounds, splitRule)) { + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + null /* secondary */, null /* splitRule */); + } else { + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), splitRule); + } } /** diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 94a175caba225..8a2d11636fe37 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -251,15 +251,47 @@ public class LockTaskController { */ boolean activityBlockedFromFinish(ActivityRecord activity) { final Task task = activity.getTask(); - if (activity == task.getRootActivity() - && activity == task.getTopNonFinishingActivity() - && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV - && isRootTask(task)) { - Slog.i(TAG, "Not finishing task in lock task mode"); - showLockTaskToast(); - return true; + if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || !isRootTask(task)) { + return false; } - return false; + + final ActivityRecord taskTop = task.getTopNonFinishingActivity(); + final ActivityRecord taskRoot = task.getRootActivity(); + // If task has more than one Activity, verify if there's only adjacent TaskFragments that + // should be finish together in the Task. + if (activity != taskRoot || activity != taskTop) { + final TaskFragment taskFragment = activity.getTaskFragment(); + final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment(); + if (taskFragment.asTask() != null + || !taskFragment.isDelayLastActivityRemoval() + || adjacentTaskFragment == null) { + // Don't block activity from finishing if the TaskFragment don't have any adjacent + // TaskFragment, or it won't finish together with its adjacent TaskFragment. + return false; + } + + final boolean hasOtherActivityInTaskFragment = + taskFragment.getActivity(a -> !a.finishing && a != activity) != null; + if (hasOtherActivityInTaskFragment) { + // Don't block activity from finishing if there's other Activity in the same + // TaskFragment. + return false; + } + + final boolean hasOtherActivityInTask = task.getActivity(a -> !a.finishing + && a != activity && a.getTaskFragment() != adjacentTaskFragment) != null; + if (hasOtherActivityInTask) { + // Do not block activity from finishing if there are another running activities + // after the current and adjacent TaskFragments are removed. Note that we don't + // check activities in adjacent TaskFragment because it will be finished together + // with TaskFragment regardless of numbers of activities. + return false; + } + } + + Slog.i(TAG, "Not finishing task in lock task mode"); + showLockTaskToast(); + return true; } /** diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index f32ab1e53318d..77d0dd85424b8 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -232,7 +232,7 @@ class TaskFragment extends WindowContainer { /** * Whether to delay the last activity of TaskFragment being immediately removed while finishing. * This should only be set on a embedded TaskFragment, where the organizer can have the - * opportunity to perform other actions or animations. + * opportunity to perform animations and finishing the adjacent TaskFragment. */ private boolean mDelayLastActivityRemoval;