• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
25 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
26 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
27 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
28 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
29 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
30 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
31 import static android.app.WindowConfiguration.activityTypeToString;
32 import static android.app.WindowConfiguration.isFloating;
33 import static android.app.WindowConfiguration.windowingModeToString;
34 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
35 import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
36 import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
37 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
38 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
39 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
40 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
41 import static android.view.Surface.ROTATION_270;
42 import static android.view.Surface.ROTATION_90;
43 
44 import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
45 import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
46 import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
47 
48 import android.annotation.CallSuper;
49 import android.annotation.NonNull;
50 import android.app.WindowConfiguration;
51 import android.content.pm.ApplicationInfo;
52 import android.content.res.Configuration;
53 import android.graphics.Point;
54 import android.graphics.Rect;
55 import android.os.LocaleList;
56 import android.util.DisplayMetrics;
57 import android.util.proto.ProtoOutputStream;
58 import android.view.DisplayInfo;
59 
60 import com.android.internal.annotations.VisibleForTesting;
61 
62 import java.io.PrintWriter;
63 import java.util.ArrayList;
64 
65 /**
66  * Contains common logic for classes that have override configurations and are organized in a
67  * hierarchy.
68  */
69 public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
70     /**
71      * {@link #Rect} returned from {@link #getRequestedOverrideBounds()} to prevent original value
72      * from being set directly.
73      */
74     private Rect mReturnBounds = new Rect();
75 
76     /**
77      * Contains requested override configuration settings applied to this configuration container.
78      */
79     private Configuration mRequestedOverrideConfiguration = new Configuration();
80 
81     /**
82      * Contains the requested override configuration with parent and policy constraints applied.
83      * This is the set of overrides that gets applied to the full and merged configurations.
84      */
85     private Configuration mResolvedOverrideConfiguration = new Configuration();
86 
87     /** True if mRequestedOverrideConfiguration is not empty */
88     private boolean mHasOverrideConfiguration;
89 
90     /**
91      * Contains full configuration applied to this configuration container. Corresponds to full
92      * parent's config with applied {@link #mResolvedOverrideConfiguration}.
93      */
94     private Configuration mFullConfiguration = new Configuration();
95 
96     /**
97      * Contains merged override configuration settings from the top of the hierarchy down to this
98      * particular instance. It is different from {@link #mFullConfiguration} because it starts from
99      * topmost container's override config instead of global config.
100      */
101     private Configuration mMergedOverrideConfiguration = new Configuration();
102 
103     private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>();
104 
105     // TODO: Can't have ag/2592611 soon enough!
106     private final Configuration mRequestsTmpConfig = new Configuration();
107     private final Configuration mResolvedTmpConfig = new Configuration();
108 
109     // Used for setting bounds
110     private final Rect mTmpRect = new Rect();
111 
112     static final int BOUNDS_CHANGE_NONE = 0;
113 
114     /**
115      * Return value from {@link #setBounds(Rect)} indicating the position of the override bounds
116      * changed.
117      */
118     static final int BOUNDS_CHANGE_POSITION = 1;
119 
120     /**
121      * Return value from {@link #setBounds(Rect)} indicating the size of the override bounds
122      * changed.
123      */
124     static final int BOUNDS_CHANGE_SIZE = 1 << 1;
125 
126     /**
127      * Returns full configuration applied to this configuration container.
128      * This method should be used for getting settings applied in each particular level of the
129      * hierarchy.
130      */
131     @NonNull
getConfiguration()132     public Configuration getConfiguration() {
133         return mFullConfiguration;
134     }
135 
136     /**
137      * Notify that parent config changed and we need to update full configuration.
138      * @see #mFullConfiguration
139      */
onConfigurationChanged(Configuration newParentConfig)140     public void onConfigurationChanged(Configuration newParentConfig) {
141         mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
142         resolveOverrideConfiguration(newParentConfig);
143         mFullConfiguration.setTo(newParentConfig);
144         // Do not inherit always-on-top property from parent, otherwise the always-on-top
145         // property is propagated to all children. In that case, newly added child is
146         // always being positioned at bottom (behind the always-on-top siblings).
147         mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
148         mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
149         onMergedOverrideConfigurationChanged();
150         if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
151             // This depends on the assumption that change-listeners don't do
152             // their own override resolution. This way, dependent hierarchies
153             // can stay properly synced-up with a primary hierarchy's constraints.
154             // Since the hierarchies will be merged, this whole thing will go away
155             // before the assumption will be broken.
156             // Inform listeners of the change.
157             for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
158                 mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
159                         mResolvedOverrideConfiguration);
160             }
161         }
162         for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
163             mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
164                     mMergedOverrideConfiguration);
165         }
166         for (int i = getChildCount() - 1; i >= 0; --i) {
167             dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
168         }
169     }
170 
171     /**
172      * Dispatches the configuration to child when {@link #onConfigurationChanged(Configuration)} is
173      * called. This allows the derived classes to override how to dispatch the configuration.
174      */
dispatchConfigurationToChild(E child, Configuration config)175     void dispatchConfigurationToChild(E child, Configuration config) {
176         child.onConfigurationChanged(config);
177     }
178 
179     /**
180      * Resolves the current requested override configuration into
181      * {@link #mResolvedOverrideConfiguration}
182      *
183      * @param newParentConfig The new parent configuration to resolve overrides against.
184      */
resolveOverrideConfiguration(Configuration newParentConfig)185     void resolveOverrideConfiguration(Configuration newParentConfig) {
186         mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
187     }
188 
189     /**
190      * If necessary, override configuration fields related to app bounds.
191      * This will happen when the app is targeting SDK earlier than 35.
192      * The insets and configuration has decoupled since SDK level 35, to make the system
193      * compatible to existing apps, override the configuration with legacy metrics. In legacy
194      * metrics, fields such as appBounds will exclude some of the system bar areas.
195      * The override contains all potentially affected fields in Configuration, including
196      * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
197      * All overrides to those fields should be in this method.
198      *
199      * Task is only needed for split-screen to apply an offset special handling.
200      *
201      * TODO: Consider integrate this with computeConfigByResolveHint()
202      */
applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo, Configuration newParentConfiguration, Configuration inOutConfig, boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform, boolean hasCompatDisplayInsets, Task task)203     static void applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo,
204             Configuration newParentConfiguration, Configuration inOutConfig,
205             boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform,
206             boolean hasCompatDisplayInsets, Task task) {
207         if (displayContent == null) {
208             return;
209         }
210         final boolean useOverrideInsetsForConfig =
211                 displayContent.mWmService.mFlags.mInsetsDecoupledConfiguration
212                         ? !appInfo.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
213                                 && !appInfo.isChangeEnabled(
214                                         OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION)
215                         : appInfo.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
216         final int parentWindowingMode =
217                 newParentConfiguration.windowConfiguration.getWindowingMode();
218         final boolean isFloating = isFloating(parentWindowingMode)
219                 // Check the requested windowing mode of activity as well in case it is
220                 // switching between PiP and fullscreen.
221                 && (inOutConfig.windowConfiguration.getWindowingMode() == WINDOWING_MODE_UNDEFINED
222                         || isFloating(inOutConfig.windowConfiguration.getWindowingMode()));
223         int rotation = newParentConfiguration.windowConfiguration.getRotation();
224         if (rotation == ROTATION_UNDEFINED && !hasFixedRotationTransform) {
225             rotation = displayContent.getRotation();
226         }
227         if (!optsOutEdgeToEdge && (!useOverrideInsetsForConfig
228                 || hasCompatDisplayInsets
229                 || rotation == ROTATION_UNDEFINED)) {
230             // If the insets configuration decoupled logic is not enabled for the app, or the app
231             // already has a compat override, or the context doesn't contain enough info to
232             // calculate the override, skip the override.
233             return;
234         }
235         if (isFloating) {
236             // Floating window won't have any insets affect configuration. Skip the override.
237             return;
238         }
239         // Make sure the orientation related fields will be updated by the override insets, because
240         // fixed rotation has assigned the fields from display's configuration.
241         if (hasFixedRotationTransform) {
242             inOutConfig.windowConfiguration.setAppBounds(null);
243             inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
244             inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
245             inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
246             inOutConfig.orientation = ORIENTATION_UNDEFINED;
247         }
248 
249         // Override starts here.
250         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
251         final int dw = rotated
252                 ? displayContent.mBaseDisplayHeight
253                 : displayContent.mBaseDisplayWidth;
254         final int dh = rotated
255                 ? displayContent.mBaseDisplayWidth
256                 : displayContent.mBaseDisplayHeight;
257         // This should be the only place override the configuration for ActivityRecord. Override
258         // the value if not calculated yet.
259         Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
260         Rect outConfigBounds = new Rect(outAppBounds);
261         if (outAppBounds == null || outAppBounds.isEmpty()) {
262             inOutConfig.windowConfiguration.setAppBounds(
263                     newParentConfiguration.windowConfiguration.getBounds());
264             outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
265             outConfigBounds.set(outAppBounds);
266             if (task != null) {
267                 task = task.getCreatedByOrganizerTask();
268                 if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) {
269                     outAppBounds.offset(task.mOffsetXForInsets, task.mOffsetYForInsets);
270                 }
271             }
272             final DisplayPolicy.DecorInsets.Info decor =
273                     displayContent.getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh);
274             if (!outAppBounds.intersect(decor.mOverrideNonDecorFrame)) {
275                 // TODO (b/364883053): When a split screen is requested from an app intent for a new
276                 //  task, the bounds is not the final bounds, and this is also not a bounds change
277                 //  event handled correctly with the offset. Revert back to legacy method for this
278                 //  case.
279                 if (inOutConfig.windowConfiguration.getWindowingMode()
280                         == WINDOWING_MODE_MULTI_WINDOW) {
281                     outAppBounds.inset(decor.mOverrideNonDecorInsets);
282                 }
283             }
284             if (!outConfigBounds.intersect(decor.mOverrideConfigFrame)) {
285                 if (inOutConfig.windowConfiguration.getWindowingMode()
286                         == WINDOWING_MODE_MULTI_WINDOW) {
287                     outAppBounds.inset(decor.mOverrideConfigInsets);
288                 }
289             }
290             if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) {
291                 outAppBounds.offset(-task.mOffsetXForInsets, -task.mOffsetYForInsets);
292             }
293         }
294         float density = inOutConfig.densityDpi;
295         if (density == Configuration.DENSITY_DPI_UNDEFINED) {
296             density = newParentConfiguration.densityDpi;
297         }
298         density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
299         if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
300             inOutConfig.screenWidthDp = (int) (outConfigBounds.width() / density + 0.5f);
301         }
302         if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
303             inOutConfig.screenHeightDp = (int) (outConfigBounds.height() / density + 0.5f);
304         }
305         if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
306                 && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
307             // For the case of PIP transition and multi-window environment, the
308             // smallestScreenWidthDp is handled already. Override only if the app is in
309             // fullscreen.
310             final DisplayInfo info = new DisplayInfo(displayContent.getDisplayInfo());
311             displayContent.computeSizeRanges(info, rotated, dw, dh,
312                     displayContent.getDisplayMetrics().density,
313                     inOutConfig, true /* overrideConfig */);
314         }
315 
316         // It's possible that screen size will be considered in different orientation with or
317         // without considering the system bar insets. Override orientation as well.
318         if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
319             inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
320                     ? ORIENTATION_PORTRAIT
321                     : ORIENTATION_LANDSCAPE;
322         }
323     }
324 
325     /** Returns {@code true} if requested override override configuration is not empty. */
hasRequestedOverrideConfiguration()326     boolean hasRequestedOverrideConfiguration() {
327         return mHasOverrideConfiguration;
328     }
329 
330     /** Returns requested override configuration applied to this configuration container. */
331     @NonNull
getRequestedOverrideConfiguration()332     public Configuration getRequestedOverrideConfiguration() {
333         return mRequestedOverrideConfiguration;
334     }
335 
336     /** Returns the resolved override configuration. */
337     @NonNull
getResolvedOverrideConfiguration()338     Configuration getResolvedOverrideConfiguration() {
339         return mResolvedOverrideConfiguration;
340     }
341 
342     /**
343      * Update override configuration and recalculate full config.
344      * @see #mRequestedOverrideConfiguration
345      * @see #mFullConfiguration
346      */
onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration)347     public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
348         updateRequestedOverrideConfiguration(overrideConfiguration);
349         // Update full configuration of this container and all its children.
350         final ConfigurationContainer parent = getParent();
351         onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
352     }
353 
354     /** Updates override configuration without recalculate full config. */
updateRequestedOverrideConfiguration(Configuration overrideConfiguration)355     void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) {
356         // Pre-compute this here, so we don't need to go through the entire Configuration when
357         // writing to proto (which has significant cost if we write a lot of empty configurations).
358         mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
359         mRequestedOverrideConfiguration.setTo(overrideConfiguration);
360         final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
361         if (mHasOverrideConfiguration && providesMaxBounds()
362                 && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
363             mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
364         }
365     }
366 
367     /**
368      * Get merged override configuration from the top of the hierarchy down to this particular
369      * instance. This should be reported to client as override config.
370      */
371     @NonNull
getMergedOverrideConfiguration()372     public Configuration getMergedOverrideConfiguration() {
373         return mMergedOverrideConfiguration;
374     }
375 
376     /**
377      * Update merged override configuration based on corresponding parent's config. If there is no
378      * parent, merged override configuration will set equal to current override config. This
379      * doesn't cascade on its own since it's called by {@link #onConfigurationChanged}.
380      * @see #mMergedOverrideConfiguration
381      */
onMergedOverrideConfigurationChanged()382     void onMergedOverrideConfigurationChanged() {
383         final ConfigurationContainer parent = getParent();
384         if (parent != null) {
385             mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration());
386             // Do not inherit always-on-top property from parent, otherwise the always-on-top
387             // property is propagated to all children. In that case, newly added child is
388             // always being positioned at bottom (behind the always-on-top siblings).
389             mMergedOverrideConfiguration.windowConfiguration.unsetAlwaysOnTop();
390             mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration);
391         } else {
392             mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
393         }
394     }
395 
396     /**
397      * Indicates whether this container chooses not to override any bounds from its parent, either
398      * because it doesn't request to override them or the request is dropped during configuration
399      * resolution. In this case, it will inherit the bounds of the first ancestor which specifies a
400      * bounds subject to policy constraints.
401      *
402      * @return {@code true} if this container level uses bounds from parent level. {@code false}
403      *         otherwise.
404      */
matchParentBounds()405     public boolean matchParentBounds() {
406         return getResolvedOverrideBounds().isEmpty();
407     }
408 
409     /**
410      * Returns whether the bounds specified are considered the same as the existing requested
411      * override bounds. This is either when the two bounds are equal or the requested override
412      * bounds are empty and the specified bounds is null.
413      *
414      * @return {@code true} if the bounds are equivalent, {@code false} otherwise
415      */
equivalentRequestedOverrideBounds(Rect bounds)416     public boolean equivalentRequestedOverrideBounds(Rect bounds) {
417         return equivalentBounds(getRequestedOverrideBounds(),  bounds);
418     }
419 
420     /** Similar to {@link #equivalentRequestedOverrideBounds(Rect)}, but compares max bounds. */
equivalentRequestedOverrideMaxBounds(Rect bounds)421     public boolean equivalentRequestedOverrideMaxBounds(Rect bounds) {
422         return equivalentBounds(getRequestedOverrideMaxBounds(),  bounds);
423     }
424 
425     /**
426      * Returns whether the two bounds are equal to each other or are a combination of null or empty.
427      */
equivalentBounds(Rect bounds, Rect other)428     public static boolean equivalentBounds(Rect bounds, Rect other) {
429         return bounds == other
430                 || (bounds != null && (bounds.equals(other) || (bounds.isEmpty() && other == null)))
431                 || (other != null && other.isEmpty() && bounds == null);
432     }
433 
434     /**
435      * Returns the effective bounds of this container, inheriting the first non-empty bounds set in
436      * its ancestral hierarchy, including itself.
437      */
getBounds()438     public Rect getBounds() {
439         mReturnBounds.set(getConfiguration().windowConfiguration.getBounds());
440         return mReturnBounds;
441     }
442 
getBounds(Rect outBounds)443     public void getBounds(Rect outBounds) {
444         outBounds.set(getBounds());
445     }
446 
447     /** Similar to {@link #getBounds()}, but reports the max bounds. */
getMaxBounds()448     public Rect getMaxBounds() {
449         mReturnBounds.set(getConfiguration().windowConfiguration.getMaxBounds());
450         return mReturnBounds;
451     }
452 
453     /**
454      * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}.
455      */
getPosition(Point out)456     public void getPosition(Point out) {
457         Rect bounds = getBounds();
458         out.set(bounds.left, bounds.top);
459     }
460 
getResolvedOverrideBounds()461     Rect getResolvedOverrideBounds() {
462         mReturnBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
463         return mReturnBounds;
464     }
465 
466     /**
467      * Returns the bounds requested on this container. These may not be the actual bounds the
468      * container ends up with due to policy constraints. The {@link Rect} handed back is
469      * shared for all calls to this method and should not be modified.
470      */
getRequestedOverrideBounds()471     public Rect getRequestedOverrideBounds() {
472         mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getBounds());
473 
474         return mReturnBounds;
475     }
476 
477     /** Similar to {@link #getRequestedOverrideBounds()}, but returns the max bounds. */
getRequestedOverrideMaxBounds()478     public Rect getRequestedOverrideMaxBounds() {
479         mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getMaxBounds());
480 
481         return mReturnBounds;
482     }
483 
484     /**
485      * Returns {@code true} if the {@link WindowConfiguration} in the requested override
486      * {@link Configuration} specifies bounds.
487      */
hasOverrideBounds()488     public boolean hasOverrideBounds() {
489         return !getRequestedOverrideBounds().isEmpty();
490     }
491 
492     /**
493      * Sets the passed in {@link Rect} to the current bounds.
494      * @see #getRequestedOverrideBounds()
495      */
getRequestedOverrideBounds(Rect outBounds)496     public void getRequestedOverrideBounds(Rect outBounds) {
497         outBounds.set(getRequestedOverrideBounds());
498     }
499 
500     /**
501      * Sets the bounds at the current hierarchy level, overriding any bounds set on an ancestor.
502      * This value will be reported when {@link #getBounds()} and
503      * {@link #getRequestedOverrideBounds()}. If
504      * an empty {@link Rect} or null is specified, this container will be considered to match its
505      * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent.
506      *
507      * @param bounds The bounds defining the container size.
508      *
509      * @return a bitmask representing the types of changes made to the bounds.
510      */
setBounds(Rect bounds)511     public int setBounds(Rect bounds) {
512         int boundsChange = diffRequestedOverrideBounds(bounds);
513         final boolean overrideMaxBounds = providesMaxBounds()
514                 && diffRequestedOverrideMaxBounds(bounds) != BOUNDS_CHANGE_NONE;
515 
516         if (boundsChange == BOUNDS_CHANGE_NONE && !overrideMaxBounds) {
517             return boundsChange;
518         }
519 
520         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
521         mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
522         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
523 
524         return boundsChange;
525     }
526 
setBounds(int left, int top, int right, int bottom)527     public int setBounds(int left, int top, int right, int bottom) {
528         mTmpRect.set(left, top, right, bottom);
529         return setBounds(mTmpRect);
530     }
531 
532     /**
533      * Returns {@code true} if this {@link ConfigurationContainer} provides the maximum bounds to
534      * its child {@link ConfigurationContainer}s. Returns {@code false}, otherwise.
535      * <p>
536      * The maximum bounds is how large a window can be expanded.
537      * </p>
538      */
providesMaxBounds()539     protected boolean providesMaxBounds() {
540         return false;
541     }
542 
diffRequestedOverrideMaxBounds(Rect bounds)543     int diffRequestedOverrideMaxBounds(Rect bounds) {
544         if (equivalentRequestedOverrideMaxBounds(bounds)) {
545             return BOUNDS_CHANGE_NONE;
546         }
547 
548         int boundsChange = BOUNDS_CHANGE_NONE;
549 
550         final Rect existingBounds = getRequestedOverrideMaxBounds();
551 
552         if (bounds == null || existingBounds.left != bounds.left
553                 || existingBounds.top != bounds.top) {
554             boundsChange |= BOUNDS_CHANGE_POSITION;
555         }
556 
557         if (bounds == null || existingBounds.width() != bounds.width()
558                 || existingBounds.height() != bounds.height()) {
559             boundsChange |= BOUNDS_CHANGE_SIZE;
560         }
561 
562         return boundsChange;
563     }
564 
diffRequestedOverrideBounds(Rect bounds)565     int diffRequestedOverrideBounds(Rect bounds) {
566         if (equivalentRequestedOverrideBounds(bounds)) {
567             return BOUNDS_CHANGE_NONE;
568         }
569 
570         int boundsChange = BOUNDS_CHANGE_NONE;
571 
572         final Rect existingBounds = getRequestedOverrideBounds();
573 
574         if (bounds == null || existingBounds.left != bounds.left
575                 || existingBounds.top != bounds.top) {
576             boundsChange |= BOUNDS_CHANGE_POSITION;
577         }
578 
579         if (bounds == null || existingBounds.width() != bounds.width()
580                 || existingBounds.height() != bounds.height()) {
581             boundsChange |= BOUNDS_CHANGE_SIZE;
582         }
583 
584         return boundsChange;
585     }
586 
getWindowConfiguration()587     public WindowConfiguration getWindowConfiguration() {
588         return mFullConfiguration.windowConfiguration;
589     }
590 
591     /** Returns the windowing mode the configuration container is currently in. */
getWindowingMode()592     public int getWindowingMode() {
593         return mFullConfiguration.windowConfiguration.getWindowingMode();
594     }
595 
596     /** Returns the windowing mode override that is requested by this container. */
getRequestedOverrideWindowingMode()597     public int getRequestedOverrideWindowingMode() {
598         return mRequestedOverrideConfiguration.windowConfiguration.getWindowingMode();
599     }
600 
601     /** Sets the requested windowing mode override for the configuration container. */
setWindowingMode( int windowingMode)602     public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
603         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
604         mRequestsTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
605         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
606     }
607 
608     /** Sets the always on top flag for this configuration container.
609      *  When you call this function, make sure that the following functions are called as well to
610      *  keep proper z-order.
611      *  - {@link TaskDisplayArea#positionChildAt(int POSITION_TOP, Task, boolean)};
612      * */
setAlwaysOnTop(boolean alwaysOnTop)613     public void setAlwaysOnTop(boolean alwaysOnTop) {
614         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
615         mRequestsTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
616         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
617     }
618 
619     /**
620      * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
621      * with another activity.
622      */
inMultiWindowMode()623     public boolean inMultiWindowMode() {
624         /*@WindowConfiguration.WindowingMode*/ int windowingMode =
625                 mFullConfiguration.windowConfiguration.getWindowingMode();
626         return WindowConfiguration.inMultiWindowMode(windowingMode);
627     }
628 
inPinnedWindowingMode()629     public boolean inPinnedWindowingMode() {
630         return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
631     }
632 
inFreeformWindowingMode()633     public boolean inFreeformWindowingMode() {
634         return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
635     }
636 
637     /** Returns the activity type associated with the configuration container. */
638     /*@WindowConfiguration.ActivityType*/
getActivityType()639     public int getActivityType() {
640         return mFullConfiguration.windowConfiguration.getActivityType();
641     }
642 
643     /** Sets the activity type to associate with the configuration container. */
setActivityType( int activityType)644     public void setActivityType(/*@WindowConfiguration.ActivityType*/ int activityType) {
645         int currentActivityType = getActivityType();
646         if (currentActivityType == activityType) {
647             return;
648         }
649         if (currentActivityType != ACTIVITY_TYPE_UNDEFINED) {
650             throw new IllegalStateException("Can't change activity type once set: " + this
651                     + " activityType=" + activityTypeToString(activityType));
652         }
653         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
654         mRequestsTmpConfig.windowConfiguration.setActivityType(activityType);
655         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
656     }
657 
isActivityTypeHome()658     public boolean isActivityTypeHome() {
659         return getActivityType() == ACTIVITY_TYPE_HOME;
660     }
661 
isActivityTypeRecents()662     public boolean isActivityTypeRecents() {
663         return getActivityType() == ACTIVITY_TYPE_RECENTS;
664     }
665 
isActivityTypeHomeOrRecents()666     final boolean isActivityTypeHomeOrRecents() {
667         final int activityType = getActivityType();
668         return activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
669     }
670 
isActivityTypeAssistant()671     public boolean isActivityTypeAssistant() {
672         return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
673     }
674 
675     /**
676      * Applies app-specific nightMode and {@link LocaleList} on requested configuration.
677      * @return true if any of the requested configuration has been updated.
678      */
applyAppSpecificConfig(Integer nightMode, LocaleList locales, @Configuration.GrammaticalGender Integer gender)679     public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales,
680             @Configuration.GrammaticalGender Integer gender) {
681         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
682         boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig,
683                 nightMode);
684         boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig,
685                 locales);
686         boolean newGenderSet = setOverrideGender(mRequestsTmpConfig,
687                 gender == null ? Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED : gender);
688         if (newNightModeSet || newLocalesSet || newGenderSet) {
689             onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
690         }
691         return newNightModeSet || newLocalesSet || newGenderSet;
692     }
693 
694     /**
695      * Overrides the night mode applied to this ConfigurationContainer.
696      * @return true if the nightMode has been changed.
697      */
setOverrideNightMode(Configuration requestsTmpConfig, int nightMode)698     private boolean setOverrideNightMode(Configuration requestsTmpConfig, int nightMode) {
699         final int currentUiMode = mRequestedOverrideConfiguration.uiMode;
700         final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK;
701         final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK;
702         if (currentNightMode == validNightMode) {
703             return false;
704         }
705         requestsTmpConfig.uiMode = validNightMode
706                 | (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK);
707         return true;
708     }
709 
710     /**
711      * Overrides the locales applied to this ConfigurationContainer.
712      * @return true if the LocaleList has been changed.
713      */
setOverrideLocales(Configuration requestsTmpConfig, @NonNull LocaleList overrideLocales)714     private boolean setOverrideLocales(Configuration requestsTmpConfig,
715             @NonNull LocaleList overrideLocales) {
716         if (mRequestedOverrideConfiguration.getLocales().equals(overrideLocales)) {
717             return false;
718         }
719         requestsTmpConfig.setLocales(overrideLocales);
720         requestsTmpConfig.userSetLocale = true;
721         return true;
722     }
723 
724     /**
725      * Overrides the gender to this ConfigurationContainer.
726      *
727      * @return true if the grammatical gender has been changed.
728      */
setOverrideGender(Configuration requestsTmpConfig, @Configuration.GrammaticalGender int gender)729     protected boolean setOverrideGender(Configuration requestsTmpConfig,
730             @Configuration.GrammaticalGender int gender) {
731         // Noop, only ActivityRecord and WindowProcessController have enough knowledge about the
732         // app to apply gender correctly.
733         return false;
734     }
735 
isActivityTypeDream()736     public boolean isActivityTypeDream() {
737         return getActivityType() == ACTIVITY_TYPE_DREAM;
738     }
739 
isActivityTypeStandard()740     public boolean isActivityTypeStandard() {
741         return getActivityType() == ACTIVITY_TYPE_STANDARD;
742     }
743 
isActivityTypeStandardOrUndefined()744     public boolean isActivityTypeStandardOrUndefined() {
745         /*@WindowConfiguration.ActivityType*/ final int activityType = getActivityType();
746         return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED;
747     }
748 
isCompatibleActivityType(int currentType, int otherType)749     public static boolean isCompatibleActivityType(int currentType, int otherType) {
750         if (currentType == otherType) {
751             return true;
752         }
753         if (currentType == ACTIVITY_TYPE_ASSISTANT) {
754             // Assistant activities are only compatible with themselves...
755             return false;
756         }
757         // Otherwise we are compatible if us or other is not currently defined.
758         return currentType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
759     }
760 
761     /**
762      * Returns true if this container is compatible with the input windowing mode and activity type.
763      * The container is compatible:
764      * - If {@param activityType} and {@param windowingMode} match this container activity type and
765      * windowing mode.
766      * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
767      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
768      * standard or undefined and its windowing mode matches {@param windowingMode}.
769      * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
770      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
771      * also standard or undefined and its activity type matches {@param activityType} regardless of
772      * if {@param windowingMode} matches the containers windowing mode.
773      */
isCompatible(int windowingMode, int activityType)774     public boolean isCompatible(int windowingMode, int activityType) {
775         final int thisActivityType = getActivityType();
776         final int thisWindowingMode = getWindowingMode();
777         final boolean sameActivityType = thisActivityType == activityType;
778         final boolean sameWindowingMode = thisWindowingMode == windowingMode;
779 
780         if (sameActivityType && sameWindowingMode) {
781             return true;
782         }
783 
784         if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
785                 || !isActivityTypeStandardOrUndefined()) {
786             // Only activity type need to match for non-standard activity types that are defined.
787             return sameActivityType;
788         }
789 
790         // Otherwise we are compatible if the windowing mode is the same.
791         return sameWindowingMode;
792     }
793 
registerConfigurationChangeListener(ConfigurationContainerListener listener)794     void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
795         registerConfigurationChangeListener(listener, true /* shouldDispatchConfig */);
796     }
797 
registerConfigurationChangeListener(ConfigurationContainerListener listener, boolean shouldDispatchConfig)798     void registerConfigurationChangeListener(ConfigurationContainerListener listener,
799             boolean shouldDispatchConfig) {
800         if (mChangeListeners.contains(listener)) {
801             return;
802         }
803         mChangeListeners.add(listener);
804         if (shouldDispatchConfig) {
805             listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
806             listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
807         }
808     }
809 
unregisterConfigurationChangeListener(ConfigurationContainerListener listener)810     void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
811         mChangeListeners.remove(listener);
812     }
813 
814     @VisibleForTesting
containsListener(ConfigurationContainerListener listener)815     boolean containsListener(ConfigurationContainerListener listener) {
816         return mChangeListeners.contains(listener);
817     }
818 
819     /**
820      * Must be called when new parent for the container was set.
821      */
onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent)822     void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
823         // Removing parent usually means that we've detached this entity to destroy it or to attach
824         // to another parent. In both cases we don't need to update the configuration now.
825         if (newParent != null) {
826             // Update full configuration of this container and all its children.
827             onConfigurationChanged(newParent.mFullConfiguration);
828         }
829     }
830 
831     /**
832      * Write to a protocol buffer output stream. Protocol buffer message definition is at
833      * {@link com.android.server.wm.ConfigurationContainerProto}.
834      *
835      * @param proto    Stream to write the ConfigurationContainer object to.
836      * @param fieldId  Field Id of the ConfigurationContainer as defined in the parent
837      *                 message.
838      * @param logLevel Determines the amount of data to be written to the Protobuf.
839      * @hide
840      */
841     @CallSuper
dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTracingLogLevel int logLevel)842     protected void dumpDebug(ProtoOutputStream proto, long fieldId,
843             @WindowTracingLogLevel int logLevel) {
844         final long token = proto.start(fieldId);
845 
846         if (logLevel == WindowTracingLogLevel.ALL || mHasOverrideConfiguration) {
847             mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
848                     logLevel == WindowTracingLogLevel.CRITICAL);
849         }
850 
851         // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
852         // required to mitigate performance overhead
853         if (logLevel == WindowTracingLogLevel.ALL) {
854             mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
855             mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
856                     false /* critical */);
857         }
858 
859         if (logLevel == WindowTracingLogLevel.TRIM) {
860             // Required for Fass to automatically detect pip transitions in Winscope traces
861             dumpDebugWindowingMode(proto);
862         }
863 
864         proto.end(token);
865     }
866 
dumpDebugWindowingMode(ProtoOutputStream proto)867     private void dumpDebugWindowingMode(ProtoOutputStream proto) {
868         final long fullConfigToken = proto.start(FULL_CONFIGURATION);
869         final long windowConfigToken = proto.start(WINDOW_CONFIGURATION);
870 
871         int windowingMode = mFullConfiguration.windowConfiguration.getWindowingMode();
872         proto.write(WINDOWING_MODE, windowingMode);
873 
874         proto.end(windowConfigToken);
875         proto.end(fullConfigToken);
876     }
877 
878     /**
879      * Dumps the names of this container children in the input print writer indenting each
880      * level with the input prefix.
881      */
dumpChildrenNames(PrintWriter pw, String prefix)882     public void dumpChildrenNames(PrintWriter pw, String prefix) {
883         dumpChildrenNames(pw, prefix, true /* isLastChild */);
884     }
885 
886     /**
887      * Dumps the names of this container children in the input print writer indenting each
888      * level with the input prefix.
889      */
dumpChildrenNames(PrintWriter pw, String prefix, boolean isLastChild)890     public void dumpChildrenNames(PrintWriter pw, String prefix, boolean isLastChild) {
891         int curWinMode = getWindowingMode();
892         String winMode = windowingModeToString(curWinMode);
893         if (curWinMode != WINDOWING_MODE_UNDEFINED &&
894                 curWinMode != WINDOWING_MODE_FULLSCREEN) {
895             winMode = winMode.toUpperCase();
896         }
897         int requestedWinMode = getRequestedOverrideWindowingMode();
898         String overrideWinMode = windowingModeToString(requestedWinMode);
899         if (requestedWinMode != WINDOWING_MODE_UNDEFINED &&
900                 requestedWinMode != WINDOWING_MODE_FULLSCREEN) {
901             overrideWinMode = overrideWinMode.toUpperCase();
902         }
903         String actType = activityTypeToString(getActivityType());
904         if (getActivityType() != ACTIVITY_TYPE_UNDEFINED
905                 && getActivityType() != ACTIVITY_TYPE_STANDARD) {
906             actType = actType.toUpperCase();
907         }
908         pw.print(prefix + (isLastChild ? "└─ " : "├─ "));
909         pw.println(getName()
910                 + " type=" + actType
911                 + " mode=" + winMode
912                 + " override-mode=" + overrideWinMode
913                 + " requested-bounds=" + getRequestedOverrideBounds().toShortString()
914                 + " bounds=" + getBounds().toShortString());
915 
916         String childPrefix = prefix + (isLastChild ? "   " : "│  ");
917         for (int i = getChildCount() - 1; i >= 0; --i) {
918             final E cc = getChildAt(i);
919             cc.dumpChildrenNames(pw, childPrefix, i == 0 /* isLastChild */);
920         }
921     }
922 
getName()923     String getName() {
924         return toString();
925     }
926 
isAlwaysOnTop()927     public boolean isAlwaysOnTop() {
928         return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
929     }
930 
hasChild()931     boolean hasChild() {
932         return getChildCount() > 0;
933     }
934 
getChildCount()935     abstract protected int getChildCount();
936 
getChildAt(int index)937     abstract protected E getChildAt(int index);
938 
getParent()939     abstract protected ConfigurationContainer getParent();
940 
941 }
942