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