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.content.res.Configuration;
28 import android.os.Binder;
29 import android.os.Bundle;
30 import android.os.IBinder;
31 import android.os.Parcel;
32 import android.os.Parcelable;
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         @Nullable
193         private TaskFragmentParentInfo mTaskFragmentParentInfo;
194 
Change(@hangeType int type)195         public Change(@ChangeType int type) {
196             mType = type;
197         }
198 
Change(Parcel in)199         private Change(Parcel in) {
200             mType = in.readInt();
201             mTaskFragmentToken = in.readStrongBinder();
202             mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
203             mTaskId = in.readInt();
204             mErrorCallbackToken = in.readStrongBinder();
205             mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
206             mActivityIntent = in.readTypedObject(Intent.CREATOR);
207             mActivityToken = in.readStrongBinder();
208             mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
209         }
210 
211         @Override
writeToParcel(@onNull Parcel dest, int flags)212         public void writeToParcel(@NonNull Parcel dest, int flags) {
213             dest.writeInt(mType);
214             dest.writeStrongBinder(mTaskFragmentToken);
215             dest.writeTypedObject(mTaskFragmentInfo, flags);
216             dest.writeInt(mTaskId);
217             dest.writeStrongBinder(mErrorCallbackToken);
218             dest.writeBundle(mErrorBundle);
219             dest.writeTypedObject(mActivityIntent, flags);
220             dest.writeStrongBinder(mActivityToken);
221             dest.writeTypedObject(mTaskFragmentParentInfo, flags);
222         }
223 
224         /** The change is related to the TaskFragment created with this unique token. */
225         @NonNull
setTaskFragmentToken(@onNull IBinder taskFragmentToken)226         public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
227             mTaskFragmentToken = requireNonNull(taskFragmentToken);
228             return this;
229         }
230 
231         /** Info of the embedded TaskFragment. */
232         @NonNull
setTaskFragmentInfo(@onNull TaskFragmentInfo info)233         public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
234             mTaskFragmentInfo = requireNonNull(info);
235             return this;
236         }
237 
238         /** Task id the parent Task. */
239         @NonNull
setTaskId(int taskId)240         public Change setTaskId(int taskId) {
241             mTaskId = taskId;
242             return this;
243         }
244 
245         // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
246         /** Configuration of the parent Task. */
247         @NonNull
setTaskConfiguration(@onNull Configuration configuration)248         public Change setTaskConfiguration(@NonNull Configuration configuration) {
249             return this;
250         }
251 
252         /**
253          * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
254          * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
255          * report back.
256          */
257         @NonNull
setErrorCallbackToken(@ullable IBinder errorCallbackToken)258         public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
259             mErrorCallbackToken = errorCallbackToken;
260             return this;
261         }
262 
263         /**
264          * Bundle with necessary info about the failure operation of
265          * {@link #TYPE_TASK_FRAGMENT_ERROR}.
266          */
267         @NonNull
setErrorBundle(@onNull Bundle errorBundle)268         public Change setErrorBundle(@NonNull Bundle errorBundle) {
269             mErrorBundle = requireNonNull(errorBundle);
270             return this;
271         }
272 
273         /**
274          * Intent of the activity that is reparented to the Task for
275          * {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}.
276          */
277         @NonNull
setActivityIntent(@onNull Intent intent)278         public Change setActivityIntent(@NonNull Intent intent) {
279             mActivityIntent = requireNonNull(intent);
280             return this;
281         }
282 
283         /**
284          * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}.
285          * If the activity belongs to the same process as the organizer, this will be the actual
286          * activity token; if the activity belongs to a different process, the server will generate
287          * a temporary token that the organizer can use to reparent the activity through
288          * {@link WindowContainerTransaction} if needed.
289          */
290         @NonNull
setActivityToken(@onNull IBinder activityToken)291         public Change setActivityToken(@NonNull IBinder activityToken) {
292             mActivityToken = requireNonNull(activityToken);
293             return this;
294         }
295 
296         // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
297         /**
298          * Sets info of the parent Task of the embedded TaskFragment.
299          * @see TaskFragmentParentInfo
300          *
301          * @hide pending unhide
302          */
303         @NonNull
setTaskFragmentParentInfo(@onNull TaskFragmentParentInfo info)304         public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
305             mTaskFragmentParentInfo = requireNonNull(info);
306             return this;
307         }
308 
309         @ChangeType
getType()310         public int getType() {
311             return mType;
312         }
313 
314         @Nullable
getTaskFragmentToken()315         public IBinder getTaskFragmentToken() {
316             return mTaskFragmentToken;
317         }
318 
319         @Nullable
getTaskFragmentInfo()320         public TaskFragmentInfo getTaskFragmentInfo() {
321             return mTaskFragmentInfo;
322         }
323 
getTaskId()324         public int getTaskId() {
325             return mTaskId;
326         }
327 
328         // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
329         @Nullable
getTaskConfiguration()330         public Configuration getTaskConfiguration() {
331             return mTaskFragmentParentInfo.getConfiguration();
332         }
333 
334         @Nullable
getErrorCallbackToken()335         public IBinder getErrorCallbackToken() {
336             return mErrorCallbackToken;
337         }
338 
339         @NonNull
getErrorBundle()340         public Bundle getErrorBundle() {
341             return mErrorBundle != null ? mErrorBundle : Bundle.EMPTY;
342         }
343 
344         @SuppressLint("IntentBuilderName") // This is not creating new Intent.
345         @Nullable
getActivityIntent()346         public Intent getActivityIntent() {
347             return mActivityIntent;
348         }
349 
350         @Nullable
getActivityToken()351         public IBinder getActivityToken() {
352             return mActivityToken;
353         }
354 
355         // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
356         /** @hide pending unhide */
357         @Nullable
getTaskFragmentParentInfo()358         public TaskFragmentParentInfo getTaskFragmentParentInfo() {
359             return mTaskFragmentParentInfo;
360         }
361 
362         @Override
toString()363         public String toString() {
364             return "Change{ type=" + mType + " }";
365         }
366 
367         @Override
describeContents()368         public int describeContents() {
369             return 0;
370         }
371 
372         @NonNull
373         public static final Creator<Change> CREATOR = new Creator<>() {
374             @Override
375             public Change createFromParcel(Parcel in) {
376                 return new Change(in);
377             }
378 
379             @Override
380             public Change[] newArray(int size) {
381                 return new Change[size];
382             }
383         };
384     }
385 }
386