/* * Copyright (C) 2022 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 static java.util.Objects.requireNonNull; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.view.SurfaceControl; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; /** * Used to communicate information about what are changing on embedded TaskFragments belonging to * the same TaskFragmentOrganizer. A transaction can contain multiple changes. * @see TaskFragmentTransaction.Change * @hide */ @TestApi public final class TaskFragmentTransaction implements Parcelable { /** Unique token to represent this transaction. */ private final IBinder mTransactionToken; /** Changes in this transaction. */ private final ArrayList mChanges = new ArrayList<>(); public TaskFragmentTransaction() { mTransactionToken = new Binder(); } private TaskFragmentTransaction(Parcel in) { mTransactionToken = in.readStrongBinder(); in.readTypedList(mChanges, Change.CREATOR); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStrongBinder(mTransactionToken); dest.writeTypedList(mChanges); } @NonNull public IBinder getTransactionToken() { return mTransactionToken; } /** Adds a {@link Change} to this transaction. */ public void addChange(@Nullable Change change) { if (change != null) { mChanges.add(change); } } /** Whether this transaction contains any {@link Change}. */ public boolean isEmpty() { return mChanges.isEmpty(); } @NonNull public List getChanges() { return mChanges; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("TaskFragmentTransaction{token="); sb.append(mTransactionToken); sb.append(" changes=["); for (int i = 0; i < mChanges.size(); ++i) { if (i > 0) { sb.append(','); } sb.append(mChanges.get(i)); } sb.append("]}"); return sb.toString(); } @Override public int describeContents() { return 0; } @NonNull public static final Creator CREATOR = new Creator<>() { @Override public TaskFragmentTransaction createFromParcel(Parcel in) { return new TaskFragmentTransaction(in); } @Override public TaskFragmentTransaction[] newArray(int size) { return new TaskFragmentTransaction[size]; } }; /** Change type: the TaskFragment is attached to the hierarchy. */ public static final int TYPE_TASK_FRAGMENT_APPEARED = 1; /** Change type: the status of the TaskFragment is changed. */ public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2; /** Change type: the TaskFragment is removed from the hierarchy. */ public static final int TYPE_TASK_FRAGMENT_VANISHED = 3; /** Change type: the status of the parent leaf Task is changed. */ public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4; /** Change type: the TaskFragment related operation failed on the server side. */ public static final int TYPE_TASK_FRAGMENT_ERROR = 5; /** * Change type: an Activity is reparented to the Task. For example, when an Activity enters and * then exits Picture-in-picture, it will be reparented back to its original Task. In this case, * we need to notify the organizer so that it can check if the Activity matches any split rule. */ public static final int TYPE_ACTIVITY_REPARENTED_TO_TASK = 6; @IntDef(prefix = { "TYPE_" }, value = { TYPE_TASK_FRAGMENT_APPEARED, TYPE_TASK_FRAGMENT_INFO_CHANGED, TYPE_TASK_FRAGMENT_VANISHED, TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, TYPE_TASK_FRAGMENT_ERROR, TYPE_ACTIVITY_REPARENTED_TO_TASK }) @Retention(RetentionPolicy.SOURCE) @interface ChangeType {} /** Represents the change an embedded TaskFragment undergoes. */ public static final class Change implements Parcelable { /** @see ChangeType */ @ChangeType private final int mType; /** @see #setTaskFragmentToken(IBinder) */ @Nullable private IBinder mTaskFragmentToken; /** @see #setTaskFragmentInfo(TaskFragmentInfo) */ @Nullable private TaskFragmentInfo mTaskFragmentInfo; /** @see #setTaskId(int) */ private int mTaskId; /** @see #setErrorCallbackToken(IBinder) */ @Nullable private IBinder mErrorCallbackToken; /** @see #setErrorBundle(Bundle) */ @Nullable private Bundle mErrorBundle; /** @see #setActivityIntent(Intent) */ @Nullable private Intent mActivityIntent; /** @see #setActivityToken(IBinder) */ @Nullable private IBinder mActivityToken; /** @see #setOtherActivityToken(IBinder) */ @Nullable private IBinder mOtherActivityToken; @Nullable private TaskFragmentParentInfo mTaskFragmentParentInfo; @Nullable private SurfaceControl mSurfaceControl; public Change(@ChangeType int type) { mType = type; } private Change(Parcel in) { mType = in.readInt(); mTaskFragmentToken = in.readStrongBinder(); mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR); mTaskId = in.readInt(); mErrorCallbackToken = in.readStrongBinder(); mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader()); mActivityIntent = in.readTypedObject(Intent.CREATOR); mActivityToken = in.readStrongBinder(); mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR); mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR); mOtherActivityToken = in.readStrongBinder(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); dest.writeStrongBinder(mTaskFragmentToken); dest.writeTypedObject(mTaskFragmentInfo, flags); dest.writeInt(mTaskId); dest.writeStrongBinder(mErrorCallbackToken); dest.writeBundle(mErrorBundle); dest.writeTypedObject(mActivityIntent, flags); 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. */ @NonNull public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) { mTaskFragmentToken = requireNonNull(taskFragmentToken); return this; } /** Info of the embedded TaskFragment. */ @NonNull public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) { mTaskFragmentInfo = requireNonNull(info); return this; } /** Task id the parent Task. */ @NonNull public Change setTaskId(int taskId) { mTaskId = taskId; return this; } /** * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction} * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to * report back. */ @NonNull public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) { mErrorCallbackToken = errorCallbackToken; return this; } /** * Bundle with necessary info about the failure operation of * {@link #TYPE_TASK_FRAGMENT_ERROR}. */ @NonNull public Change setErrorBundle(@NonNull Bundle errorBundle) { mErrorBundle = requireNonNull(errorBundle); return this; } /** * Intent of the activity that is reparented to the Task for * {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}. */ @NonNull public Change setActivityIntent(@NonNull Intent intent) { mActivityIntent = requireNonNull(intent); return this; } /** * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}. * If the activity belongs to the same process as the organizer, this will be the actual * activity token; if the activity belongs to a different process, the server will generate * a temporary token that the organizer can use to reparent the activity through * {@link WindowContainerTransaction} if needed. */ @NonNull public Change setActivityToken(@NonNull IBinder activityToken) { mActivityToken = requireNonNull(activityToken); return this; } /** * Token of another activity. *

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 * * @hide */ @NonNull public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) { mTaskFragmentParentInfo = requireNonNull(info); return this; } /** @hide */ @NonNull public Change setTaskFragmentSurfaceControl(@Nullable SurfaceControl sc) { mSurfaceControl = sc; return this; } @ChangeType public int getType() { return mType; } @Nullable public IBinder getTaskFragmentToken() { return mTaskFragmentToken; } @Nullable public TaskFragmentInfo getTaskFragmentInfo() { return mTaskFragmentInfo; } public int getTaskId() { return mTaskId; } @Nullable public IBinder getErrorCallbackToken() { return mErrorCallbackToken; } @NonNull public Bundle getErrorBundle() { return mErrorBundle != null ? mErrorBundle : Bundle.EMPTY; } @SuppressLint("IntentBuilderName") // This is not creating new Intent. @Nullable public Intent getActivityIntent() { return mActivityIntent; } @Nullable public IBinder getActivityToken() { return mActivityToken; } /** @hide */ @Nullable public IBinder getOtherActivityToken() { return mOtherActivityToken; } /** * Obtains the {@link TaskFragmentParentInfo} for this transaction. */ @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages. @Nullable public TaskFragmentParentInfo getTaskFragmentParentInfo() { return mTaskFragmentParentInfo; } /** * Gets the {@link SurfaceControl} of the TaskFragment. This field is {@code null} for * a regular {@link TaskFragmentOrganizer} and is only available for a system * {@link TaskFragmentOrganizer} in the * {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_APPEARED} event. See * {@link ITaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer, * boolean)} * * @hide */ @Nullable public SurfaceControl getTaskFragmentSurfaceControl() { return mSurfaceControl; } @Override public String toString() { return "Change{ type=" + mType + " }"; } @Override public int describeContents() { return 0; } @NonNull public static final Creator CREATOR = new Creator<>() { @Override public Change createFromParcel(Parcel in) { return new Change(in); } @Override public Change[] newArray(int size) { return new Change[size]; } }; } }