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