From 39c6eaa64ddfbe5eb772a6dc8f6ea5b6b29437f1 Mon Sep 17 00:00:00 2001 From: Shivam Agrawal Date: Tue, 15 Mar 2022 17:18:14 +0000 Subject: [PATCH 1/2] Revert "Removing TaskFragment token from LaunchActivityItem" This reverts commit 6dc124025d2b21cdc54c143e9cb54aab68b3f0d8 in tm-dev. The revert is necessary because SplitController needs to access the initial task fragment token from the activity client to ensure that split info callbacks are sent before an activity is created. Test: existing tests pass Bug: b/218791665 Change-Id: I7d6506312b46af026ec19e4aad9d0f31482e175a --- core/java/android/app/ActivityThread.java | 7 ++++++- .../servertransaction/LaunchActivityItem.java | 21 ++++++++++++------- .../servertransaction/ObjectPoolTests.java | 2 +- .../app/servertransaction/TestUtils.java | 8 ++++++- .../TransactionParcelTests.java | 1 + .../activity/ActivityThreadClientTest.java | 2 +- .../server/wm/ActivityTaskSupervisor.java | 3 ++- 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3b843a9c622a9..852dd974ae422 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -536,6 +536,9 @@ 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; int ident; @UnsupportedAppUsage Intent intent; @@ -618,7 +621,8 @@ public final class ActivityThread extends ClientTransactionHandler PersistableBundle persistentState, List pendingResults, List pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client, - IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble) { + IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble, + IBinder initialTaskFragmentToken) { this.token = token; this.assistToken = assistToken; this.shareableActivityToken = shareableActivityToken; @@ -639,6 +643,7 @@ public final class ActivityThread extends ClientTransactionHandler compatInfo); mActivityOptions = activityOptions; mLaunchedFromBubble = launchedFromBubble; + mInitialTaskFragmentToken = initialTaskFragmentToken; init(); } diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index d7e09519bfb7e..076dbef9ebc43 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -72,6 +72,7 @@ public class LaunchActivityItem extends ClientTransactionItem { private IBinder mAssistToken; private IBinder mShareableActivityToken; private boolean mLaunchedFromBubble; + private IBinder mTaskFragmentToken; /** * It is only non-null if the process is the first time to launch activity. It is only an * optimization for quick look up of the interface so the field is ignored for comparison. @@ -95,7 +96,8 @@ public class LaunchActivityItem extends ClientTransactionItem { ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, - client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble); + client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble, + mTaskFragmentToken); client.handleLaunchActivity(r, pendingActions, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -119,7 +121,7 @@ public class LaunchActivityItem extends ClientTransactionItem { List pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, IActivityClientController activityClientController, IBinder shareableActivityToken, - boolean launchedFromBubble) { + boolean launchedFromBubble, IBinder taskFragmentToken) { LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); if (instance == null) { instance = new LaunchActivityItem(); @@ -128,7 +130,7 @@ public class LaunchActivityItem extends ClientTransactionItem { voiceInteractor, procState, state, persistentState, pendingResults, pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken, activityClientController, shareableActivityToken, - launchedFromBubble); + launchedFromBubble, taskFragmentToken); return instance; } @@ -136,7 +138,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void recycle() { setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null, - null, false, null, null, null, null, false); + null, false, null, null, null, null, false, null); ObjectPool.recycle(this); } @@ -166,6 +168,7 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeStrongInterface(mActivityClientController); dest.writeStrongBinder(mShareableActivityToken); dest.writeBoolean(mLaunchedFromBubble); + dest.writeStrongBinder(mTaskFragmentToken); } /** Read from Parcel. */ @@ -184,7 +187,8 @@ public class LaunchActivityItem extends ClientTransactionItem { in.readStrongBinder(), IActivityClientController.Stub.asInterface(in.readStrongBinder()), in.readStrongBinder(), - in.readBoolean()); + in.readBoolean(), + in.readStrongBinder()); } public static final @NonNull Creator CREATOR = @@ -222,7 +226,8 @@ public class LaunchActivityItem extends ClientTransactionItem { && mIsForward == other.mIsForward && Objects.equals(mProfilerInfo, other.mProfilerInfo) && Objects.equals(mAssistToken, other.mAssistToken) - && Objects.equals(mShareableActivityToken, other.mShareableActivityToken); + && Objects.equals(mShareableActivityToken, other.mShareableActivityToken) + && Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken); } @Override @@ -244,6 +249,7 @@ public class LaunchActivityItem extends ClientTransactionItem { result = 31 * result + Objects.hashCode(mProfilerInfo); result = 31 * result + Objects.hashCode(mAssistToken); result = 31 * result + Objects.hashCode(mShareableActivityToken); + result = 31 * result + Objects.hashCode(mTaskFragmentToken); return result; } @@ -291,7 +297,7 @@ public class LaunchActivityItem extends ClientTransactionItem { List pendingResults, List pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, IActivityClientController activityClientController, - IBinder shareableActivityToken, boolean launchedFromBubble) { + IBinder shareableActivityToken, boolean launchedFromBubble, IBinder taskFragmentToken) { instance.mIntent = intent; instance.mIdent = ident; instance.mInfo = info; @@ -312,5 +318,6 @@ public class LaunchActivityItem extends ClientTransactionItem { instance.mActivityClientController = activityClientController; instance.mShareableActivityToken = shareableActivityToken; instance.mLaunchedFromBubble = launchedFromBubble; + instance.mTaskFragmentToken = taskFragmentToken; } } diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 3e261a7113acf..50639be57f22c 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -157,7 +157,7 @@ public class ObjectPoolTests { .setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList()) .setIsForward(true).setAssistToken(assistToken) .setShareableActivityToken(shareableActivityToken) - .build(); + .setTaskFragmentToken(new Binder()).build(); LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build(); LaunchActivityItem item = itemSupplier.get(); diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java index 1467fed898c42..26d9628ba55b0 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java +++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java @@ -110,6 +110,7 @@ class TestUtils { private IBinder mAssistToken; private IBinder mShareableActivityToken; private boolean mLaunchedFromBubble; + private IBinder mTaskFragmentToken; LaunchActivityItemBuilder setIntent(Intent intent) { mIntent = intent; @@ -206,13 +207,18 @@ class TestUtils { return this; } + LaunchActivityItemBuilder setTaskFragmentToken(IBinder taskFragmentToken) { + mTaskFragmentToken = taskFragmentToken; + return this; + } + LaunchActivityItem build() { return LaunchActivityItem.obtain(mIntent, mIdent, mInfo, mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */, mShareableActivityToken, - mLaunchedFromBubble); + mLaunchedFromBubble, mTaskFragmentToken); } } } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index beadc44645169..8276d10be4cd3 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -203,6 +203,7 @@ public class TransactionParcelTests { .setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic()) .setPendingNewIntents(referrerIntentList()).setIsForward(true) .setAssistToken(new Binder()).setShareableActivityToken(new Binder()) + .setTaskFragmentToken(new Binder()) .build(); writeAndPrepareForReading(item); diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index b006a1681a996..8d3751e6ad6cd 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -345,7 +345,7 @@ public class ActivityThreadClientTest { null /* pendingResults */, null /* pendingNewIntents */, null /* activityOptions */, true /* isForward */, null /* profilerInfo */, mThread /* client */, null /* asssitToken */, null /* shareableActivityToken */, - false /* launchedFromBubble */); + false /* launchedFromBubble */, null /* taskfragmentToken */); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index eb5ca9c2f43b4..95de040551b1e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -897,6 +897,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { proc.getThread(), r.token); final boolean isTransitionForward = r.isTransitionForward(); + final IBinder fragmentToken = r.getTaskFragment().getFragmentToken(); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global @@ -907,7 +908,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, r.takeOptions(), isTransitionForward, proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController, - r.shareableActivityToken, r.getLaunchedFromBubble())); + r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken)); // Set desired final state. final ActivityLifecycleItem lifecycleItem; From d8659858914ba6d66885b4a8202f4876a054463e Mon Sep 17 00:00:00 2001 From: Shivam Agrawal Date: Tue, 29 Mar 2022 22:19:51 -0400 Subject: [PATCH 2/2] Send Split Info Callback before Activity#onCreate The onTaskFragmentInfo callback from the server triggered by the creation of a new activity may not reach the client before Activity#onCreate is called because of undeterministic IPC call order. However, the application should get a split info update that includes the newly launched activity before the Activity#onCreate is called for that activity so that the application knows that the activity will be in a split, which may influence UI decisions. Bug: b/218791665 b/200303250 Test: atest ActivityEmbeddingLaunchTests Change-Id: If69c077db8ca390d41446e1329a448b3dd074293 --- .../extensions/embedding/SplitController.java | 42 ++++++++++++++++++- .../embedding/TaskFragmentContainer.java | 2 +- .../com/android/server/wm/TaskFragment.java | 2 +- 3 files changed, 42 insertions(+), 4 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 418ff0e7263a1..c7d62530a43d8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -602,14 +602,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return null; } + private void updateCallbackIfNecessary() { + updateCallbackIfNecessary(true /* deferCallbackUntilAllActivitiesCreated */); + } + /** * Notifies listeners about changes to split states if necessary. + * + * @param deferCallbackUntilAllActivitiesCreated boolean to indicate whether the split info + * callback should be deferred until all the + * organized activities have been created. */ - private void updateCallbackIfNecessary() { + private void updateCallbackIfNecessary(boolean deferCallbackUntilAllActivitiesCreated) { if (mEmbeddingCallback == null) { return; } - if (!allActivitiesCreated()) { + if (deferCallbackUntilAllActivitiesCreated && !allActivitiesCreated()) { return; } List currentSplitStates = getActiveSplitStates(); @@ -824,6 +832,36 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter { + @Override + public void onActivityPreCreated(Activity activity, Bundle savedInstanceState) { + final IBinder activityToken = activity.getActivityToken(); + final IBinder initialTaskFragmentToken = ActivityThread.currentActivityThread() + .getActivityClient(activityToken).mInitialTaskFragmentToken; + // 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) { + return; + } + for (int i = mTaskContainers.size() - 1; i >= 0; i--) { + final List containers = mTaskContainers.valueAt(i) + .mContainers; + for (int j = containers.size() - 1; j >= 0; j--) { + final TaskFragmentContainer container = containers.get(j); + if (!container.hasActivity(activityToken) + && container.getTaskFragmentToken().equals(initialTaskFragmentToken)) { + // The onTaskFragmentInfoChanged callback containing this activity has not + // reached the client yet, so add the activity to the pending appeared + // activities and send a split info callback to the client before + // {@link Activity#onCreate} is called. + container.addPendingAppearedActivity(activity); + updateCallbackIfNecessary( + false /* deferCallbackUntilAllActivitiesCreated */); + return; + } + } + } + } + @Override public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) { // Calling after Activity#onCreate is complete to allow the app launch something diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index e49af41d4eac4..9a12669f078ac 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -120,7 +120,7 @@ class TaskFragmentContainer { } ActivityStack toActivityStack() { - return new ActivityStack(collectActivities(), mInfo.getRunningActivityCount() == 0); + return new ActivityStack(collectActivities(), isEmpty()); } void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 23df429c3f243..cf32457211afa 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -2264,7 +2264,7 @@ class TaskFragment extends WindowContainer { mFragmentToken, mRemoteToken.toWindowContainerToken(), getConfiguration(), - getChildCount() == 0, + runningActivityCount[0] == 0, runningActivityCount[0], isVisible(), childActivities,