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