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