• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)539     public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales) {
540         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
541         boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig,
542                 nightMode);
543         boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig,
544                 locales);
545         if (newNightModeSet || newLocalesSet) {
546             onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
547         }
548         return newNightModeSet || newLocalesSet;
549     }
550 
551     /**
552      * Overrides the night mode applied to this ConfigurationContainer.
553      * @return true if the nightMode has been changed.
554      */
setOverrideNightMode(Configuration requestsTmpConfig, int nightMode)555     private boolean setOverrideNightMode(Configuration requestsTmpConfig, int nightMode) {
556         final int currentUiMode = mRequestedOverrideConfiguration.uiMode;
557         final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK;
558         final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK;
559         if (currentNightMode == validNightMode) {
560             return false;
561         }
562         requestsTmpConfig.uiMode = validNightMode
563                 | (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK);
564         return true;
565     }
566 
567     /**
568      * Overrides the locales applied to this ConfigurationContainer.
569      * @return true if the LocaleList has been changed.
570      */
setOverrideLocales(Configuration requestsTmpConfig, @NonNull LocaleList overrideLocales)571     private boolean setOverrideLocales(Configuration requestsTmpConfig,
572             @NonNull LocaleList overrideLocales) {
573         if (mRequestedOverrideConfiguration.getLocales().equals(overrideLocales)) {
574             return false;
575         }
576         requestsTmpConfig.setLocales(overrideLocales);
577         requestsTmpConfig.userSetLocale = true;
578         return true;
579     }
580 
isActivityTypeDream()581     public boolean isActivityTypeDream() {
582         return getActivityType() == ACTIVITY_TYPE_DREAM;
583     }
584 
isActivityTypeStandard()585     public boolean isActivityTypeStandard() {
586         return getActivityType() == ACTIVITY_TYPE_STANDARD;
587     }
588 
isActivityTypeStandardOrUndefined()589     public boolean isActivityTypeStandardOrUndefined() {
590         /*@WindowConfiguration.ActivityType*/ final int activityType = getActivityType();
591         return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED;
592     }
593 
isCompatibleActivityType(int currentType, int otherType)594     public static boolean isCompatibleActivityType(int currentType, int otherType) {
595         if (currentType == otherType) {
596             return true;
597         }
598         if (currentType == ACTIVITY_TYPE_ASSISTANT) {
599             // Assistant activities are only compatible with themselves...
600             return false;
601         }
602         // Otherwise we are compatible if us or other is not currently defined.
603         return currentType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
604     }
605 
606     /**
607      * Returns true if this container is compatible with the input windowing mode and activity type.
608      * The container is compatible:
609      * - If {@param activityType} and {@param windowingMode} match this container activity type and
610      * windowing mode.
611      * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
612      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
613      * standard or undefined and its windowing mode matches {@param windowingMode}.
614      * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
615      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
616      * also standard or undefined and its activity type matches {@param activityType} regardless of
617      * if {@param windowingMode} matches the containers windowing mode.
618      */
isCompatible(int windowingMode, int activityType)619     public boolean isCompatible(int windowingMode, int activityType) {
620         final int thisActivityType = getActivityType();
621         final int thisWindowingMode = getWindowingMode();
622         final boolean sameActivityType = thisActivityType == activityType;
623         final boolean sameWindowingMode = thisWindowingMode == windowingMode;
624 
625         if (sameActivityType && sameWindowingMode) {
626             return true;
627         }
628 
629         if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
630                 || !isActivityTypeStandardOrUndefined()) {
631             // Only activity type need to match for non-standard activity types that are defined.
632             return sameActivityType;
633         }
634 
635         // Otherwise we are compatible if the windowing mode is the same.
636         return sameWindowingMode;
637     }
638 
registerConfigurationChangeListener(ConfigurationContainerListener listener)639     void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
640         registerConfigurationChangeListener(listener, true /* shouldDispatchConfig */);
641     }
642 
registerConfigurationChangeListener(ConfigurationContainerListener listener, boolean shouldDispatchConfig)643     void registerConfigurationChangeListener(ConfigurationContainerListener listener,
644             boolean shouldDispatchConfig) {
645         if (mChangeListeners.contains(listener)) {
646             return;
647         }
648         mChangeListeners.add(listener);
649         if (shouldDispatchConfig) {
650             listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
651             listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
652         }
653     }
654 
unregisterConfigurationChangeListener(ConfigurationContainerListener listener)655     void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
656         mChangeListeners.remove(listener);
657     }
658 
659     @VisibleForTesting
containsListener(ConfigurationContainerListener listener)660     boolean containsListener(ConfigurationContainerListener listener) {
661         return mChangeListeners.contains(listener);
662     }
663 
664     /**
665      * Must be called when new parent for the container was set.
666      */
onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent)667     void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
668         // Removing parent usually means that we've detached this entity to destroy it or to attach
669         // to another parent. In both cases we don't need to update the configuration now.
670         if (newParent != null) {
671             // Update full configuration of this container and all its children.
672             onConfigurationChanged(newParent.mFullConfiguration);
673             // Update merged override configuration of this container and all its children.
674             onMergedOverrideConfigurationChanged();
675         }
676     }
677 
678     /**
679      * Write to a protocol buffer output stream. Protocol buffer message definition is at
680      * {@link com.android.server.wm.ConfigurationContainerProto}.
681      *
682      * @param proto    Stream to write the ConfigurationContainer object to.
683      * @param fieldId  Field Id of the ConfigurationContainer as defined in the parent
684      *                 message.
685      * @param logLevel Determines the amount of data to be written to the Protobuf.
686      * @hide
687      */
688     @CallSuper
dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)689     protected void dumpDebug(ProtoOutputStream proto, long fieldId,
690             @WindowTraceLogLevel int logLevel) {
691         final long token = proto.start(fieldId);
692 
693         if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) {
694             mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
695                     logLevel == WindowTraceLogLevel.CRITICAL);
696         }
697 
698         // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
699         // required to mitigate performance overhead
700         if (logLevel == WindowTraceLogLevel.ALL) {
701             mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
702             mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
703                     false /* critical */);
704         }
705 
706         if (logLevel == WindowTraceLogLevel.TRIM) {
707             // Required for Fass to automatically detect pip transitions in Winscope traces
708             dumpDebugWindowingMode(proto);
709         }
710 
711         proto.end(token);
712     }
713 
dumpDebugWindowingMode(ProtoOutputStream proto)714     private void dumpDebugWindowingMode(ProtoOutputStream proto) {
715         final long fullConfigToken = proto.start(FULL_CONFIGURATION);
716         final long windowConfigToken = proto.start(WINDOW_CONFIGURATION);
717 
718         int windowingMode = mFullConfiguration.windowConfiguration.getWindowingMode();
719         proto.write(WINDOWING_MODE, windowingMode);
720 
721         proto.end(windowConfigToken);
722         proto.end(fullConfigToken);
723     }
724 
725     /**
726      * Dumps the names of this container children in the input print writer indenting each
727      * level with the input prefix.
728      */
dumpChildrenNames(PrintWriter pw, String prefix)729     public void dumpChildrenNames(PrintWriter pw, String prefix) {
730         final String childPrefix = prefix + " ";
731         pw.println(getName()
732                 + " type=" + activityTypeToString(getActivityType())
733                 + " mode=" + windowingModeToString(getWindowingMode())
734                 + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode())
735                 + " requested-bounds=" + getRequestedOverrideBounds().toShortString()
736                 + " bounds=" + getBounds().toShortString());
737         for (int i = getChildCount() - 1; i >= 0; --i) {
738             final E cc = getChildAt(i);
739             pw.print(childPrefix + "#" + i + " ");
740             cc.dumpChildrenNames(pw, childPrefix);
741         }
742     }
743 
getName()744     String getName() {
745         return toString();
746     }
747 
isAlwaysOnTop()748     public boolean isAlwaysOnTop() {
749         return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
750     }
751 
hasChild()752     boolean hasChild() {
753         return getChildCount() > 0;
754     }
755 
getChildCount()756     abstract protected int getChildCount();
757 
getChildAt(int index)758     abstract protected E getChildAt(int index);
759 
getParent()760     abstract protected ConfigurationContainer getParent();
761 
762 }
763