diff options
8 files changed, 247 insertions, 18 deletions
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java index 32e3f5ad10ca..1e5b0971aad6 100644 --- a/core/java/android/window/TaskFragmentTransaction.java +++ b/core/java/android/window/TaskFragmentTransaction.java @@ -189,6 +189,10 @@ public final class TaskFragmentTransaction implements Parcelable { @Nullable private IBinder mActivityToken; + /** @see #setOtherActivityToken(IBinder) */ + @Nullable + private IBinder mOtherActivityToken; + @Nullable private TaskFragmentParentInfo mTaskFragmentParentInfo; @@ -210,6 +214,7 @@ public final class TaskFragmentTransaction implements Parcelable { mActivityToken = in.readStrongBinder(); mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR); mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR); + mOtherActivityToken = in.readStrongBinder(); } @Override @@ -224,6 +229,7 @@ public final class TaskFragmentTransaction implements Parcelable { dest.writeStrongBinder(mActivityToken); dest.writeTypedObject(mTaskFragmentParentInfo, flags); dest.writeTypedObject(mSurfaceControl, flags); + dest.writeStrongBinder(mOtherActivityToken); } /** The change is related to the TaskFragment created with this unique token. */ @@ -292,6 +298,21 @@ public final class TaskFragmentTransaction implements Parcelable { } /** + * Token of another activity. + * <p>For {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}, it is the next activity (behind the + * reparented one) that fills the Task and occludes other activities. It will be the + * actual activity token if the activity belongs to the same process as the organizer. + * Otherwise, it is {@code null}. + * + * @hide + */ + @NonNull + public Change setOtherActivityToken(@NonNull IBinder activityToken) { + mOtherActivityToken = requireNonNull(activityToken); + return this; + } + + /** * Sets info of the parent Task of the embedded TaskFragment. * @see TaskFragmentParentInfo * @@ -350,6 +371,12 @@ public final class TaskFragmentTransaction implements Parcelable { return mActivityToken; } + /** @hide */ + @Nullable + public IBinder getOtherActivityToken() { + return mOtherActivityToken; + } + /** * Obtains the {@link TaskFragmentParentInfo} for this transaction. */ diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index 4d1b87a3d97a..b4678f6d812d 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -124,7 +124,10 @@ flag { flag { namespace: "windowing_sdk" - name: "pip_restore_to_overlay" + name: "fix_pip_restore_to_overlay" description: "Restore exit-pip activity back to ActivityEmbedding overlay" bug: "297887697" + metadata { + purpose: PURPOSE_BUGFIX + } }
\ No newline at end of file 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 13c2d1f73461..b764b6ee065f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -50,6 +50,7 @@ import static androidx.window.extensions.embedding.SplitPresenter.getActivityInt import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions; import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds; import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit; +import static androidx.window.extensions.embedding.TaskFragmentContainer.OverlayContainerRestoreParams; import android.annotation.CallbackExecutor; import android.app.Activity; @@ -133,6 +134,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final List<EmbeddingRule> mSplitRules = new ArrayList<>(); /** + * Stores the token of the associated Activity that maps to the + * {@link OverlayContainerRestoreParams} of the most recent created overlay container. + */ + @GuardedBy("mLock") + final ArrayMap<IBinder, OverlayContainerRestoreParams> mOverlayRestoreParams = new ArrayMap<>(); + + /** * A developer-defined {@link SplitAttributes} calculator to compute the current * {@link SplitAttributes} with the current device and window states. * It is registered via {@link #setSplitAttributesCalculator(Function)} @@ -686,11 +694,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen exception); break; case TYPE_ACTIVITY_REPARENTED_TO_TASK: + final IBinder candidateAssociatedActToken, lastOverlayToken; + if (Flags.fixPipRestoreToOverlay()) { + candidateAssociatedActToken = change.getOtherActivityToken(); + lastOverlayToken = change.getTaskFragmentToken(); + } else { + candidateAssociatedActToken = lastOverlayToken = null; + } onActivityReparentedToTask( wct, taskId, change.getActivityIntent(), - change.getActivityToken()); + change.getActivityToken(), + candidateAssociatedActToken, + lastOverlayToken); break; default: throw new IllegalArgumentException( @@ -917,11 +934,28 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * different process, the server will generate a temporary token that * the organizer can use to reparent the activity through * {@link WindowContainerTransaction} if needed. + * @param candidateAssociatedActToken The token of the candidate associated-activity. + * @param lastOverlayToken The last parent overlay container token. */ @VisibleForTesting @GuardedBy("mLock") void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct, - int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) { + int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken, + @Nullable IBinder candidateAssociatedActToken, @Nullable IBinder lastOverlayToken) { + // Reparent the activity to an overlay container if needed. + final OverlayContainerRestoreParams params = getOverlayContainerRestoreParams( + candidateAssociatedActToken, lastOverlayToken); + if (params != null) { + final Activity associatedActivity = getActivity(candidateAssociatedActToken); + final TaskFragmentContainer targetContainer = createOrUpdateOverlayTaskFragmentIfNeeded( + wct, params.mOptions, params.mIntent, associatedActivity); + if (targetContainer != null) { + wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(), + activityToken); + return; + } + } + // If the activity belongs to the current app process, we treat it as a new activity // launch. final Activity activity = getActivity(activityToken); @@ -966,6 +1000,43 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** + * Returns the {@link OverlayContainerRestoreParams} that stored last time the {@code + * associatedActivityToken} associated with and only if data matches the {@code overlayToken}. + * Otherwise, return {@code null}. + */ + @VisibleForTesting + @GuardedBy("mLock") + @Nullable + OverlayContainerRestoreParams getOverlayContainerRestoreParams( + @Nullable IBinder associatedActivityToken, @Nullable IBinder overlayToken) { + if (!Flags.fixPipRestoreToOverlay()) { + return null; + } + + if (associatedActivityToken == null || overlayToken == null) { + return null; + } + + final TaskFragmentContainer.OverlayContainerRestoreParams params = + mOverlayRestoreParams.get(associatedActivityToken); + if (params == null) { + return null; + } + + if (params.mOverlayToken != overlayToken) { + // Not the same overlay container, no need to restore. + return null; + } + + final Activity associatedActivity = getActivity(associatedActivityToken); + if (associatedActivity == null || associatedActivity.isFinishing()) { + return null; + } + + return params; + } + + /** * Called when the {@link WindowContainerTransaction} created with * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side. * @@ -1433,6 +1504,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen for (int i = mTaskContainers.size() - 1; i >= 0; i--) { mTaskContainers.valueAt(i).onFinishingActivityPaused(wct, activityToken); } + + mOverlayRestoreParams.remove(activity.getActivityToken()); updateCallbackIfNecessary(); } @@ -1450,6 +1523,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen for (int i = mTaskContainers.size() - 1; i >= 0; i--) { mTaskContainers.valueAt(i).onActivityDestroyed(wct, activityToken); } + + mOverlayRestoreParams.remove(activity.getActivityToken()); // We didn't trigger the callback if there were any pending appeared activities, so check // again after the pending is removed. updateCallbackIfNecessary(); 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 482554351b97..d0b6a01bb51e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -36,6 +36,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.Collections; @@ -274,6 +275,15 @@ class TaskFragmentContainer { addPendingAppearedActivity(pendingAppearedActivity); } mPendingAppearedIntent = pendingAppearedIntent; + + // Save the information necessary for restoring the overlay when needed. + if (Flags.fixPipRestoreToOverlay() && overlayTag != null && pendingAppearedIntent != null + && associatedActivity != null && !associatedActivity.isFinishing()) { + final IBinder associatedActivityToken = associatedActivity.getActivityToken(); + final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken, + launchOptions, pendingAppearedIntent); + mController.mOverlayRestoreParams.put(associatedActivityToken, params); + } } /** @@ -1105,4 +1115,25 @@ class TaskFragmentContainer { } return sb.append("]").toString(); } + + static class OverlayContainerRestoreParams { + /** The token of the overlay container */ + @NonNull + final IBinder mOverlayToken; + + /** The launch options to create this container. */ + @NonNull + final Bundle mOptions; + + /** The Intent that used to be started in the overlay container. */ + @NonNull + final Intent mIntent; + + OverlayContainerRestoreParams(@NonNull IBinder overlayToken, @NonNull Bundle options, + @NonNull Intent intent) { + mOverlayToken = overlayToken; + mOptions = options; + mIntent = intent; + } + } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 9ebcb759115d..f3222572a3e2 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -836,6 +836,30 @@ public class OverlayPresentationTest { any()); } + @Test + public void testOnActivityReparentedToTask_overlayRestoration() { + mSetFlagRule.enableFlags(Flags.FLAG_FIX_PIP_RESTORE_TO_OVERLAY); + + // Prepares and mock the data necessary for the test. + final IBinder activityToken = mActivity.getActivityToken(); + final Intent intent = new Intent(); + final IBinder fillTaskActivityToken = new Binder(); + final IBinder lastOverlayToken = new Binder(); + final TaskFragmentContainer overlayContainer = mSplitController.newContainer(intent, + mActivity, TASK_ID); + final TaskFragmentContainer.OverlayContainerRestoreParams params = mock( + TaskFragmentContainer.OverlayContainerRestoreParams.class); + doReturn(params).when(mSplitController).getOverlayContainerRestoreParams(any(), any()); + doReturn(overlayContainer).when(mSplitController).createOrUpdateOverlayTaskFragmentIfNeeded( + any(), any(), any(), any()); + + // Verify the activity should be reparented to the overlay container. + mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken, + fillTaskActivityToken, lastOverlayToken); + verify(mTransaction).reparentActivityToTaskFragment( + eq(overlayContainer.getTaskFragmentToken()), eq(activityToken)); + } + /** * A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded} */ 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 7d86ec2272af..35353dbe36be 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 @@ -397,7 +397,8 @@ public class SplitControllerTest { @Test public void testOnActivityReparentedToTask_sameProcess() { mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, new Intent(), - mActivity.getActivityToken()); + mActivity.getActivityToken(), null /* fillTaskActivityToken */, + null /* lastOverlayToken */); // Treated as on activity created, but allow to split as primary. verify(mSplitController).resolveActivityToContainer(mTransaction, @@ -413,7 +414,8 @@ public class SplitControllerTest { final IBinder activityToken = new Binder(); final Intent intent = new Intent(); - mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken); + mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken, + null /* fillTaskActivityToken */, null /* lastOverlayToken */); // Treated as starting new intent verify(mSplitController, never()).resolveActivityToContainer(any(), any(), anyBoolean()); @@ -1210,7 +1212,7 @@ public class SplitControllerTest { mSplitController.onTransactionReady(transaction); verify(mSplitController).onActivityReparentedToTask(any(), eq(TASK_ID), eq(intent), - eq(activityToken)); + eq(activityToken), any(), any()); verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(), anyInt(), anyBoolean()); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 76e7f535c60f..d053bbb28ad1 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -77,7 +77,6 @@ import static android.content.pm.ActivityInfo.FLAG_NO_HISTORY; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED; import static android.content.pm.ActivityInfo.FLAG_TURN_SCREEN_ON; -import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION; import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED; import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; @@ -87,6 +86,7 @@ import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; +import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION; import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN; import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE; import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM; @@ -121,6 +121,7 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.WindowManager.ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15; +import static android.view.WindowManager.ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -128,7 +129,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED; import static android.view.WindowManager.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING; -import static android.view.WindowManager.ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_UNSET; @@ -682,6 +682,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // it references to gets removed. This should also be cleared when we move out of pip. private Task mLastParentBeforePip; + // The token of the previous TaskFragment parent of this embedded ActivityRecord when it is + // reparented to a new Task due to picture-in-picture. + // Note that the TaskFragment may be finished and no longer attached in WM hierarchy. + @Nullable + private IBinder mLastEmbeddedParentTfTokenBeforePip; + // Only set if this instance is a launch-into-pip Activity, points to the // host Activity the launch-into-pip Activity is originated from. private ActivityRecord mLaunchIntoPipHostActivity; @@ -1806,6 +1812,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLastTaskFragmentOrganizerBeforePip = organizedTf != null ? organizedTf.getTaskFragmentOrganizer() : null; + if (organizedTf != null + // Not necessary for content pip. + && launchIntoPipHostActivity == null) { + mLastEmbeddedParentTfTokenBeforePip = organizedTf.getFragmentToken(); + } } void clearLastParentBeforePip() { @@ -1815,12 +1826,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } mLaunchIntoPipHostActivity = null; mLastTaskFragmentOrganizerBeforePip = null; + mLastEmbeddedParentTfTokenBeforePip = null; } @Nullable Task getLastParentBeforePip() { return mLastParentBeforePip; } + @Nullable IBinder getLastEmbeddedParentTfTokenBeforePip() { + return mLastEmbeddedParentTfTokenBeforePip; + } + @Nullable ActivityRecord getLaunchIntoPipHostActivity() { return mLaunchIntoPipHostActivity; } diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 24b533a23af6..c4e932abecd3 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -365,7 +365,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr @Nullable TaskFragmentTransaction.Change prepareActivityReparentedToTask( - @NonNull ActivityRecord activity) { + @NonNull ActivityRecord activity, @Nullable ActivityRecord nextFillTaskActivity, + @Nullable IBinder lastParentTfToken) { if (activity.finishing) { Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing"); return null; @@ -408,10 +409,21 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr } ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d", activity.token, task.mTaskId); - return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENTED_TO_TASK) - .setTaskId(task.mTaskId) - .setActivityIntent(trimIntent(activity.intent)) - .setActivityToken(activityToken); + + final TaskFragmentTransaction.Change change = + new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENTED_TO_TASK) + .setTaskId(task.mTaskId) + .setActivityIntent(trimIntent(activity.intent)) + .setActivityToken(activityToken); + if (lastParentTfToken != null) { + change.setTaskFragmentToken(lastParentTfToken); + } + // Only pass the activity token to the client if it belongs to the same process. + if (Flags.fixPipRestoreToOverlay() && nextFillTaskActivity != null + && nextFillTaskActivity.getPid() == mOrganizerPid) { + change.setOtherActivityToken(nextFillTaskActivity.token); + } + return change; } void dispatchTransaction(@NonNull TaskFragmentTransaction transaction) { @@ -733,13 +745,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr } void onActivityReparentedToTask(@NonNull ActivityRecord activity) { + final Task task = activity.getTask(); final ITaskFragmentOrganizer organizer; if (activity.mLastTaskFragmentOrganizerBeforePip != null) { // If the activity is previously embedded in an organized TaskFragment. organizer = activity.mLastTaskFragmentOrganizerBeforePip; } else { // Find the topmost TaskFragmentOrganizer. - final Task task = activity.getTask(); final TaskFragment[] organizedTf = new TaskFragment[1]; task.forAllLeafTaskFragments(tf -> { if (tf.isOrganizedTaskFragment()) { @@ -757,10 +769,24 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists"); return; } - addPendingEvent(new PendingTaskFragmentEvent.Builder( + + final IBinder parentTfTokenBeforePip = activity.getLastEmbeddedParentTfTokenBeforePip(); + final PendingTaskFragmentEvent.Builder builder = new PendingTaskFragmentEvent.Builder( PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENTED_TO_TASK, organizer) .setActivity(activity) - .build()); + .setTaskFragmentToken(activity.getLastEmbeddedParentTfTokenBeforePip()); + + // Sets the next activity behinds the reparented Activity that's also not in the last + // embedded parent TF. + final ActivityRecord candidateAssociatedActivity = task.getActivity( + ar -> ar != activity && !ar.finishing + && ar.getTaskFragment().getFragmentToken() != parentTfTokenBeforePip); + if (candidateAssociatedActivity != null && (!candidateAssociatedActivity.isEmbedded() + || candidateAssociatedActivity.getTaskFragment().fillsParent())) { + builder.setOtherActivity(candidateAssociatedActivity); + } + + addPendingEvent(builder.build()); } void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer, @@ -889,11 +915,16 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr @Nullable private final TaskFragment mTaskFragment; @Nullable + private final IBinder mTaskFragmentToken; + @Nullable private final IBinder mErrorCallbackToken; @Nullable private final Throwable mException; @Nullable private final ActivityRecord mActivity; + // An additional Activity that's needed to send back to the client other than the mActivity. + @Nullable + private final ActivityRecord mOtherActivity; @Nullable private final Task mTask; // Set when the event is deferred due to the host task is invisible. The defer time will @@ -905,17 +936,21 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr private PendingTaskFragmentEvent(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg, @Nullable TaskFragment taskFragment, + @Nullable IBinder taskFragmentToken, @Nullable IBinder errorCallbackToken, @Nullable Throwable exception, @Nullable ActivityRecord activity, + @Nullable ActivityRecord otherActivity, @Nullable Task task, @TaskFragmentOperation.OperationType int opType) { mEventType = eventType; mTaskFragmentOrg = taskFragmentOrg; mTaskFragment = taskFragment; + mTaskFragmentToken = taskFragmentToken; mErrorCallbackToken = errorCallbackToken; mException = exception; mActivity = activity; + mOtherActivity = otherActivity; mTask = task; mOpType = opType; } @@ -943,12 +978,16 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr @Nullable private TaskFragment mTaskFragment; @Nullable + private IBinder mTaskFragmentToken; + @Nullable private IBinder mErrorCallbackToken; @Nullable private Throwable mException; @Nullable private ActivityRecord mActivity; @Nullable + private ActivityRecord mOtherActivity; + @Nullable private Task mTask; @TaskFragmentOperation.OperationType private int mOpType; @@ -963,6 +1002,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr return this; } + Builder setTaskFragmentToken(@Nullable IBinder fragmentToken) { + mTaskFragmentToken = fragmentToken; + return this; + } + Builder setErrorCallbackToken(@Nullable IBinder errorCallbackToken) { mErrorCallbackToken = errorCallbackToken; return this; @@ -978,6 +1022,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr return this; } + Builder setOtherActivity(@NonNull ActivityRecord otherActivity) { + mOtherActivity = otherActivity; + return this; + } + Builder setTask(@NonNull Task task) { mTask = requireNonNull(task); return this; @@ -990,7 +1039,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr PendingTaskFragmentEvent build() { return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment, - mErrorCallbackToken, mException, mActivity, mTask, mOpType); + mTaskFragmentToken, mErrorCallbackToken, mException, mActivity, + mOtherActivity, mTask, mOpType); } } } @@ -1191,7 +1241,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType, event.mException); case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENTED_TO_TASK: - return state.prepareActivityReparentedToTask(event.mActivity); + return state.prepareActivityReparentedToTask(event.mActivity, event.mOtherActivity, + event.mTaskFragmentToken); default: throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType); } |