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