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.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
28 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
29 import static android.app.WindowConfiguration.activityTypeToString;
30 import static android.app.WindowConfiguration.windowingModeToString;
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.app.WindowConfiguration;
38 import android.content.res.Configuration;
39 import android.graphics.Point;
40 import android.graphics.Rect;
41 import android.util.proto.ProtoOutputStream;
42 
43 import com.android.internal.annotations.VisibleForTesting;
44 
45 import java.io.PrintWriter;
46 import java.util.ArrayList;
47 
48 /**
49  * Contains common logic for classes that have override configurations and are organized in a
50  * hierarchy.
51  */
52 public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
53     /**
54      * {@link #Rect} returned from {@link #getRequestedOverrideBounds()} to prevent original value
55      * from being set directly.
56      */
57     private Rect mReturnBounds = new Rect();
58 
59     /**
60      * Contains requested override configuration settings applied to this configuration container.
61      */
62     private Configuration mRequestedOverrideConfiguration = new Configuration();
63 
64     /**
65      * Contains the requested override configuration with parent and policy constraints applied.
66      * This is the set of overrides that gets applied to the full and merged configurations.
67      */
68     private Configuration mResolvedOverrideConfiguration = new Configuration();
69 
70     /** True if mRequestedOverrideConfiguration is not empty */
71     private boolean mHasOverrideConfiguration;
72 
73     /**
74      * Contains full configuration applied to this configuration container. Corresponds to full
75      * parent's config with applied {@link #mResolvedOverrideConfiguration}.
76      */
77     private Configuration mFullConfiguration = new Configuration();
78 
79     /**
80      * Contains merged override configuration settings from the top of the hierarchy down to this
81      * particular instance. It is different from {@link #mFullConfiguration} because it starts from
82      * topmost container's override config instead of global config.
83      */
84     private Configuration mMergedOverrideConfiguration = new Configuration();
85 
86     private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>();
87 
88     // TODO: Can't have ag/2592611 soon enough!
89     private final Configuration mRequestsTmpConfig = new Configuration();
90     private final Configuration mResolvedTmpConfig = new Configuration();
91 
92     // Used for setting bounds
93     private final Rect mTmpRect = new Rect();
94 
95     static final int BOUNDS_CHANGE_NONE = 0;
96     // Return value from {@link setBounds} indicating the position of the override bounds changed.
97     static final int BOUNDS_CHANGE_POSITION = 1;
98     // Return value from {@link setBounds} indicating the size of the override bounds changed.
99     static final int BOUNDS_CHANGE_SIZE = 1 << 1;
100 
101     /**
102      * Returns full configuration applied to this configuration container.
103      * This method should be used for getting settings applied in each particular level of the
104      * hierarchy.
105      */
getConfiguration()106     public Configuration getConfiguration() {
107         return mFullConfiguration;
108     }
109 
110     /**
111      * Notify that parent config changed and we need to update full configuration.
112      * @see #mFullConfiguration
113      */
onConfigurationChanged(Configuration newParentConfig)114     public void onConfigurationChanged(Configuration newParentConfig) {
115         mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
116         resolveOverrideConfiguration(newParentConfig);
117         mFullConfiguration.setTo(newParentConfig);
118         mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
119         onMergedOverrideConfigurationChanged();
120         if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
121             // This depends on the assumption that change-listeners don't do
122             // their own override resolution. This way, dependent hierarchies
123             // can stay properly synced-up with a primary hierarchy's constraints.
124             // Since the hierarchies will be merged, this whole thing will go away
125             // before the assumption will be broken.
126             // Inform listeners of the change.
127             for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
128                 mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
129                         mResolvedOverrideConfiguration);
130             }
131         }
132         for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
133             mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
134                     mMergedOverrideConfiguration);
135         }
136         for (int i = getChildCount() - 1; i >= 0; --i) {
137             final ConfigurationContainer child = getChildAt(i);
138             child.onConfigurationChanged(mFullConfiguration);
139         }
140     }
141 
142     /**
143      * Resolves the current requested override configuration into
144      * {@link #mResolvedOverrideConfiguration}
145      *
146      * @param newParentConfig The new parent configuration to resolve overrides against.
147      */
resolveOverrideConfiguration(Configuration newParentConfig)148     void resolveOverrideConfiguration(Configuration newParentConfig) {
149         mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
150     }
151 
152     /** Returns requested override configuration applied to this configuration container. */
getRequestedOverrideConfiguration()153     public Configuration getRequestedOverrideConfiguration() {
154         return mRequestedOverrideConfiguration;
155     }
156 
157     /** Returns the resolved override configuration. */
getResolvedOverrideConfiguration()158     Configuration getResolvedOverrideConfiguration() {
159         return mResolvedOverrideConfiguration;
160     }
161 
162     /**
163      * Update override configuration and recalculate full config.
164      * @see #mRequestedOverrideConfiguration
165      * @see #mFullConfiguration
166      */
onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration)167     public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
168         // Pre-compute this here, so we don't need to go through the entire Configuration when
169         // writing to proto (which has significant cost if we write a lot of empty configurations).
170         mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
171         mRequestedOverrideConfiguration.setTo(overrideConfiguration);
172         // Update full configuration of this container and all its children.
173         final ConfigurationContainer parent = getParent();
174         onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
175     }
176 
177     /**
178      * Get merged override configuration from the top of the hierarchy down to this particular
179      * instance. This should be reported to client as override config.
180      */
getMergedOverrideConfiguration()181     public Configuration getMergedOverrideConfiguration() {
182         return mMergedOverrideConfiguration;
183     }
184 
185     /**
186      * Update merged override configuration based on corresponding parent's config and notify all
187      * its children. If there is no parent, merged override configuration will set equal to current
188      * override config.
189      * @see #mMergedOverrideConfiguration
190      */
onMergedOverrideConfigurationChanged()191     void onMergedOverrideConfigurationChanged() {
192         final ConfigurationContainer parent = getParent();
193         if (parent != null) {
194             mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration());
195             mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration);
196         } else {
197             mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
198         }
199         for (int i = getChildCount() - 1; i >= 0; --i) {
200             final ConfigurationContainer child = getChildAt(i);
201             child.onMergedOverrideConfigurationChanged();
202         }
203     }
204 
205     /**
206      * Indicates whether this container has not requested any bounds different from its parent. In
207      * this case, it will inherit the bounds of the first ancestor which specifies a bounds subject
208      * to policy constraints.
209      *
210      * @return {@code true} if no explicit bounds have been requested at this container level.
211      *         {@code false} otherwise.
212      */
matchParentBounds()213     public boolean matchParentBounds() {
214         return getRequestedOverrideBounds().isEmpty();
215     }
216 
217     /**
218      * Returns whether the bounds specified are considered the same as the existing requested
219      * override bounds. This is either when the two bounds are equal or the requested override
220      * bounds are empty and the specified bounds is null.
221      *
222      * @return {@code true} if the bounds are equivalent, {@code false} otherwise
223      */
equivalentRequestedOverrideBounds(Rect bounds)224     public boolean equivalentRequestedOverrideBounds(Rect bounds) {
225         return equivalentBounds(getRequestedOverrideBounds(),  bounds);
226     }
227 
228     /**
229      * Returns whether the two bounds are equal to each other or are a combination of null or empty.
230      */
equivalentBounds(Rect bounds, Rect other)231     public static boolean equivalentBounds(Rect bounds, Rect other) {
232         return bounds == other
233                 || (bounds != null && (bounds.equals(other) || (bounds.isEmpty() && other == null)))
234                 || (other != null && other.isEmpty() && bounds == null);
235     }
236 
237     /**
238      * Returns the effective bounds of this container, inheriting the first non-empty bounds set in
239      * its ancestral hierarchy, including itself.
240      * @return
241      */
getBounds()242     public Rect getBounds() {
243         mReturnBounds.set(getConfiguration().windowConfiguration.getBounds());
244         return mReturnBounds;
245     }
246 
getBounds(Rect outBounds)247     public void getBounds(Rect outBounds) {
248         outBounds.set(getBounds());
249     }
250 
251     /**
252      * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}.
253      */
getPosition(Point out)254     public void getPosition(Point out) {
255         Rect bounds = getBounds();
256         out.set(bounds.left, bounds.top);
257     }
258 
getResolvedOverrideBounds()259     Rect getResolvedOverrideBounds() {
260         mReturnBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
261         return mReturnBounds;
262     }
263 
264     /**
265      * Returns the bounds requested on this container. These may not be the actual bounds the
266      * container ends up with due to policy constraints. The {@link Rect} handed back is
267      * shared for all calls to this method and should not be modified.
268      */
getRequestedOverrideBounds()269     public Rect getRequestedOverrideBounds() {
270         mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getBounds());
271 
272         return mReturnBounds;
273     }
274 
275     /**
276      * Returns {@code true} if the {@link WindowConfiguration} in the requested override
277      * {@link Configuration} specifies bounds.
278      */
hasOverrideBounds()279     public boolean hasOverrideBounds() {
280         return !getRequestedOverrideBounds().isEmpty();
281     }
282 
283     /**
284      * Sets the passed in {@link Rect} to the current bounds.
285      * @see {@link #getRequestedOverrideBounds()}.
286      */
getRequestedOverrideBounds(Rect outBounds)287     public void getRequestedOverrideBounds(Rect outBounds) {
288         outBounds.set(getRequestedOverrideBounds());
289     }
290 
291     /**
292      * Sets the bounds at the current hierarchy level, overriding any bounds set on an ancestor.
293      * This value will be reported when {@link #getBounds()} and
294      * {@link #getRequestedOverrideBounds()}. If
295      * an empty {@link Rect} or null is specified, this container will be considered to match its
296      * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent.
297      * @param bounds The bounds defining the container size.
298      * @return a bitmask representing the types of changes made to the bounds.
299      */
setBounds(Rect bounds)300     public int setBounds(Rect bounds) {
301         int boundsChange = diffRequestedOverrideBounds(bounds);
302 
303         if (boundsChange == BOUNDS_CHANGE_NONE) {
304             return boundsChange;
305         }
306 
307 
308         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
309         mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
310         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
311 
312         return boundsChange;
313     }
314 
setBounds(int left, int top, int right, int bottom)315     public int setBounds(int left, int top, int right, int bottom) {
316         mTmpRect.set(left, top, right, bottom);
317         return setBounds(mTmpRect);
318     }
319 
diffRequestedOverrideBounds(Rect bounds)320     int diffRequestedOverrideBounds(Rect bounds) {
321         if (equivalentRequestedOverrideBounds(bounds)) {
322             return BOUNDS_CHANGE_NONE;
323         }
324 
325         int boundsChange = BOUNDS_CHANGE_NONE;
326 
327         final Rect existingBounds = getRequestedOverrideBounds();
328 
329         if (bounds == null || existingBounds.left != bounds.left
330                 || existingBounds.top != bounds.top) {
331             boundsChange |= BOUNDS_CHANGE_POSITION;
332         }
333 
334         if (bounds == null || existingBounds.width() != bounds.width()
335                 || existingBounds.height() != bounds.height()) {
336             boundsChange |= BOUNDS_CHANGE_SIZE;
337         }
338 
339         return boundsChange;
340     }
341 
hasOverrideConfiguration()342     boolean hasOverrideConfiguration() {
343         return mHasOverrideConfiguration;
344     }
345 
getWindowConfiguration()346     public WindowConfiguration getWindowConfiguration() {
347         return mFullConfiguration.windowConfiguration;
348     }
349 
350     /** Returns the windowing mode the configuration container is currently in. */
getWindowingMode()351     public int getWindowingMode() {
352         return mFullConfiguration.windowConfiguration.getWindowingMode();
353     }
354 
355     /** Returns the windowing mode override that is requested by this container. */
getRequestedOverrideWindowingMode()356     public int getRequestedOverrideWindowingMode() {
357         return mRequestedOverrideConfiguration.windowConfiguration.getWindowingMode();
358     }
359 
360     /** Sets the requested windowing mode override for the configuration container. */
setWindowingMode( int windowingMode)361     public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
362         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
363         mRequestsTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
364         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
365     }
366 
367     /** Sets the always on top flag for this configuration container.
368      *  When you call this function, make sure that the following functions are called as well to
369      *  keep proper z-order.
370      *  - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
371      * */
setAlwaysOnTop(boolean alwaysOnTop)372     public void setAlwaysOnTop(boolean alwaysOnTop) {
373         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
374         mRequestsTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
375         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
376     }
377 
378     /** Sets the windowing mode for the configuration container. */
setDisplayWindowingMode(int windowingMode)379     void setDisplayWindowingMode(int windowingMode) {
380         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
381         mRequestsTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode);
382         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
383     }
384 
385     /**
386      * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
387      * with another activity.
388      */
inMultiWindowMode()389     public boolean inMultiWindowMode() {
390         /*@WindowConfiguration.WindowingMode*/ int windowingMode =
391                 mFullConfiguration.windowConfiguration.getWindowingMode();
392         return WindowConfiguration.inMultiWindowMode(windowingMode);
393     }
394 
395     /** Returns true if this container is currently in split-screen windowing mode. */
inSplitScreenWindowingMode()396     public boolean inSplitScreenWindowingMode() {
397         /*@WindowConfiguration.WindowingMode*/ int windowingMode =
398                 mFullConfiguration.windowConfiguration.getWindowingMode();
399 
400         return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
401                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
402     }
403 
404     /** Returns true if this container is currently in split-screen secondary windowing mode. */
inSplitScreenSecondaryWindowingMode()405     public boolean inSplitScreenSecondaryWindowingMode() {
406         /*@WindowConfiguration.WindowingMode*/ int windowingMode =
407                 mFullConfiguration.windowConfiguration.getWindowingMode();
408 
409         return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
410     }
411 
inSplitScreenPrimaryWindowingMode()412     public boolean inSplitScreenPrimaryWindowingMode() {
413         return mFullConfiguration.windowConfiguration.getWindowingMode()
414                 == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
415     }
416 
417     /**
418      * Returns true if this container can be put in either
419      * {@link WindowConfiguration#WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
420      * {@link WindowConfiguration##WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on
421      * its current state.
422      */
supportsSplitScreenWindowingMode()423     public boolean supportsSplitScreenWindowingMode() {
424         return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
425     }
426 
inPinnedWindowingMode()427     public boolean inPinnedWindowingMode() {
428         return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
429     }
430 
inFreeformWindowingMode()431     public boolean inFreeformWindowingMode() {
432         return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
433     }
434 
435     /** Returns the activity type associated with the the configuration container. */
436     /*@WindowConfiguration.ActivityType*/
getActivityType()437     public int getActivityType() {
438         return mFullConfiguration.windowConfiguration.getActivityType();
439     }
440 
441     /** Sets the activity type to associate with the configuration container. */
setActivityType( int activityType)442     public void setActivityType(/*@WindowConfiguration.ActivityType*/ int activityType) {
443         int currentActivityType = getActivityType();
444         if (currentActivityType == activityType) {
445             return;
446         }
447         if (currentActivityType != ACTIVITY_TYPE_UNDEFINED) {
448             throw new IllegalStateException("Can't change activity type once set: " + this
449                     + " activityType=" + activityTypeToString(activityType));
450         }
451         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
452         mRequestsTmpConfig.windowConfiguration.setActivityType(activityType);
453         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
454     }
455 
isActivityTypeHome()456     public boolean isActivityTypeHome() {
457         return getActivityType() == ACTIVITY_TYPE_HOME;
458     }
459 
isActivityTypeRecents()460     public boolean isActivityTypeRecents() {
461         return getActivityType() == ACTIVITY_TYPE_RECENTS;
462     }
463 
isActivityTypeAssistant()464     public boolean isActivityTypeAssistant() {
465         return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
466     }
467 
isActivityTypeDream()468     public boolean isActivityTypeDream() {
469         return getActivityType() == ACTIVITY_TYPE_DREAM;
470     }
471 
isActivityTypeStandard()472     public boolean isActivityTypeStandard() {
473         return getActivityType() == ACTIVITY_TYPE_STANDARD;
474     }
475 
isActivityTypeStandardOrUndefined()476     public boolean isActivityTypeStandardOrUndefined() {
477         /*@WindowConfiguration.ActivityType*/ final int activityType = getActivityType();
478         return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED;
479     }
480 
hasCompatibleActivityType(ConfigurationContainer other)481     public boolean hasCompatibleActivityType(ConfigurationContainer other) {
482         /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType();
483         /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType();
484 
485         if (thisType == otherType) {
486             return true;
487         }
488         if (thisType == ACTIVITY_TYPE_ASSISTANT) {
489             // Assistant activities are only compatible with themselves...
490             return false;
491         }
492         // Otherwise we are compatible if us or other is not currently defined.
493         return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
494     }
495 
496     /**
497      * Returns true if this container is compatible with the input windowing mode and activity type.
498      * The container is compatible:
499      * - If {@param activityType} and {@param windowingMode} match this container activity type and
500      * windowing mode.
501      * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
502      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
503      * standard or undefined and its windowing mode matches {@param windowingMode}.
504      * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
505      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
506      * also standard or undefined and its activity type matches {@param activityType} regardless of
507      * if {@param windowingMode} matches the containers windowing mode.
508      */
isCompatible(int windowingMode, int activityType)509     public boolean isCompatible(int windowingMode, int activityType) {
510         final int thisActivityType = getActivityType();
511         final int thisWindowingMode = getWindowingMode();
512         final boolean sameActivityType = thisActivityType == activityType;
513         final boolean sameWindowingMode = thisWindowingMode == windowingMode;
514 
515         if (sameActivityType && sameWindowingMode) {
516             return true;
517         }
518 
519         if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
520                 || !isActivityTypeStandardOrUndefined()) {
521             // Only activity type need to match for non-standard activity types that are defined.
522             return sameActivityType;
523         }
524 
525         // Otherwise we are compatible if the windowing mode is the same.
526         return sameWindowingMode;
527     }
528 
registerConfigurationChangeListener(ConfigurationContainerListener listener)529     void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
530         if (mChangeListeners.contains(listener)) {
531             return;
532         }
533         mChangeListeners.add(listener);
534         listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
535         listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
536     }
537 
unregisterConfigurationChangeListener(ConfigurationContainerListener listener)538     void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
539         mChangeListeners.remove(listener);
540     }
541 
542     @VisibleForTesting
containsListener(ConfigurationContainerListener listener)543     boolean containsListener(ConfigurationContainerListener listener) {
544         return mChangeListeners.contains(listener);
545     }
546 
547     /**
548      * Must be called when new parent for the container was set.
549      */
onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent)550     void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
551         // Removing parent usually means that we've detached this entity to destroy it or to attach
552         // to another parent. In both cases we don't need to update the configuration now.
553         if (newParent != null) {
554             // Update full configuration of this container and all its children.
555             onConfigurationChanged(newParent.mFullConfiguration);
556             // Update merged override configuration of this container and all its children.
557             onMergedOverrideConfigurationChanged();
558         }
559     }
560 
561     /**
562      * Write to a protocol buffer output stream. Protocol buffer message definition is at
563      * {@link com.android.server.wm.ConfigurationContainerProto}.
564      *
565      * @param proto    Stream to write the ConfigurationContainer object to.
566      * @param fieldId  Field Id of the ConfigurationContainer as defined in the parent
567      *                 message.
568      * @param logLevel Determines the amount of data to be written to the Protobuf.
569      * @hide
570      */
571     @CallSuper
dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)572     protected void dumpDebug(ProtoOutputStream proto, long fieldId,
573             @WindowTraceLogLevel int logLevel) {
574         // Critical log level logs only visible elements to mitigate performance overheard
575         if (logLevel != WindowTraceLogLevel.ALL && !mHasOverrideConfiguration) {
576             return;
577         }
578 
579         final long token = proto.start(fieldId);
580         mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
581                 logLevel == WindowTraceLogLevel.CRITICAL);
582         if (logLevel == WindowTraceLogLevel.ALL) {
583             mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
584             mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
585                     false /* critical */);
586         }
587         proto.end(token);
588     }
589 
590     /**
591      * Dumps the names of this container children in the input print writer indenting each
592      * level with the input prefix.
593      */
dumpChildrenNames(PrintWriter pw, String prefix)594     public void dumpChildrenNames(PrintWriter pw, String prefix) {
595         final String childPrefix = prefix + " ";
596         pw.println(getName()
597                 + " type=" + activityTypeToString(getActivityType())
598                 + " mode=" + windowingModeToString(getWindowingMode())
599                 + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode()));
600         for (int i = getChildCount() - 1; i >= 0; --i) {
601             final E cc = getChildAt(i);
602             pw.print(childPrefix + "#" + i + " ");
603             cc.dumpChildrenNames(pw, childPrefix);
604         }
605     }
606 
getName()607     String getName() {
608         return toString();
609     }
610 
isAlwaysOnTop()611     public boolean isAlwaysOnTop() {
612         return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
613     }
614 
hasChild()615     boolean hasChild() {
616         return getChildCount() > 0;
617     }
618 
getChildCount()619     abstract protected int getChildCount();
620 
getChildAt(int index)621     abstract protected E getChildAt(int index);
622 
getParent()623     abstract protected ConfigurationContainer getParent();
624 
625 }
626