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