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