Merge "Prevent activity being destroyed immediately if embedded" into sc-v2-dev
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 342df4f..387837d 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -516,11 +516,13 @@
*/
@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 @@
}
}
}
+
+ /**
+ * 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 dd00189..4206d03 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 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 @@
@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 @@
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 @@
*/
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 @@
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 8fd710a..4fd2126 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 @@
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 @@
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 a7bce20..ac85ac8 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 @@
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 @@
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 @@
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 e2a5a24..856c1e0 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 @@
// 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 @@
}
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 70b0ccd..6e4792bd 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 @@
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 @@
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
}
mAdjacentTaskFragment = null;
+ mDelayLastActivityRemoval = false;
}
void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) {
@@ -422,6 +430,20 @@
}
/**
+ * 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 @@
});
}
+ 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 aa147c4..45c47ba 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 @@
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 c35f317..5c79f5c 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 @@
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.