Merge "Prevent activity being destroyed immediately if embedded" into sc-v2-dev am: eec6c21ef8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15537319

Change-Id: Ia84d2e44c707eab2975f50344a5e5f6f43e88213
This commit is contained in:
Louis Chang
2021-08-23 09:51:49 +00:00
committed by Automerger Merge Worker
8 changed files with 156 additions and 19 deletions

View File

@@ -516,11 +516,13 @@ public final class WindowContainerTransaction implements Parcelable {
*/ */
@NonNull @NonNull
public WindowContainerTransaction setAdjacentTaskFragments( public WindowContainerTransaction setAdjacentTaskFragments(
@NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2) { @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2,
@Nullable TaskFragmentAdjacentOptions options) {
final HierarchyOp hierarchyOp = final HierarchyOp hierarchyOp =
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS) new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS)
.setContainer(fragmentToken1) .setContainer(fragmentToken1)
.setReparentContainer(fragmentToken2) .setReparentContainer(fragmentToken2)
.setLaunchOptions(options != null ? options.toBundle() : null)
.build(); .build();
mHierarchyOps.add(hierarchyOp); mHierarchyOps.add(hierarchyOp);
return this; 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;
}
}
} }

View File

@@ -36,6 +36,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.window.extensions.embedding.SplitRule;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -100,7 +101,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds, @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds,
@NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken,
@NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent, @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent,
@Nullable Bundle activityOptions) { @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
final IBinder ownerToken = launchingActivity.getActivityToken(); final IBinder ownerToken = launchingActivity.getActivityToken();
// Create or resize the launching TaskFragment. // Create or resize the launching TaskFragment.
@@ -117,7 +118,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
activityOptions); activityOptions);
// Set adjacent to each other so that the containers below will be invisible. // 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) { void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
resizeTaskFragment(wct, fragmentToken, new Rect()); 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); 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, TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken,
Rect bounds, @WindowingMode int windowingMode) { Rect bounds, @WindowingMode int windowingMode) {
if (mFragmentInfos.containsKey(fragmentToken)) { if (mFragmentInfos.containsKey(fragmentToken)) {

View File

@@ -39,16 +39,10 @@ class SplitContainer {
mSecondaryContainer = secondaryContainer; mSecondaryContainer = secondaryContainer;
mSplitRule = splitRule; mSplitRule = splitRule;
final boolean isPlaceholderContainer = isPlaceholderContainer(); if (shouldFinishPrimaryWithSecondary(splitRule)) {
final boolean shouldFinishPrimaryWithSecondary = (mSplitRule instanceof SplitPairRule)
&& ((SplitPairRule) mSplitRule).shouldFinishPrimaryWithSecondary();
final boolean shouldFinishSecondaryWithPrimary = (mSplitRule instanceof SplitPairRule)
&& ((SplitPairRule) mSplitRule).shouldFinishSecondaryWithPrimary();
if (shouldFinishPrimaryWithSecondary || isPlaceholderContainer) {
mSecondaryContainer.addActivityToFinishOnExit(primaryActivity); mSecondaryContainer.addActivityToFinishOnExit(primaryActivity);
} }
if (shouldFinishSecondaryWithPrimary || isPlaceholderContainer) { if (shouldFinishSecondaryWithPrimary(splitRule)) {
mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
} }
} }
@@ -71,4 +65,18 @@ class SplitContainer {
boolean isPlaceholderContainer() { boolean isPlaceholderContainer() {
return (mSplitRule instanceof SplitPlaceholderRule); 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;
}
} }

View File

@@ -109,8 +109,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
secondaryContainer.setLastRequestedBounds(secondaryRectBounds); secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
// Set adjacent to each other so that the containers below will be invisible. // Set adjacent to each other so that the containers below will be invisible.
wct.setAdjacentTaskFragments( setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); secondaryContainer.getTaskFragmentToken(), rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -144,8 +144,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
secondaryActivity, secondaryRectBounds, primaryContainer); secondaryActivity, secondaryRectBounds, primaryContainer);
// Set adjacent to each other so that the containers below will be invisible. // Set adjacent to each other so that the containers below will be invisible.
wct.setAdjacentTaskFragments( setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); secondaryContainer.getTaskFragmentToken(), rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -212,7 +212,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
rule); rule);
startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds, startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds, launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
activityIntent, activityOptions); activityIntent, activityOptions, rule);
applyTransaction(wct); applyTransaction(wct);
primaryContainer.setLastRequestedBounds(primaryRectBounds); primaryContainer.setLastRequestedBounds(primaryRectBounds);

View File

@@ -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 // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
final ActivityRecord next = getDisplayArea().topRunningActivity( final ActivityRecord next = getDisplayArea().topRunningActivity(
true /* considerKeyguardState */); 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 // 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 // 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 // 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 (isCurrentVisible) {
if (isNextNotYetVisible) { if (isNextNotYetVisible || delayRemoval) {
// Add this activity to the list of stopping activities. It will be processed and // Add this activity to the list of stopping activities. It will be processed and
// destroyed when the next activity reports idle. // destroyed when the next activity reports idle.
addToStopping(false /* scheduleIdle */, false /* idleDelayed */, addToStopping(false /* scheduleIdle */, false /* idleDelayed */,

View File

@@ -220,6 +220,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@Nullable @Nullable
private IBinder mFragmentToken; 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 * The PID of the organizer that created this TaskFragment. It should be the same as the PID
* of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}. * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}.
@@ -319,6 +326,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mAdjacentTaskFragment.mAdjacentTaskFragment = null; mAdjacentTaskFragment.mAdjacentTaskFragment = null;
} }
mAdjacentTaskFragment = null; mAdjacentTaskFragment = null;
mDelayLastActivityRemoval = false;
} }
void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) { void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) {
@@ -421,6 +429,20 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false; return false;
} }
/**
* 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}. * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
*/ */
@@ -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() { boolean shouldDeferRemoval() {
if (!hasChild()) { if (!hasChild()) {
return false; return false;

View File

@@ -741,6 +741,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break; break;
} }
tf1.setAdjacentTaskFragment(tf2); 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; break;
} }
return effects; return effects;

View File

@@ -326,7 +326,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mTransaction.startActivityInTaskFragment( mTransaction.startActivityInTaskFragment(
mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */); mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); 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 // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
// testing the security check here. // testing the security check here.