From 397b1509bb329b7dbc47ad781cd02a25d9799830 Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Thu, 17 Jun 2021 10:39:10 +0800 Subject: [PATCH] Embedded Activities in TaskFragments - Adding TaskFragments into the leaf Task to embed activities when activity started to the side. - Delete TaskFragment - Reparent TaskFragments Bug: 189385246 Bug: 190433129 Test: wm presubmit and have test case in updating CL. Change-Id: I7e7f5109457dd083d3b148d3ce676ad9aa5ad89f --- .../java/android/window/TaskFragmentInfo.java | 24 +----- .../server/wm/ActivityStartController.java | 12 +++ .../android/server/wm/ActivityStarter.java | 36 ++++++-- .../core/java/com/android/server/wm/Task.java | 4 +- .../com/android/server/wm/TaskFragment.java | 38 +++++---- .../server/wm/WindowOrganizerController.java | 82 +++++++++++++++++-- .../com/android/server/wm/WindowState.java | 15 +++- 7 files changed, 158 insertions(+), 53 deletions(-) diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java index e032153bc0139..f864c91be31f8 100644 --- a/core/java/android/window/TaskFragmentInfo.java +++ b/core/java/android/window/TaskFragmentInfo.java @@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.WindowingMode; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ComponentName; import android.content.res.Configuration; import android.os.IBinder; import android.os.Parcel; @@ -39,13 +38,6 @@ public final class TaskFragmentInfo implements Parcelable { @NonNull private final IBinder mFragmentToken; - /** - * The component name of the initial root activity of this TaskFragment, which will be used - * to configure the relationships for TaskFragments. - */ - @NonNull - private final ComponentName mInitialComponentName; - @NonNull private final WindowContainerToken mToken; @@ -59,14 +51,12 @@ public final class TaskFragmentInfo implements Parcelable { private final boolean mIsVisible; public TaskFragmentInfo( - @NonNull IBinder fragmentToken, @NonNull ComponentName initialComponentName, - @NonNull WindowContainerToken token, @NonNull Configuration configuration, - boolean isEmpty, boolean isVisible) { - if (fragmentToken == null || initialComponentName == null) { + @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token, + @NonNull Configuration configuration, boolean isEmpty, boolean isVisible) { + if (fragmentToken == null) { throw new IllegalArgumentException("Invalid TaskFragmentInfo."); } mFragmentToken = fragmentToken; - mInitialComponentName = initialComponentName; mToken = token; mConfiguration.setTo(configuration); mIsEmpty = isEmpty; @@ -77,10 +67,6 @@ public final class TaskFragmentInfo implements Parcelable { return mFragmentToken; } - public ComponentName getInitialComponentName() { - return mInitialComponentName; - } - public WindowContainerToken getToken() { return mToken; } @@ -112,7 +98,6 @@ public final class TaskFragmentInfo implements Parcelable { } return mFragmentToken.equals(that.mFragmentToken) - && mInitialComponentName.equals(that.mInitialComponentName) && mToken.equals(that.mToken) && mIsEmpty == that.mIsEmpty && mIsVisible == that.mIsVisible @@ -121,7 +106,6 @@ public final class TaskFragmentInfo implements Parcelable { private TaskFragmentInfo(Parcel in) { mFragmentToken = in.readStrongBinder(); - mInitialComponentName = in.readTypedObject(ComponentName.CREATOR); mToken = in.readTypedObject(WindowContainerToken.CREATOR); mConfiguration.readFromParcel(in); mIsEmpty = in.readBoolean(); @@ -131,7 +115,6 @@ public final class TaskFragmentInfo implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStrongBinder(mFragmentToken); - dest.writeTypedObject(mInitialComponentName, flags); dest.writeTypedObject(mToken, flags); mConfiguration.writeToParcel(dest, flags); dest.writeBoolean(mIsEmpty); @@ -156,7 +139,6 @@ public final class TaskFragmentInfo implements Parcelable { public String toString() { return "TaskFragmentInfo{" + " fragmentToken=" + mFragmentToken - + " initialComponentName=" + mInitialComponentName + " token=" + mToken + " isEmpty=" + mIsEmpty + " isVisible=" + mIsVisible diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index b6f2f243040ea..d08d285beda8d 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -27,6 +27,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.IApplicationThread; @@ -38,6 +39,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.UserHandle; import android.provider.Settings; @@ -488,6 +490,16 @@ public class ActivityStartController { return START_SUCCESS; } + void startActivityInTaskFragment(@NonNull TaskFragment taskFragment, + @NonNull Intent activityIntent, @Nullable Bundle activityOptions) { + obtainStarter(activityIntent, "startActivityInTaskFragment") + .setActivityOptions(activityOptions) + .setInTaskFragment(taskFragment) + .setCallingUid(Binder.getCallingUid()) + .setCallingPid(Binder.getCallingPid()) + .execute(); + } + void registerRemoteAnimationForNextActivityStart(String packageName, RemoteAnimationAdapter adapter) { mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 3dc4892c874c3..10c1f6b252096 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -177,6 +177,7 @@ class ActivityStarter { private int mPreferredWindowingMode; private Task mInTask; + private TaskFragment mInTaskFragment; @VisibleForTesting boolean mAddingToTask; private Task mReuseTask; @@ -342,6 +343,7 @@ class ActivityStarter { boolean avoidMoveToFront; ActivityRecord[] outActivity; Task inTask; + TaskFragment inTaskFragment; String reason; ProfilerInfo profilerInfo; Configuration globalConfig; @@ -392,6 +394,7 @@ class ActivityStarter { componentSpecified = false; outActivity = null; inTask = null; + inTaskFragment = null; reason = null; profilerInfo = null; globalConfig = null; @@ -407,7 +410,7 @@ class ActivityStarter { /** * Adopts all values from passed in request. */ - void set(Request request) { + void set(@NonNull Request request) { caller = request.caller; intent = request.intent; intentGrants = request.intentGrants; @@ -432,6 +435,7 @@ class ActivityStarter { componentSpecified = request.componentSpecified; outActivity = request.outActivity; inTask = request.inTask; + inTaskFragment = request.inTaskFragment; reason = request.reason; profilerInfo = request.profilerInfo; globalConfig = request.globalConfig; @@ -574,6 +578,7 @@ class ActivityStarter { mPreferredWindowingMode = starter.mPreferredWindowingMode; mInTask = starter.mInTask; + mInTaskFragment = starter.mInTaskFragment; mAddingToTask = starter.mAddingToTask; mReuseTask = starter.mReuseTask; @@ -835,6 +840,7 @@ class ActivityStarter { final int startFlags = request.startFlags; final SafeActivityOptions options = request.activityOptions; Task inTask = request.inTask; + mInTaskFragment = request.inTaskFragment; int err = ActivityManager.START_SUCCESS; // Pull the optional Ephemeral Installer-only bundle out of the options early. @@ -2204,6 +2210,7 @@ class ActivityStarter { mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED; mInTask = null; + mInTaskFragment = null; mAddingToTask = false; mReuseTask = null; @@ -2691,11 +2698,23 @@ class ActivityStarter { mIntentDelivered = true; } - private void addOrReparentStartingActivity(Task parent, String reason) { - if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { - parent.addChild(mStartActivity); + private void addOrReparentStartingActivity(@NonNull Task task, String reason) { + TaskFragment newParent = task; + if (mInTaskFragment != null) { + // mInTaskFragment is created and added to the leaf task by task fragment organizer's + // request. If the task was resolved and different than mInTaskFragment, reparent the + // task to mInTaskFragment for embedding. + if (mInTaskFragment.getTask() != task) { + task.reparent(mInTaskFragment, POSITION_TOP); + } else { + newParent = mInTaskFragment; + } + } + if (mStartActivity.getTaskFragment() == null + || mStartActivity.getTaskFragment() == newParent) { + newParent.addChild(mStartActivity, POSITION_TOP); } else { - mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason); + mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason); } } @@ -2927,6 +2946,11 @@ class ActivityStarter { return this; } + ActivityStarter setInTaskFragment(TaskFragment taskFragment) { + mRequest.inTaskFragment = taskFragment; + return this; + } + ActivityStarter setWaitResult(WaitResult result) { mRequest.waitResult = result; return this; @@ -3010,5 +3034,7 @@ class ActivityStarter { pw.print(mDoResume); pw.print(" mAddingToTask="); pw.println(mAddingToTask); + pw.print(" mInTaskFragment="); + pw.println(mInTaskFragment); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a6bf520561acf..791fdbbb679ad 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -629,7 +629,7 @@ class Task extends TaskFragment { IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear, boolean _removeWithTaskOrganizer) { - super(atmService, _createdByOrganizer); + super(atmService, null /* fragmentToken */, _createdByOrganizer); mTaskId = _taskId; mUserId = _userId; @@ -643,7 +643,6 @@ class Task extends TaskFragment { : new PersistedTaskSnapshotData(); // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED). setOrientation(SCREEN_ORIENTATION_UNSET); - mRemoteToken = new RemoteToken(this); affinityIntent = _affinityIntent; affinity = _affinity; rootAffinity = _rootAffinity; @@ -4986,7 +4985,6 @@ class Task extends TaskFragment { // Slot the activity into the history root task and proceed ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s " + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace()); - task.positionChildAtTop(r); // The transition animation and starting window are not needed if {@code allowMoveToFront} // is false, because the activity won't be visible. diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index b263909d7e6d7..25d30336e0e71 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -74,7 +74,6 @@ import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.ResumeActivityItem; -import android.content.ComponentName; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; @@ -155,6 +154,9 @@ class TaskFragment extends WindowContainer { */ int mMinHeight; + /** Avoid reentrant of {@link #removeImmediately()}. */ + private boolean mRemoving; + // The TaskFragment that adjacent to this one. private TaskFragment mAdjacentTaskFragment; @@ -196,23 +198,13 @@ class TaskFragment extends WindowContainer { boolean mCreatedByOrganizer; /** Organizer that organizing this TaskFragment. */ - // TODO(b/190433129) set the value when creating TaskFragment from WCT. @Nullable private ITaskFragmentOrganizer mTaskFragmentOrganizer; /** Client assigned unique token for this TaskFragment if this is created by an organizer. */ - // TODO(b/190433129) set the value when creating TaskFragment from WCT. @Nullable private IBinder mFragmentToken; - /** - * The component name of the root activity that initiated this TaskFragment, which will be used - * to configure the relationships for TaskFragments. - */ - // TODO(b/190433129) set the value when creating TaskFragment from WCT. - @Nullable - private ComponentName mInitialComponentName; - private final Rect mTmpInsets = new Rect(); private final Rect mTmpBounds = new Rect(); private final Rect mTmpFullBounds = new Rect(); @@ -260,15 +252,18 @@ class TaskFragment extends WindowContainer { } } - TaskFragment(ActivityTaskManagerService atmService, boolean createdByOrganizer) { + TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken, + boolean createdByOrganizer) { super(atmService.mWindowManager); mAtmService = atmService; - mTaskSupervisor = atmService.mTaskSupervisor; + mTaskSupervisor = mAtmService.mTaskSupervisor; mRootWindowContainer = mAtmService.mRootWindowContainer; mCreatedByOrganizer = createdByOrganizer; mTaskFragmentOrganizerController = mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController; + mFragmentToken = fragmentToken; + mRemoteToken = new RemoteToken(this); } void setAdjacentTaskFragment(TaskFragment taskFragment) { @@ -276,6 +271,10 @@ class TaskFragment extends WindowContainer { taskFragment.mAdjacentTaskFragment = this; } + void setTaskFragmentOrganizer(ITaskFragmentOrganizer organizer) { + mTaskFragmentOrganizer = organizer; + } + TaskFragment getAdjacentTaskFragment() { return mAdjacentTaskFragment; } @@ -1981,7 +1980,6 @@ class TaskFragment extends WindowContainer { } } - // TODO(b/190433129) call when TaskFragment is removed from WCT#deleteTaskFragment private void sendTaskFragmentVanished() { if (mTaskFragmentOrganizer != null) { mTaskFragmentOrganizerController.onTaskFragmentVanished(mTaskFragmentOrganizer, this); @@ -1995,7 +1993,6 @@ class TaskFragment extends WindowContainer { TaskFragmentInfo getTaskFragmentInfo() { return new TaskFragmentInfo( mFragmentToken, - mInitialComponentName, mRemoteToken.toWindowContainerToken(), getConfiguration(), getChildCount() == 0, @@ -2026,6 +2023,17 @@ class TaskFragment extends WindowContainer { mMinHeight = minHeight; } + @Override + void removeImmediately() { + if (mRemoving) { + return; + } + mRemoving = true; + super.removeImmediately(); + sendTaskFragmentVanished(); + mRemoving = false; + } + boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) { boolean printed = false; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 3d36cc517e98e..a7bf887e98c9e 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -48,6 +48,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.view.SurfaceControl; @@ -106,6 +107,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final TaskFragmentOrganizerController mTaskFragmentOrganizerController; final TransitionController mTransitionController; + /** + * A Map which manages the relationship between + * {@link TaskFragmentCreationParams.mFragmentToken fragmentToken} and {@link TaskFragment} + */ + private final ArrayMap mLaunchTaskFragments = new ArrayMap<>(); WindowOrganizerController(ActivityTaskManagerService atm) { mService = atm; @@ -558,12 +564,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: final TaskFragmentCreationParams taskFragmentCreationOptions = hop.getTaskFragmentCreationOptions(); - // TODO(b/190433129) add actual implementation on WM Core + createTaskFragment(taskFragmentCreationOptions); break; case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: wc = WindowContainer.fromBinder(hop.getContainer()); if (wc == null || !wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc); break; } final TaskFragment taskFragment = wc.asTaskFragment(); @@ -571,23 +577,40 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new IllegalArgumentException( "Can only delete organized TaskFragment, but not Task."); } - // TODO(b/190433129) add actual implementation on WM Core + deleteTaskFragment(taskFragment); break; case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: fragmentToken = hop.getContainer(); + if (!mLaunchTaskFragments.containsKey(fragmentToken)) { + throw new IllegalArgumentException( + "Not allowed to operate with invalid fragment token"); + } final Intent activityIntent = hop.getActivityIntent(); final Bundle activityOptions = hop.getLaunchOptions(); - // TODO(b/190433129) add actual implementation on WM Core + mService.getActivityStartController() + .startActivityInTaskFragment(mLaunchTaskFragments.get(fragmentToken), + activityIntent, activityOptions); + // TODO(b/189385246) : report the failure back to the organizer if the activity + // start failed break; case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: fragmentToken = hop.getNewParent(); final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); - // TODO(b/190433129) add actual implementation on WM Core + if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) { + throw new IllegalArgumentException( + "Not allowed to operate with invalid fragment token or activity."); + } + activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP); break; case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer()); final WindowContainer newParent = WindowContainer.fromBinder(hop.getNewParent()); - // TODO(b/190433129) add actual implementation on WM Core + if (oldParent == null || !oldParent.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + + oldParent); + break; + } + reparentTaskFragment(oldParent, newParent); break; } return effects; @@ -866,4 +889,51 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private void enforceTaskPermission(String func) { mService.enforceTaskPermission(func); } + + void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams) { + final ActivityRecord ownerActivity = + ActivityRecord.forTokenLocked(creationParams.getOwnerToken()); + if (ownerActivity == null || ownerActivity.getTask() == null) { + // TODO(b/189385246) : report the failure back to the organizer + return; + } + // The ownerActivity has to belong to the same app as the root Activity of the target Task. + final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity(); + if (rootActivity.getUid() != ownerActivity.getUid()) { + // TODO(b/189385246) : report the failure back to the organizer + return; + } + final TaskFragment taskFragment = new TaskFragment(mService, + creationParams.getFragmentToken(), true /* createdByOrganizer */); + ownerActivity.getTask().addChild(taskFragment, POSITION_TOP); + taskFragment.setWindowingMode(creationParams.getWindowingMode()); + taskFragment.setBounds(creationParams.getInitialBounds()); + taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer()); + mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); + } + + void reparentTaskFragment(@NonNull WindowContainer oldParent, + @Nullable WindowContainer newParent) { + WindowContainer parent = newParent; + if (parent == null && oldParent.asTaskFragment() != null) { + parent = oldParent.asTaskFragment().getTask(); + } + if (parent == null) { + // TODO(b/189385246) : report the failure back to the organizer + return; + } + while (oldParent.hasChild()) { + oldParent.getChildAt(0).reparent(parent, POSITION_TOP); + } + } + + void deleteTaskFragment(@NonNull TaskFragment taskFragment) { + final int index = mLaunchTaskFragments.indexOfValue(taskFragment); + if (index < 0) { + throw new IllegalArgumentException( + "Not allowed to operate with invalid taskFragment"); + } + mLaunchTaskFragments.removeAt(index); + taskFragment.removeImmediately(); + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8ec6ed2908f86..fdea9c6daa126 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1729,6 +1729,10 @@ class WindowState extends WindowContainer implements WindowManagerP return mActivityRecord != null ? mActivityRecord.getTask() : null; } + @Nullable TaskFragment getTaskFragment() { + return mActivityRecord != null ? mActivityRecord.getTaskFragment() : null; + } + @Nullable Task getRootTask() { final Task task = getTask(); if (task != null) { @@ -2908,9 +2912,14 @@ class WindowState extends WindowContainer implements WindowManagerP // means we need to intercept touches outside of that window. The dim layer // user associated with the window (task or root task) will give us the good // bounds, as they would be used to display the dim layer. - final Task task = getTask(); - if (task != null) { - task.getDimBounds(mTmpRect); + final TaskFragment taskFragment = getTaskFragment(); + if (taskFragment != null) { + final Task task = taskFragment.asTask(); + if (task != null) { + task.getDimBounds(mTmpRect); + } else { + mTmpRect.set(taskFragment.getBounds()); + } } else if (getRootTask() != null) { getRootTask().getDimBounds(mTmpRect); }