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 com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
25 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
26 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
27 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
28 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
29 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
30 import static android.app.WindowConfiguration.activityTypeToString;
31 import static android.app.WindowConfiguration.isFloating;
32 import static android.app.WindowConfiguration.windowingModeToString;
33 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
34 import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
35 import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
36 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
37 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
38 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
39 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
40 import static android.view.Surface.ROTATION_270;
41 import static android.view.Surface.ROTATION_90;
42 
43 import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
44 import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
45 import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
46 
47 import android.annotation.CallSuper;
48 import android.annotation.NonNull;
49 import android.app.WindowConfiguration;
50 import android.content.pm.ApplicationInfo;
51 import android.content.res.Configuration;
52 import android.graphics.Point;
53 import android.graphics.Rect;
54 import android.os.LocaleList;
55 import android.util.DisplayMetrics;
56 import android.util.proto.ProtoOutputStream;
57 import android.view.DisplayInfo;
58 
59 import com.android.internal.annotations.VisibleForTesting;
60 
61 import java.io.PrintWriter;
62 import java.util.ArrayList;
63 
64 /**
65  * Contains common logic for classes that have override configurations and are organized in a
66  * hierarchy.
67  */
68 public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
69     /**
70      * {@link #Rect} returned from {@link #getRequestedOverrideBounds()} to prevent original value
71      * from being set directly.
72      */
73     private Rect mReturnBounds = new Rect();
74 
75     /**
76      * Contains requested override configuration settings applied to this configuration container.
77      */
78     private Configuration mRequestedOverrideConfiguration = new Configuration();
79 
80     /**
81      * Contains the requested override configuration with parent and policy constraints applied.
82      * This is the set of overrides that gets applied to the full and merged configurations.
83      */
84     private Configuration mResolvedOverrideConfiguration = new Configuration();
85 
86     /** True if mRequestedOverrideConfiguration is not empty */
87     private boolean mHasOverrideConfiguration;
88 
89     /**
90      * Contains full configuration applied to this configuration container. Corresponds to full
91      * parent's config with applied {@link #mResolvedOverrideConfiguration}.
92      */
93     private Configuration mFullConfiguration = new Configuration();
94 
95     /**
96      * Contains merged override configuration settings from the top of the hierarchy down to this
97      * particular instance. It is different from {@link #mFullConfiguration} because it starts from
98      * topmost container's override config instead of global config.
99      */
100     private Configuration mMergedOverrideConfiguration = new Configuration();
101 
102     private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>();
103 
104     // TODO: Can't have ag/2592611 soon enough!
105     private final Configuration mRequestsTmpConfig = new Configuration();
106     private final Configuration mResolvedTmpConfig = new Configuration();
107 
108     // Used for setting bounds
109     private final Rect mTmpRect = new Rect();
110 
111     static final int BOUNDS_CHANGE_NONE = 0;
112 
113     /**
114      * Return value from {@link #setBounds(Rect)} indicating the position of the override bounds
115      * changed.
116      */
117     static final int BOUNDS_CHANGE_POSITION = 1;
118 
119     /**
120      * Return value from {@link #setBounds(Rect)} indicating the size of the override bounds
121      * changed.
122      */
123     static final int BOUNDS_CHANGE_SIZE = 1 << 1;
124 
125     /**
126      * Returns full configuration applied to this configuration container.
127      * This method should be used for getting settings applied in each particular level of the
128      * hierarchy.
129      */
130     @NonNull
getConfiguration()131     public Configuration getConfiguration() {
132         return mFullConfiguration;
133     }
134 
135     /**
136      * Notify that parent config changed and we need to update full configuration.
137      * @see #mFullConfiguration
138      */
onConfigurationChanged(Configuration newParentConfig)139     public void onConfigurationChanged(Configuration newParentConfig) {
140         mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
141         resolveOverrideConfiguration(newParentConfig);
142         mFullConfiguration.setTo(newParentConfig);
143         // Do not inherit always-on-top property from parent, otherwise the always-on-top
144         // property is propagated to all children. In that case, newly added child is
145         // always being positioned at bottom (behind the always-on-top siblings).
146         mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
147         mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
148         onMergedOverrideConfigurationChanged();
149         if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
150             // This depends on the assumption that change-listeners don't do
151             // their own override resolution. This way, dependent hierarchies
152             // can stay properly synced-up with a primary hierarchy's constraints.
153             // Since the hierarchies will be merged, this whole thing will go away
154             // before the assumption will be broken.
155             // Inform listeners of the change.
156             for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
157                 mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
158                         mResolvedOverrideConfiguration);
159             }
160         }
161         for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
162             mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
163                     mMergedOverrideConfiguration);
164         }
165         for (int i = getChildCount() - 1; i >= 0; --i) {
166             dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
167         }
168     }
169 
170     /**
171      * Dispatches the configuration to child when {@link #onConfigurationChanged(Configuration)} is
172      * called. This allows the derived classes to override how to dispatch the configuration.
173      */
dispatchConfigurationToChild(E child, Configuration config)174     void dispatchConfigurationToChild(E child, Configuration config) {
175         child.onConfigurationChanged(config);
176     }
177 
178     /**
179      * Resolves the current requested override configuration into
180      * {@link #mResolvedOverrideConfiguration}
181      *
182      * @param newParentConfig The new parent configuration to resolve overrides against.
183      */
resolveOverrideConfiguration(Configuration newParentConfig)184     void resolveOverrideConfiguration(Configuration newParentConfig) {
185         mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
186     }
187 
188     /**
189      * If necessary, override configuration fields related to app bounds.
190      * This will happen when the app is targeting SDK earlier than 35.
191      * The insets and configuration has decoupled since SDK level 35, to make the system
192      * compatible to existing apps, override the configuration with legacy metrics. In legacy
193      * metrics, fields such as appBounds will exclude some of the system bar areas.
194      * The override contains all potentially affected fields in Configuration, including
195      * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
196      * All overrides to those fields should be in this method.
197      *
198      * TODO: Consider integrate this with computeConfigByResolveHint()
199      */
applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo, Configuration newParentConfiguration, Configuration inOutConfig, boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform, boolean hasCompatDisplayInsets)200     static void applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo,
201             Configuration newParentConfiguration, Configuration inOutConfig,
202             boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform,
203             boolean hasCompatDisplayInsets) {
204         if (displayContent == null) {
205             return;
206         }
207         final boolean useOverrideInsetsForConfig =
208                 displayContent.mWmService.mFlags.mInsetsDecoupledConfiguration
209                         ? !appInfo.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
210                                 && !appInfo.isChangeEnabled(
211                                         OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION)
212                         : appInfo.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
213         final int parentWindowingMode =
214                 newParentConfiguration.windowConfiguration.getWindowingMode();
215         final boolean isFloating = isFloating(parentWindowingMode)
216                 // Check the requested windowing mode of activity as well in case it is
217                 // switching between PiP and fullscreen.
218                 && (inOutConfig.windowConfiguration.getWindowingMode() == WINDOWING_MODE_UNDEFINED
219                         || isFloating(inOutConfig.windowConfiguration.getWindowingMode()));
220         int rotation = newParentConfiguration.windowConfiguration.getRotation();
221         if (rotation == ROTATION_UNDEFINED && !hasFixedRotationTransform) {
222             rotation = displayContent.getRotation();
223         }
224         if (!optsOutEdgeToEdge && (!useOverrideInsetsForConfig
225                 || hasCompatDisplayInsets
226                 || isFloating
227                 || rotation == ROTATION_UNDEFINED)) {
228             // If the insets configuration decoupled logic is not enabled for the app, or the app
229             // already has a compat override, or the context doesn't contain enough info to
230             // calculate the override, skip the override.
231             return;
232         }
233         // Make sure the orientation related fields will be updated by the override insets, because
234         // fixed rotation has assigned the fields from display's configuration.
235         if (hasFixedRotationTransform) {
236             inOutConfig.windowConfiguration.setAppBounds(null);
237             inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
238             inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
239             inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
240             inOutConfig.orientation = ORIENTATION_UNDEFINED;
241         }
242 
243         // Override starts here.
244         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
245         final int dw = rotated
246                 ? displayContent.mBaseDisplayHeight
247                 : displayContent.mBaseDisplayWidth;
248         final int dh = rotated
249                 ? displayContent.mBaseDisplayWidth
250                 : displayContent.mBaseDisplayHeight;
251         // This should be the only place override the configuration for ActivityRecord. Override
252         // the value if not calculated yet.
253         Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
254         if (outAppBounds == null || outAppBounds.isEmpty()) {
255             inOutConfig.windowConfiguration.setAppBounds(
256                     newParentConfiguration.windowConfiguration.getBounds());
257             outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
258             outAppBounds.inset(displayContent.getDisplayPolicy()
259                     .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets);
260         }
261         float density = inOutConfig.densityDpi;
262         if (density == Configuration.DENSITY_DPI_UNDEFINED) {
263             density = newParentConfiguration.densityDpi;
264         }
265         density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
266         if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
267             inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
268         }
269         if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
270             inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
271         }
272         if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
273                 && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
274             // For the case of PIP transition and multi-window environment, the
275             // smallestScreenWidthDp is handled already. Override only if the app is in
276             // fullscreen.
277             final DisplayInfo info = new DisplayInfo(displayContent.getDisplayInfo());
278             displayContent.computeSizeRanges(info, rotated, dw, dh,
279                     displayContent.getDisplayMetrics().density,
280                     inOutConfig, true /* overrideConfig */);
281         }
282 
283         // It's possible that screen size will be considered in different orientation with or
284         // without considering the system bar insets. Override orientation as well.
285         if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
286             inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
287                     ? ORIENTATION_PORTRAIT
288                     : ORIENTATION_LANDSCAPE;
289         }
290     }
291 
292     /** Returns {@code true} if requested override override configuration is not empty. */
hasRequestedOverrideConfiguration()293     boolean hasRequestedOverrideConfiguration() {
294         return mHasOverrideConfiguration;
295     }
296 
297     /** Returns requested override configuration applied to this configuration container. */
298     @NonNull
getRequestedOverrideConfiguration()299     public Configuration getRequestedOverrideConfiguration() {
300         return mRequestedOverrideConfiguration;
301     }
302 
303     /** Returns the resolved override configuration. */
304     @NonNull
getResolvedOverrideConfiguration()305     Configuration getResolvedOverrideConfiguration() {
306         return mResolvedOverrideConfiguration;
307     }
308 
309     /**
310      * Update override configuration and recalculate full config.
311      * @see #mRequestedOverrideConfiguration
312      * @see #mFullConfiguration
313      */
onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration)314     public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
315         updateRequestedOverrideConfiguration(overrideConfiguration);
316         // Update full configuration of this container and all its children.
317         final ConfigurationContainer parent = getParent();
318         onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
319     }
320 
321     /** Updates override configuration without recalculate full config. */
updateRequestedOverrideConfiguration(Configuration overrideConfiguration)322     void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) {
323         // Pre-compute this here, so we don't need to go through the entire Configuration when
324         // writing to proto (which has significant cost if we write a lot of empty configurations).
325         mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
326         mRequestedOverrideConfiguration.setTo(overrideConfiguration);
327         final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
328         if (mHasOverrideConfiguration && providesMaxBounds()
329                 && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
330             mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
331         }
332     }
333 
334     /**
335      * Get merged override configuration from the top of the hierarchy down to this particular
336      * instance. This should be reported to client as override config.
337      */
338     @NonNull
getMergedOverrideConfiguration()339     public Configuration getMergedOverrideConfiguration() {
340         return mMergedOverrideConfiguration;
341     }
342 
343     /**
344      * Update merged override configuration based on corresponding parent's config. If there is no
345      * parent, merged override configuration will set equal to current override config. This
346      * doesn't cascade on its own since it's called by {@link #onConfigurationChanged}.
347      * @see #mMergedOverrideConfiguration
348      */
onMergedOverrideConfigurationChanged()349     void onMergedOverrideConfigurationChanged() {
350         final ConfigurationContainer parent = getParent();
351         if (parent != null) {
352             mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration());
353             // Do not inherit always-on-top property from parent, otherwise the always-on-top
354             // property is propagated to all children. In that case, newly added child is
355             // always being positioned at bottom (behind the always-on-top siblings).
356             mMergedOverrideConfiguration.windowConfiguration.unsetAlwaysOnTop();
357             mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration);
358         } else {
359             mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
360         }
361     }
362 
363     /**
364      * Indicates whether this container chooses not to override any bounds from its parent, either
365      * because it doesn't request to override them or the request is dropped during configuration
366      * resolution. In this case, it will inherit the bounds of the first ancestor which specifies a
367      * bounds subject to policy constraints.
368      *
369      * @return {@code true} if this container level uses bounds from parent level. {@code false}
370      *         otherwise.
371      */
matchParentBounds()372     public boolean matchParentBounds() {
373         return getResolvedOverrideBounds().isEmpty();
374     }
375 
376     /**
377      * Returns whether the bounds specified are considered the same as the existing requested
378      * override bounds. This is either when the two bounds are equal or the requested override
379      * bounds are empty and the specified bounds is null.
380      *
381      * @return {@code true} if the bounds are equivalent, {@code false} otherwise
382      */
equivalentRequestedOverrideBounds(Rect bounds)383     public boolean equivalentRequestedOverrideBounds(Rect bounds) {
384         return equivalentBounds(getRequestedOverrideBounds(),  bounds);
385     }
386 
387     /** Similar to {@link #equivalentRequestedOverrideBounds(Rect)}, but compares max bounds. */
equivalentRequestedOverrideMaxBounds(Rect bounds)388     public boolean equivalentRequestedOverrideMaxBounds(Rect bounds) {
389         return equivalentBounds(getRequestedOverrideMaxBounds(),  bounds);
390     }
391 
392     /**
393      * Returns whether the two bounds are equal to each other or are a combination of null or empty.
394      */
equivalentBounds(Rect bounds, Rect other)395     public static boolean equivalentBounds(Rect bounds, Rect other) {
396         return bounds == other
397                 || (bounds != null && (bounds.equals(other) || (bounds.isEmpty() && other == null)))
398                 || (other != null && other.isEmpty() && bounds == null);
399     }
400 
401     /**
402      * Returns the effective bounds of this container, inheriting the first non-empty bounds set in
403      * its ancestral hierarchy, including itself.
404      */
getBounds()405     public Rect getBounds() {
406         mReturnBounds.set(getConfiguration().windowConfiguration.getBounds());
407         return mReturnBounds;
408     }
409 
getBounds(Rect outBounds)410     public void getBounds(Rect outBounds) {
411         outBounds.set(getBounds());
412     }
413 
414     /** Similar to {@link #getBounds()}, but reports the max bounds. */
getMaxBounds()415     public Rect getMaxBounds() {
416         mReturnBounds.set(getConfiguration().windowConfiguration.getMaxBounds());
417         return mReturnBounds;
418     }
419 
420     /**
421      * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}.
422      */
getPosition(Point out)423     public void getPosition(Point out) {
424         Rect bounds = getBounds();
425         out.set(bounds.left, bounds.top);
426     }
427 
getResolvedOverrideBounds()428     Rect getResolvedOverrideBounds() {
429         mReturnBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
430         return mReturnBounds;
431     }
432 
433     /**
434      * Returns the bounds requested on this container. These may not be the actual bounds the
435      * container ends up with due to policy constraints. The {@link Rect} handed back is
436      * shared for all calls to this method and should not be modified.
437      */
getRequestedOverrideBounds()438     public Rect getRequestedOverrideBounds() {
439         mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getBounds());
440 
441         return mReturnBounds;
442     }
443 
444     /** Similar to {@link #getRequestedOverrideBounds()}, but returns the max bounds. */
getRequestedOverrideMaxBounds()445     public Rect getRequestedOverrideMaxBounds() {
446         mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getMaxBounds());
447 
448         return mReturnBounds;
449     }
450 
451     /**
452      * Returns {@code true} if the {@link WindowConfiguration} in the requested override
453      * {@link Configuration} specifies bounds.
454      */
hasOverrideBounds()455     public boolean hasOverrideBounds() {
456         return !getRequestedOverrideBounds().isEmpty();
457     }
458 
459     /**
460      * Sets the passed in {@link Rect} to the current bounds.
461      * @see #getRequestedOverrideBounds()
462      */
getRequestedOverrideBounds(Rect outBounds)463     public void getRequestedOverrideBounds(Rect outBounds) {
464         outBounds.set(getRequestedOverrideBounds());
465     }
466 
467     /**
468      * Sets the bounds at the current hierarchy level, overriding any bounds set on an ancestor.
469      * This value will be reported when {@link #getBounds()} and
470      * {@link #getRequestedOverrideBounds()}. If
471      * an empty {@link Rect} or null is specified, this container will be considered to match its
472      * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent.
473      *
474      * @param bounds The bounds defining the container size.
475      *
476      * @return a bitmask representing the types of changes made to the bounds.
477      */
setBounds(Rect bounds)478     public int setBounds(Rect bounds) {
479         int boundsChange = diffRequestedOverrideBounds(bounds);
480         final boolean overrideMaxBounds = providesMaxBounds()
481                 && diffRequestedOverrideMaxBounds(bounds) != BOUNDS_CHANGE_NONE;
482 
483         if (boundsChange == BOUNDS_CHANGE_NONE && !overrideMaxBounds) {
484             return boundsChange;
485         }
486 
487         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
488         mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
489         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
490 
491         return boundsChange;
492     }
493 
setBounds(int left, int top, int right, int bottom)494     public int setBounds(int left, int top, int right, int bottom) {
495         mTmpRect.set(left, top, right, bottom);
496         return setBounds(mTmpRect);
497     }
498 
499     /**
500      * Returns {@code true} if this {@link ConfigurationContainer} provides the maximum bounds to
501      * its child {@link ConfigurationContainer}s. Returns {@code false}, otherwise.
502      * <p>
503      * The maximum bounds is how large a window can be expanded.
504      * </p>
505      */
providesMaxBounds()506     protected boolean providesMaxBounds() {
507         return false;
508     }
509 
diffRequestedOverrideMaxBounds(Rect bounds)510     int diffRequestedOverrideMaxBounds(Rect bounds) {
511         if (equivalentRequestedOverrideMaxBounds(bounds)) {
512             return BOUNDS_CHANGE_NONE;
513         }
514 
515         int boundsChange = BOUNDS_CHANGE_NONE;
516 
517         final Rect existingBounds = getRequestedOverrideMaxBounds();
518 
519         if (bounds == null || existingBounds.left != bounds.left
520                 || existingBounds.top != bounds.top) {
521             boundsChange |= BOUNDS_CHANGE_POSITION;
522         }
523 
524         if (bounds == null || existingBounds.width() != bounds.width()
525                 || existingBounds.height() != bounds.height()) {
526             boundsChange |= BOUNDS_CHANGE_SIZE;
527         }
528 
529         return boundsChange;
530     }
531 
diffRequestedOverrideBounds(Rect bounds)532     int diffRequestedOverrideBounds(Rect bounds) {
533         if (equivalentRequestedOverrideBounds(bounds)) {
534             return BOUNDS_CHANGE_NONE;
535         }
536 
537         int boundsChange = BOUNDS_CHANGE_NONE;
538 
539         final Rect existingBounds = getRequestedOverrideBounds();
540 
541         if (bounds == null || existingBounds.left != bounds.left
542                 || existingBounds.top != bounds.top) {
543             boundsChange |= BOUNDS_CHANGE_POSITION;
544         }
545 
546         if (bounds == null || existingBounds.width() != bounds.width()
547                 || existingBounds.height() != bounds.height()) {
548             boundsChange |= BOUNDS_CHANGE_SIZE;
549         }
550 
551         return boundsChange;
552     }
553 
getWindowConfiguration()554     public WindowConfiguration getWindowConfiguration() {
555         return mFullConfiguration.windowConfiguration;
556     }
557 
558     /** Returns the windowing mode the configuration container is currently in. */
getWindowingMode()559     public int getWindowingMode() {
560         return mFullConfiguration.windowConfiguration.getWindowingMode();
561     }
562 
563     /** Returns the windowing mode override that is requested by this container. */
getRequestedOverrideWindowingMode()564     public int getRequestedOverrideWindowingMode() {
565         return mRequestedOverrideConfiguration.windowConfiguration.getWindowingMode();
566     }
567 
568     /** Sets the requested windowing mode override for the configuration container. */
setWindowingMode( int windowingMode)569     public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
570         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
571         mRequestsTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
572         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
573     }
574 
575     /** Sets the always on top flag for this configuration container.
576      *  When you call this function, make sure that the following functions are called as well to
577      *  keep proper z-order.
578      *  - {@link TaskDisplayArea#positionChildAt(int POSITION_TOP, Task, boolean)};
579      * */
setAlwaysOnTop(boolean alwaysOnTop)580     public void setAlwaysOnTop(boolean alwaysOnTop) {
581         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
582         mRequestsTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
583         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
584     }
585 
586     /**
587      * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
588      * with another activity.
589      */
inMultiWindowMode()590     public boolean inMultiWindowMode() {
591         /*@WindowConfiguration.WindowingMode*/ int windowingMode =
592                 mFullConfiguration.windowConfiguration.getWindowingMode();
593         return WindowConfiguration.inMultiWindowMode(windowingMode);
594     }
595 
inPinnedWindowingMode()596     public boolean inPinnedWindowingMode() {
597         return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
598     }
599 
inFreeformWindowingMode()600     public boolean inFreeformWindowingMode() {
601         return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
602     }
603 
604     /** Returns the activity type associated with the configuration container. */
605     /*@WindowConfiguration.ActivityType*/
getActivityType()606     public int getActivityType() {
607         return mFullConfiguration.windowConfiguration.getActivityType();
608     }
609 
610     /** Sets the activity type to associate with the configuration container. */
setActivityType( int activityType)611     public void setActivityType(/*@WindowConfiguration.ActivityType*/ int activityType) {
612         int currentActivityType = getActivityType();
613         if (currentActivityType == activityType) {
614             return;
615         }
616         if (currentActivityType != ACTIVITY_TYPE_UNDEFINED) {
617             throw new IllegalStateException("Can't change activity type once set: " + this
618                     + " activityType=" + activityTypeToString(activityType));
619         }
620         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
621         mRequestsTmpConfig.windowConfiguration.setActivityType(activityType);
622         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
623     }
624 
isActivityTypeHome()625     public boolean isActivityTypeHome() {
626         return getActivityType() == ACTIVITY_TYPE_HOME;
627     }
628 
isActivityTypeRecents()629     public boolean isActivityTypeRecents() {
630         return getActivityType() == ACTIVITY_TYPE_RECENTS;
631     }
632 
isActivityTypeHomeOrRecents()633     final boolean isActivityTypeHomeOrRecents() {
634         final int activityType = getActivityType();
635         return activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
636     }
637 
isActivityTypeAssistant()638     public boolean isActivityTypeAssistant() {
639         return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
640     }
641 
642     /**
643      * Applies app-specific nightMode and {@link LocaleList} on requested configuration.
644      * @return true if any of the requested configuration has been updated.
645      */
applyAppSpecificConfig(Integer nightMode, LocaleList locales, @Configuration.GrammaticalGender Integer gender)646     public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales,
647             @Configuration.GrammaticalGender Integer gender) {
648         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
649         boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig,
650                 nightMode);
651         boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig,
652                 locales);
653         boolean newGenderSet = setOverrideGender(mRequestsTmpConfig,
654                 gender == null ? Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED : gender);
655         if (newNightModeSet || newLocalesSet || newGenderSet) {
656             onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
657         }
658         return newNightModeSet || newLocalesSet || newGenderSet;
659     }
660 
661     /**
662      * Overrides the night mode applied to this ConfigurationContainer.
663      * @return true if the nightMode has been changed.
664      */
setOverrideNightMode(Configuration requestsTmpConfig, int nightMode)665     private boolean setOverrideNightMode(Configuration requestsTmpConfig, int nightMode) {
666         final int currentUiMode = mRequestedOverrideConfiguration.uiMode;
667         final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK;
668         final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK;
669         if (currentNightMode == validNightMode) {
670             return false;
671         }
672         requestsTmpConfig.uiMode = validNightMode
673                 | (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK);
674         return true;
675     }
676 
677     /**
678      * Overrides the locales applied to this ConfigurationContainer.
679      * @return true if the LocaleList has been changed.
680      */
setOverrideLocales(Configuration requestsTmpConfig, @NonNull LocaleList overrideLocales)681     private boolean setOverrideLocales(Configuration requestsTmpConfig,
682             @NonNull LocaleList overrideLocales) {
683         if (mRequestedOverrideConfiguration.getLocales().equals(overrideLocales)) {
684             return false;
685         }
686         requestsTmpConfig.setLocales(overrideLocales);
687         requestsTmpConfig.userSetLocale = true;
688         return true;
689     }
690 
691     /**
692      * Overrides the gender to this ConfigurationContainer.
693      *
694      * @return true if the grammatical gender has been changed.
695      */
setOverrideGender(Configuration requestsTmpConfig, @Configuration.GrammaticalGender int gender)696     protected boolean setOverrideGender(Configuration requestsTmpConfig,
697             @Configuration.GrammaticalGender int gender) {
698         // Noop, only ActivityRecord and WindowProcessController have enough knowledge about the
699         // app to apply gender correctly.
700         return false;
701     }
702 
isActivityTypeDream()703     public boolean isActivityTypeDream() {
704         return getActivityType() == ACTIVITY_TYPE_DREAM;
705     }
706 
isActivityTypeStandard()707     public boolean isActivityTypeStandard() {
708         return getActivityType() == ACTIVITY_TYPE_STANDARD;
709     }
710 
isActivityTypeStandardOrUndefined()711     public boolean isActivityTypeStandardOrUndefined() {
712         /*@WindowConfiguration.ActivityType*/ final int activityType = getActivityType();
713         return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED;
714     }
715 
isCompatibleActivityType(int currentType, int otherType)716     public static boolean isCompatibleActivityType(int currentType, int otherType) {
717         if (currentType == otherType) {
718             return true;
719         }
720         if (currentType == ACTIVITY_TYPE_ASSISTANT) {
721             // Assistant activities are only compatible with themselves...
722             return false;
723         }
724         // Otherwise we are compatible if us or other is not currently defined.
725         return currentType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
726     }
727 
728     /**
729      * Returns true if this container is compatible with the input windowing mode and activity type.
730      * The container is compatible:
731      * - If {@param activityType} and {@param windowingMode} match this container activity type and
732      * windowing mode.
733      * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
734      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
735      * standard or undefined and its windowing mode matches {@param windowingMode}.
736      * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
737      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
738      * also standard or undefined and its activity type matches {@param activityType} regardless of
739      * if {@param windowingMode} matches the containers windowing mode.
740      */
isCompatible(int windowingMode, int activityType)741     public boolean isCompatible(int windowingMode, int activityType) {
742         final int thisActivityType = getActivityType();
743         final int thisWindowingMode = getWindowingMode();
744         final boolean sameActivityType = thisActivityType == activityType;
745         final boolean sameWindowingMode = thisWindowingMode == windowingMode;
746 
747         if (sameActivityType && sameWindowingMode) {
748             return true;
749         }
750 
751         if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
752                 || !isActivityTypeStandardOrUndefined()) {
753             // Only activity type need to match for non-standard activity types that are defined.
754             return sameActivityType;
755         }
756 
757         // Otherwise we are compatible if the windowing mode is the same.
758         return sameWindowingMode;
759     }
760 
registerConfigurationChangeListener(ConfigurationContainerListener listener)761     void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
762         registerConfigurationChangeListener(listener, true /* shouldDispatchConfig */);
763     }
764 
registerConfigurationChangeListener(ConfigurationContainerListener listener, boolean shouldDispatchConfig)765     void registerConfigurationChangeListener(ConfigurationContainerListener listener,
766             boolean shouldDispatchConfig) {
767         if (mChangeListeners.contains(listener)) {
768             return;
769         }
770         mChangeListeners.add(listener);
771         if (shouldDispatchConfig) {
772             listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
773             listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
774         }
775     }
776 
unregisterConfigurationChangeListener(ConfigurationContainerListener listener)777     void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
778         mChangeListeners.remove(listener);
779     }
780 
781     @VisibleForTesting
containsListener(ConfigurationContainerListener listener)782     boolean containsListener(ConfigurationContainerListener listener) {
783         return mChangeListeners.contains(listener);
784     }
785 
786     /**
787      * Must be called when new parent for the container was set.
788      */
onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent)789     void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
790         // Removing parent usually means that we've detached this entity to destroy it or to attach
791         // to another parent. In both cases we don't need to update the configuration now.
792         if (newParent != null) {
793             // Update full configuration of this container and all its children.
794             onConfigurationChanged(newParent.mFullConfiguration);
795         }
796     }
797 
798     /**
799      * Write to a protocol buffer output stream. Protocol buffer message definition is at
800      * {@link com.android.server.wm.ConfigurationContainerProto}.
801      *
802      * @param proto    Stream to write the ConfigurationContainer object to.
803      * @param fieldId  Field Id of the ConfigurationContainer as defined in the parent
804      *                 message.
805      * @param logLevel Determines the amount of data to be written to the Protobuf.
806      * @hide
807      */
808     @CallSuper
dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)809     protected void dumpDebug(ProtoOutputStream proto, long fieldId,
810             @WindowTraceLogLevel int logLevel) {
811         final long token = proto.start(fieldId);
812 
813         if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) {
814             mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
815                     logLevel == WindowTraceLogLevel.CRITICAL);
816         }
817 
818         // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
819         // required to mitigate performance overhead
820         if (logLevel == WindowTraceLogLevel.ALL) {
821             mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
822             mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
823                     false /* critical */);
824         }
825 
826         if (logLevel == WindowTraceLogLevel.TRIM) {
827             // Required for Fass to automatically detect pip transitions in Winscope traces
828             dumpDebugWindowingMode(proto);
829         }
830 
831         proto.end(token);
832     }
833 
dumpDebugWindowingMode(ProtoOutputStream proto)834     private void dumpDebugWindowingMode(ProtoOutputStream proto) {
835         final long fullConfigToken = proto.start(FULL_CONFIGURATION);
836         final long windowConfigToken = proto.start(WINDOW_CONFIGURATION);
837 
838         int windowingMode = mFullConfiguration.windowConfiguration.getWindowingMode();
839         proto.write(WINDOWING_MODE, windowingMode);
840 
841         proto.end(windowConfigToken);
842         proto.end(fullConfigToken);
843     }
844 
845     /**
846      * Dumps the names of this container children in the input print writer indenting each
847      * level with the input prefix.
848      */
dumpChildrenNames(PrintWriter pw, String prefix)849     public void dumpChildrenNames(PrintWriter pw, String prefix) {
850         dumpChildrenNames(pw, prefix, true /* isLastChild */);
851     }
852 
853     /**
854      * Dumps the names of this container children in the input print writer indenting each
855      * level with the input prefix.
856      */
dumpChildrenNames(PrintWriter pw, String prefix, boolean isLastChild)857     public void dumpChildrenNames(PrintWriter pw, String prefix, boolean isLastChild) {
858         int curWinMode = getWindowingMode();
859         String winMode = windowingModeToString(curWinMode);
860         if (curWinMode != WINDOWING_MODE_UNDEFINED &&
861                 curWinMode != WINDOWING_MODE_FULLSCREEN) {
862             winMode = winMode.toUpperCase();
863         }
864         int requestedWinMode = getRequestedOverrideWindowingMode();
865         String overrideWinMode = windowingModeToString(requestedWinMode);
866         if (requestedWinMode != WINDOWING_MODE_UNDEFINED &&
867                 requestedWinMode != WINDOWING_MODE_FULLSCREEN) {
868             overrideWinMode = overrideWinMode.toUpperCase();
869         }
870         String actType = activityTypeToString(getActivityType());
871         if (getActivityType() != ACTIVITY_TYPE_UNDEFINED
872                 && getActivityType() != ACTIVITY_TYPE_STANDARD) {
873             actType = actType.toUpperCase();
874         }
875         pw.print(prefix + (isLastChild ? "└─ " : "├─ "));
876         pw.println(getName()
877                 + " type=" + actType
878                 + " mode=" + winMode
879                 + " override-mode=" + overrideWinMode
880                 + " requested-bounds=" + getRequestedOverrideBounds().toShortString()
881                 + " bounds=" + getBounds().toShortString());
882 
883         String childPrefix = prefix + (isLastChild ? "   " : "│  ");
884         for (int i = getChildCount() - 1; i >= 0; --i) {
885             final E cc = getChildAt(i);
886             cc.dumpChildrenNames(pw, childPrefix, i == 0 /* isLastChild */);
887         }
888     }
889 
getName()890     String getName() {
891         return toString();
892     }
893 
isAlwaysOnTop()894     public boolean isAlwaysOnTop() {
895         return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
896     }
897 
hasChild()898     boolean hasChild() {
899         return getChildCount() > 0;
900     }
901 
getChildCount()902     abstract protected int getChildCount();
903 
getChildAt(int index)904     abstract protected E getChildAt(int index);
905 
getParent()906     abstract protected ConfigurationContainer getParent();
907 
908 }
909