diff options
| author | 2021-08-18 17:21:04 +0800 | |
|---|---|---|
| committer | 2021-08-23 12:04:15 +0800 | |
| commit | ef49a02eddb7341bc5031175dce3d343af9f4ad1 (patch) | |
| tree | 1cbfc1d31f4065e3d68e39a6278e72db87faa863 | |
| parent | ed8dd882dc91b9c1ed61c0fb70edf0a321b3489a (diff) | |
Prevent activity being destroyed immediately if embedded
The embedded activity was destroyed immediately when being
finished because the next top activity was on the adjacent
TaskFragment and was already visible. Therefore, the finishing
animation was played before the organizer requested to
finish the activity on the adjacent TaskFragment.
Prevent the last activity of the embedded TaskFragment to
be removed immediately if the organizer requested to.
Bug: 189386466
Test: finish both activities on separate TaskFragments
Change-Id: I916eddc4dcf0de3bc7ed7264296f441d1b7bb726
8 files changed, 156 insertions, 19 deletions
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 342df4f9eee3..387837d82acf 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -516,11 +516,13 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction setAdjacentTaskFragments( - @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2) { + @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2, + @Nullable TaskFragmentAdjacentOptions options) { final HierarchyOp hierarchyOp = new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS) .setContainer(fragmentToken1) .setReparentContainer(fragmentToken2) + .setLaunchOptions(options != null ? options.toBundle() : null) .build(); mHierarchyOps.add(hierarchyOp); return this; @@ -1298,4 +1300,52 @@ public final class WindowContainerTransaction implements Parcelable { } } } + + /** + * Helper class for building an options Bundle that can be used to set adjacent rules of + * TaskFragments. + * @hide + */ + public static class TaskFragmentAdjacentOptions { + private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL = + "android:transaction.adjacent.option.delay_primary_removal"; + private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL = + "android:transaction.adjacent.option.delay_secondary_removal"; + + private boolean mDelayPrimaryLastActivityRemoval; + private boolean mDelaySecondaryLastActivityRemoval; + + public TaskFragmentAdjacentOptions() { + } + + public TaskFragmentAdjacentOptions(@NonNull Bundle bundle) { + mDelayPrimaryLastActivityRemoval = bundle.getBoolean( + DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL); + mDelaySecondaryLastActivityRemoval = bundle.getBoolean( + DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL); + } + + public void setDelayPrimaryLastActivityRemoval(boolean delay) { + mDelayPrimaryLastActivityRemoval = delay; + } + + public void setDelaySecondaryLastActivityRemoval(boolean delay) { + mDelaySecondaryLastActivityRemoval = delay; + } + + public boolean isDelayPrimaryLastActivityRemoval() { + return mDelayPrimaryLastActivityRemoval; + } + + public boolean isDelaySecondaryLastActivityRemoval() { + return mDelaySecondaryLastActivityRemoval; + } + + Bundle toBundle() { + final Bundle b = new Bundle(); + b.putBoolean(DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL, mDelayPrimaryLastActivityRemoval); + b.putBoolean(DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL, mDelaySecondaryLastActivityRemoval); + return b; + } + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java index dd00189c3bf1..4206d0375e30 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java @@ -36,6 +36,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.window.extensions.embedding.SplitRule; import java.util.Map; import java.util.concurrent.Executor; @@ -100,7 +101,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds, @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent, - @Nullable Bundle activityOptions) { + @Nullable Bundle activityOptions, @NonNull SplitRule rule) { final IBinder ownerToken = launchingActivity.getActivityToken(); // Create or resize the launching TaskFragment. @@ -117,7 +118,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { activityOptions); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments(launchingFragmentToken, secondaryFragmentToken); + setAdjacentTaskFragments(wct, launchingFragmentToken, secondaryFragmentToken, rule); } /** @@ -127,7 +128,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { */ void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) { resizeTaskFragment(wct, fragmentToken, new Rect()); - wct.setAdjacentTaskFragments(fragmentToken, null); + setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */); } /** @@ -187,6 +188,21 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent, activityOptions); } + void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) { + WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = null; + final boolean finishSecondaryWithPrimary = + splitRule != null && SplitContainer.shouldFinishSecondaryWithPrimary(splitRule); + final boolean finishPrimaryWithSecondary = + splitRule != null && SplitContainer.shouldFinishPrimaryWithSecondary(splitRule); + if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) { + adjacentOptions = new WindowContainerTransaction.TaskFragmentAdjacentOptions(); + adjacentOptions.setDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary); + adjacentOptions.setDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary); + } + wct.setAdjacentTaskFragments(primary, secondary, adjacentOptions); + } + TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken, Rect bounds, @WindowingMode int windowingMode) { if (mFragmentInfos.containsKey(fragmentToken)) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java index 8fd710a7986d..4fd2126dfa27 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java @@ -39,16 +39,10 @@ class SplitContainer { mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; - final boolean isPlaceholderContainer = isPlaceholderContainer(); - final boolean shouldFinishPrimaryWithSecondary = (mSplitRule instanceof SplitPairRule) - && ((SplitPairRule) mSplitRule).shouldFinishPrimaryWithSecondary(); - final boolean shouldFinishSecondaryWithPrimary = (mSplitRule instanceof SplitPairRule) - && ((SplitPairRule) mSplitRule).shouldFinishSecondaryWithPrimary(); - - if (shouldFinishPrimaryWithSecondary || isPlaceholderContainer) { + if (shouldFinishPrimaryWithSecondary(splitRule)) { mSecondaryContainer.addActivityToFinishOnExit(primaryActivity); } - if (shouldFinishSecondaryWithPrimary || isPlaceholderContainer) { + if (shouldFinishSecondaryWithPrimary(splitRule)) { mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); } } @@ -71,4 +65,18 @@ class SplitContainer { boolean isPlaceholderContainer() { return (mSplitRule instanceof SplitPlaceholderRule); } + + static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { + final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; + final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule) + && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary(); + return shouldFinishPrimaryWithSecondary || isPlaceholderContainer; + } + + static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) { + final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; + final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule) + && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary(); + return shouldFinishSecondaryWithPrimary || isPlaceholderContainer; + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java index a7bce20cae8b..ac85ac8cbc34 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java @@ -109,8 +109,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryContainer.setLastRequestedBounds(secondaryRectBounds); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments( - primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -144,8 +144,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryActivity, secondaryRectBounds, primaryContainer); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments( - primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -212,7 +212,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { rule); startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds, launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds, - activityIntent, activityOptions); + activityIntent, activityOptions, rule); applyTransaction(wct); primaryContainer.setLastRequestedBounds(primaryRectBounds); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b5fd111f6d77..60e7d109ec2e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3234,6 +3234,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere final ActivityRecord next = getDisplayArea().topRunningActivity( true /* considerKeyguardState */); + + // If the finishing activity is the last activity of a organized TaskFragment and has an + // adjacent TaskFragment, check if the activity removal should be delayed. + boolean delayRemoval = false; + final TaskFragment taskFragment = getTaskFragment(); + if (next != null && taskFragment != null && taskFragment.isEmbedded()) { + final TaskFragment organized = taskFragment.getOrganizedTaskFragment(); + final TaskFragment adjacent = + organized != null ? organized.getAdjacentTaskFragment() : null; + if (adjacent != null && organized.topRunningActivity() == null) { + delayRemoval = organized.isDelayLastActivityRemoval(); + } + } + // isNextNotYetVisible is to check if the next activity is invisible, or it has been // requested to be invisible but its windows haven't reported as invisible. If so, it // implied that the current finishing activity should be added into stopping list rather @@ -3248,7 +3262,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (isCurrentVisible) { - if (isNextNotYetVisible) { + if (isNextNotYetVisible || delayRemoval) { // Add this activity to the list of stopping activities. It will be processed and // destroyed when the next activity reports idle. addToStopping(false /* scheduleIdle */, false /* idleDelayed */, diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 70b0ccd76474..6e4792bdbd31 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -221,6 +221,13 @@ class TaskFragment extends WindowContainer<WindowContainer> { private IBinder mFragmentToken; /** + * 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. + */ + private boolean mDelayLastActivityRemoval; + + /** * The PID of the organizer that created this TaskFragment. It should be the same as the PID * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}. * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment. @@ -319,6 +326,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { mAdjacentTaskFragment.mAdjacentTaskFragment = null; } mAdjacentTaskFragment = null; + mDelayLastActivityRemoval = false; } void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) { @@ -422,6 +430,20 @@ class TaskFragment extends WindowContainer<WindowContainer> { } /** + * Returns the TaskFragment that is being organized, which could be this or the ascendant + * TaskFragment. + */ + @Nullable + TaskFragment getOrganizedTaskFragment() { + if (mTaskFragmentOrganizer != null) { + return this; + } + + TaskFragment parentTaskFragment = getParent().asTaskFragment(); + return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null; + } + + /** * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}. */ private void warnForNonLeafTaskFragment(String func) { @@ -2086,6 +2108,17 @@ class TaskFragment extends WindowContainer<WindowContainer> { }); } + void setDelayLastActivityRemoval(boolean delay) { + if (!mIsEmbedded) { + Slog.w(TAG, "Set delaying last activity removal on a non-embedded TF."); + } + mDelayLastActivityRemoval = delay; + } + + boolean isDelayLastActivityRemoval() { + return mDelayLastActivityRemoval; + } + boolean shouldDeferRemoval() { if (!hasChild()) { return false; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index aa147c4c8712..45c47bab1175 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -741,6 +741,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } tf1.setAdjacentTaskFragment(tf2); + + final Bundle bundle = hop.getLaunchOptions(); + final WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = + bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentOptions( + bundle) : null; + if (adjacentOptions == null) { + break; + } + + tf1.setDelayLastActivityRemoval( + adjacentOptions.isDelayPrimaryLastActivityRemoval()); + if (tf2 != null) { + tf2.setDelayLastActivityRemoval( + adjacentOptions.isDelaySecondaryLastActivityRemoval()); + } break; } return effects; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index c35f31702961..5c79f5ca2c66 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -326,7 +326,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mTransaction.startActivityInTaskFragment( mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */); mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); - mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class)); + mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class), + null /* options */); // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are // testing the security check here. |