Loading core/java/android/app/ActivityThread.java +4 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -641,7 +640,7 @@ public final class ActivityThread extends ClientTransactionHandler compatInfo); mActivityOptions = activityOptions; mLaunchedFromBubble = launchedFromBubble; mInitialTaskFragmentToken = initialTaskFragmentToken; mTaskFragmentToken = taskFragmentToken; init(); } Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +10 −7 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -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; } /** Loading Loading @@ -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) { Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +7 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +35 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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 Loading libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -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, Loading Loading
core/java/android/app/ActivityThread.java +4 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -641,7 +640,7 @@ public final class ActivityThread extends ClientTransactionHandler compatInfo); mActivityOptions = activityOptions; mLaunchedFromBubble = launchedFromBubble; mInitialTaskFragmentToken = initialTaskFragmentToken; mTaskFragmentToken = taskFragmentToken; init(); } Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +10 −7 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -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; } /** Loading Loading @@ -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) { Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +7 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +35 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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 Loading
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -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, Loading