1 /*
2  * Copyright (C) 2017 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.app;
18 
19 import static android.app.ActivityThread.isSystem;
20 import static android.app.WindowConfigurationProto.ACTIVITY_TYPE;
21 import static android.app.WindowConfigurationProto.APP_BOUNDS;
22 import static android.app.WindowConfigurationProto.BOUNDS;
23 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
24 import static android.view.Surface.rotationToString;
25 
26 import android.annotation.IntDef;
27 import android.annotation.NonNull;
28 import android.annotation.TestApi;
29 import android.compat.annotation.UnsupportedAppUsage;
30 import android.content.res.Configuration;
31 import android.graphics.Rect;
32 import android.os.Parcel;
33 import android.os.Parcelable;
34 import android.util.proto.ProtoInputStream;
35 import android.util.proto.ProtoOutputStream;
36 import android.util.proto.WireTypeMismatchException;
37 import android.view.DisplayInfo;
38 
39 import java.io.IOException;
40 
41 /**
42  * Class that contains windowing configuration/state for other objects that contain windows directly
43  * or indirectly. E.g. Activities, Task, Displays, ...
44  * The test class is {@link com.android.server.wm.WindowConfigurationTests} which must be kept
45  * up-to-date and ran anytime changes are made to this class.
46  * @hide
47  */
48 @TestApi
49 public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {
50     /**
51      * bounds that can differ from app bounds, which may include things such as insets.
52      *
53      * TODO: Investigate combining with {@link mAppBounds}. Can the latter be a product of the
54      * former?
55      */
56     private Rect mBounds = new Rect();
57 
58     /**
59      * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
60      * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
61      * the display level. Lower levels can override these values to provide custom bounds to enforce
62      * features such as a max aspect ratio.
63      */
64     private Rect mAppBounds;
65 
66     /**
67      * The current rotation of this window container relative to the default
68      * orientation of the display it is on (regardless of how deep in the hierarchy
69      * it is). It is used by the configuration hierarchy to apply rotation-dependent
70      * policy during bounds calculation.
71      */
72     private int mRotation = ROTATION_UNDEFINED;
73 
74     /** Rotation is not defined, use the parent containers rotation. */
75     public static final int ROTATION_UNDEFINED = -1;
76 
77     /** The current windowing mode of the configuration. */
78     private @WindowingMode int mWindowingMode;
79 
80     /** The display windowing mode of the configuration */
81     private @WindowingMode int mDisplayWindowingMode;
82 
83     /** Windowing mode is currently not defined. */
84     public static final int WINDOWING_MODE_UNDEFINED = 0;
85     /** Occupies the full area of the screen or the parent container. */
86     public static final int WINDOWING_MODE_FULLSCREEN = 1;
87     /** Always on-top (always visible). of other siblings in its parent container. */
88     public static final int WINDOWING_MODE_PINNED = 2;
89     /** The primary container driving the screen to be in split-screen mode. */
90     public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3;
91     /**
92      * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in
93      * split-screen mode.
94      * NOTE: Containers launched with the windowing mode with APIs like
95      * {@link ActivityOptions#setLaunchWindowingMode(int)} will be launched in
96      * {@link #WINDOWING_MODE_FULLSCREEN} if the display isn't currently in split-screen windowing
97      * mode
98      * @see #WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
99      */
100     public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4;
101     /**
102      * Alias for {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} that makes it clear that the usage
103      * points for APIs like {@link ActivityOptions#setLaunchWindowingMode(int)} that the container
104      * will launch into fullscreen or split-screen secondary depending on if the device is currently
105      * in fullscreen mode or split-screen mode.
106      */
107     public static final int WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY =
108             WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
109     /** Can be freely resized within its parent container. */
110     public static final int WINDOWING_MODE_FREEFORM = 5;
111 
112     /** @hide */
113     @IntDef(prefix = { "WINDOWING_MODE_" }, value = {
114             WINDOWING_MODE_UNDEFINED,
115             WINDOWING_MODE_FULLSCREEN,
116             WINDOWING_MODE_PINNED,
117             WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
118             WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
119             WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY,
120             WINDOWING_MODE_FREEFORM,
121     })
122     public @interface WindowingMode {}
123 
124     /** The current activity type of the configuration. */
125     private @ActivityType int mActivityType;
126 
127     /** Activity type is currently not defined. */
128     public static final int ACTIVITY_TYPE_UNDEFINED = 0;
129     /** Standard activity type. Nothing special about the activity... */
130     public static final int ACTIVITY_TYPE_STANDARD = 1;
131     /** Home/Launcher activity type. */
132     public static final int ACTIVITY_TYPE_HOME = 2;
133     /** Recents/Overview activity type. There is only one activity with this type in the system. */
134     public static final int ACTIVITY_TYPE_RECENTS = 3;
135     /** Assistant activity type. */
136     public static final int ACTIVITY_TYPE_ASSISTANT = 4;
137 
138     /** @hide */
139     @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
140             ACTIVITY_TYPE_UNDEFINED,
141             ACTIVITY_TYPE_STANDARD,
142             ACTIVITY_TYPE_HOME,
143             ACTIVITY_TYPE_RECENTS,
144             ACTIVITY_TYPE_ASSISTANT,
145     })
146     public @interface ActivityType {}
147 
148     /** The current always on top status of the configuration. */
149     private @AlwaysOnTop int mAlwaysOnTop;
150 
151     /** Always on top is currently not defined. */
152     private static final int ALWAYS_ON_TOP_UNDEFINED = 0;
153     /** Always on top is currently on for this configuration. */
154     private static final int ALWAYS_ON_TOP_ON = 1;
155     /** Always on top is currently off for this configuration. */
156     private static final int ALWAYS_ON_TOP_OFF = 2;
157 
158     /** @hide */
159     @IntDef(prefix = { "ALWAYS_ON_TOP_" }, value = {
160             ALWAYS_ON_TOP_UNDEFINED,
161             ALWAYS_ON_TOP_ON,
162             ALWAYS_ON_TOP_OFF,
163     })
164     private @interface AlwaysOnTop {}
165 
166     /** Bit that indicates that the {@link #mBounds} changed.
167      * @hide */
168     public static final int WINDOW_CONFIG_BOUNDS = 1 << 0;
169     /** Bit that indicates that the {@link #mAppBounds} changed.
170      * @hide */
171     public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 1;
172     /** Bit that indicates that the {@link #mWindowingMode} changed.
173      * @hide */
174     public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 2;
175     /** Bit that indicates that the {@link #mActivityType} changed.
176      * @hide */
177     public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
178     /** Bit that indicates that the {@link #mAlwaysOnTop} changed.
179      * @hide */
180     public static final int WINDOW_CONFIG_ALWAYS_ON_TOP = 1 << 4;
181     /** Bit that indicates that the {@link #mRotation} changed.
182      * @hide */
183     public static final int WINDOW_CONFIG_ROTATION = 1 << 5;
184     /** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
185      * @hide */
186     public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 6;
187 
188     /** @hide */
189     @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
190             WINDOW_CONFIG_BOUNDS,
191             WINDOW_CONFIG_APP_BOUNDS,
192             WINDOW_CONFIG_WINDOWING_MODE,
193             WINDOW_CONFIG_ACTIVITY_TYPE,
194             WINDOW_CONFIG_ALWAYS_ON_TOP,
195             WINDOW_CONFIG_ROTATION,
196             WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
197     })
198     public @interface WindowConfig {}
199 
200     /** @hide */
201     public static final int PINNED_WINDOWING_MODE_ELEVATION_IN_DIP = 5;
202 
203     @UnsupportedAppUsage
WindowConfiguration()204     public WindowConfiguration() {
205         unset();
206     }
207 
208     /** @hide */
WindowConfiguration(WindowConfiguration configuration)209     public WindowConfiguration(WindowConfiguration configuration) {
210         setTo(configuration);
211     }
212 
WindowConfiguration(Parcel in)213     private WindowConfiguration(Parcel in) {
214         readFromParcel(in);
215     }
216 
217     @Override
writeToParcel(Parcel dest, int flags)218     public void writeToParcel(Parcel dest, int flags) {
219         dest.writeParcelable(mBounds, flags);
220         dest.writeParcelable(mAppBounds, flags);
221         dest.writeInt(mWindowingMode);
222         dest.writeInt(mActivityType);
223         dest.writeInt(mAlwaysOnTop);
224         dest.writeInt(mRotation);
225         dest.writeInt(mDisplayWindowingMode);
226     }
227 
readFromParcel(Parcel source)228     private void readFromParcel(Parcel source) {
229         mBounds = source.readParcelable(Rect.class.getClassLoader());
230         mAppBounds = source.readParcelable(Rect.class.getClassLoader());
231         mWindowingMode = source.readInt();
232         mActivityType = source.readInt();
233         mAlwaysOnTop = source.readInt();
234         mRotation = source.readInt();
235         mDisplayWindowingMode = source.readInt();
236     }
237 
238     @Override
describeContents()239     public int describeContents() {
240         return 0;
241     }
242 
243     /** @hide */
244     public static final @android.annotation.NonNull Creator<WindowConfiguration> CREATOR = new Creator<WindowConfiguration>() {
245         @Override
246         public WindowConfiguration createFromParcel(Parcel in) {
247             return new WindowConfiguration(in);
248         }
249 
250         @Override
251         public WindowConfiguration[] newArray(int size) {
252             return new WindowConfiguration[size];
253         }
254     };
255 
256     /**
257      * Sets the bounds to the provided {@link Rect}.
258      * @param rect the new bounds value.
259      */
setBounds(Rect rect)260     public void setBounds(Rect rect) {
261         if (rect == null) {
262             mBounds.setEmpty();
263             return;
264         }
265 
266         mBounds.set(rect);
267     }
268 
269     /**
270      * Set {@link #mAppBounds} to the input Rect.
271      * @param rect The rect value to set {@link #mAppBounds} to.
272      * @see #getAppBounds()
273      */
setAppBounds(Rect rect)274     public void setAppBounds(Rect rect) {
275         if (rect == null) {
276             mAppBounds = null;
277             return;
278         }
279 
280         setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
281     }
282 
283 
284 
285     /**
286      * Sets whether this window should be always on top.
287      * @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false}
288      * @hide
289      */
setAlwaysOnTop(boolean alwaysOnTop)290     public void setAlwaysOnTop(boolean alwaysOnTop) {
291         mAlwaysOnTop = alwaysOnTop ? ALWAYS_ON_TOP_ON : ALWAYS_ON_TOP_OFF;
292     }
293 
setAlwaysOnTop(@lwaysOnTop int alwaysOnTop)294     private void setAlwaysOnTop(@AlwaysOnTop int alwaysOnTop) {
295         mAlwaysOnTop = alwaysOnTop;
296     }
297 
298     /**
299      * @see #setAppBounds(Rect)
300      * @see #getAppBounds()
301      * @hide
302      */
setAppBounds(int left, int top, int right, int bottom)303     public void setAppBounds(int left, int top, int right, int bottom) {
304         if (mAppBounds == null) {
305             mAppBounds = new Rect();
306         }
307 
308         mAppBounds.set(left, top, right, bottom);
309     }
310 
311     /** @see #setAppBounds(Rect) */
getAppBounds()312     public Rect getAppBounds() {
313         return mAppBounds;
314     }
315 
316     /** @see #setBounds(Rect) */
getBounds()317     public Rect getBounds() {
318         return mBounds;
319     }
320 
getRotation()321     public int getRotation() {
322         return mRotation;
323     }
324 
setRotation(int rotation)325     public void setRotation(int rotation) {
326         mRotation = rotation;
327     }
328 
setWindowingMode(@indowingMode int windowingMode)329     public void setWindowingMode(@WindowingMode int windowingMode) {
330         mWindowingMode = windowingMode;
331     }
332 
333     @WindowingMode
getWindowingMode()334     public int getWindowingMode() {
335         return mWindowingMode;
336     }
337 
338     /** @hide */
setDisplayWindowingMode(@indowingMode int windowingMode)339     public void setDisplayWindowingMode(@WindowingMode int windowingMode) {
340         mDisplayWindowingMode = windowingMode;
341     }
342 
343 
setActivityType(@ctivityType int activityType)344     public void setActivityType(@ActivityType int activityType) {
345         if (mActivityType == activityType) {
346             return;
347         }
348 
349         // Error check within system server that we are not changing activity type which can be
350         // dangerous. It is okay for things to change in the application process as it doesn't
351         // affect how other things is the system is managed.
352         if (isSystem()
353                 && mActivityType != ACTIVITY_TYPE_UNDEFINED
354                 && activityType != ACTIVITY_TYPE_UNDEFINED) {
355             throw new IllegalStateException("Can't change activity type once set: " + this
356                     + " activityType=" + activityTypeToString(activityType));
357         }
358         mActivityType = activityType;
359     }
360 
361     @ActivityType
getActivityType()362     public int getActivityType() {
363         return mActivityType;
364     }
365 
setTo(WindowConfiguration other)366     public void setTo(WindowConfiguration other) {
367         setBounds(other.mBounds);
368         setAppBounds(other.mAppBounds);
369         setWindowingMode(other.mWindowingMode);
370         setActivityType(other.mActivityType);
371         setAlwaysOnTop(other.mAlwaysOnTop);
372         setRotation(other.mRotation);
373         setDisplayWindowingMode(other.mDisplayWindowingMode);
374     }
375 
376     /** Set this object to completely undefined.
377      * @hide */
unset()378     public void unset() {
379         setToDefaults();
380     }
381 
382     /** @hide */
setToDefaults()383     public void setToDefaults() {
384         setAppBounds(null);
385         setBounds(null);
386         setWindowingMode(WINDOWING_MODE_UNDEFINED);
387         setActivityType(ACTIVITY_TYPE_UNDEFINED);
388         setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
389         setRotation(ROTATION_UNDEFINED);
390         setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED);
391     }
392 
393     /**
394      * Copies the fields from delta into this Configuration object, keeping
395      * track of which ones have changed. Any undefined fields in {@code delta}
396      * are ignored and not copied in to the current Configuration.
397      *
398      * @return a bit mask of the changed fields, as per {@link #diff}
399      * @hide
400      */
updateFrom(@onNull WindowConfiguration delta)401     public @WindowConfig int updateFrom(@NonNull WindowConfiguration delta) {
402         int changed = 0;
403         // Only allow override if bounds is not empty
404         if (!delta.mBounds.isEmpty() && !delta.mBounds.equals(mBounds)) {
405             changed |= WINDOW_CONFIG_BOUNDS;
406             setBounds(delta.mBounds);
407         }
408         if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) {
409             changed |= WINDOW_CONFIG_APP_BOUNDS;
410             setAppBounds(delta.mAppBounds);
411         }
412         if (delta.mWindowingMode != WINDOWING_MODE_UNDEFINED
413                 && mWindowingMode != delta.mWindowingMode) {
414             changed |= WINDOW_CONFIG_WINDOWING_MODE;
415             setWindowingMode(delta.mWindowingMode);
416         }
417         if (delta.mActivityType != ACTIVITY_TYPE_UNDEFINED
418                 && mActivityType != delta.mActivityType) {
419             changed |= WINDOW_CONFIG_ACTIVITY_TYPE;
420             setActivityType(delta.mActivityType);
421         }
422         if (delta.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED
423                 && mAlwaysOnTop != delta.mAlwaysOnTop) {
424             changed |= WINDOW_CONFIG_ALWAYS_ON_TOP;
425             setAlwaysOnTop(delta.mAlwaysOnTop);
426         }
427         if (delta.mRotation != ROTATION_UNDEFINED && delta.mRotation != mRotation) {
428             changed |= WINDOW_CONFIG_ROTATION;
429             setRotation(delta.mRotation);
430         }
431         if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED
432                 && mDisplayWindowingMode != delta.mDisplayWindowingMode) {
433             changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
434             setDisplayWindowingMode(delta.mDisplayWindowingMode);
435         }
436         return changed;
437     }
438 
439     /**
440      * Return a bit mask of the differences between this Configuration object and the given one.
441      * Does not change the values of either. Any undefined fields in <var>other</var> are ignored.
442      * @param other The configuration to diff against.
443      * @param compareUndefined If undefined values should be compared.
444      * @return Returns a bit mask indicating which configuration
445      * values has changed, containing any combination of {@link WindowConfig} flags.
446      *
447      * @see Configuration#diff(Configuration)
448      * @hide
449      */
diff(WindowConfiguration other, boolean compareUndefined)450     public @WindowConfig long diff(WindowConfiguration other, boolean compareUndefined) {
451         long changes = 0;
452 
453         if (!mBounds.equals(other.mBounds)) {
454             changes |= WINDOW_CONFIG_BOUNDS;
455         }
456 
457         // Make sure that one of the values is not null and that they are not equal.
458         if ((compareUndefined || other.mAppBounds != null)
459                 && mAppBounds != other.mAppBounds
460                 && (mAppBounds == null || !mAppBounds.equals(other.mAppBounds))) {
461             changes |= WINDOW_CONFIG_APP_BOUNDS;
462         }
463 
464         if ((compareUndefined || other.mWindowingMode != WINDOWING_MODE_UNDEFINED)
465                 && mWindowingMode != other.mWindowingMode) {
466             changes |= WINDOW_CONFIG_WINDOWING_MODE;
467         }
468 
469         if ((compareUndefined || other.mActivityType != ACTIVITY_TYPE_UNDEFINED)
470                 && mActivityType != other.mActivityType) {
471             changes |= WINDOW_CONFIG_ACTIVITY_TYPE;
472         }
473 
474         if ((compareUndefined || other.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED)
475                 && mAlwaysOnTop != other.mAlwaysOnTop) {
476             changes |= WINDOW_CONFIG_ALWAYS_ON_TOP;
477         }
478 
479         if ((compareUndefined || other.mRotation != ROTATION_UNDEFINED)
480                 && mRotation != other.mRotation) {
481             changes |= WINDOW_CONFIG_ROTATION;
482         }
483 
484         if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED)
485                 && mDisplayWindowingMode != other.mDisplayWindowingMode) {
486             changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
487         }
488 
489         return changes;
490     }
491 
492     @Override
compareTo(WindowConfiguration that)493     public int compareTo(WindowConfiguration that) {
494         int n = 0;
495         if (mAppBounds == null && that.mAppBounds != null) {
496             return 1;
497         } else if (mAppBounds != null && that.mAppBounds == null) {
498             return -1;
499         } else if (mAppBounds != null && that.mAppBounds != null) {
500             n = mAppBounds.left - that.mAppBounds.left;
501             if (n != 0) return n;
502             n = mAppBounds.top - that.mAppBounds.top;
503             if (n != 0) return n;
504             n = mAppBounds.right - that.mAppBounds.right;
505             if (n != 0) return n;
506             n = mAppBounds.bottom - that.mAppBounds.bottom;
507             if (n != 0) return n;
508         }
509 
510         n = mBounds.left - that.mBounds.left;
511         if (n != 0) return n;
512         n = mBounds.top - that.mBounds.top;
513         if (n != 0) return n;
514         n = mBounds.right - that.mBounds.right;
515         if (n != 0) return n;
516         n = mBounds.bottom - that.mBounds.bottom;
517         if (n != 0) return n;
518 
519         n = mWindowingMode - that.mWindowingMode;
520         if (n != 0) return n;
521         n = mActivityType - that.mActivityType;
522         if (n != 0) return n;
523         n = mAlwaysOnTop - that.mAlwaysOnTop;
524         if (n != 0) return n;
525         n = mRotation - that.mRotation;
526         if (n != 0) return n;
527         n = mDisplayWindowingMode - that.mDisplayWindowingMode;
528         if (n != 0) return n;
529 
530         // if (n != 0) return n;
531         return n;
532     }
533 
534     /** @hide */
535     @Override
equals(Object that)536     public boolean equals(Object that) {
537         if (that == null) return false;
538         if (that == this) return true;
539         if (!(that instanceof WindowConfiguration)) {
540             return false;
541         }
542         return this.compareTo((WindowConfiguration) that) == 0;
543     }
544 
545     /** @hide */
546     @Override
hashCode()547     public int hashCode() {
548         int result = 0;
549         if (mAppBounds != null) {
550             result = 31 * result + mAppBounds.hashCode();
551         }
552         result = 31 * result + mBounds.hashCode();
553 
554         result = 31 * result + mWindowingMode;
555         result = 31 * result + mActivityType;
556         result = 31 * result + mAlwaysOnTop;
557         result = 31 * result + mRotation;
558         result = 31 * result + mDisplayWindowingMode;
559         return result;
560     }
561 
562     /** @hide */
563     @Override
toString()564     public String toString() {
565         return "{ mBounds=" + mBounds
566                 + " mAppBounds=" + mAppBounds
567                 + " mWindowingMode=" + windowingModeToString(mWindowingMode)
568                 + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
569                 + " mActivityType=" + activityTypeToString(mActivityType)
570                 + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
571                 + " mRotation=" + (mRotation == ROTATION_UNDEFINED
572                         ? "undefined" : rotationToString(mRotation))
573                 + "}";
574     }
575 
576     /**
577      * Write to a protocol buffer output stream.
578      * Protocol buffer message definition at {@link android.app.WindowConfigurationProto}
579      *
580      * @param protoOutputStream Stream to write the WindowConfiguration object to.
581      * @param fieldId           Field Id of the WindowConfiguration as defined in the parent message
582      * @hide
583      */
writeToProto(ProtoOutputStream protoOutputStream, long fieldId)584     public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
585         final long token = protoOutputStream.start(fieldId);
586         if (mAppBounds != null) {
587             mAppBounds.writeToProto(protoOutputStream, APP_BOUNDS);
588         }
589         protoOutputStream.write(WINDOWING_MODE, mWindowingMode);
590         protoOutputStream.write(ACTIVITY_TYPE, mActivityType);
591         if (mBounds != null) {
592             mBounds.writeToProto(protoOutputStream, BOUNDS);
593         }
594         protoOutputStream.end(token);
595     }
596 
597     /**
598      * Read from a protocol buffer input stream.
599      * Protocol buffer message definition at {@link android.app.WindowConfigurationProto}
600      *
601      * @param proto   Stream to read the WindowConfiguration object from.
602      * @param fieldId Field Id of the WindowConfiguration as defined in the parent message
603      * @hide
604      */
readFromProto(ProtoInputStream proto, long fieldId)605     public void readFromProto(ProtoInputStream proto, long fieldId)
606             throws IOException, WireTypeMismatchException {
607         final long token = proto.start(fieldId);
608         try {
609             while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
610                 switch (proto.getFieldNumber()) {
611                     case (int) APP_BOUNDS:
612                         mAppBounds = new Rect();
613                         mAppBounds.readFromProto(proto, APP_BOUNDS);
614                         break;
615                     case (int) BOUNDS:
616                         mBounds = new Rect();
617                         mBounds.readFromProto(proto, BOUNDS);
618                         break;
619                     case (int) WINDOWING_MODE:
620                         mWindowingMode = proto.readInt(WINDOWING_MODE);
621                         break;
622                     case (int) ACTIVITY_TYPE:
623                         mActivityType = proto.readInt(ACTIVITY_TYPE);
624                         break;
625                 }
626             }
627         } finally {
628             // Let caller handle any exceptions
629             proto.end(token);
630         }
631     }
632 
633     /**
634      * Returns true if the activities associated with this window configuration display a shadow
635      * around their border.
636      * @hide
637      */
hasWindowShadow()638     public boolean hasWindowShadow() {
639         return tasksAreFloating();
640     }
641 
642     /**
643      * Returns true if the activities associated with this window configuration display a decor
644      * view.
645      * @hide
646      */
hasWindowDecorCaption()647     public boolean hasWindowDecorCaption() {
648         return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM
649                 || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM);
650     }
651 
652     /**
653      * Returns true if the tasks associated with this window configuration can be resized
654      * independently of their parent container.
655      * @hide
656      */
canResizeTask()657     public boolean canResizeTask() {
658         return mWindowingMode == WINDOWING_MODE_FREEFORM;
659     }
660 
661     /** Returns true if the task bounds should persist across power cycles.
662      * @hide */
persistTaskBounds()663     public boolean persistTaskBounds() {
664         return mWindowingMode == WINDOWING_MODE_FREEFORM;
665     }
666 
667     /**
668      * Returns true if the tasks associated with this window configuration are floating.
669      * Floating tasks are laid out differently as they are allowed to extend past the display bounds
670      * without overscan insets.
671      * @hide
672      */
tasksAreFloating()673     public boolean tasksAreFloating() {
674         return isFloating(mWindowingMode);
675     }
676 
677     /**
678      * Returns true if the windowingMode represents a floating window.
679      * @hide
680      */
isFloating(int windowingMode)681     public static boolean isFloating(int windowingMode) {
682         return windowingMode == WINDOWING_MODE_FREEFORM || windowingMode == WINDOWING_MODE_PINNED;
683     }
684 
685     /**
686      * Returns true if the windowingMode represents a split window.
687      * @hide
688      */
isSplitScreenWindowingMode(int windowingMode)689     public static boolean isSplitScreenWindowingMode(int windowingMode) {
690         return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
691                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
692     }
693 
694     /**
695      * Returns true if the windows associated with this window configuration can receive input keys.
696      * @hide
697      */
canReceiveKeys()698     public boolean canReceiveKeys() {
699         return mWindowingMode != WINDOWING_MODE_PINNED;
700     }
701 
702     /**
703      * Returns true if the container associated with this window configuration is always-on-top of
704      * its siblings.
705      * @hide
706      */
isAlwaysOnTop()707     public boolean isAlwaysOnTop() {
708         return mWindowingMode == WINDOWING_MODE_PINNED
709                 || (mWindowingMode == WINDOWING_MODE_FREEFORM && mAlwaysOnTop == ALWAYS_ON_TOP_ON);
710     }
711 
712     /**
713      * Returns true if any visible windows belonging to apps with this window configuration should
714      * be kept on screen when the app is killed due to something like the low memory killer.
715      * @hide
716      */
keepVisibleDeadAppWindowOnScreen()717     public boolean keepVisibleDeadAppWindowOnScreen() {
718         return mWindowingMode != WINDOWING_MODE_PINNED;
719     }
720 
721     /**
722      * Returns true if the backdrop on the client side should match the frame of the window.
723      * Returns false, if the backdrop should be fullscreen.
724      * @hide
725      */
useWindowFrameForBackdrop()726     public boolean useWindowFrameForBackdrop() {
727         return mWindowingMode == WINDOWING_MODE_FREEFORM || mWindowingMode == WINDOWING_MODE_PINNED;
728     }
729 
730     /**
731      * Returns true if this container may be scaled without resizing, and windows within may need
732      * to be configured as such.
733      * @hide
734      */
windowsAreScaleable()735     public boolean windowsAreScaleable() {
736         return mWindowingMode == WINDOWING_MODE_PINNED;
737     }
738 
739     /**
740      * Returns true if windows in this container should be given move animations by default.
741      * @hide
742      */
hasMovementAnimations()743     public boolean hasMovementAnimations() {
744         return mWindowingMode != WINDOWING_MODE_PINNED;
745     }
746 
747     /**
748      * Returns true if this container can be put in either
749      * {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
750      * {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on its current state.
751      * @hide
752      */
supportSplitScreenWindowingMode()753     public boolean supportSplitScreenWindowingMode() {
754         return supportSplitScreenWindowingMode(mActivityType);
755     }
756 
757     /** @hide */
supportSplitScreenWindowingMode(int activityType)758     public static boolean supportSplitScreenWindowingMode(int activityType) {
759         return activityType != ACTIVITY_TYPE_ASSISTANT;
760     }
761 
762     /** @hide */
windowingModeToString(@indowingMode int windowingMode)763     public static String windowingModeToString(@WindowingMode int windowingMode) {
764         switch (windowingMode) {
765             case WINDOWING_MODE_UNDEFINED: return "undefined";
766             case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
767             case WINDOWING_MODE_PINNED: return "pinned";
768             case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary";
769             case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary";
770             case WINDOWING_MODE_FREEFORM: return "freeform";
771         }
772         return String.valueOf(windowingMode);
773     }
774 
775     /** @hide */
activityTypeToString(@ctivityType int applicationType)776     public static String activityTypeToString(@ActivityType int applicationType) {
777         switch (applicationType) {
778             case ACTIVITY_TYPE_UNDEFINED: return "undefined";
779             case ACTIVITY_TYPE_STANDARD: return "standard";
780             case ACTIVITY_TYPE_HOME: return "home";
781             case ACTIVITY_TYPE_RECENTS: return "recents";
782             case ACTIVITY_TYPE_ASSISTANT: return "assistant";
783         }
784         return String.valueOf(applicationType);
785     }
786 
787     /** @hide */
alwaysOnTopToString(@lwaysOnTop int alwaysOnTop)788     public static String alwaysOnTopToString(@AlwaysOnTop int alwaysOnTop) {
789         switch (alwaysOnTop) {
790             case ALWAYS_ON_TOP_UNDEFINED: return "undefined";
791             case ALWAYS_ON_TOP_ON: return "on";
792             case ALWAYS_ON_TOP_OFF: return "off";
793         }
794         return String.valueOf(alwaysOnTop);
795     }
796 }
797