• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.WindowConfiguration;
23 import android.content.pm.ActivityInfo;
24 import android.content.res.Configuration;
25 import android.graphics.Rect;
26 import android.os.Bundle;
27 import android.os.IBinder;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.util.ArrayMap;
31 import android.view.SurfaceControl;
32 
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Map;
37 
38 /**
39  * Represents a collection of operations on some WindowContainers that should be applied all at
40  * once.
41  *
42  * @hide
43  */
44 @TestApi
45 public final class WindowContainerTransaction implements Parcelable {
46     private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
47 
48     // Flat list because re-order operations are order-dependent
49     private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
50 
51     public WindowContainerTransaction() {}
52 
53     private WindowContainerTransaction(Parcel in) {
54         in.readMap(mChanges, null /* loader */);
55         in.readList(mHierarchyOps, null /* loader */);
56     }
57 
58     private Change getOrCreateChange(IBinder token) {
59         Change out = mChanges.get(token);
60         if (out == null) {
61             out = new Change();
62             mChanges.put(token, out);
63         }
64         return out;
65     }
66 
67     /**
68      * Resize a container.
69      */
70     @NonNull
71     public WindowContainerTransaction setBounds(
72             @NonNull WindowContainerToken container,@NonNull Rect bounds) {
73         Change chg = getOrCreateChange(container.asBinder());
74         chg.mConfiguration.windowConfiguration.setBounds(bounds);
75         chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
76         chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
77         return this;
78     }
79 
80     /**
81      * Resize a container's app bounds. This is the bounds used to report appWidth/Height to an
82      * app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from
83      * the full bounds.
84      */
85     @NonNull
86     public WindowContainerTransaction setAppBounds(
87             @NonNull WindowContainerToken container,@NonNull Rect appBounds) {
88         Change chg = getOrCreateChange(container.asBinder());
89         chg.mConfiguration.windowConfiguration.setAppBounds(appBounds);
90         chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
91         chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
92         return this;
93     }
94 
95     /**
96      * Resize a container's configuration size. The configuration size is what gets reported to the
97      * app via screenWidth/HeightDp and influences which resources get loaded. This size is
98      * derived by subtracting the overlapping portions of both the statusbar and the navbar from
99      * the full bounds.
100      */
101     @NonNull
102     public WindowContainerTransaction setScreenSizeDp(
103             @NonNull WindowContainerToken container, int w, int h) {
104         Change chg = getOrCreateChange(container.asBinder());
105         chg.mConfiguration.screenWidthDp = w;
106         chg.mConfiguration.screenHeightDp = h;
107         chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE;
108         return this;
109     }
110 
111     /**
112      * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task
113      * has finished the enter animation with the given bounds.
114      */
115     @NonNull
116     public WindowContainerTransaction scheduleFinishEnterPip(
117             @NonNull WindowContainerToken container,@NonNull Rect bounds) {
118         Change chg = getOrCreateChange(container.asBinder());
119         chg.mPinnedBounds = new Rect(bounds);
120         chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
121 
122         return this;
123     }
124 
125     /**
126      * Send a SurfaceControl transaction to the server, which the server will apply in sync with
127      * the next bounds change. As this uses deferred transaction and not BLAST it is only
128      * able to sync with a single window, and the first visible window in this hierarchy of type
129      * BASE_APPLICATION to resize will be used. If there are bound changes included in this
130      * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl
131      * transaction will be synced with those bounds. If there are no changes, then
132      * the SurfaceControl transaction will be synced with the next bounds change. This means
133      * that you can call this, apply the WindowContainer transaction, and then later call
134      * dismissPip() to achieve synchronization.
135      */
136     @NonNull
137     public WindowContainerTransaction setBoundsChangeTransaction(
138             @NonNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t) {
139         Change chg = getOrCreateChange(container.asBinder());
140         chg.mBoundsChangeTransaction = t;
141         chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION;
142         return this;
143     }
144 
145     /**
146      * Like {@link #setBoundsChangeTransaction} but instead queues up a setPosition/WindowCrop
147      * on a container's surface control. This is useful when a boundsChangeTransaction needs to be
148      * queued up on a Task that won't be organized until the end of this window-container
149      * transaction.
150      *
151      * This requires that, at the end of this transaction, `task` will be organized; otherwise
152      * the server will throw an IllegalArgumentException.
153      *
154      * WARNING: Use this carefully. Whatever is set here should match the expected bounds after
155      *          the transaction completes since it will likely be replaced by it. This call is
156      *          intended to pre-emptively set bounds on a surface in sync with a buffer when
157      *          otherwise the new bounds and the new buffer would update on different frames.
158      *
159      * TODO(b/134365562): remove once TaskOrg drives full-screen or BLAST is enabled.
160      *
161      * @hide
162      */
163     @NonNull
164     public WindowContainerTransaction setBoundsChangeTransaction(
165             @NonNull WindowContainerToken task, @NonNull Rect surfaceBounds) {
166         Change chg = getOrCreateChange(task.asBinder());
167         if (chg.mBoundsChangeSurfaceBounds == null) {
168             chg.mBoundsChangeSurfaceBounds = new Rect();
169         }
170         chg.mBoundsChangeSurfaceBounds.set(surfaceBounds);
171         chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION_RECT;
172         return this;
173     }
174 
175     /**
176      * Set the windowing mode of children of a given root task, without changing
177      * the windowing mode of the Task itself. This can be used during transitions
178      * for example to make the activity render it's fullscreen configuration
179      * while the Task is still in PIP, so you can complete the animation.
180      *
181      * TODO(b/134365562): Can be removed once TaskOrg drives full-screen
182      */
183     @NonNull
184     public WindowContainerTransaction setActivityWindowingMode(
185             @NonNull WindowContainerToken container, int windowingMode) {
186         Change chg = getOrCreateChange(container.asBinder());
187         chg.mActivityWindowingMode = windowingMode;
188         return this;
189     }
190 
191     /**
192      * Sets the windowing mode of the given container.
193      */
194     @NonNull
195     public WindowContainerTransaction setWindowingMode(
196             @NonNull WindowContainerToken container, int windowingMode) {
197         Change chg = getOrCreateChange(container.asBinder());
198         chg.mWindowingMode = windowingMode;
199         return this;
200     }
201 
202     /**
203      * Sets whether a container or any of its children can be focusable. When {@code false}, no
204      * child can be focused; however, when {@code true}, it is still possible for children to be
205      * non-focusable due to WM policy.
206      */
207     @NonNull
208     public WindowContainerTransaction setFocusable(
209             @NonNull WindowContainerToken container, boolean focusable) {
210         Change chg = getOrCreateChange(container.asBinder());
211         chg.mFocusable = focusable;
212         chg.mChangeMask |= Change.CHANGE_FOCUSABLE;
213         return this;
214     }
215 
216     /**
217      * Sets whether a container or its children should be hidden. When {@code false}, the existing
218      * visibility of the container applies, but when {@code true} the container will be forced
219      * to be hidden.
220      */
221     @NonNull
222     public WindowContainerTransaction setHidden(
223             @NonNull WindowContainerToken container, boolean hidden) {
224         Change chg = getOrCreateChange(container.asBinder());
225         chg.mHidden = hidden;
226         chg.mChangeMask |= Change.CHANGE_HIDDEN;
227         return this;
228     }
229 
230     /**
231      * Set the smallestScreenWidth of a container.
232      */
233     @NonNull
234     public WindowContainerTransaction setSmallestScreenWidthDp(
235             @NonNull WindowContainerToken container, int widthDp) {
236         Change cfg = getOrCreateChange(container.asBinder());
237         cfg.mConfiguration.smallestScreenWidthDp = widthDp;
238         cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
239         return this;
240     }
241 
242     /**
243      * Sets whether a container should ignore the orientation request from apps and windows below
244      * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When
245      * {@code false}, it may rotate based on the orientation request; When {@code true}, it can
246      * never specify orientation, but shows the fixed-orientation apps below it in the letterbox.
247      * @hide
248      */
249     @NonNull
250     public WindowContainerTransaction setIgnoreOrientationRequest(
251             @NonNull WindowContainerToken container, boolean ignoreOrientationRequest) {
252         Change chg = getOrCreateChange(container.asBinder());
253         chg.mIgnoreOrientationRequest = ignoreOrientationRequest;
254         chg.mChangeMask |= Change.CHANGE_IGNORE_ORIENTATION_REQUEST;
255         return this;
256     }
257 
258     /**
259      * Reparents a container into another one. The effect of a {@code null} parent can vary. For
260      * example, reparenting a stack to {@code null} will reparent it to its display.
261      *
262      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
263      *              the bottom.
264      */
265     @NonNull
266     public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
267             @Nullable WindowContainerToken parent, boolean onTop) {
268         mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
269                 parent == null ? null : parent.asBinder(),
270                 onTop));
271         return this;
272     }
273 
274     /**
275      * Reorders a container within its parent.
276      *
277      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
278      *              the bottom.
279      */
280     @NonNull
281     public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) {
282         mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop));
283         return this;
284     }
285 
286     /**
287      * Reparent's all children tasks of {@param currentParent} in the specified
288      * {@param windowingMode} and {@param activityType} to {@param newParent} in their current
289      * z-order.
290      *
291      * @param currentParent of the tasks to perform the operation no.
292      *                      {@code null} will perform the operation on the display.
293      * @param newParent for the tasks. {@code null} will perform the operation on the display.
294      * @param windowingModes of the tasks to reparent.
295      * @param activityTypes of the tasks to reparent.
296      * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
297      *              the bottom.
298      */
299     @NonNull
300     public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
301             @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
302             @Nullable int[] activityTypes, boolean onTop) {
303         mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent(
304                 currentParent != null ? currentParent.asBinder() : null,
305                 newParent != null ? newParent.asBinder() : null,
306                 windowingModes,
307                 activityTypes,
308                 onTop));
309         return this;
310     }
311 
312     /**
313      * Sets whether a container should be the launch root for the specified windowing mode and
314      * activity type. This currently only applies to Task containers created by organizer.
315      */
316     @NonNull
317     public WindowContainerTransaction setLaunchRoot(@NonNull WindowContainerToken container,
318             @Nullable int[] windowingModes, @Nullable int[] activityTypes) {
319         mHierarchyOps.add(HierarchyOp.createForSetLaunchRoot(
320                 container.asBinder(),
321                 windowingModes,
322                 activityTypes));
323         return this;
324     }
325 
326     /**
327      * Sets to containers adjacent to each other. Containers below two visible adjacent roots will
328      * be made invisible. This currently only applies to Task containers created by organizer.
329      * @param root1 the first root.
330      * @param root2 the second root.
331      */
332     @NonNull
333     public WindowContainerTransaction setAdjacentRoots(
334             @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
335         mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
336                 root1.asBinder(),
337                 root2.asBinder()));
338         return this;
339     }
340 
341     /**
342      * Sets the container as launch adjacent flag root. Task starting with
343      * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} will be launching to.
344      *
345      * @hide
346      */
347     @NonNull
348     public WindowContainerTransaction setLaunchAdjacentFlagRoot(
349             @NonNull WindowContainerToken container) {
350         mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(),
351                 false /* clearRoot */));
352         return this;
353     }
354 
355     /**
356      * Clears launch adjacent flag root for the display area of passing container.
357      *
358      * @hide
359      */
360     @NonNull
361     public WindowContainerTransaction clearLaunchAdjacentFlagRoot(
362             @NonNull WindowContainerToken container) {
363         mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(),
364                 true /* clearRoot */));
365         return this;
366     }
367 
368     /**
369      * Starts a task by id. The task is expected to already exist (eg. as a recent task).
370      * @param taskId Id of task to start.
371      * @param options bundle containing ActivityOptions for the task's top activity.
372      * @hide
373      */
374     @NonNull
375     public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
376         mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
377         return this;
378     }
379 
380     /**
381      * Merges another WCT into this one.
382      * @param transfer When true, this will transfer everything from other potentially leaving
383      *                 other in an unusable state. When false, other is left alone, but
384      *                 SurfaceFlinger Transactions will not be merged.
385      * @hide
386      */
387     public void merge(WindowContainerTransaction other, boolean transfer) {
388         for (int i = 0, n = other.mChanges.size(); i < n; ++i) {
389             final IBinder key = other.mChanges.keyAt(i);
390             Change existing = mChanges.get(key);
391             if (existing == null) {
392                 existing = new Change();
393                 mChanges.put(key, existing);
394             }
395             existing.merge(other.mChanges.valueAt(i), transfer);
396         }
397         for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) {
398             mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
399                     : new HierarchyOp(other.mHierarchyOps.get(i)));
400         }
401     }
402 
403     /** @hide */
404     public boolean isEmpty() {
405         return mChanges.isEmpty() && mHierarchyOps.isEmpty();
406     }
407 
408     /** @hide */
409     public Map<IBinder, Change> getChanges() {
410         return mChanges;
411     }
412 
413     /** @hide */
414     public List<HierarchyOp> getHierarchyOps() {
415         return mHierarchyOps;
416     }
417 
418     @Override
419     @NonNull
420     public String toString() {
421         return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps
422                 + " }";
423     }
424 
425     @Override
426     /** @hide */
427     public void writeToParcel(@NonNull Parcel dest, int flags) {
428         dest.writeMap(mChanges);
429         dest.writeList(mHierarchyOps);
430     }
431 
432     @Override
433     /** @hide */
434     public int describeContents() {
435         return 0;
436     }
437 
438     @NonNull
439     public static final Creator<WindowContainerTransaction> CREATOR =
440             new Creator<WindowContainerTransaction>() {
441                 @Override
442                 public WindowContainerTransaction createFromParcel(Parcel in) {
443                     return new WindowContainerTransaction(in);
444                 }
445 
446                 @Override
447                 public WindowContainerTransaction[] newArray(int size) {
448                     return new WindowContainerTransaction[size];
449                 }
450             };
451 
452     /**
453      * Holds changes on a single WindowContainer including Configuration changes.
454      * @hide
455      */
456     public static class Change implements Parcelable {
457         public static final int CHANGE_FOCUSABLE = 1;
458         public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
459         public static final int CHANGE_PIP_CALLBACK = 1 << 2;
460         public static final int CHANGE_HIDDEN = 1 << 3;
461         public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
462         public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
463 
464         private final Configuration mConfiguration = new Configuration();
465         private boolean mFocusable = true;
466         private boolean mHidden = false;
467         private boolean mIgnoreOrientationRequest = false;
468 
469         private int mChangeMask = 0;
470         private @ActivityInfo.Config int mConfigSetMask = 0;
471         private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
472 
473         private Rect mPinnedBounds = null;
474         private SurfaceControl.Transaction mBoundsChangeTransaction = null;
475         private Rect mBoundsChangeSurfaceBounds = null;
476 
477         private int mActivityWindowingMode = -1;
478         private int mWindowingMode = -1;
479 
480         public Change() {}
481 
482         protected Change(Parcel in) {
483             mConfiguration.readFromParcel(in);
484             mFocusable = in.readBoolean();
485             mHidden = in.readBoolean();
486             mIgnoreOrientationRequest = in.readBoolean();
487             mChangeMask = in.readInt();
488             mConfigSetMask = in.readInt();
489             mWindowSetMask = in.readInt();
490             if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) {
491                 mPinnedBounds = new Rect();
492                 mPinnedBounds.readFromParcel(in);
493             }
494             if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) {
495                 mBoundsChangeTransaction =
496                     SurfaceControl.Transaction.CREATOR.createFromParcel(in);
497             }
498             if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION_RECT) != 0) {
499                 mBoundsChangeSurfaceBounds = new Rect();
500                 mBoundsChangeSurfaceBounds.readFromParcel(in);
501             }
502 
503             mWindowingMode = in.readInt();
504             mActivityWindowingMode = in.readInt();
505         }
506 
507         /**
508          * @param transfer When true, this will transfer other into this leaving other in an
509          *                 undefined state. Use this if you don't intend to use other. When false,
510          *                 SurfaceFlinger Transactions will not merge.
511          */
512         public void merge(Change other, boolean transfer) {
513             mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask);
514             mConfigSetMask |= other.mConfigSetMask;
515             mWindowSetMask |= other.mWindowSetMask;
516             if ((other.mChangeMask & CHANGE_FOCUSABLE) != 0) {
517                 mFocusable = other.mFocusable;
518             }
519             if (transfer && (other.mChangeMask & CHANGE_BOUNDS_TRANSACTION) != 0) {
520                 mBoundsChangeTransaction = other.mBoundsChangeTransaction;
521                 other.mBoundsChangeTransaction = null;
522             }
523             if ((other.mChangeMask & CHANGE_PIP_CALLBACK) != 0) {
524                 mPinnedBounds = transfer ? other.mPinnedBounds : new Rect(other.mPinnedBounds);
525             }
526             if ((other.mChangeMask & CHANGE_HIDDEN) != 0) {
527                 mHidden = other.mHidden;
528             }
529             if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
530                 mIgnoreOrientationRequest = other.mIgnoreOrientationRequest;
531             }
532             mChangeMask |= other.mChangeMask;
533             if (other.mActivityWindowingMode >= 0) {
534                 mActivityWindowingMode = other.mActivityWindowingMode;
535             }
536             if (other.mWindowingMode >= 0) {
537                 mWindowingMode = other.mWindowingMode;
538             }
539             if (other.mBoundsChangeSurfaceBounds != null) {
540                 mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds
541                         : new Rect(other.mBoundsChangeSurfaceBounds);
542             }
543         }
544 
545         public int getWindowingMode() {
546             return mWindowingMode;
547         }
548 
549         public int getActivityWindowingMode() {
550             return mActivityWindowingMode;
551         }
552 
553         public Configuration getConfiguration() {
554             return mConfiguration;
555         }
556 
557         /** Gets the requested focusable state */
558         public boolean getFocusable() {
559             if ((mChangeMask & CHANGE_FOCUSABLE) == 0) {
560                 throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first");
561             }
562             return mFocusable;
563         }
564 
565         /** Gets the requested hidden state */
566         public boolean getHidden() {
567             if ((mChangeMask & CHANGE_HIDDEN) == 0) {
568                 throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first");
569             }
570             return mHidden;
571         }
572 
573         /** Gets the requested state of whether to ignore orientation request. */
574         public boolean getIgnoreOrientationRequest() {
575             if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) {
576                 throw new RuntimeException("IgnoreOrientationRequest not set. "
577                         + "Check CHANGE_IGNORE_ORIENTATION_REQUEST first");
578             }
579             return mIgnoreOrientationRequest;
580         }
581 
582         public int getChangeMask() {
583             return mChangeMask;
584         }
585 
586         @ActivityInfo.Config
587         public int getConfigSetMask() {
588             return mConfigSetMask;
589         }
590 
591         @WindowConfiguration.WindowConfig
592         public int getWindowSetMask() {
593             return mWindowSetMask;
594         }
595 
596         /**
597          * Returns the bounds to be used for scheduling the enter pip callback
598          * or null if no callback is to be scheduled.
599          */
600         public Rect getEnterPipBounds() {
601             return mPinnedBounds;
602         }
603 
604         public SurfaceControl.Transaction getBoundsChangeTransaction() {
605             return mBoundsChangeTransaction;
606         }
607 
608         public Rect getBoundsChangeSurfaceBounds() {
609             return mBoundsChangeSurfaceBounds;
610         }
611 
612         @Override
613         public String toString() {
614             final boolean changesBounds =
615                     (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
616                             && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS)
617                                     != 0);
618             final boolean changesAppBounds =
619                     (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
620                             && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS)
621                                     != 0);
622             final boolean changesSs = (mConfigSetMask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0;
623             final boolean changesSss =
624                     (mConfigSetMask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0;
625             StringBuilder sb = new StringBuilder();
626             sb.append('{');
627             if (changesBounds) {
628                 sb.append("bounds:" + mConfiguration.windowConfiguration.getBounds() + ",");
629             }
630             if (changesAppBounds) {
631                 sb.append("appbounds:" + mConfiguration.windowConfiguration.getAppBounds() + ",");
632             }
633             if (changesSss) {
634                 sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ",");
635             }
636             if (changesSs) {
637                 sb.append("sw/h:" + mConfiguration.screenWidthDp + "x"
638                         + mConfiguration.screenHeightDp + ",");
639             }
640             if ((mChangeMask & CHANGE_FOCUSABLE) != 0) {
641                 sb.append("focusable:" + mFocusable + ",");
642             }
643             if (mBoundsChangeTransaction != null) {
644                 sb.append("hasBoundsTransaction,");
645             }
646             if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
647                 sb.append("ignoreOrientationRequest:" + mIgnoreOrientationRequest + ",");
648             }
649             sb.append("}");
650             return sb.toString();
651         }
652 
653         @Override
654         public void writeToParcel(Parcel dest, int flags) {
655             mConfiguration.writeToParcel(dest, flags);
656             dest.writeBoolean(mFocusable);
657             dest.writeBoolean(mHidden);
658             dest.writeBoolean(mIgnoreOrientationRequest);
659             dest.writeInt(mChangeMask);
660             dest.writeInt(mConfigSetMask);
661             dest.writeInt(mWindowSetMask);
662 
663             if (mPinnedBounds != null) {
664                 mPinnedBounds.writeToParcel(dest, flags);
665             }
666             if (mBoundsChangeTransaction != null) {
667                 mBoundsChangeTransaction.writeToParcel(dest, flags);
668             }
669             if (mBoundsChangeSurfaceBounds != null) {
670                 mBoundsChangeSurfaceBounds.writeToParcel(dest, flags);
671             }
672 
673             dest.writeInt(mWindowingMode);
674             dest.writeInt(mActivityWindowingMode);
675         }
676 
677         @Override
678         public int describeContents() {
679             return 0;
680         }
681 
682         public static final Creator<Change> CREATOR = new Creator<Change>() {
683             @Override
684             public Change createFromParcel(Parcel in) {
685                 return new Change(in);
686             }
687 
688             @Override
689             public Change[] newArray(int size) {
690                 return new Change[size];
691             }
692         };
693     }
694 
695     /**
696      * Holds information about a reparent/reorder operation in the hierarchy. This is separate from
697      * Changes because they must be executed in the same order that they are added.
698      * @hide
699      */
700     public static class HierarchyOp implements Parcelable {
701         public static final int HIERARCHY_OP_TYPE_REPARENT = 0;
702         public static final int HIERARCHY_OP_TYPE_REORDER = 1;
703         public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2;
704         public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3;
705         public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
706         public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5;
707         public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6;
708 
709         // The following key(s) are for use with mLaunchOptions:
710         // When launching a task (eg. from recents), this is the taskId to be launched.
711         public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId";
712 
713         private final int mType;
714 
715         // Container we are performing the operation on.
716         private final IBinder mContainer;
717 
718         // If this is same as mContainer, then only change position, don't reparent.
719         private final IBinder mReparent;
720 
721         // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
722         private final boolean mToTop;
723 
724         final private int[]  mWindowingModes;
725         final private int[] mActivityTypes;
726 
727         private final Bundle mLaunchOptions;
728 
729         public static HierarchyOp createForReparent(
730                 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
731             return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT,
732                     container, reparent, null, null, toTop, null);
733         }
734 
735         public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
736             return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER,
737                     container, container, null, null, toTop, null);
738         }
739 
740         public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
741                 IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) {
742             return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,
743                     currentParent, newParent, windowingModes, activityTypes, onTop, null);
744         }
745 
746         public static HierarchyOp createForSetLaunchRoot(IBinder container,
747                 int[] windowingModes, int[] activityTypes) {
748             return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT,
749                     container, null, windowingModes, activityTypes, false, null);
750         }
751 
752         public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
753             return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
754                     root1, root2, null, null, false, null);
755         }
756 
757         /** Create a hierarchy op for launching a task. */
758         public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {
759             final Bundle fullOptions = options == null ? new Bundle() : options;
760             fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);
761             return new HierarchyOp(HIERARCHY_OP_TYPE_LAUNCH_TASK, null, null, null, null, true,
762                     fullOptions);
763         }
764 
765         /** Create a hierarchy op for setting launch adjacent flag root. */
766         public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
767                 boolean clearRoot) {
768             return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT, container, null,
769                     null, null, clearRoot, null);
770         }
771 
772 
773         private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent,
774                 int[] windowingModes, int[] activityTypes, boolean toTop,
775                 @Nullable Bundle launchOptions) {
776             mType = type;
777             mContainer = container;
778             mReparent = reparent;
779             mWindowingModes = windowingModes != null ?
780                     Arrays.copyOf(windowingModes, windowingModes.length) : null;
781             mActivityTypes = activityTypes != null ?
782                     Arrays.copyOf(activityTypes, activityTypes.length) : null;
783             mToTop = toTop;
784             mLaunchOptions = launchOptions;
785         }
786 
787         public HierarchyOp(@NonNull HierarchyOp copy) {
788             mType = copy.mType;
789             mContainer = copy.mContainer;
790             mReparent = copy.mReparent;
791             mToTop = copy.mToTop;
792             mWindowingModes = copy.mWindowingModes;
793             mActivityTypes = copy.mActivityTypes;
794             mLaunchOptions = copy.mLaunchOptions;
795         }
796 
797         protected HierarchyOp(Parcel in) {
798             mType = in.readInt();
799             mContainer = in.readStrongBinder();
800             mReparent = in.readStrongBinder();
801             mToTop = in.readBoolean();
802             mWindowingModes = in.createIntArray();
803             mActivityTypes = in.createIntArray();
804             mLaunchOptions = in.readBundle();
805         }
806 
807         public int getType() {
808             return mType;
809         }
810 
811         public boolean isReparent() {
812             return mType == HIERARCHY_OP_TYPE_REPARENT;
813         }
814 
815         @Nullable
816         public IBinder getNewParent() {
817             return mReparent;
818         }
819 
820         @NonNull
821         public IBinder getContainer() {
822             return mContainer;
823         }
824 
825         @NonNull
826         public IBinder getAdjacentRoot() {
827             return mReparent;
828         }
829 
830         public boolean getToTop() {
831             return mToTop;
832         }
833 
834         public int[] getWindowingModes() {
835             return mWindowingModes;
836         }
837 
838         public int[] getActivityTypes() {
839             return mActivityTypes;
840         }
841 
842         @Nullable
843         public Bundle getLaunchOptions() {
844             return mLaunchOptions;
845         }
846 
847         @Override
848         public String toString() {
849             switch (mType) {
850                 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
851                     return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
852                             + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
853                             + " mActivityType=" + mActivityTypes + "}";
854                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
855                     return "{SetLaunchRoot: container=" + mContainer
856                             + " mWindowingMode=" + mWindowingModes
857                             + " mActivityType=" + mActivityTypes + "}";
858                 case HIERARCHY_OP_TYPE_REPARENT:
859                     return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
860                             + mReparent + "}";
861                 case HIERARCHY_OP_TYPE_REORDER:
862                     return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
863                 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
864                     return "{SetAdjacentRoot: container=" + mContainer
865                             + " adjacentRoot=" + mReparent + "}";
866                 case HIERARCHY_OP_TYPE_LAUNCH_TASK:
867                     return "{LaunchTask: " + mLaunchOptions + "}";
868                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
869                     return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
870                             + "}";
871                 default:
872                     return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
873                             + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
874                             + " mActivityType=" + mActivityTypes + "}";
875             }
876         }
877 
878         @Override
879         public void writeToParcel(Parcel dest, int flags) {
880             dest.writeInt(mType);
881             dest.writeStrongBinder(mContainer);
882             dest.writeStrongBinder(mReparent);
883             dest.writeBoolean(mToTop);
884             dest.writeIntArray(mWindowingModes);
885             dest.writeIntArray(mActivityTypes);
886             dest.writeBundle(mLaunchOptions);
887         }
888 
889         @Override
890         public int describeContents() {
891             return 0;
892         }
893 
894         public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() {
895             @Override
896             public HierarchyOp createFromParcel(Parcel in) {
897                 return new HierarchyOp(in);
898             }
899 
900             @Override
901             public HierarchyOp[] newArray(int size) {
902                 return new HierarchyOp[size];
903             }
904         };
905     }
906 }
907