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)539 public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales) { 540 mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); 541 boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig, 542 nightMode); 543 boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig, 544 locales); 545 if (newNightModeSet || newLocalesSet) { 546 onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); 547 } 548 return newNightModeSet || newLocalesSet; 549 } 550 551 /** 552 * Overrides the night mode applied to this ConfigurationContainer. 553 * @return true if the nightMode has been changed. 554 */ setOverrideNightMode(Configuration requestsTmpConfig, int nightMode)555 private boolean setOverrideNightMode(Configuration requestsTmpConfig, int nightMode) { 556 final int currentUiMode = mRequestedOverrideConfiguration.uiMode; 557 final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK; 558 final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK; 559 if (currentNightMode == validNightMode) { 560 return false; 561 } 562 requestsTmpConfig.uiMode = validNightMode 563 | (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK); 564 return true; 565 } 566 567 /** 568 * Overrides the locales applied to this ConfigurationContainer. 569 * @return true if the LocaleList has been changed. 570 */ setOverrideLocales(Configuration requestsTmpConfig, @NonNull LocaleList overrideLocales)571 private boolean setOverrideLocales(Configuration requestsTmpConfig, 572 @NonNull LocaleList overrideLocales) { 573 if (mRequestedOverrideConfiguration.getLocales().equals(overrideLocales)) { 574 return false; 575 } 576 requestsTmpConfig.setLocales(overrideLocales); 577 requestsTmpConfig.userSetLocale = true; 578 return true; 579 } 580 isActivityTypeDream()581 public boolean isActivityTypeDream() { 582 return getActivityType() == ACTIVITY_TYPE_DREAM; 583 } 584 isActivityTypeStandard()585 public boolean isActivityTypeStandard() { 586 return getActivityType() == ACTIVITY_TYPE_STANDARD; 587 } 588 isActivityTypeStandardOrUndefined()589 public boolean isActivityTypeStandardOrUndefined() { 590 /*@WindowConfiguration.ActivityType*/ final int activityType = getActivityType(); 591 return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED; 592 } 593 isCompatibleActivityType(int currentType, int otherType)594 public static boolean isCompatibleActivityType(int currentType, int otherType) { 595 if (currentType == otherType) { 596 return true; 597 } 598 if (currentType == ACTIVITY_TYPE_ASSISTANT) { 599 // Assistant activities are only compatible with themselves... 600 return false; 601 } 602 // Otherwise we are compatible if us or other is not currently defined. 603 return currentType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED; 604 } 605 606 /** 607 * Returns true if this container is compatible with the input windowing mode and activity type. 608 * The container is compatible: 609 * - If {@param activityType} and {@param windowingMode} match this container activity type and 610 * windowing mode. 611 * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or 612 * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also 613 * standard or undefined and its windowing mode matches {@param windowingMode}. 614 * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or 615 * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't 616 * also standard or undefined and its activity type matches {@param activityType} regardless of 617 * if {@param windowingMode} matches the containers windowing mode. 618 */ isCompatible(int windowingMode, int activityType)619 public boolean isCompatible(int windowingMode, int activityType) { 620 final int thisActivityType = getActivityType(); 621 final int thisWindowingMode = getWindowingMode(); 622 final boolean sameActivityType = thisActivityType == activityType; 623 final boolean sameWindowingMode = thisWindowingMode == windowingMode; 624 625 if (sameActivityType && sameWindowingMode) { 626 return true; 627 } 628 629 if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD) 630 || !isActivityTypeStandardOrUndefined()) { 631 // Only activity type need to match for non-standard activity types that are defined. 632 return sameActivityType; 633 } 634 635 // Otherwise we are compatible if the windowing mode is the same. 636 return sameWindowingMode; 637 } 638 registerConfigurationChangeListener(ConfigurationContainerListener listener)639 void registerConfigurationChangeListener(ConfigurationContainerListener listener) { 640 registerConfigurationChangeListener(listener, true /* shouldDispatchConfig */); 641 } 642 registerConfigurationChangeListener(ConfigurationContainerListener listener, boolean shouldDispatchConfig)643 void registerConfigurationChangeListener(ConfigurationContainerListener listener, 644 boolean shouldDispatchConfig) { 645 if (mChangeListeners.contains(listener)) { 646 return; 647 } 648 mChangeListeners.add(listener); 649 if (shouldDispatchConfig) { 650 listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration); 651 listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration); 652 } 653 } 654 unregisterConfigurationChangeListener(ConfigurationContainerListener listener)655 void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) { 656 mChangeListeners.remove(listener); 657 } 658 659 @VisibleForTesting containsListener(ConfigurationContainerListener listener)660 boolean containsListener(ConfigurationContainerListener listener) { 661 return mChangeListeners.contains(listener); 662 } 663 664 /** 665 * Must be called when new parent for the container was set. 666 */ onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent)667 void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { 668 // Removing parent usually means that we've detached this entity to destroy it or to attach 669 // to another parent. In both cases we don't need to update the configuration now. 670 if (newParent != null) { 671 // Update full configuration of this container and all its children. 672 onConfigurationChanged(newParent.mFullConfiguration); 673 // Update merged override configuration of this container and all its children. 674 onMergedOverrideConfigurationChanged(); 675 } 676 } 677 678 /** 679 * Write to a protocol buffer output stream. Protocol buffer message definition is at 680 * {@link com.android.server.wm.ConfigurationContainerProto}. 681 * 682 * @param proto Stream to write the ConfigurationContainer object to. 683 * @param fieldId Field Id of the ConfigurationContainer as defined in the parent 684 * message. 685 * @param logLevel Determines the amount of data to be written to the Protobuf. 686 * @hide 687 */ 688 @CallSuper dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)689 protected void dumpDebug(ProtoOutputStream proto, long fieldId, 690 @WindowTraceLogLevel int logLevel) { 691 final long token = proto.start(fieldId); 692 693 if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) { 694 mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION, 695 logLevel == WindowTraceLogLevel.CRITICAL); 696 } 697 698 // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't 699 // required to mitigate performance overhead 700 if (logLevel == WindowTraceLogLevel.ALL) { 701 mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */); 702 mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION, 703 false /* critical */); 704 } 705 706 if (logLevel == WindowTraceLogLevel.TRIM) { 707 // Required for Fass to automatically detect pip transitions in Winscope traces 708 dumpDebugWindowingMode(proto); 709 } 710 711 proto.end(token); 712 } 713 dumpDebugWindowingMode(ProtoOutputStream proto)714 private void dumpDebugWindowingMode(ProtoOutputStream proto) { 715 final long fullConfigToken = proto.start(FULL_CONFIGURATION); 716 final long windowConfigToken = proto.start(WINDOW_CONFIGURATION); 717 718 int windowingMode = mFullConfiguration.windowConfiguration.getWindowingMode(); 719 proto.write(WINDOWING_MODE, windowingMode); 720 721 proto.end(windowConfigToken); 722 proto.end(fullConfigToken); 723 } 724 725 /** 726 * Dumps the names of this container children in the input print writer indenting each 727 * level with the input prefix. 728 */ dumpChildrenNames(PrintWriter pw, String prefix)729 public void dumpChildrenNames(PrintWriter pw, String prefix) { 730 final String childPrefix = prefix + " "; 731 pw.println(getName() 732 + " type=" + activityTypeToString(getActivityType()) 733 + " mode=" + windowingModeToString(getWindowingMode()) 734 + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode()) 735 + " requested-bounds=" + getRequestedOverrideBounds().toShortString() 736 + " bounds=" + getBounds().toShortString()); 737 for (int i = getChildCount() - 1; i >= 0; --i) { 738 final E cc = getChildAt(i); 739 pw.print(childPrefix + "#" + i + " "); 740 cc.dumpChildrenNames(pw, childPrefix); 741 } 742 } 743 getName()744 String getName() { 745 return toString(); 746 } 747 isAlwaysOnTop()748 public boolean isAlwaysOnTop() { 749 return mFullConfiguration.windowConfiguration.isAlwaysOnTop(); 750 } 751 hasChild()752 boolean hasChild() { 753 return getChildCount() > 0; 754 } 755 getChildCount()756 abstract protected int getChildCount(); 757 getChildAt(int index)758 abstract protected E getChildAt(int index); 759 getParent()760 abstract protected ConfigurationContainer getParent(); 761 762 } 763