From 9d4244ee6f80a3cf4c11cdc76202f26aff6110fb Mon Sep 17 00:00:00 2001 From: Chris Li Date: Sat, 21 May 2022 00:44:41 +0800 Subject: [PATCH] Do not resolve activity creation if it is in unknown TaskFragment This can happen in two cases: 1. The activity is embedded in another app. Then the current organizer shouldn't control it. 2. The activity is in a TaskFragment that the organizer just request to remove, but hasn't reach the server side. Then the activity will soon be finished with the TaskFragment. In either case, the organizer shouldn't need to resolve it. Otherwise it may incorrectly change the state, such as entering split for a finishing activity. Bug: 233295443 Test: atest WMJetpackUnitTests:SplitControllerTest Change-Id: I5d2028dd45abd8d20a8e82bc4e51c4d6dc6e26ee --- .../extensions/embedding/SplitController.java | 30 +++++++++++++++++-- .../embedding/SplitControllerTest.java | 9 ++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 9e7f9d22db189..23cf1d9654c4f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -363,7 +363,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * @param activity the activity that is newly added to the Task. * @param isOnReparent whether the activity is reparented to the Task instead of new launched. * We only support to split as primary for reparented activity for now. - * @return {@code true} if the activity was placed in TaskFragment container. + * @return {@code true} if the activity has been handled, such as placed in a TaskFragment, or + * in a state that the caller shouldn't handle. */ @VisibleForTesting boolean resolveActivityToContainer(@NonNull Activity activity, boolean isOnReparent) { @@ -373,6 +374,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } + if (!isOnReparent && getContainerWithActivity(activity) == null + && getInitialTaskFragmentToken(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. + // 2. the organizer has already requested to remove the TaskFragment. + // In either case, return true since we don't want any extra handling. + Log.d(TAG, "Activity is in a TaskFragment that is not recorded by the organizer. r=" + + activity); + return true; + } + /* * We will check the following to see if there is any embedding rule matched: * 1. Whether the new launched activity should always expand. @@ -1285,6 +1298,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return ActivityThread.currentActivityThread().getActivity(activityToken); } + /** + * Gets the token of the initial TaskFragment that embedded this activity. Do not rely on it + * after creation because the activity could be reparented. + */ + @VisibleForTesting + @Nullable + IBinder getInitialTaskFragmentToken(@NonNull Activity activity) { + final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread() + .getActivityClient(activity.getActivityToken()); + return record != null ? record.mInitialTaskFragmentToken : null; + } + /** * Returns {@code true} if an Activity with the provided component name should always be * expanded to occupy full task bounds. Such activity must not be put in a split. @@ -1358,8 +1383,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void onActivityPreCreated(Activity activity, Bundle savedInstanceState) { final IBinder activityToken = activity.getActivityToken(); - final IBinder initialTaskFragmentToken = ActivityThread.currentActivityThread() - .getActivityClient(activityToken).mInitialTaskFragmentToken; + final IBinder initialTaskFragmentToken = getInitialTaskFragmentToken(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) { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 7fbacbaa8761c..2fd491312f63d 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -733,6 +733,15 @@ public class SplitControllerTest { assertSplitPair(mActivity, primaryActivity); } + @Test + public void testResolveActivityToContainer_inUnknownTaskFragment() { + doReturn(new Binder()).when(mSplitController).getInitialTaskFragmentToken(mActivity); + + // No need to handle when the new launched activity is in an unknown TaskFragment. + assertTrue(mSplitController.resolveActivityToContainer(mActivity, + false /* isOnReparent */)); + } + @Test public void testGetPlaceholderOptions() { doReturn(true).when(mActivity).isResumed();