Merge "Update TaskFragmentToken for Activity relaunch" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
379c38b1ae
@@ -534,9 +534,8 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
// A reusable token for other purposes, e.g. content capture, translation. It shouldn't be
|
||||
// used without security checks
|
||||
public IBinder shareableActivityToken;
|
||||
// The token of the initial TaskFragment that embedded this activity. Do not rely on it
|
||||
// after creation because the activity could be reparented.
|
||||
@Nullable public IBinder mInitialTaskFragmentToken;
|
||||
// The token of the TaskFragment that embedded this activity.
|
||||
@Nullable public IBinder mTaskFragmentToken;
|
||||
int ident;
|
||||
@UnsupportedAppUsage
|
||||
Intent intent;
|
||||
@@ -620,7 +619,7 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
|
||||
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
|
||||
IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble,
|
||||
IBinder initialTaskFragmentToken) {
|
||||
IBinder taskFragmentToken) {
|
||||
this.token = token;
|
||||
this.assistToken = assistToken;
|
||||
this.shareableActivityToken = shareableActivityToken;
|
||||
@@ -641,7 +640,7 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
compatInfo);
|
||||
mActivityOptions = activityOptions;
|
||||
mLaunchedFromBubble = launchedFromBubble;
|
||||
mInitialTaskFragmentToken = initialTaskFragmentToken;
|
||||
mTaskFragmentToken = taskFragmentToken;
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
@@ -583,7 +583,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
|
||||
if (!isOnReparent && getContainerWithActivity(activity) == null
|
||||
&& getInitialTaskFragmentToken(activity) != null) {
|
||||
&& getTaskFragmentTokenFromActivityClientRecord(activity) != null) {
|
||||
// We can't find the new launched activity in any recorded container, but it is
|
||||
// currently placed in an embedded TaskFragment. This can happen in two cases:
|
||||
// 1. the activity is embedded in another app.
|
||||
@@ -866,11 +866,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mLock")
|
||||
void onActivityDestroyed(@NonNull Activity activity) {
|
||||
// Remove any pending appeared activity, as the server won't send finished activity to the
|
||||
// organizer.
|
||||
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
|
||||
mTaskContainers.valueAt(i).cleanupPendingAppearedActivity(activity);
|
||||
mTaskContainers.valueAt(i).onActivityDestroyed(activity);
|
||||
}
|
||||
// We didn't trigger the callback if there were any pending appeared activities, so check
|
||||
// again after the pending is removed.
|
||||
@@ -1605,15 +1606,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token of the initial TaskFragment that embedded this activity. Do not rely on it
|
||||
* after creation because the activity could be reparented.
|
||||
* Gets the token of the TaskFragment that embedded this activity. It is available as soon as
|
||||
* the activity is created and attached, so it can be used during {@link #onActivityCreated}
|
||||
* before the server notifies the organizer to avoid racing condition.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
IBinder getInitialTaskFragmentToken(@NonNull Activity activity) {
|
||||
IBinder getTaskFragmentTokenFromActivityClientRecord(@NonNull Activity activity) {
|
||||
final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread()
|
||||
.getActivityClient(activity.getActivityToken());
|
||||
return record != null ? record.mInitialTaskFragmentToken : null;
|
||||
return record != null ? record.mTaskFragmentToken : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1691,7 +1693,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
synchronized (mLock) {
|
||||
final IBinder activityToken = activity.getActivityToken();
|
||||
final IBinder initialTaskFragmentToken = getInitialTaskFragmentToken(activity);
|
||||
final IBinder initialTaskFragmentToken =
|
||||
getTaskFragmentTokenFromActivityClientRecord(activity);
|
||||
// If the activity is not embedded, then it will not have an initial task fragment
|
||||
// token so no further action is needed.
|
||||
if (initialTaskFragmentToken == null) {
|
||||
|
||||
@@ -137,6 +137,13 @@ class TaskContainer {
|
||||
return mContainers.isEmpty() && mFinishedContainer.isEmpty();
|
||||
}
|
||||
|
||||
/** Called when the activity is destroyed. */
|
||||
void onActivityDestroyed(@NonNull Activity activity) {
|
||||
for (TaskFragmentContainer container : mContainers) {
|
||||
container.onActivityDestroyed(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the pending appeared activity from all TaskFragments in this Task. */
|
||||
void cleanupPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
|
||||
for (TaskFragmentContainer container : mContainers) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.WindowConfiguration.WindowingMode;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
@@ -190,6 +191,19 @@ class TaskFragmentContainer {
|
||||
// Remove the pending activity from other TaskFragments.
|
||||
mTaskContainer.cleanupPendingAppearedActivity(pendingAppearedActivity);
|
||||
mPendingAppearedActivities.add(pendingAppearedActivity);
|
||||
updateActivityClientRecordTaskFragmentToken(pendingAppearedActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link ActivityThread.ActivityClientRecord#mTaskFragmentToken} for the
|
||||
* activity. This makes sure the token is up-to-date if the activity is relaunched later.
|
||||
*/
|
||||
private void updateActivityClientRecordTaskFragmentToken(@NonNull Activity activity) {
|
||||
final ActivityThread.ActivityClientRecord record = ActivityThread
|
||||
.currentActivityThread().getActivityClient(activity.getActivityToken());
|
||||
if (record != null) {
|
||||
record.mTaskFragmentToken = mToken;
|
||||
}
|
||||
}
|
||||
|
||||
void removePendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
|
||||
@@ -197,8 +211,29 @@ class TaskFragmentContainer {
|
||||
}
|
||||
|
||||
void clearPendingAppearedActivities() {
|
||||
final List<Activity> cleanupActivities = new ArrayList<>(mPendingAppearedActivities);
|
||||
// Clear mPendingAppearedActivities so that #getContainerWithActivity won't return the
|
||||
// current TaskFragment.
|
||||
mPendingAppearedActivities.clear();
|
||||
mPendingAppearedIntent = null;
|
||||
|
||||
// For removed pending activities, we need to update the them to their previous containers.
|
||||
for (Activity activity : cleanupActivities) {
|
||||
final TaskFragmentContainer curContainer = mController.getContainerWithActivity(
|
||||
activity);
|
||||
if (curContainer != null) {
|
||||
curContainer.updateActivityClientRecordTaskFragmentToken(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the activity is destroyed. */
|
||||
void onActivityDestroyed(@NonNull Activity activity) {
|
||||
removePendingAppearedActivity(activity);
|
||||
if (mInfo != null) {
|
||||
// Remove the activity now because there can be a delay before the server callback.
|
||||
mInfo.getActivities().remove(activity.getActivityToken());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -930,7 +930,8 @@ public class SplitControllerTest {
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_inUnknownTaskFragment() {
|
||||
doReturn(new Binder()).when(mSplitController).getInitialTaskFragmentToken(mActivity);
|
||||
doReturn(new Binder()).when(mSplitController)
|
||||
.getTaskFragmentTokenFromActivityClientRecord(mActivity);
|
||||
|
||||
// No need to handle when the new launched activity is in an unknown TaskFragment.
|
||||
assertTrue(mSplitController.resolveActivityToContainer(mTransaction, mActivity,
|
||||
|
||||
@@ -316,6 +316,25 @@ public class TaskFragmentContainerTest {
|
||||
assertEquals(activity, container.getBottomMostActivity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnActivityDestroyed() {
|
||||
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
mIntent, taskContainer, mController);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
final List<IBinder> activities = new ArrayList<>();
|
||||
activities.add(mActivity.getActivityToken());
|
||||
doReturn(activities).when(mInfo).getActivities();
|
||||
container.setInfo(mTransaction, mInfo);
|
||||
|
||||
assertTrue(container.hasActivity(mActivity.getActivityToken()));
|
||||
|
||||
taskContainer.onActivityDestroyed(mActivity);
|
||||
|
||||
// It should not contain the destroyed Activity.
|
||||
assertFalse(container.hasActivity(mActivity.getActivityToken()));
|
||||
}
|
||||
|
||||
/** Creates a mock activity in the organizer process. */
|
||||
private Activity createMockActivity() {
|
||||
final Activity activity = mock(Activity.class);
|
||||
|
||||
Reference in New Issue
Block a user