1 /*
2  * Copyright (C) 2020 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.TestApi;
22 import android.app.PendingIntent;
23 import android.app.WindowConfiguration;
24 import android.content.Intent;
25 import android.content.pm.ActivityInfo;
26 import android.content.res.Configuration;
27 import android.graphics.Rect;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.util.ArrayMap;
33 import android.view.SurfaceControl;
34 
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Objects;
40 
41 /**
42  * Represents a collection of operations on some WindowContainers that should be applied all at
43  * once.
44  *
45  * @hide
46  */
47 @TestApi
48 public final class WindowContainerTransaction implements Parcelable {
49     private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
50 
51     // Flat list because re-order operations are order-dependent
52     private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
53 
54     @Nullable
55     private IBinder mErrorCallbackToken;
56 
57     @Nullable
58     private ITaskFragmentOrganizer mTaskFragmentOrganizer;
59 
WindowContainerTransaction()60     public WindowContainerTransaction() {}
61 
WindowContainerTransaction(Parcel in)62     private WindowContainerTransaction(Parcel in) {
63         in.readMap(mChanges, null /* loader */);
64         in.readList(mHierarchyOps, null /* loader */);
65         mErrorCallbackToken = in.readStrongBinder();
66         mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
67     }
68 
getOrCreateChange(IBinder token)69     private Change getOrCreateChange(IBinder token) {
70         Change out = mChanges.get(token);
71         if (out == null) {
72             out = new Change();
73             mChanges.put(token, out);
74         }
75         return out;
76     }
77 
78     /**
79      * Resize a container.
80      */
81     @NonNull
setBounds( @onNull WindowContainerToken container,@NonNull Rect bounds)82     public WindowContainerTransaction setBounds(
83             @NonNull WindowContainerToken container,@NonNull Rect bounds) {
84         Change chg = getOrCreateChange(container.asBinder());
85         chg.mConfiguration.windowConfiguration.setBounds(bounds);
86         chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
87         chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
88         return this;
89     }
90 
91     /**
92      * Resize a container's app bounds. This is the bounds used to report appWidth/Height to an
93      * app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from
94      * the full bounds.
95      */
96     @NonNull
setAppBounds( @onNull WindowContainerToken container,@NonNull Rect appBounds)97     public WindowContainerTransaction setAppBounds(
98             @NonNull WindowContainerToken container,@NonNull Rect appBounds) {
99         Change chg = getOrCreateChange(container.asBinder());
100         chg.mConfiguration.windowConfiguration.setAppBounds(appBounds);
101         chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
102         chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
103         return this;
104     }
105 
106     /**
107      * Resize a container's configuration size. The configuration size is what gets reported to the
108      * app via screenWidth/HeightDp and influences which resources get loaded. This size is
109      * derived by subtracting the overlapping portions of both the statusbar and the navbar from
110      * the full bounds.
111      */
112     @NonNull
setScreenSizeDp( @onNull WindowContainerToken container, int w, int h)113     public WindowContainerTransaction setScreenSizeDp(
114             @NonNull WindowContainerToken container, int w, int h) {
115         Change chg = getOrCreateChange(container.asBinder());
116         chg.mConfiguration.screenWidthDp = w;
117         chg.mConfiguration.screenHeightDp = h;
118         chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE;
119         return this;
120     }
121 
122     /**
123      * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task
124      * has finished the enter animation with the given bounds.
125      */
126     @NonNull
scheduleFinishEnterPip( @onNull WindowContainerToken container,@NonNull Rect bounds)127     public WindowContainerTransaction scheduleFinishEnterPip(
128             @NonNull WindowContainerToken container,@NonNull Rect bounds) {
129         Change chg = getOrCreateChange(container.asBinder());
130         chg.mPinnedBounds = new Rect(bounds);
131         chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
132 
133         return this;
134     }
135 
136     /**
137      * Send a SurfaceControl transaction to the server, which the server will apply in sync with
138      * the next bounds change. As this uses deferred transaction and not BLAST it is only
139      * able to sync with a single window, and the first visible window in this hierarchy of type
140      * BASE_APPLICATION to resize will be used. If there are bound changes included in this
141      * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl
142      * transaction will be synced with those bounds. If there are no changes, then
143      * the SurfaceControl transaction will be synced with the next bounds change. This means
144      * that you can call this, apply the WindowContainer transaction, and then later call
145      * dismissPip() to achieve synchronization.
146      */
147     @NonNull
setBoundsChangeTransaction( @onNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t)148     public WindowContainerTransaction setBoundsChangeTransaction(
149             @NonNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t) {
150         Change chg = getOrCreateChange(container.asBinder());
151         chg.mBoundsChangeTransaction = t;
152         chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION;
153         return this;
154     }
155 
156     /**
157      * Like {@link #setBoundsChangeTransaction} but instead queues up a setPosition/WindowCrop
158      * on a container's surface control. This is useful when a boundsChangeTransaction needs to be
159      * queued up on a Task that won't be organized until the end of this window-container
160      * transaction.
161      *
162      * This requires that, at the end of this transaction, `task` will be organized; otherwise
163      * the server will throw an IllegalArgumentException.
164      *
165      * WARNING: Use this carefully. Whatever is set here should match the expected bounds after
166      *          the transaction completes since it will likely be replaced by it. This call is
167      *          intended to pre-emptively set bounds on a surface in sync with a buffer when
168      *          otherwise the new bounds and the new buffer would update on different frames.
169      *
170      * TODO(b/134365562): remove once TaskOrg drives full-screen or BLAST is enabled.
171      *
172      * @hide
173      */
174     @NonNull
setBoundsChangeTransaction( @onNull WindowContainerToken task, @NonNull Rect surfaceBounds)175     public WindowContainerTransaction setBoundsChangeTransaction(
176             @NonNull WindowContainerToken task, @NonNull Rect surfaceBounds) {
177         Change chg = getOrCreateChange(task.asBinder());
178         if (chg.mBoundsChangeSurfaceBounds == null) {
179             chg.mBoundsChangeSurfaceBounds = new Rect();
180         }
181         chg.mBoundsChangeSurfaceBounds.set(surfaceBounds);
182         chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION_RECT;
183         return this;
184     }
185 
186     /**
187      * Set the windowing mode of children of a given root task, without changing
188      * the windowing mode of the Task itself. This can be used during transitions
189      * for example to make the activity render it's fullscreen configuration
190      * while the Task is still in PIP, so you can complete the animation.
191      *
192      * TODO(b/134365562): Can be removed once TaskOrg drives full-screen
193      */
194     @NonNull
setActivityWindowingMode( @onNull WindowContainerToken container, int windowingMode)195     public WindowContainerTransaction setActivityWindowingMode(
196             @NonNull WindowContainerToken container, int windowingMode) {
197         Change chg = getOrCreateChange(container.asBinder());
198         chg.mActivityWindowingMode = windowingMode;
199         return this;
200     }
201 
202     /**
203      * Sets the windowing mode of the given container.
204      */
205     @NonNull
setWindowingMode( @onNull WindowContainerToken container, int windowingMode)206     public WindowContainerTransaction setWindowingMode(
207             @NonNull WindowContainerToken container, int windowingMode) {
208         Change chg = getOrCreateChange(container.asBinder());
209         chg.mWindowingMode = windowingMode;
210         return this;
211     }
212 
213     /**
214      * Sets whether a container or any of its children can be focusable. When {@code false}, no
215      * child can be focused; however, when {@code true}, it is still possible for children to be
216      * non-focusable due to WM policy.
217      */
218     @NonNull
setFocusable( @onNull WindowContainerToken container, boolean focusable)219     public WindowContainerTransaction setFocusable(
220             @NonNull WindowContainerToken container, boolean focusable) {
221         Change chg = getOrCreateChange(container.asBinder());
222         chg.mFocusable = focusable;
223         chg.mChangeMask |= Change.CHANGE_FOCUSABLE;
224         return this;
225     }
226 
227     /**
228      * Sets whether a container or its children should be hidden. When {@code false}, the existing
229      * visibility of the container applies, but when {@code true} the container will be forced
230      * to be hidden.
231      */
232     @NonNull
setHidden( @onNull WindowContainerToken container, boolean hidden)233     public WindowContainerTransaction setHidden(
234             @NonNull WindowContainerToken container, boolean hidden) {
235         Change chg = getOrCreateChange(container.asBinder());
236         chg.mHidden = hidden;
237         chg.mChangeMask |= Change.CHANGE_HIDDEN;
238         return this;
239     }
240 
241     /**
242      * Set the smallestScreenWidth of a container.
243      */
244     @NonNull
setSmallestScreenWidthDp( @onNull WindowContainerToken container, int widthDp)245     public WindowContainerTransaction setSmallestScreenWidthDp(
246             @NonNull WindowContainerToken container, int widthDp) {
247         Change cfg = getOrCreateChange(container.asBinder());
248         cfg.mConfiguration.smallestScreenWidthDp = widthDp;
249         cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
250         return this;
251     }
252 
253     /**
254      * Sets whether a container should ignore the orientation request from apps and windows below
255      * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When
256      * {@code false}, it may rotate based on the orientation request; When {@code true}, it can
257      * never specify orientation, but shows the fixed-orientation apps below it in the letterbox.
258      * @hide
259      */
260     @NonNull
setIgnoreOrientationRequest( @onNull WindowContainerToken container, boolean ignoreOrientationRequest)261     public WindowContainerTransaction setIgnoreOrientationRequest(
262             @NonNull WindowContainerToken container, boolean ignoreOrientationRequest) {
263         Change chg = getOrCreateChange(container.asBinder());
264         chg.mIgnoreOrientationRequest = ignoreOrientationRequest;
265         chg.mChangeMask |= Change.CHANGE_IGNORE_ORIENTATION_REQUEST;
266         return this;
267     }
268 
269     /**
270      * Reparents a container into another one. The effect of a {@code null} parent can vary. For
271      * example, reparenting a stack to {@code null} will reparent it to its display.
272      *
273      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
274      *              the bottom.
275      */
276     @NonNull
reparent(@onNull WindowContainerToken child, @Nullable WindowContainerToken parent, boolean onTop)277     public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
278             @Nullable WindowContainerToken parent, boolean onTop) {
279         mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
280                 parent == null ? null : parent.asBinder(),
281                 onTop));
282         return this;
283     }
284 
285     /**
286      * Reorders a container within its parent.
287      *
288      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
289      *              the bottom.
290      */
291     @NonNull
reorder(@onNull WindowContainerToken child, boolean onTop)292     public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) {
293         mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop));
294         return this;
295     }
296 
297     /**
298      * Reparent's all children tasks or the top task of {@param currentParent} in the specified
299      * {@param windowingMode} and {@param activityType} to {@param newParent} in their current
300      * z-order.
301      *
302      * @param currentParent of the tasks to perform the operation no.
303      *                      {@code null} will perform the operation on the display.
304      * @param newParent for the tasks. {@code null} will perform the operation on the display.
305      * @param windowingModes of the tasks to reparent.
306      * @param activityTypes of the tasks to reparent.
307      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
308      *              the bottom.
309      * @param reparentTopOnly When {@code true}, only reparent the top task which fit windowingModes
310      *                        and activityTypes.
311      * @hide
312      */
313     @NonNull
reparentTasks(@ullable WindowContainerToken currentParent, @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, @Nullable int[] activityTypes, boolean onTop, boolean reparentTopOnly)314     public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
315             @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
316             @Nullable int[] activityTypes, boolean onTop, boolean reparentTopOnly) {
317         mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent(
318                 currentParent != null ? currentParent.asBinder() : null,
319                 newParent != null ? newParent.asBinder() : null,
320                 windowingModes,
321                 activityTypes,
322                 onTop,
323                 reparentTopOnly));
324         return this;
325     }
326 
327     /**
328      * Reparent's all children tasks of {@param currentParent} in the specified
329      * {@param windowingMode} and {@param activityType} to {@param newParent} in their current
330      * z-order.
331      *
332      * @param currentParent of the tasks to perform the operation no.
333      *                      {@code null} will perform the operation on the display.
334      * @param newParent for the tasks. {@code null} will perform the operation on the display.
335      * @param windowingModes of the tasks to reparent.
336      * @param activityTypes of the tasks to reparent.
337      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
338      *              the bottom.
339      */
340     @NonNull
reparentTasks(@ullable WindowContainerToken currentParent, @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, @Nullable int[] activityTypes, boolean onTop)341     public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
342             @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
343             @Nullable int[] activityTypes, boolean onTop) {
344         return reparentTasks(currentParent, newParent, windowingModes, activityTypes, onTop,
345                 false /* reparentTopOnly */);
346     }
347 
348     /**
349      * Sets whether a container should be the launch root for the specified windowing mode and
350      * activity type. This currently only applies to Task containers created by organizer.
351      */
352     @NonNull
setLaunchRoot(@onNull WindowContainerToken container, @Nullable int[] windowingModes, @Nullable int[] activityTypes)353     public WindowContainerTransaction setLaunchRoot(@NonNull WindowContainerToken container,
354             @Nullable int[] windowingModes, @Nullable int[] activityTypes) {
355         mHierarchyOps.add(HierarchyOp.createForSetLaunchRoot(
356                 container.asBinder(),
357                 windowingModes,
358                 activityTypes));
359         return this;
360     }
361 
362     /**
363      * Sets to containers adjacent to each other. Containers below two visible adjacent roots will
364      * be made invisible. This currently only applies to TaskFragment containers created by
365      * organizer.
366      * @param root1 the first root.
367      * @param root2 the second root.
368      */
369     @NonNull
setAdjacentRoots( @onNull WindowContainerToken root1, @NonNull WindowContainerToken root2, boolean moveTogether)370     public WindowContainerTransaction setAdjacentRoots(
371             @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2,
372             boolean moveTogether) {
373         mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
374                 root1.asBinder(),
375                 root2.asBinder(),
376                 moveTogether));
377         return this;
378     }
379 
380     /**
381      * Sets the container as launch adjacent flag root. Task starting with
382      * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} will be launching to.
383      *
384      * @hide
385      */
386     @NonNull
setLaunchAdjacentFlagRoot( @onNull WindowContainerToken container)387     public WindowContainerTransaction setLaunchAdjacentFlagRoot(
388             @NonNull WindowContainerToken container) {
389         mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(),
390                 false /* clearRoot */));
391         return this;
392     }
393 
394     /**
395      * Clears launch adjacent flag root for the display area of passing container.
396      *
397      * @hide
398      */
399     @NonNull
clearLaunchAdjacentFlagRoot( @onNull WindowContainerToken container)400     public WindowContainerTransaction clearLaunchAdjacentFlagRoot(
401             @NonNull WindowContainerToken container) {
402         mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(),
403                 true /* clearRoot */));
404         return this;
405     }
406 
407     /**
408      * Starts a task by id. The task is expected to already exist (eg. as a recent task).
409      * @param taskId Id of task to start.
410      * @param options bundle containing ActivityOptions for the task's top activity.
411      * @hide
412      */
413     @NonNull
startTask(int taskId, @Nullable Bundle options)414     public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
415         mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
416         return this;
417     }
418 
419     /**
420      * Sends a pending intent in sync.
421      * @param sender The PendingIntent sender.
422      * @param intent The fillIn intent to patch over the sender's base intent.
423      * @param options bundle containing ActivityOptions for the task's top activity.
424      * @hide
425      */
426     @NonNull
sendPendingIntent(PendingIntent sender, Intent intent, @Nullable Bundle options)427     public WindowContainerTransaction sendPendingIntent(PendingIntent sender, Intent intent,
428             @Nullable Bundle options) {
429         mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
430                 .setLaunchOptions(options)
431                 .setPendingIntent(sender)
432                 .setActivityIntent(intent)
433                 .build());
434         return this;
435     }
436 
437     /**
438      * Creates a new TaskFragment with the given options.
439      * @param taskFragmentOptions the options used to create the TaskFragment.
440      */
441     @NonNull
createTaskFragment( @onNull TaskFragmentCreationParams taskFragmentOptions)442     public WindowContainerTransaction createTaskFragment(
443             @NonNull TaskFragmentCreationParams taskFragmentOptions) {
444         final HierarchyOp hierarchyOp =
445                 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT)
446                         .setTaskFragmentCreationOptions(taskFragmentOptions)
447                         .build();
448         mHierarchyOps.add(hierarchyOp);
449         return this;
450     }
451 
452     /**
453      * Deletes an existing TaskFragment. Any remaining activities below it will be destroyed.
454      * @param taskFragment  the TaskFragment to be removed.
455      */
456     @NonNull
deleteTaskFragment( @onNull WindowContainerToken taskFragment)457     public WindowContainerTransaction deleteTaskFragment(
458             @NonNull WindowContainerToken taskFragment) {
459         final HierarchyOp hierarchyOp =
460                 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT)
461                         .setContainer(taskFragment.asBinder())
462                         .build();
463         mHierarchyOps.add(hierarchyOp);
464         return this;
465     }
466 
467     /**
468      * Starts an activity in the TaskFragment.
469      * @param fragmentToken client assigned unique token to create TaskFragment with specified in
470      *                      {@link TaskFragmentCreationParams#getFragmentToken()}.
471      * @param callerToken  the activity token that initialized the activity launch.
472      * @param activityIntent    intent to start the activity.
473      * @param activityOptions    ActivityOptions to start the activity with.
474      * @see android.content.Context#startActivity(Intent, Bundle).
475      */
476     @NonNull
startActivityInTaskFragment( @onNull IBinder fragmentToken, @NonNull IBinder callerToken, @NonNull Intent activityIntent, @Nullable Bundle activityOptions)477     public WindowContainerTransaction startActivityInTaskFragment(
478             @NonNull IBinder fragmentToken, @NonNull IBinder callerToken,
479             @NonNull Intent activityIntent, @Nullable Bundle activityOptions) {
480         final HierarchyOp hierarchyOp =
481                 new HierarchyOp.Builder(
482                         HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT)
483                         .setContainer(fragmentToken)
484                         .setReparentContainer(callerToken)
485                         .setActivityIntent(activityIntent)
486                         .setLaunchOptions(activityOptions)
487                         .build();
488         mHierarchyOps.add(hierarchyOp);
489         return this;
490     }
491 
492     /**
493      * Moves an activity into the TaskFragment.
494      * @param fragmentToken client assigned unique token to create TaskFragment with specified in
495      *                      {@link TaskFragmentCreationParams#getFragmentToken()}.
496      * @param activityToken activity to be reparented.
497      */
498     @NonNull
reparentActivityToTaskFragment( @onNull IBinder fragmentToken, @NonNull IBinder activityToken)499     public WindowContainerTransaction reparentActivityToTaskFragment(
500             @NonNull IBinder fragmentToken, @NonNull IBinder activityToken) {
501         final HierarchyOp hierarchyOp =
502                 new HierarchyOp.Builder(
503                         HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT)
504                         .setReparentContainer(fragmentToken)
505                         .setContainer(activityToken)
506                         .build();
507         mHierarchyOps.add(hierarchyOp);
508         return this;
509     }
510 
511     /**
512      * Reparents all children of one TaskFragment to another.
513      * @param oldParent children of this TaskFragment will be reparented.
514      * @param newParent the new parent TaskFragment to move the children to. If {@code null}, the
515      *                  children will be moved to the leaf Task.
516      */
517     @NonNull
reparentChildren( @onNull WindowContainerToken oldParent, @Nullable WindowContainerToken newParent)518     public WindowContainerTransaction reparentChildren(
519             @NonNull WindowContainerToken oldParent,
520             @Nullable WindowContainerToken newParent) {
521         final HierarchyOp hierarchyOp =
522                 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN)
523                         .setContainer(oldParent.asBinder())
524                         .setReparentContainer(newParent != null ? newParent.asBinder() : null)
525                         .build();
526         mHierarchyOps.add(hierarchyOp);
527         return this;
528     }
529 
530     /**
531      * Sets to TaskFragments adjacent to each other. Containers below two visible adjacent
532      * TaskFragments will be made invisible. This is similar to
533      * {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken)}, but can be used with
534      * fragmentTokens when that TaskFragments haven't been created (but will be created in the same
535      * {@link WindowContainerTransaction}).
536      * To reset it, pass {@code null} for {@code fragmentToken2}.
537      * @param fragmentToken1    client assigned unique token to create TaskFragment with specified
538      *                          in {@link TaskFragmentCreationParams#getFragmentToken()}.
539      * @param fragmentToken2    client assigned unique token to create TaskFragment with specified
540      *                          in {@link TaskFragmentCreationParams#getFragmentToken()}. If it is
541      *                          {@code null}, the transaction will reset the adjacent TaskFragment.
542      */
543     @NonNull
setAdjacentTaskFragments( @onNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2, @Nullable TaskFragmentAdjacentParams params)544     public WindowContainerTransaction setAdjacentTaskFragments(
545             @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2,
546             @Nullable TaskFragmentAdjacentParams params) {
547         final HierarchyOp hierarchyOp =
548                 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS)
549                         .setContainer(fragmentToken1)
550                         .setReparentContainer(fragmentToken2)
551                         .setLaunchOptions(params != null ? params.toBundle() : null)
552                         .build();
553         mHierarchyOps.add(hierarchyOp);
554         return this;
555     }
556 
557     /**
558      * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
559      * trigger callback with this {@param errorCallbackToken}.
560      * @param errorCallbackToken    client provided token that will be passed back as parameter in
561      *                              the callback if there is an error on the server side.
562      * @see ITaskFragmentOrganizer#onTaskFragmentError
563      */
564     @NonNull
setErrorCallbackToken(@onNull IBinder errorCallbackToken)565     public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
566         if (mErrorCallbackToken != null) {
567             throw new IllegalStateException("Can't set multiple error token for one transaction.");
568         }
569         mErrorCallbackToken = errorCallbackToken;
570         return this;
571     }
572 
573     /**
574      * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
575      * When this is set, the server side will not check for the permission of
576      * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
577      * contains operations that are allowed for this organizer, such as modifying TaskFragments that
578      * are organized by this organizer.
579      * @hide
580      */
581     @NonNull
setTaskFragmentOrganizer(@onNull ITaskFragmentOrganizer organizer)582     WindowContainerTransaction setTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
583         if (mTaskFragmentOrganizer != null) {
584             throw new IllegalStateException("Can't set multiple organizers for one transaction.");
585         }
586         mTaskFragmentOrganizer = organizer;
587         return this;
588     }
589 
590     /**
591      * Merges another WCT into this one.
592      * @param transfer When true, this will transfer everything from other potentially leaving
593      *                 other in an unusable state. When false, other is left alone, but
594      *                 SurfaceFlinger Transactions will not be merged.
595      * @hide
596      */
merge(WindowContainerTransaction other, boolean transfer)597     public void merge(WindowContainerTransaction other, boolean transfer) {
598         for (int i = 0, n = other.mChanges.size(); i < n; ++i) {
599             final IBinder key = other.mChanges.keyAt(i);
600             Change existing = mChanges.get(key);
601             if (existing == null) {
602                 existing = new Change();
603                 mChanges.put(key, existing);
604             }
605             existing.merge(other.mChanges.valueAt(i), transfer);
606         }
607         for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) {
608             mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
609                     : new HierarchyOp(other.mHierarchyOps.get(i)));
610         }
611         if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
612                 != other.mErrorCallbackToken) {
613             throw new IllegalArgumentException("Can't merge two WCTs with different error token");
614         }
615         final IBinder taskFragmentOrganizerAsBinder = mTaskFragmentOrganizer != null
616                 ? mTaskFragmentOrganizer.asBinder()
617                 : null;
618         final IBinder otherTaskFragmentOrganizerAsBinder = other.mTaskFragmentOrganizer != null
619                 ? other.mTaskFragmentOrganizer.asBinder()
620                 : null;
621         if (!Objects.equals(taskFragmentOrganizerAsBinder, otherTaskFragmentOrganizerAsBinder)) {
622             throw new IllegalArgumentException(
623                     "Can't merge two WCTs from different TaskFragmentOrganizers");
624         }
625         mErrorCallbackToken = mErrorCallbackToken != null
626                 ? mErrorCallbackToken
627                 : other.mErrorCallbackToken;
628     }
629 
630     /** @hide */
isEmpty()631     public boolean isEmpty() {
632         return mChanges.isEmpty() && mHierarchyOps.isEmpty();
633     }
634 
635     /** @hide */
getChanges()636     public Map<IBinder, Change> getChanges() {
637         return mChanges;
638     }
639 
640     /** @hide */
getHierarchyOps()641     public List<HierarchyOp> getHierarchyOps() {
642         return mHierarchyOps;
643     }
644 
645     /** @hide */
646     @Nullable
getErrorCallbackToken()647     public IBinder getErrorCallbackToken() {
648         return mErrorCallbackToken;
649     }
650 
651     /** @hide */
652     @Nullable
getTaskFragmentOrganizer()653     public ITaskFragmentOrganizer getTaskFragmentOrganizer() {
654         return mTaskFragmentOrganizer;
655     }
656 
657     @Override
658     @NonNull
toString()659     public String toString() {
660         return "WindowContainerTransaction {"
661                 + " changes = " + mChanges
662                 + " hops = " + mHierarchyOps
663                 + " errorCallbackToken=" + mErrorCallbackToken
664                 + " taskFragmentOrganizer=" + mTaskFragmentOrganizer
665                 + " }";
666     }
667 
668     @Override
669     /** @hide */
writeToParcel(@onNull Parcel dest, int flags)670     public void writeToParcel(@NonNull Parcel dest, int flags) {
671         dest.writeMap(mChanges);
672         dest.writeList(mHierarchyOps);
673         dest.writeStrongBinder(mErrorCallbackToken);
674         dest.writeStrongInterface(mTaskFragmentOrganizer);
675     }
676 
677     @Override
678     /** @hide */
describeContents()679     public int describeContents() {
680         return 0;
681     }
682 
683     @NonNull
684     public static final Creator<WindowContainerTransaction> CREATOR =
685             new Creator<WindowContainerTransaction>() {
686                 @Override
687                 public WindowContainerTransaction createFromParcel(Parcel in) {
688                     return new WindowContainerTransaction(in);
689                 }
690 
691                 @Override
692                 public WindowContainerTransaction[] newArray(int size) {
693                     return new WindowContainerTransaction[size];
694                 }
695             };
696 
697     /**
698      * Holds changes on a single WindowContainer including Configuration changes.
699      * @hide
700      */
701     public static class Change implements Parcelable {
702         public static final int CHANGE_FOCUSABLE = 1;
703         public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
704         public static final int CHANGE_PIP_CALLBACK = 1 << 2;
705         public static final int CHANGE_HIDDEN = 1 << 3;
706         public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
707         public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
708 
709         private final Configuration mConfiguration = new Configuration();
710         private boolean mFocusable = true;
711         private boolean mHidden = false;
712         private boolean mIgnoreOrientationRequest = false;
713 
714         private int mChangeMask = 0;
715         private @ActivityInfo.Config int mConfigSetMask = 0;
716         private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
717 
718         private Rect mPinnedBounds = null;
719         private SurfaceControl.Transaction mBoundsChangeTransaction = null;
720         private Rect mBoundsChangeSurfaceBounds = null;
721 
722         private int mActivityWindowingMode = -1;
723         private int mWindowingMode = -1;
724 
Change()725         public Change() {}
726 
Change(Parcel in)727         protected Change(Parcel in) {
728             mConfiguration.readFromParcel(in);
729             mFocusable = in.readBoolean();
730             mHidden = in.readBoolean();
731             mIgnoreOrientationRequest = in.readBoolean();
732             mChangeMask = in.readInt();
733             mConfigSetMask = in.readInt();
734             mWindowSetMask = in.readInt();
735             if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) {
736                 mPinnedBounds = new Rect();
737                 mPinnedBounds.readFromParcel(in);
738             }
739             if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) {
740                 mBoundsChangeTransaction =
741                     SurfaceControl.Transaction.CREATOR.createFromParcel(in);
742             }
743             if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION_RECT) != 0) {
744                 mBoundsChangeSurfaceBounds = new Rect();
745                 mBoundsChangeSurfaceBounds.readFromParcel(in);
746             }
747 
748             mWindowingMode = in.readInt();
749             mActivityWindowingMode = in.readInt();
750         }
751 
752         /**
753          * @param transfer When true, this will transfer other into this leaving other in an
754          *                 undefined state. Use this if you don't intend to use other. When false,
755          *                 SurfaceFlinger Transactions will not merge.
756          */
merge(Change other, boolean transfer)757         public void merge(Change other, boolean transfer) {
758             mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask);
759             mConfigSetMask |= other.mConfigSetMask;
760             mWindowSetMask |= other.mWindowSetMask;
761             if ((other.mChangeMask & CHANGE_FOCUSABLE) != 0) {
762                 mFocusable = other.mFocusable;
763             }
764             if (transfer && (other.mChangeMask & CHANGE_BOUNDS_TRANSACTION) != 0) {
765                 mBoundsChangeTransaction = other.mBoundsChangeTransaction;
766                 other.mBoundsChangeTransaction = null;
767             }
768             if ((other.mChangeMask & CHANGE_PIP_CALLBACK) != 0) {
769                 mPinnedBounds = transfer ? other.mPinnedBounds : new Rect(other.mPinnedBounds);
770             }
771             if ((other.mChangeMask & CHANGE_HIDDEN) != 0) {
772                 mHidden = other.mHidden;
773             }
774             if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
775                 mIgnoreOrientationRequest = other.mIgnoreOrientationRequest;
776             }
777             mChangeMask |= other.mChangeMask;
778             if (other.mActivityWindowingMode >= 0) {
779                 mActivityWindowingMode = other.mActivityWindowingMode;
780             }
781             if (other.mWindowingMode >= 0) {
782                 mWindowingMode = other.mWindowingMode;
783             }
784             if (other.mBoundsChangeSurfaceBounds != null) {
785                 mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds
786                         : new Rect(other.mBoundsChangeSurfaceBounds);
787             }
788         }
789 
getWindowingMode()790         public int getWindowingMode() {
791             return mWindowingMode;
792         }
793 
getActivityWindowingMode()794         public int getActivityWindowingMode() {
795             return mActivityWindowingMode;
796         }
797 
getConfiguration()798         public Configuration getConfiguration() {
799             return mConfiguration;
800         }
801 
802         /** Gets the requested focusable state */
getFocusable()803         public boolean getFocusable() {
804             if ((mChangeMask & CHANGE_FOCUSABLE) == 0) {
805                 throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first");
806             }
807             return mFocusable;
808         }
809 
810         /** Gets the requested hidden state */
getHidden()811         public boolean getHidden() {
812             if ((mChangeMask & CHANGE_HIDDEN) == 0) {
813                 throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first");
814             }
815             return mHidden;
816         }
817 
818         /** Gets the requested state of whether to ignore orientation request. */
getIgnoreOrientationRequest()819         public boolean getIgnoreOrientationRequest() {
820             if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) {
821                 throw new RuntimeException("IgnoreOrientationRequest not set. "
822                         + "Check CHANGE_IGNORE_ORIENTATION_REQUEST first");
823             }
824             return mIgnoreOrientationRequest;
825         }
826 
getChangeMask()827         public int getChangeMask() {
828             return mChangeMask;
829         }
830 
831         @ActivityInfo.Config
getConfigSetMask()832         public int getConfigSetMask() {
833             return mConfigSetMask;
834         }
835 
836         @WindowConfiguration.WindowConfig
getWindowSetMask()837         public int getWindowSetMask() {
838             return mWindowSetMask;
839         }
840 
841         /**
842          * Returns the bounds to be used for scheduling the enter pip callback
843          * or null if no callback is to be scheduled.
844          */
getEnterPipBounds()845         public Rect getEnterPipBounds() {
846             return mPinnedBounds;
847         }
848 
getBoundsChangeTransaction()849         public SurfaceControl.Transaction getBoundsChangeTransaction() {
850             return mBoundsChangeTransaction;
851         }
852 
getBoundsChangeSurfaceBounds()853         public Rect getBoundsChangeSurfaceBounds() {
854             return mBoundsChangeSurfaceBounds;
855         }
856 
857         @Override
toString()858         public String toString() {
859             final boolean changesBounds =
860                     (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
861                             && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS)
862                                     != 0);
863             final boolean changesAppBounds =
864                     (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
865                             && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS)
866                                     != 0);
867             final boolean changesSs = (mConfigSetMask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0;
868             final boolean changesSss =
869                     (mConfigSetMask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0;
870             StringBuilder sb = new StringBuilder();
871             sb.append('{');
872             if (changesBounds) {
873                 sb.append("bounds:" + mConfiguration.windowConfiguration.getBounds() + ",");
874             }
875             if (changesAppBounds) {
876                 sb.append("appbounds:" + mConfiguration.windowConfiguration.getAppBounds() + ",");
877             }
878             if (changesSss) {
879                 sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ",");
880             }
881             if (changesSs) {
882                 sb.append("sw/h:" + mConfiguration.screenWidthDp + "x"
883                         + mConfiguration.screenHeightDp + ",");
884             }
885             if ((mChangeMask & CHANGE_FOCUSABLE) != 0) {
886                 sb.append("focusable:" + mFocusable + ",");
887             }
888             if (mBoundsChangeTransaction != null) {
889                 sb.append("hasBoundsTransaction,");
890             }
891             if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
892                 sb.append("ignoreOrientationRequest:" + mIgnoreOrientationRequest + ",");
893             }
894             sb.append("}");
895             return sb.toString();
896         }
897 
898         @Override
writeToParcel(Parcel dest, int flags)899         public void writeToParcel(Parcel dest, int flags) {
900             mConfiguration.writeToParcel(dest, flags);
901             dest.writeBoolean(mFocusable);
902             dest.writeBoolean(mHidden);
903             dest.writeBoolean(mIgnoreOrientationRequest);
904             dest.writeInt(mChangeMask);
905             dest.writeInt(mConfigSetMask);
906             dest.writeInt(mWindowSetMask);
907 
908             if (mPinnedBounds != null) {
909                 mPinnedBounds.writeToParcel(dest, flags);
910             }
911             if (mBoundsChangeTransaction != null) {
912                 mBoundsChangeTransaction.writeToParcel(dest, flags);
913             }
914             if (mBoundsChangeSurfaceBounds != null) {
915                 mBoundsChangeSurfaceBounds.writeToParcel(dest, flags);
916             }
917 
918             dest.writeInt(mWindowingMode);
919             dest.writeInt(mActivityWindowingMode);
920         }
921 
922         @Override
describeContents()923         public int describeContents() {
924             return 0;
925         }
926 
927         public static final Creator<Change> CREATOR = new Creator<Change>() {
928             @Override
929             public Change createFromParcel(Parcel in) {
930                 return new Change(in);
931             }
932 
933             @Override
934             public Change[] newArray(int size) {
935                 return new Change[size];
936             }
937         };
938     }
939 
940     /**
941      * Holds information about a reparent/reorder operation in the hierarchy. This is separate from
942      * Changes because they must be executed in the same order that they are added.
943      * @hide
944      */
945     public static class HierarchyOp implements Parcelable {
946         public static final int HIERARCHY_OP_TYPE_REPARENT = 0;
947         public static final int HIERARCHY_OP_TYPE_REORDER = 1;
948         public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2;
949         public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3;
950         public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
951         public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5;
952         public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6;
953         public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7;
954         public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8;
955         public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9;
956         public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10;
957         public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
958         public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
959         public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
960 
961         // The following key(s) are for use with mLaunchOptions:
962         // When launching a task (eg. from recents), this is the taskId to be launched.
963         public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId";
964 
965         private final int mType;
966 
967         // Container we are performing the operation on.
968         @Nullable
969         private IBinder mContainer;
970 
971         // If this is same as mContainer, then only change position, don't reparent.
972         @Nullable
973         private IBinder mReparent;
974 
975         // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
976         private boolean mToTop;
977 
978         private boolean mReparentTopOnly;
979 
980         // TODO(b/207185041): Remove this once having a single-top root for split screen.
981         private boolean mMoveAdjacentTogether;
982 
983         @Nullable
984         private int[]  mWindowingModes;
985 
986         @Nullable
987         private int[] mActivityTypes;
988 
989         @Nullable
990         private Bundle mLaunchOptions;
991 
992         @Nullable
993         private Intent mActivityIntent;
994 
995         // Used as options for WindowContainerTransaction#createTaskFragment().
996         @Nullable
997         private TaskFragmentCreationParams mTaskFragmentCreationOptions;
998 
999         @Nullable
1000         private PendingIntent mPendingIntent;
1001 
createForReparent( @onNull IBinder container, @Nullable IBinder reparent, boolean toTop)1002         public static HierarchyOp createForReparent(
1003                 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
1004             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
1005                     .setContainer(container)
1006                     .setReparentContainer(reparent)
1007                     .setToTop(toTop)
1008                     .build();
1009         }
1010 
createForReorder(@onNull IBinder container, boolean toTop)1011         public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
1012             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER)
1013                     .setContainer(container)
1014                     .setReparentContainer(container)
1015                     .setToTop(toTop)
1016                     .build();
1017         }
1018 
createForChildrenTasksReparent(IBinder currentParent, IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop, boolean reparentTopOnly)1019         public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
1020                 IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop,
1021                 boolean reparentTopOnly) {
1022             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT)
1023                     .setContainer(currentParent)
1024                     .setReparentContainer(newParent)
1025                     .setWindowingModes(windowingModes)
1026                     .setActivityTypes(activityTypes)
1027                     .setToTop(onTop)
1028                     .setReparentTopOnly(reparentTopOnly)
1029                     .build();
1030         }
1031 
createForSetLaunchRoot(IBinder container, int[] windowingModes, int[] activityTypes)1032         public static HierarchyOp createForSetLaunchRoot(IBinder container,
1033                 int[] windowingModes, int[] activityTypes) {
1034             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT)
1035                     .setContainer(container)
1036                     .setWindowingModes(windowingModes)
1037                     .setActivityTypes(activityTypes)
1038                     .build();
1039         }
1040 
1041         /** Create a hierarchy op for setting adjacent root tasks. */
createForAdjacentRoots(IBinder root1, IBinder root2, boolean moveTogether)1042         public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2,
1043                 boolean moveTogether) {
1044             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
1045                     .setContainer(root1)
1046                     .setReparentContainer(root2)
1047                     .setMoveAdjacentTogether(moveTogether)
1048                     .build();
1049         }
1050 
1051         /** Create a hierarchy op for launching a task. */
createForTaskLaunch(int taskId, @Nullable Bundle options)1052         public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {
1053             final Bundle fullOptions = options == null ? new Bundle() : options;
1054             fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);
1055             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK)
1056                     .setToTop(true)
1057                     .setLaunchOptions(fullOptions)
1058                     .build();
1059         }
1060 
1061         /** Create a hierarchy op for setting launch adjacent flag root. */
createForSetLaunchAdjacentFlagRoot(IBinder container, boolean clearRoot)1062         public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
1063                 boolean clearRoot) {
1064             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT)
1065                     .setContainer(container)
1066                     .setToTop(clearRoot)
1067                     .build();
1068         }
1069 
1070         /** Only creates through {@link Builder}. */
HierarchyOp(int type)1071         private HierarchyOp(int type) {
1072             mType = type;
1073         }
1074 
HierarchyOp(@onNull HierarchyOp copy)1075         public HierarchyOp(@NonNull HierarchyOp copy) {
1076             mType = copy.mType;
1077             mContainer = copy.mContainer;
1078             mReparent = copy.mReparent;
1079             mToTop = copy.mToTop;
1080             mReparentTopOnly = copy.mReparentTopOnly;
1081             mMoveAdjacentTogether = copy.mMoveAdjacentTogether;
1082             mWindowingModes = copy.mWindowingModes;
1083             mActivityTypes = copy.mActivityTypes;
1084             mLaunchOptions = copy.mLaunchOptions;
1085             mActivityIntent = copy.mActivityIntent;
1086             mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
1087             mPendingIntent = copy.mPendingIntent;
1088         }
1089 
HierarchyOp(Parcel in)1090         protected HierarchyOp(Parcel in) {
1091             mType = in.readInt();
1092             mContainer = in.readStrongBinder();
1093             mReparent = in.readStrongBinder();
1094             mToTop = in.readBoolean();
1095             mReparentTopOnly = in.readBoolean();
1096             mMoveAdjacentTogether = in.readBoolean();
1097             mWindowingModes = in.createIntArray();
1098             mActivityTypes = in.createIntArray();
1099             mLaunchOptions = in.readBundle();
1100             mActivityIntent = in.readTypedObject(Intent.CREATOR);
1101             mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
1102             mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
1103         }
1104 
getType()1105         public int getType() {
1106             return mType;
1107         }
1108 
isReparent()1109         public boolean isReparent() {
1110             return mType == HIERARCHY_OP_TYPE_REPARENT;
1111         }
1112 
1113         @Nullable
getNewParent()1114         public IBinder getNewParent() {
1115             return mReparent;
1116         }
1117 
1118         @NonNull
getContainer()1119         public IBinder getContainer() {
1120             return mContainer;
1121         }
1122 
1123         @NonNull
getAdjacentRoot()1124         public IBinder getAdjacentRoot() {
1125             return mReparent;
1126         }
1127 
1128         @NonNull
getCallingActivity()1129         public IBinder getCallingActivity() {
1130             return mReparent;
1131         }
1132 
getToTop()1133         public boolean getToTop() {
1134             return mToTop;
1135         }
1136 
getReparentTopOnly()1137         public boolean getReparentTopOnly() {
1138             return mReparentTopOnly;
1139         }
1140 
getMoveAdjacentTogether()1141         public boolean getMoveAdjacentTogether() {
1142             return mMoveAdjacentTogether;
1143         }
1144 
getWindowingModes()1145         public int[] getWindowingModes() {
1146             return mWindowingModes;
1147         }
1148 
getActivityTypes()1149         public int[] getActivityTypes() {
1150             return mActivityTypes;
1151         }
1152 
1153         @Nullable
getLaunchOptions()1154         public Bundle getLaunchOptions() {
1155             return mLaunchOptions;
1156         }
1157 
1158         @Nullable
getActivityIntent()1159         public Intent getActivityIntent() {
1160             return mActivityIntent;
1161         }
1162 
1163         @Nullable
getTaskFragmentCreationOptions()1164         public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
1165             return mTaskFragmentCreationOptions;
1166         }
1167 
1168         @Nullable
getPendingIntent()1169         public PendingIntent getPendingIntent() {
1170             return mPendingIntent;
1171         }
1172 
1173         @Override
toString()1174         public String toString() {
1175             switch (mType) {
1176                 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
1177                     return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
1178                             + " mToTop=" + mToTop + " mReparentTopOnly=" + mReparentTopOnly
1179                             + " mWindowingMode=" + Arrays.toString(mWindowingModes)
1180                             + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
1181                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
1182                     return "{SetLaunchRoot: container=" + mContainer
1183                             + " mWindowingMode=" + Arrays.toString(mWindowingModes)
1184                             + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
1185                 case HIERARCHY_OP_TYPE_REPARENT:
1186                     return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
1187                             + mReparent + "}";
1188                 case HIERARCHY_OP_TYPE_REORDER:
1189                     return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
1190                 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
1191                     return "{SetAdjacentRoot: container=" + mContainer
1192                             + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether="
1193                             + mMoveAdjacentTogether + "}";
1194                 case HIERARCHY_OP_TYPE_LAUNCH_TASK:
1195                     return "{LaunchTask: " + mLaunchOptions + "}";
1196                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
1197                     return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
1198                             + "}";
1199                 case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
1200                     return "{CreateTaskFragment: options=" + mTaskFragmentCreationOptions + "}";
1201                 case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
1202                     return "{DeleteTaskFragment: taskFragment=" + mContainer + "}";
1203                 case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
1204                     return "{StartActivityInTaskFragment: fragmentToken=" + mContainer + " intent="
1205                             + mActivityIntent + " options=" + mLaunchOptions + "}";
1206                 case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
1207                     return "{ReparentActivityToTaskFragment: fragmentToken=" + mReparent
1208                             + " activity=" + mContainer + "}";
1209                 case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
1210                     return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent
1211                             + "}";
1212                 case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
1213                     return "{SetAdjacentTaskFragments: container=" + mContainer
1214                             + " adjacentContainer=" + mReparent + "}";
1215                 default:
1216                     return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
1217                             + " mToTop=" + mToTop
1218                             + " mWindowingMode=" + Arrays.toString(mWindowingModes)
1219                             + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
1220             }
1221         }
1222 
1223         @Override
writeToParcel(Parcel dest, int flags)1224         public void writeToParcel(Parcel dest, int flags) {
1225             dest.writeInt(mType);
1226             dest.writeStrongBinder(mContainer);
1227             dest.writeStrongBinder(mReparent);
1228             dest.writeBoolean(mToTop);
1229             dest.writeBoolean(mReparentTopOnly);
1230             dest.writeBoolean(mMoveAdjacentTogether);
1231             dest.writeIntArray(mWindowingModes);
1232             dest.writeIntArray(mActivityTypes);
1233             dest.writeBundle(mLaunchOptions);
1234             dest.writeTypedObject(mActivityIntent, flags);
1235             dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
1236             dest.writeTypedObject(mPendingIntent, flags);
1237         }
1238 
1239         @Override
describeContents()1240         public int describeContents() {
1241             return 0;
1242         }
1243 
1244         public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() {
1245             @Override
1246             public HierarchyOp createFromParcel(Parcel in) {
1247                 return new HierarchyOp(in);
1248             }
1249 
1250             @Override
1251             public HierarchyOp[] newArray(int size) {
1252                 return new HierarchyOp[size];
1253             }
1254         };
1255 
1256         private static class Builder {
1257 
1258             private final int mType;
1259 
1260             @Nullable
1261             private IBinder mContainer;
1262 
1263             @Nullable
1264             private IBinder mReparent;
1265 
1266             private boolean mToTop;
1267 
1268             private boolean mReparentTopOnly;
1269 
1270             private boolean mMoveAdjacentTogether;
1271 
1272             @Nullable
1273             private int[]  mWindowingModes;
1274 
1275             @Nullable
1276             private int[] mActivityTypes;
1277 
1278             @Nullable
1279             private Bundle mLaunchOptions;
1280 
1281             @Nullable
1282             private Intent mActivityIntent;
1283 
1284             @Nullable
1285             private TaskFragmentCreationParams mTaskFragmentCreationOptions;
1286 
1287             @Nullable
1288             private PendingIntent mPendingIntent;
1289 
Builder(int type)1290             Builder(int type) {
1291                 mType = type;
1292             }
1293 
setContainer(@ullable IBinder container)1294             Builder setContainer(@Nullable IBinder container) {
1295                 mContainer = container;
1296                 return this;
1297             }
1298 
setReparentContainer(@ullable IBinder reparentContainer)1299             Builder setReparentContainer(@Nullable IBinder reparentContainer) {
1300                 mReparent = reparentContainer;
1301                 return this;
1302             }
1303 
setToTop(boolean toTop)1304             Builder setToTop(boolean toTop) {
1305                 mToTop = toTop;
1306                 return this;
1307             }
1308 
setReparentTopOnly(boolean reparentTopOnly)1309             Builder setReparentTopOnly(boolean reparentTopOnly) {
1310                 mReparentTopOnly = reparentTopOnly;
1311                 return this;
1312             }
1313 
setMoveAdjacentTogether(boolean moveAdjacentTogether)1314             Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) {
1315                 mMoveAdjacentTogether = moveAdjacentTogether;
1316                 return this;
1317             }
1318 
setWindowingModes(@ullable int[] windowingModes)1319             Builder setWindowingModes(@Nullable int[] windowingModes) {
1320                 mWindowingModes = windowingModes;
1321                 return this;
1322             }
1323 
setActivityTypes(@ullable int[] activityTypes)1324             Builder setActivityTypes(@Nullable int[] activityTypes) {
1325                 mActivityTypes = activityTypes;
1326                 return this;
1327             }
1328 
setLaunchOptions(@ullable Bundle launchOptions)1329             Builder setLaunchOptions(@Nullable Bundle launchOptions) {
1330                 mLaunchOptions = launchOptions;
1331                 return this;
1332             }
1333 
setActivityIntent(@ullable Intent activityIntent)1334             Builder setActivityIntent(@Nullable Intent activityIntent) {
1335                 mActivityIntent = activityIntent;
1336                 return this;
1337             }
1338 
setPendingIntent(@ullable PendingIntent sender)1339             Builder setPendingIntent(@Nullable PendingIntent sender) {
1340                 mPendingIntent = sender;
1341                 return this;
1342             }
1343 
setTaskFragmentCreationOptions( @ullable TaskFragmentCreationParams taskFragmentCreationOptions)1344             Builder setTaskFragmentCreationOptions(
1345                     @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) {
1346                 mTaskFragmentCreationOptions = taskFragmentCreationOptions;
1347                 return this;
1348             }
1349 
build()1350             HierarchyOp build() {
1351                 final HierarchyOp hierarchyOp = new HierarchyOp(mType);
1352                 hierarchyOp.mContainer = mContainer;
1353                 hierarchyOp.mReparent = mReparent;
1354                 hierarchyOp.mWindowingModes = mWindowingModes != null
1355                         ? Arrays.copyOf(mWindowingModes, mWindowingModes.length)
1356                         : null;
1357                 hierarchyOp.mActivityTypes = mActivityTypes != null
1358                         ? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
1359                         : null;
1360                 hierarchyOp.mToTop = mToTop;
1361                 hierarchyOp.mReparentTopOnly = mReparentTopOnly;
1362                 hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether;
1363                 hierarchyOp.mLaunchOptions = mLaunchOptions;
1364                 hierarchyOp.mActivityIntent = mActivityIntent;
1365                 hierarchyOp.mPendingIntent = mPendingIntent;
1366                 hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
1367 
1368                 return hierarchyOp;
1369             }
1370         }
1371     }
1372 
1373     /**
1374      * Helper class for building an options Bundle that can be used to set adjacent rules of
1375      * TaskFragments.
1376      */
1377     public static class TaskFragmentAdjacentParams {
1378         private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL =
1379                 "android:transaction.adjacent.option.delay_primary_removal";
1380         private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL =
1381                 "android:transaction.adjacent.option.delay_secondary_removal";
1382 
1383         private boolean mDelayPrimaryLastActivityRemoval;
1384         private boolean mDelaySecondaryLastActivityRemoval;
1385 
TaskFragmentAdjacentParams()1386         public TaskFragmentAdjacentParams() {
1387         }
1388 
TaskFragmentAdjacentParams(@onNull Bundle bundle)1389         public TaskFragmentAdjacentParams(@NonNull Bundle bundle) {
1390             mDelayPrimaryLastActivityRemoval = bundle.getBoolean(
1391                     DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL);
1392             mDelaySecondaryLastActivityRemoval = bundle.getBoolean(
1393                     DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL);
1394         }
1395 
1396         /** @see #shouldDelayPrimaryLastActivityRemoval() */
setShouldDelayPrimaryLastActivityRemoval(boolean delay)1397         public void setShouldDelayPrimaryLastActivityRemoval(boolean delay) {
1398             mDelayPrimaryLastActivityRemoval = delay;
1399         }
1400 
1401         /** @see #shouldDelaySecondaryLastActivityRemoval() */
setShouldDelaySecondaryLastActivityRemoval(boolean delay)1402         public void setShouldDelaySecondaryLastActivityRemoval(boolean delay) {
1403             mDelaySecondaryLastActivityRemoval = delay;
1404         }
1405 
1406         /**
1407          * Whether to delay the last activity of the primary adjacent TaskFragment being immediately
1408          * removed while finishing.
1409          * <p>
1410          * It is usually set to {@code true} to give organizer an opportunity to perform other
1411          * actions or animations. An example is to finish together with the adjacent TaskFragment.
1412          * </p>
1413          */
shouldDelayPrimaryLastActivityRemoval()1414         public boolean shouldDelayPrimaryLastActivityRemoval() {
1415             return mDelayPrimaryLastActivityRemoval;
1416         }
1417 
1418         /**
1419          * Similar to {@link #shouldDelayPrimaryLastActivityRemoval()}, but for the secondary
1420          * TaskFragment.
1421          */
shouldDelaySecondaryLastActivityRemoval()1422         public boolean shouldDelaySecondaryLastActivityRemoval() {
1423             return mDelaySecondaryLastActivityRemoval;
1424         }
1425 
toBundle()1426         Bundle toBundle() {
1427             final Bundle b = new Bundle();
1428             b.putBoolean(DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL, mDelayPrimaryLastActivityRemoval);
1429             b.putBoolean(DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL, mDelaySecondaryLastActivityRemoval);
1430             return b;
1431         }
1432     }
1433 }
1434