1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.window;
18 
19 import static java.util.Objects.requireNonNull;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SuppressLint;
25 import android.annotation.TestApi;
26 import android.content.Intent;
27 import android.os.Binder;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.view.SurfaceControl;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * Used to communicate information about what are changing on embedded TaskFragments belonging to
41  * the same TaskFragmentOrganizer. A transaction can contain multiple changes.
42  * @see TaskFragmentTransaction.Change
43  * @hide
44  */
45 @TestApi
46 public final class TaskFragmentTransaction implements Parcelable {
47 
48     /** Unique token to represent this transaction. */
49     private final IBinder mTransactionToken;
50 
51     /** Changes in this transaction. */
52     private final ArrayList<Change> mChanges = new ArrayList<>();
53 
TaskFragmentTransaction()54     public TaskFragmentTransaction() {
55         mTransactionToken = new Binder();
56     }
57 
TaskFragmentTransaction(Parcel in)58     private TaskFragmentTransaction(Parcel in) {
59         mTransactionToken = in.readStrongBinder();
60         in.readTypedList(mChanges, Change.CREATOR);
61     }
62 
63     @Override
writeToParcel(@onNull Parcel dest, int flags)64     public void writeToParcel(@NonNull Parcel dest, int flags) {
65         dest.writeStrongBinder(mTransactionToken);
66         dest.writeTypedList(mChanges);
67     }
68 
69     @NonNull
getTransactionToken()70     public IBinder getTransactionToken() {
71         return mTransactionToken;
72     }
73 
74     /** Adds a {@link Change} to this transaction. */
addChange(@ullable Change change)75     public void addChange(@Nullable Change change) {
76         if (change != null) {
77             mChanges.add(change);
78         }
79     }
80 
81     /** Whether this transaction contains any {@link Change}. */
isEmpty()82     public boolean isEmpty() {
83         return mChanges.isEmpty();
84     }
85 
86     @NonNull
getChanges()87     public List<Change> getChanges() {
88         return mChanges;
89     }
90 
91     @Override
toString()92     public String toString() {
93         StringBuilder sb = new StringBuilder();
94         sb.append("TaskFragmentTransaction{token=");
95         sb.append(mTransactionToken);
96         sb.append(" changes=[");
97         for (int i = 0; i < mChanges.size(); ++i) {
98             if (i > 0) {
99                 sb.append(',');
100             }
101             sb.append(mChanges.get(i));
102         }
103         sb.append("]}");
104         return sb.toString();
105     }
106 
107     @Override
describeContents()108     public int describeContents() {
109         return 0;
110     }
111 
112     @NonNull
113     public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
114         @Override
115         public TaskFragmentTransaction createFromParcel(Parcel in) {
116             return new TaskFragmentTransaction(in);
117         }
118 
119         @Override
120         public TaskFragmentTransaction[] newArray(int size) {
121             return new TaskFragmentTransaction[size];
122         }
123     };
124 
125     /** Change type: the TaskFragment is attached to the hierarchy. */
126     public static final int TYPE_TASK_FRAGMENT_APPEARED = 1;
127 
128     /** Change type: the status of the TaskFragment is changed. */
129     public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2;
130 
131     /** Change type: the TaskFragment is removed from the hierarchy. */
132     public static final int TYPE_TASK_FRAGMENT_VANISHED = 3;
133 
134     /** Change type: the status of the parent leaf Task is changed. */
135     public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4;
136 
137     /** Change type: the TaskFragment related operation failed on the server side. */
138     public static final int TYPE_TASK_FRAGMENT_ERROR = 5;
139 
140     /**
141      * Change type: an Activity is reparented to the Task. For example, when an Activity enters and
142      * then exits Picture-in-picture, it will be reparented back to its original Task. In this case,
143      * we need to notify the organizer so that it can check if the Activity matches any split rule.
144      */
145     public static final int TYPE_ACTIVITY_REPARENTED_TO_TASK = 6;
146 
147     @IntDef(prefix = { "TYPE_" }, value = {
148             TYPE_TASK_FRAGMENT_APPEARED,
149             TYPE_TASK_FRAGMENT_INFO_CHANGED,
150             TYPE_TASK_FRAGMENT_VANISHED,
151             TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED,
152             TYPE_TASK_FRAGMENT_ERROR,
153             TYPE_ACTIVITY_REPARENTED_TO_TASK
154     })
155     @Retention(RetentionPolicy.SOURCE)
156     @interface ChangeType {}
157 
158     /** Represents the change an embedded TaskFragment undergoes. */
159     public static final class Change implements Parcelable {
160 
161         /** @see ChangeType */
162         @ChangeType
163         private final int mType;
164 
165         /** @see #setTaskFragmentToken(IBinder) */
166         @Nullable
167         private IBinder mTaskFragmentToken;
168 
169         /** @see #setTaskFragmentInfo(TaskFragmentInfo) */
170         @Nullable
171         private TaskFragmentInfo mTaskFragmentInfo;
172 
173         /** @see #setTaskId(int) */
174         private int mTaskId;
175 
176         /** @see #setErrorCallbackToken(IBinder) */
177         @Nullable
178         private IBinder mErrorCallbackToken;
179 
180         /** @see #setErrorBundle(Bundle) */
181         @Nullable
182         private Bundle mErrorBundle;
183 
184         /** @see #setActivityIntent(Intent) */
185         @Nullable
186         private Intent mActivityIntent;
187 
188         /** @see #setActivityToken(IBinder) */
189         @Nullable
190         private IBinder mActivityToken;
191 
192         /** @see #setOtherActivityToken(IBinder) */
193         @Nullable
194         private IBinder mOtherActivityToken;
195 
196         @Nullable
197         private TaskFragmentParentInfo mTaskFragmentParentInfo;
198 
199         @Nullable
200         private SurfaceControl mSurfaceControl;
201 
Change(@hangeType int type)202         public Change(@ChangeType int type) {
203             mType = type;
204         }
205 
Change(Parcel in)206         private Change(Parcel in) {
207             mType = in.readInt();
208             mTaskFragmentToken = in.readStrongBinder();
209             mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
210             mTaskId = in.readInt();
211             mErrorCallbackToken = in.readStrongBinder();
212             mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
213             mActivityIntent = in.readTypedObject(Intent.CREATOR);
214             mActivityToken = in.readStrongBinder();
215             mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
216             mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR);
217             mOtherActivityToken = in.readStrongBinder();
218         }
219 
220         @Override
writeToParcel(@onNull Parcel dest, int flags)221         public void writeToParcel(@NonNull Parcel dest, int flags) {
222             dest.writeInt(mType);
223             dest.writeStrongBinder(mTaskFragmentToken);
224             dest.writeTypedObject(mTaskFragmentInfo, flags);
225             dest.writeInt(mTaskId);
226             dest.writeStrongBinder(mErrorCallbackToken);
227             dest.writeBundle(mErrorBundle);
228             dest.writeTypedObject(mActivityIntent, flags);
229             dest.writeStrongBinder(mActivityToken);
230             dest.writeTypedObject(mTaskFragmentParentInfo, flags);
231             dest.writeTypedObject(mSurfaceControl, flags);
232             dest.writeStrongBinder(mOtherActivityToken);
233         }
234 
235         /** The change is related to the TaskFragment created with this unique token. */
236         @NonNull
setTaskFragmentToken(@onNull IBinder taskFragmentToken)237         public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
238             mTaskFragmentToken = requireNonNull(taskFragmentToken);
239             return this;
240         }
241 
242         /** Info of the embedded TaskFragment. */
243         @NonNull
setTaskFragmentInfo(@onNull TaskFragmentInfo info)244         public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
245             mTaskFragmentInfo = requireNonNull(info);
246             return this;
247         }
248 
249         /** Task id the parent Task. */
250         @NonNull
setTaskId(int taskId)251         public Change setTaskId(int taskId) {
252             mTaskId = taskId;
253             return this;
254         }
255 
256         /**
257          * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
258          * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
259          * report back.
260          */
261         @NonNull
setErrorCallbackToken(@ullable IBinder errorCallbackToken)262         public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
263             mErrorCallbackToken = errorCallbackToken;
264             return this;
265         }
266 
267         /**
268          * Bundle with necessary info about the failure operation of
269          * {@link #TYPE_TASK_FRAGMENT_ERROR}.
270          */
271         @NonNull
setErrorBundle(@onNull Bundle errorBundle)272         public Change setErrorBundle(@NonNull Bundle errorBundle) {
273             mErrorBundle = requireNonNull(errorBundle);
274             return this;
275         }
276 
277         /**
278          * Intent of the activity that is reparented to the Task for
279          * {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}.
280          */
281         @NonNull
setActivityIntent(@onNull Intent intent)282         public Change setActivityIntent(@NonNull Intent intent) {
283             mActivityIntent = requireNonNull(intent);
284             return this;
285         }
286 
287         /**
288          * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}.
289          * If the activity belongs to the same process as the organizer, this will be the actual
290          * activity token; if the activity belongs to a different process, the server will generate
291          * a temporary token that the organizer can use to reparent the activity through
292          * {@link WindowContainerTransaction} if needed.
293          */
294         @NonNull
setActivityToken(@onNull IBinder activityToken)295         public Change setActivityToken(@NonNull IBinder activityToken) {
296             mActivityToken = requireNonNull(activityToken);
297             return this;
298         }
299 
300         /**
301          * Token of another activity.
302          * <p>For {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}, it is the next activity (behind the
303          * reparented one) that fills the Task and occludes other activities. It will be the
304          * actual activity token if the activity belongs to the same process as the organizer.
305          * Otherwise, it is {@code null}.
306          *
307          * @hide
308          */
309         @NonNull
setOtherActivityToken(@onNull IBinder activityToken)310         public Change setOtherActivityToken(@NonNull IBinder activityToken) {
311             mOtherActivityToken = requireNonNull(activityToken);
312             return this;
313         }
314 
315         /**
316          * Sets info of the parent Task of the embedded TaskFragment.
317          * @see TaskFragmentParentInfo
318          *
319          * @hide
320          */
321         @NonNull
setTaskFragmentParentInfo(@onNull TaskFragmentParentInfo info)322         public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
323             mTaskFragmentParentInfo = requireNonNull(info);
324             return this;
325         }
326 
327         /** @hide */
328         @NonNull
setTaskFragmentSurfaceControl(@ullable SurfaceControl sc)329         public Change setTaskFragmentSurfaceControl(@Nullable SurfaceControl sc) {
330             mSurfaceControl = sc;
331             return this;
332         }
333 
334         @ChangeType
getType()335         public int getType() {
336             return mType;
337         }
338 
339         @Nullable
getTaskFragmentToken()340         public IBinder getTaskFragmentToken() {
341             return mTaskFragmentToken;
342         }
343 
344         @Nullable
getTaskFragmentInfo()345         public TaskFragmentInfo getTaskFragmentInfo() {
346             return mTaskFragmentInfo;
347         }
348 
getTaskId()349         public int getTaskId() {
350             return mTaskId;
351         }
352 
353         @Nullable
getErrorCallbackToken()354         public IBinder getErrorCallbackToken() {
355             return mErrorCallbackToken;
356         }
357 
358         @NonNull
getErrorBundle()359         public Bundle getErrorBundle() {
360             return mErrorBundle != null ? mErrorBundle : Bundle.EMPTY;
361         }
362 
363         @SuppressLint("IntentBuilderName") // This is not creating new Intent.
364         @Nullable
getActivityIntent()365         public Intent getActivityIntent() {
366             return mActivityIntent;
367         }
368 
369         @Nullable
getActivityToken()370         public IBinder getActivityToken() {
371             return mActivityToken;
372         }
373 
374         /** @hide */
375         @Nullable
getOtherActivityToken()376         public IBinder getOtherActivityToken() {
377             return mOtherActivityToken;
378         }
379 
380         /**
381          * Obtains the {@link TaskFragmentParentInfo} for this transaction.
382          */
383         @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
384         @Nullable
getTaskFragmentParentInfo()385         public TaskFragmentParentInfo getTaskFragmentParentInfo() {
386             return mTaskFragmentParentInfo;
387         }
388 
389         /**
390          * Gets the {@link SurfaceControl} of the TaskFragment. This field is {@code null} for
391          * a regular {@link TaskFragmentOrganizer} and is only available for a system
392          * {@link TaskFragmentOrganizer} in the
393          * {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_APPEARED} event. See
394          * {@link ITaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer,
395          * boolean)}
396          *
397          * @hide
398          */
399         @Nullable
getTaskFragmentSurfaceControl()400         public SurfaceControl getTaskFragmentSurfaceControl() {
401             return mSurfaceControl;
402         }
403 
404         @Override
toString()405         public String toString() {
406             return "Change{ type=" + mType + " }";
407         }
408 
409         @Override
describeContents()410         public int describeContents() {
411             return 0;
412         }
413 
414         @NonNull
415         public static final Creator<Change> CREATOR = new Creator<>() {
416             @Override
417             public Change createFromParcel(Parcel in) {
418                 return new Change(in);
419             }
420 
421             @Override
422             public Change[] newArray(int size) {
423                 return new Change[size];
424             }
425         };
426     }
427 }
428