diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl new file mode 100644 index 0000000000000..0bfc2541061ca --- /dev/null +++ b/core/java/android/window/ITaskFragmentOrganizer.aidl @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.content.res.Configuration; +import android.os.IBinder; +import android.window.TaskFragmentAppearedInfo; +import android.window.TaskFragmentInfo; + +/** @hide */ +oneway interface ITaskFragmentOrganizer { + void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo); + void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo); + void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo); + + /** + * Called when the parent leaf Task of organized TaskFragments is changed. + * When the leaf Task is changed, the organizer may want to update the TaskFragments in one + * transaction. + * + * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new + * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override + * bounds. + */ + void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig); +} diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl new file mode 100644 index 0000000000000..0ca8a864dba53 --- /dev/null +++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.window.ITaskFragmentOrganizer; + +/** @hide */ +interface ITaskFragmentOrganizerController { + + /** + * Registers a TaskFragmentOrganizer to manage TaskFragments. + */ + void registerOrganizer(in ITaskFragmentOrganizer organizer); + + /** + * Unregisters a previously registered TaskFragmentOrganizer. + */ + void unregisterOrganizer(in ITaskFragmentOrganizer organizer); +} diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index 1223d72f643ed..39cdf5af50ce8 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -20,6 +20,7 @@ import android.view.SurfaceControl; import android.os.IBinder; import android.window.IDisplayAreaOrganizerController; +import android.window.ITaskFragmentOrganizerController; import android.window.ITaskOrganizerController; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; @@ -77,6 +78,9 @@ interface IWindowOrganizerController { /** @return An interface enabling the management of display area organizers. */ IDisplayAreaOrganizerController getDisplayAreaOrganizerController(); + /** @return An interface enabling the management of task fragment organizers. */ + ITaskFragmentOrganizerController getTaskFragmentOrganizerController(); + /** * Registers a transition player with Core. There is only one of these at a time and calling * this will replace the existing one if set. diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/window/TaskFragmentAppearedInfo.aidl new file mode 100644 index 0000000000000..3729c09168a66 --- /dev/null +++ b/core/java/android/window/TaskFragmentAppearedInfo.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +/** + * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer. + * @hide + */ +parcelable TaskFragmentAppearedInfo; diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java new file mode 100644 index 0000000000000..234b30c0662c2 --- /dev/null +++ b/core/java/android/window/TaskFragmentAppearedInfo.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.SurfaceControl; + +/** + * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer. + * @hide + */ +public final class TaskFragmentAppearedInfo implements Parcelable { + + @NonNull + private final TaskFragmentInfo mTaskFragmentInfo; + + @NonNull + private final SurfaceControl mLeash; + + public TaskFragmentAppearedInfo( + @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) { + mTaskFragmentInfo = taskFragmentInfo; + mLeash = leash; + } + + public TaskFragmentInfo getTaskFragmentInfo() { + return mTaskFragmentInfo; + } + + public SurfaceControl getLeash() { + return mLeash; + } + + private TaskFragmentAppearedInfo(Parcel in) { + mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR); + mLeash = in.readTypedObject(SurfaceControl.CREATOR); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedObject(mTaskFragmentInfo, flags); + dest.writeTypedObject(mLeash, flags); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public TaskFragmentAppearedInfo createFromParcel(Parcel in) { + return new TaskFragmentAppearedInfo(in); + } + + @Override + public TaskFragmentAppearedInfo[] newArray(int size) { + return new TaskFragmentAppearedInfo[size]; + } + }; + + @Override + public String toString() { + return "TaskFragmentAppearedInfo{" + + " taskFragmentInfo=" + mTaskFragmentInfo + + "}"; + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/window/TaskFragmentCreationParams.aidl b/core/java/android/window/TaskFragmentCreationParams.aidl new file mode 100644 index 0000000000000..fde50892640ba --- /dev/null +++ b/core/java/android/window/TaskFragmentCreationParams.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +/** + * Data object for options to create TaskFragment with. + * @hide + */ +parcelable TaskFragmentCreationParams; diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java new file mode 100644 index 0000000000000..0616e7323099e --- /dev/null +++ b/core/java/android/window/TaskFragmentCreationParams.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Data object for options to create TaskFragment with. + * @hide + */ +public final class TaskFragmentCreationParams implements Parcelable { + + /** The organizer that will organize this TaskFragment. */ + @NonNull + private final ITaskFragmentOrganizer mOrganizer; + + /** + * Unique token assigned from the client organizer to identify the {@link TaskFragmentInfo} when + * a new TaskFragment is created with this option. + */ + @NonNull + private final IBinder mFragmentToken; + + /** + * Activity token used to identify the leaf Task to create the TaskFragment in. It has to belong + * to the same app as the root Activity of the target Task. + */ + @NonNull + private final IBinder mOwnerToken; + + /** The initial bounds of the TaskFragment. Fill parent if empty. */ + @NonNull + private final Rect mInitialBounds = new Rect(); + + private TaskFragmentCreationParams( + @NonNull ITaskFragmentOrganizer organizer, @NonNull IBinder fragmentToken, + @NonNull IBinder ownerToken, @NonNull Rect initialBounds) { + mOrganizer = organizer; + mFragmentToken = fragmentToken; + mOwnerToken = ownerToken; + mInitialBounds.set(initialBounds); + } + + public ITaskFragmentOrganizer getOrganizer() { + return mOrganizer; + } + + public IBinder getFragmentToken() { + return mFragmentToken; + } + + public IBinder getOwnerToken() { + return mOwnerToken; + } + + public Rect getInitialBounds() { + return mInitialBounds; + } + + private TaskFragmentCreationParams(Parcel in) { + mOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder()); + mFragmentToken = in.readStrongBinder(); + mOwnerToken = in.readStrongBinder(); + mInitialBounds.readFromParcel(in); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongInterface(mOrganizer); + dest.writeStrongBinder(mFragmentToken); + dest.writeStrongBinder(mOwnerToken); + mInitialBounds.writeToParcel(dest, flags); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public TaskFragmentCreationParams createFromParcel(Parcel in) { + return new TaskFragmentCreationParams(in); + } + + @Override + public TaskFragmentCreationParams[] newArray(int size) { + return new TaskFragmentCreationParams[size]; + } + }; + + @Override + public String toString() { + return "TaskFragmentCreationParams{" + + " organizer=" + mOrganizer + + " fragmentToken=" + mFragmentToken + + " ownerToken=" + mOwnerToken + + " initialBounds=" + mInitialBounds + + "}"; + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/window/TaskFragmentInfo.aidl b/core/java/android/window/TaskFragmentInfo.aidl new file mode 100644 index 0000000000000..461a7364803f0 --- /dev/null +++ b/core/java/android/window/TaskFragmentInfo.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +/** + * Stores information about a particular TaskFragment. + * @hide + */ +parcelable TaskFragmentInfo; diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java new file mode 100644 index 0000000000000..2092a092d73ae --- /dev/null +++ b/core/java/android/window/TaskFragmentInfo.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.res.Configuration; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Stores information about a particular TaskFragment. + * @hide + */ +public final class TaskFragmentInfo implements Parcelable { + + /** + * Client assigned unique token in {@link TaskFragmentCreationParams#fragmentToken} to create + * this TaskFragment with. + */ + @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; + + @NonNull + private final Configuration mConfiguration = new Configuration(); + + /** Whether the TaskFragment contains any child Activity. */ + private final boolean mIsEmpty; + + /** Whether this TaskFragment is visible on the window hierarchy. */ + 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) { + throw new IllegalArgumentException("Invalid TaskFragmentInfo."); + } + mFragmentToken = fragmentToken; + mInitialComponentName = initialComponentName; + mToken = token; + mConfiguration.setTo(configuration); + mIsEmpty = isEmpty; + mIsVisible = isVisible; + } + + public IBinder getFragmentToken() { + return mFragmentToken; + } + + public ComponentName getInitialComponentName() { + return mInitialComponentName; + } + + public WindowContainerToken getToken() { + return mToken; + } + + public Configuration getConfiguration() { + return mConfiguration; + } + + public boolean isEmpty() { + return mIsEmpty; + } + + public boolean isVisible() { + return mIsVisible; + } + + private TaskFragmentInfo(Parcel in) { + mFragmentToken = in.readStrongBinder(); + mInitialComponentName = in.readTypedObject(ComponentName.CREATOR); + mToken = in.readTypedObject(WindowContainerToken.CREATOR); + mConfiguration.readFromParcel(in); + mIsEmpty = in.readBoolean(); + mIsVisible = in.readBoolean(); + } + + @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); + dest.writeBoolean(mIsVisible); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public TaskFragmentInfo createFromParcel(Parcel in) { + return new TaskFragmentInfo(in); + } + + @Override + public TaskFragmentInfo[] newArray(int size) { + return new TaskFragmentInfo[size]; + } + }; + + @Override + public String toString() { + return "TaskFragmentInfo{" + + " fragmentToken=" + mFragmentToken + + " initialComponentName=" + mInitialComponentName + + " token=" + mToken + + " isEmpty=" + mIsEmpty + + " isVisible=" + mIsVisible + + "}"; + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java new file mode 100644 index 0000000000000..51722cccb44d1 --- /dev/null +++ b/core/java/android/window/TaskFragmentOrganizer.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.content.res.Configuration; +import android.os.IBinder; +import android.os.RemoteException; + +import java.util.concurrent.Executor; + +/** + * Interface for WindowManager to delegate control of {@link com.android.server.wm.TaskFragment}. + * @hide + */ +public class TaskFragmentOrganizer extends WindowOrganizer { + + /** + * Callbacks from WM Core are posted on this executor. + */ + private final Executor mExecutor; + + public TaskFragmentOrganizer(@NonNull Executor executor) { + mExecutor = executor; + } + + /** + * Gets the executor to run callbacks on. + */ + @NonNull + public Executor getExecutor() { + return mExecutor; + } + + /** + * Registers a TaskFragmentOrganizer to manage TaskFragments. + */ + @CallSuper + public void registerOrganizer() { + try { + getController().registerOrganizer(mInterface); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters a previously registered TaskFragmentOrganizer. + */ + @CallSuper + public void unregisterOrganizer() { + try { + getController().unregisterOrganizer(mInterface); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** Called when a TaskFragment is created and organized by this organizer. */ + public void onTaskFragmentAppeared( + @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {} + + /** Called when the status of an organized TaskFragment is changed. */ + public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {} + + /** Called when an organized TaskFragment is removed. */ + public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {} + + /** + * Called when the parent leaf Task of organized TaskFragments is changed. + * When the leaf Task is changed, the organizer may want to update the TaskFragments in one + * transaction. + * + * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new + * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override + * bounds. + */ + public void onTaskFragmentParentInfoChanged( + @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {} + + private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() { + @Override + public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo)); + } + + @Override + public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo)); + } + + @Override + public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo)); + } + + @Override + public void onTaskFragmentParentInfoChanged( + @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged( + fragmentToken, parentConfig)); + } + }; + + private ITaskFragmentOrganizerController getController() { + try { + return getWindowOrganizerController().getTaskFragmentOrganizerController(); + } catch (RemoteException e) { + return null; + } + } +} diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 45ed31c35fa44..8c3dc2e2044e9 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.app.WindowConfiguration; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; @@ -325,7 +326,8 @@ public final class WindowContainerTransaction implements Parcelable { /** * Sets to containers adjacent to each other. Containers below two visible adjacent roots will - * be made invisible. This currently only applies to Task containers created by organizer. + * be made invisible. This currently only applies to TaskFragment containers created by + * organizer. * @param root1 the first root. * @param root2 the second root. */ @@ -377,6 +379,102 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + /** + * Creates a new TaskFragment with the given options. + * @param taskFragmentOptions the options used to create the TaskFragment. + * @hide + */ + @NonNull + public WindowContainerTransaction createTaskFragment( + @NonNull TaskFragmentCreationParams taskFragmentOptions) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT) + .setTaskFragmentCreationOptions(taskFragmentOptions) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Deletes an existing TaskFragment. Any remaining activities below it will be destroyed. + * @param taskFragment the TaskFragment to be removed. + * @hide + */ + @NonNull + public WindowContainerTransaction deleteTaskFragment( + @NonNull WindowContainerToken taskFragment) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT) + .setContainer(taskFragment.asBinder()) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Starts an activity in the TaskFragment. + * @param fragmentToken client assigned unique token to create TaskFragment with specified in + * {@link TaskFragmentCreationParams#fragmentToken}. + * @param activityIntent intent to start the activity. + * @param activityOptions ActivityOptions to start the activity with. + * @see android.content.Context#startActivity(Intent, Bundle). + * @hide + */ + @NonNull + public WindowContainerTransaction startActivityInTaskFragment( + @NonNull IBinder fragmentToken, @NonNull Intent activityIntent, + @Nullable Bundle activityOptions) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT) + .setContainer(fragmentToken) + .setActivityIntent(activityIntent) + .setLaunchOptions(activityOptions) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Moves an activity into the TaskFragment. + * @param fragmentToken client assigned unique token to create TaskFragment with specified in + * {@link TaskFragmentCreationParams#fragmentToken}. + * @param activityToken activity to be reparented. + * @hide + */ + @NonNull + public WindowContainerTransaction reparentActivityToTaskFragment( + @NonNull IBinder fragmentToken, @NonNull IBinder activityToken) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT) + .setReparentContainer(fragmentToken) + .setContainer(activityToken) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Reparents all children of one TaskFragment to another. + * @param oldParent children of this TaskFragment will be reparented. + * @param newParent the new parent TaskFragment to move the children to. If {@code null}, the + * children will be moved to the leaf Task. + * @hide + */ + @NonNull + public WindowContainerTransaction reparentChildren( + @NonNull WindowContainerToken oldParent, + @Nullable WindowContainerToken newParent) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN) + .setContainer(oldParent.asBinder()) + .setReparentContainer(newParent.asBinder()) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + /** * Merges another WCT into this one. * @param transfer When true, this will transfer everything from other potentially leaving @@ -705,6 +803,11 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4; public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5; public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6; + public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7; + public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8; + public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9; + public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10; + public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -732,6 +835,13 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private Bundle mLaunchOptions; + @Nullable + private Intent mActivityIntent; + + // Used as options for WindowContainerTransaction#createTaskFragment(). + @Nullable + private TaskFragmentCreationParams mTaskFragmentCreationOptions; + public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) @@ -808,6 +918,8 @@ public final class WindowContainerTransaction implements Parcelable { mWindowingModes = copy.mWindowingModes; mActivityTypes = copy.mActivityTypes; mLaunchOptions = copy.mLaunchOptions; + mActivityIntent = copy.mActivityIntent; + mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions; } protected HierarchyOp(Parcel in) { @@ -818,6 +930,8 @@ public final class WindowContainerTransaction implements Parcelable { mWindowingModes = in.createIntArray(); mActivityTypes = in.createIntArray(); mLaunchOptions = in.readBundle(); + mActivityIntent = in.readTypedObject(Intent.CREATOR); + mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR); } public int getType() { @@ -860,6 +974,16 @@ public final class WindowContainerTransaction implements Parcelable { return mLaunchOptions; } + @Nullable + public Intent getActivityIntent() { + return mActivityIntent; + } + + @Nullable + public TaskFragmentCreationParams getTaskFragmentCreationOptions() { + return mTaskFragmentCreationOptions; + } + @Override public String toString() { switch (mType) { @@ -884,6 +1008,19 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop + "}"; + case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: + return "{CreateTaskFragment: options=" + mTaskFragmentCreationOptions + "}"; + case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: + return "{DeleteTaskFragment: taskFragment=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: + return "{StartActivityInTaskFragment: fragmentToken=" + mContainer + " intent=" + + mActivityIntent + " options=" + mLaunchOptions + "}"; + case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: + return "{ReparentActivityToTaskFragment: fragmentToken=" + mReparent + + " activity=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: + return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent + + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes @@ -900,6 +1037,8 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeIntArray(mWindowingModes); dest.writeIntArray(mActivityTypes); dest.writeBundle(mLaunchOptions); + dest.writeTypedObject(mActivityIntent, flags); + dest.writeTypedObject(mTaskFragmentCreationOptions, flags); } @Override @@ -940,6 +1079,12 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private Bundle mLaunchOptions; + @Nullable + private Intent mActivityIntent; + + @Nullable + private TaskFragmentCreationParams mTaskFragmentCreationOptions; + Builder(int type) { mType = type; } @@ -974,6 +1119,17 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setActivityIntent(@Nullable Intent activityIntent) { + mActivityIntent = activityIntent; + return this; + } + + Builder setTaskFragmentCreationOptions( + @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) { + mTaskFragmentCreationOptions = taskFragmentCreationOptions; + return this; + } + HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; @@ -986,6 +1142,8 @@ public final class WindowContainerTransaction implements Parcelable { : null; hierarchyOp.mToTop = mToTop; hierarchyOp.mLaunchOptions = mLaunchOptions; + hierarchyOp.mActivityIntent = mActivityIntent; + hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions; return hierarchyOp; } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 90137708e1796..b80c79c0f82c9 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -721,6 +721,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1311436264": { + "message": "Unregister task fragment organizer=%s uid=%d pid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "-1305966693": { "message": "Sending position change to %s, onTop: %b", "level": "VERBOSE", @@ -1219,6 +1225,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-706481945": { + "message": "TaskFragment parent info changed name=%s parentTaskId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "-705939410": { "message": "Waiting for pause to complete...", "level": "VERBOSE", @@ -1381,6 +1393,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowAnimator.java" }, + "-542756093": { + "message": "TaskFragment vanished name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "-532081937": { "message": " Commit activity becoming invisible: %s", "level": "VERBOSE", @@ -2743,6 +2761,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/TaskFragment.java" }, + "1022095595": { + "message": "TaskFragment info changed name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "1023413388": { "message": "Finish waiting for pause of: %s", "level": "VERBOSE", @@ -2929,6 +2953,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1284122013": { + "message": "TaskFragment appeared name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "1288731814": { "message": "WindowState.hideLw: setting mFocusMayChange true", "level": "INFO", @@ -3223,6 +3253,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "1653025361": { + "message": "Register task fragment organizer=%s uid=%d pid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "1653210583": { "message": "Removing app %s delayed=%b animation=%s animating=%b", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 561c2f29d88ab..b4923ad102077 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -602,18 +602,6 @@ class Task extends TaskFragment { */ private boolean mForceNotOrganized; - /** - * This task was created by the task organizer which has the following implementations. - *
    - * The task won't be removed when it is empty. Removal has to be an explicit request - * from the task organizer. - *
  • Unlike other non-root tasks, it's direct children are visible to the task - * organizer for ordering purposes.
  • - *
- */ - @VisibleForTesting - boolean mCreatedByOrganizer; - // Tracking cookie for the creation of this task. IBinder mLaunchCookie; @@ -641,7 +629,7 @@ class Task extends TaskFragment { IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear, boolean _removeWithTaskOrganizer) { - super(atmService); + super(atmService, _createdByOrganizer); mTaskId = _taskId; mUserId = _userId; @@ -693,7 +681,6 @@ class Task extends TaskFragment { mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper); mCurrentUser = mAtmService.mAmInternal.getCurrentUserId(); - mCreatedByOrganizer = _createdByOrganizer; mLaunchCookie = _launchCookie; mDeferTaskAppear = _deferTaskAppear; mRemoveWithTaskOrganizer = _removeWithTaskOrganizer; diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 0d71159f297bd..9e042a69deba2 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -74,13 +74,17 @@ 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; import android.os.RemoteException; import android.util.DisplayMetrics; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; +import android.window.ITaskFragmentOrganizer; +import android.window.TaskFragmentInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -181,6 +185,38 @@ class TaskFragment extends WindowContainer { @Nullable private ActivityRecord mResumedActivity = null; + /** + * This TaskFragment was created by an organizer which has the following implementations. + *
    + *
  • The TaskFragment won't be removed when it is empty. Removal has to be an explicit + * request from the organizer.
  • + *
  • If this fragment is a Task object then unlike other non-root tasks, it's direct + * children are visible to the organizer for ordering purposes.
  • + *
  • A TaskFragment can be created by {@link android.window.TaskFragmentOrganizer}, and + * a Task can be created by {@link android.window.TaskOrganizer}.
  • + *
+ */ + @VisibleForTesting + 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(); @@ -229,12 +265,13 @@ class TaskFragment extends WindowContainer { } } - TaskFragment(ActivityTaskManagerService atmService) { + TaskFragment(ActivityTaskManagerService atmService, boolean createdByOrganizer) { super(atmService.mWindowManager); mAtmService = atmService; mTaskSupervisor = atmService.mTaskSupervisor; mRootWindowContainer = mAtmService.mRootWindowContainer; + mCreatedByOrganizer = createdByOrganizer; } void setAdjacentTaskFragment(TaskFragment taskFragment) { @@ -1900,6 +1937,25 @@ class TaskFragment extends WindowContainer { return getTopChild().getActivityType(); } + /** + * Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be + * called from {@link Task}. + */ + TaskFragmentInfo getTaskFragmentInfo() { + return new TaskFragmentInfo( + mFragmentToken, + mInitialComponentName, + mRemoteToken.toWindowContainerToken(), + getConfiguration(), + getChildCount() == 0, + isVisible()); + } + + @Nullable + IBinder getFragmentToken() { + return mFragmentToken; + } + 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/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java new file mode 100644 index 0000000000000..b0a8548ead20d --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; + +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.view.SurfaceControl; +import android.window.ITaskFragmentOrganizer; +import android.window.ITaskFragmentOrganizerController; +import android.window.TaskFragmentAppearedInfo; + +import com.android.internal.protolog.common.ProtoLog; + +import java.util.Map; +import java.util.Set; + +/** + * Stores and manages the client {@link android.window.TaskFragmentOrganizer}. + */ +public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerController.Stub { + private static final String TAG = "TaskFragmentOrganizerController"; + + private final ActivityTaskManagerService mAtmService; + private final WindowManagerGlobalLock mGlobalLock; + private final Set mOrganizers = new ArraySet<>(); + private final Map mDeathRecipients = new ArrayMap<>(); + + private class DeathRecipient implements IBinder.DeathRecipient { + final ITaskFragmentOrganizer mOrganizer; + + DeathRecipient(ITaskFragmentOrganizer organizer) { + mOrganizer = organizer; + } + + @Override + public void binderDied() { + removeOrganizer(mOrganizer); + } + } + + TaskFragmentOrganizerController(ActivityTaskManagerService atm) { + mAtmService = atm; + mGlobalLock = atm.mGlobalLock; + } + + @Override + public void registerOrganizer(ITaskFragmentOrganizer organizer) { + final int pid = Binder.getCallingPid(); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "Register task fragment organizer=%s uid=%d pid=%d", + organizer.asBinder(), uid, pid); + if (mOrganizers.contains(organizer)) { + throw new IllegalStateException( + "Replacing existing organizer currently unsupported"); + } + + final DeathRecipient dr = new DeathRecipient(organizer); + try { + organizer.asBinder().linkToDeath(dr, 0); + } catch (RemoteException e) { + // Oh well... + } + + mOrganizers.add(organizer); + mDeathRecipients.put(organizer, dr); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void unregisterOrganizer(ITaskFragmentOrganizer organizer) { + final int pid = Binder.getCallingPid(); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "Unregister task fragment organizer=%s uid=%d pid=%d", + organizer.asBinder(), uid, pid); + if (!mOrganizers.contains(organizer)) { + throw new IllegalStateException( + "The task fragment organizer hasn't been registered."); + } + + final DeathRecipient dr = mDeathRecipients.get(organizer); + organizer.asBinder().unlinkToDeath(dr, 0); + + removeOrganizer(organizer); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName()); + try { + final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(), + "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared"); + organizer.onTaskFragmentAppeared( + new TaskFragmentAppearedInfo(tf.getTaskFragmentInfo(), outSurfaceControl)); + } catch (RemoteException e) { + // Oh well... + } + } + + void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s", tf.getName()); + try { + organizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo()); + } catch (RemoteException e) { + // Oh well... + } + } + + void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment tf) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName()); + try { + organizer.onTaskFragmentVanished(tf.getTaskFragmentInfo()); + } catch (RemoteException e) { + // Oh well... + } + } + + void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) { + final Task parent = tf.getParent().asTask(); + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "TaskFragment parent info changed name=%s parentTaskId=%d", + tf.getName(), parent.mTaskId); + try { + organizer.onTaskFragmentParentInfoChanged( + tf.getFragmentToken(), parent.getConfiguration()); + } catch (RemoteException e) { + // Oh well... + } + } + + private void removeOrganizer(ITaskFragmentOrganizer organizer) { + synchronized (mGlobalLock) { + mOrganizers.remove(organizer); + mDeathRecipients.remove(organizer); + } + // TODO(b/190432728) move child activities of organized TaskFragment to leaf Task + } +} diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 29c6391056c75..72f73572782c7 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,12 +17,17 @@ package com.android.server.wm; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; @@ -34,6 +39,7 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; @@ -46,10 +52,12 @@ import android.util.ArraySet; import android.util.Slog; import android.view.SurfaceControl; import android.window.IDisplayAreaOrganizerController; +import android.window.ITaskFragmentOrganizerController; import android.window.ITaskOrganizerController; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; import android.window.IWindowOrganizerController; +import android.window.TaskFragmentCreationParams; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; @@ -95,6 +103,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final TaskOrganizerController mTaskOrganizerController; final DisplayAreaOrganizerController mDisplayAreaOrganizerController; + final TaskFragmentOrganizerController mTaskFragmentOrganizerController; final TransitionController mTransitionController; @@ -103,6 +112,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mGlobalLock = atm.mGlobalLock; mTaskOrganizerController = new TaskOrganizerController(mService); mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService); + mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm); mTransitionController = new TransitionController(atm); } @@ -501,13 +511,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } + final WindowContainer wc; + final IBinder fragmentToken; switch (type) { case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); break; case HIERARCHY_OP_TYPE_REORDER: case HIERARCHY_OP_TYPE_REPARENT: - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + wc = WindowContainer.fromBinder(hop.getContainer()); if (wc == null || !wc.isAttached()) { Slog.e(TAG, "Attempt to operate on detached container: " + wc); break; @@ -543,6 +555,40 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); mService.startActivityFromRecents(taskId, launchOpts); break; + case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: + final TaskFragmentCreationParams taskFragmentCreationOptions = + hop.getTaskFragmentCreationOptions(); + // TODO(b/190433129) add actual implementation on WM Core + 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); + break; + } + final TaskFragment taskFragment = wc.asTaskFragment(); + if (taskFragment == null || taskFragment.asTask() != null) { + throw new IllegalArgumentException( + "Can only delete organized TaskFragment, but not Task."); + } + // TODO(b/190433129) add actual implementation on WM Core + break; + case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: + fragmentToken = hop.getContainer(); + final Intent activityIntent = hop.getActivityIntent(); + final Bundle activityOptions = hop.getLaunchOptions(); + // TODO(b/190433129) add actual implementation on WM Core + 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 + 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 + break; } return effects; } @@ -704,8 +750,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { - final Task root1 = WindowContainer.fromBinder(hop.getContainer()).asTask(); - final Task root2 = WindowContainer.fromBinder(hop.getAdjacentRoot()).asTask(); + final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment(); + final TaskFragment root2 = + WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment(); if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) { throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + " organizer root1=" + root1 + " root2=" + root2); @@ -747,6 +794,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return mDisplayAreaOrganizerController; } + @Override + public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() { + return mTaskFragmentOrganizerController; + } + @VisibleForTesting int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);