1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.wm; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 26 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 27 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 28 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; 29 import static android.app.WindowConfiguration.activityTypeToString; 30 import static android.app.WindowConfiguration.windowingModeToString; 31 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.app.WindowConfiguration; 38 import android.content.res.Configuration; 39 import android.graphics.Point; 40 import android.graphics.Rect; 41 import android.util.proto.ProtoOutputStream; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 45 import java.io.PrintWriter; 46 import java.util.ArrayList; 47 48 /** 49 * Contains common logic for classes that have override configurations and are organized in a 50 * hierarchy. 51 */ 52 public abstract class ConfigurationContainer<E extends ConfigurationContainer> { 53 /** 54 * {@link #Rect} returned from {@link #getRequestedOverrideBounds()} to prevent original value 55 * from being set directly. 56 */ 57 private Rect mReturnBounds = new Rect(); 58 59 /** 60 * Contains requested override configuration settings applied to this configuration container. 61 */ 62 private Configuration mRequestedOverrideConfiguration = new Configuration(); 63 64 /** 65 * Contains the requested override configuration with parent and policy constraints applied. 66 * This is the set of overrides that gets applied to the full and merged configurations. 67 */ 68 private Configuration mResolvedOverrideConfiguration = new Configuration(); 69 70 /** True if mRequestedOverrideConfiguration is not empty */ 71 private boolean mHasOverrideConfiguration; 72 73 /** 74 * Contains full configuration applied to this configuration container. Corresponds to full 75 * parent's config with applied {@link #mResolvedOverrideConfiguration}. 76 */ 77 private Configuration mFullConfiguration = new Configuration(); 78 79 /** 80 * Contains merged override configuration settings from the top of the hierarchy down to this 81 * particular instance. It is different from {@link #mFullConfiguration} because it starts from 82 * topmost container's override config instead of global config. 83 */ 84 private Configuration mMergedOverrideConfiguration = new Configuration(); 85 86 private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>(); 87 88 // TODO: Can't have ag/2592611 soon enough! 89 private final Configuration mRequestsTmpConfig = new Configuration(); 90 private final Configuration mResolvedTmpConfig = new Configuration(); 91 92 // Used for setting bounds 93 private final Rect mTmpRect = new Rect(); 94 95 static final int BOUNDS_CHANGE_NONE = 0; 96 // Return value from {@link setBounds} indicating the position of the override bounds changed. 97 static final int BOUNDS_CHANGE_POSITION = 1; 98 // Return value from {@link setBounds} indicating the size of the override bounds changed. 99 static final int BOUNDS_CHANGE_SIZE = 1 << 1; 100 101 /** 102 * Returns full configuration applied to this configuration container. 103 * This method should be used for getting settings applied in each particular level of the 104 * hierarchy. 105 */ getConfiguration()106 public Configuration getConfiguration() { 107 return mFullConfiguration; 108 } 109 110 /** 111 * Notify that parent config changed and we need to update full configuration. 112 * @see #mFullConfiguration 113 */ onConfigurationChanged(Configuration newParentConfig)114 public void onConfigurationChanged(Configuration newParentConfig) { 115 mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration); 116 resolveOverrideConfiguration(newParentConfig); 117 mFullConfiguration.setTo(newParentConfig); 118 mFullConfiguration.updateFrom(mResolvedOverrideConfiguration); 119 onMergedOverrideConfigurationChanged(); 120 if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) { 121 // This depends on the assumption that change-listeners don't do 122 // their own override resolution. This way, dependent hierarchies 123 // can stay properly synced-up with a primary hierarchy's constraints. 124 // Since the hierarchies will be merged, this whole thing will go away 125 // before the assumption will be broken. 126 // Inform listeners of the change. 127 for (int i = mChangeListeners.size() - 1; i >= 0; --i) { 128 mChangeListeners.get(i).onRequestedOverrideConfigurationChanged( 129 mResolvedOverrideConfiguration); 130 } 131 } 132 for (int i = mChangeListeners.size() - 1; i >= 0; --i) { 133 mChangeListeners.get(i).onMergedOverrideConfigurationChanged( 134 mMergedOverrideConfiguration); 135 } 136 for (int i = getChildCount() - 1; i >= 0; --i) { 137 final ConfigurationContainer child = getChildAt(i); 138 child.onConfigurationChanged(mFullConfiguration); 139 } 140 } 141 142 /** 143 * Resolves the current requested override configuration into 144 * {@link #mResolvedOverrideConfiguration} 145 * 146 * @param newParentConfig The new parent configuration to resolve overrides against. 147 */ resolveOverrideConfiguration(Configuration newParentConfig)148 void resolveOverrideConfiguration(Configuration newParentConfig) { 149 mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration); 150 } 151 152 /** Returns requested override configuration applied to this configuration container. */ getRequestedOverrideConfiguration()153 public Configuration getRequestedOverrideConfiguration() { 154 return mRequestedOverrideConfiguration; 155 } 156 157 /** Returns the resolved override configuration. */ getResolvedOverrideConfiguration()158 Configuration getResolvedOverrideConfiguration() { 159 return mResolvedOverrideConfiguration; 160 } 161 162 /** 163 * Update override configuration and recalculate full config. 164 * @see #mRequestedOverrideConfiguration 165 * @see #mFullConfiguration 166 */ onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration)167 public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { 168 // Pre-compute this here, so we don't need to go through the entire Configuration when 169 // writing to proto (which has significant cost if we write a lot of empty configurations). 170 mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration); 171 mRequestedOverrideConfiguration.setTo(overrideConfiguration); 172 // Update full configuration of this container and all its children. 173 final ConfigurationContainer parent = getParent(); 174 onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); 175 } 176 177 /** 178 * Get merged override configuration from the top of the hierarchy down to this particular 179 * instance. This should be reported to client as override config. 180 */ getMergedOverrideConfiguration()181 public Configuration getMergedOverrideConfiguration() { 182 return mMergedOverrideConfiguration; 183 } 184 185 /** 186 * Update merged override configuration based on corresponding parent's config and notify all 187 * its children. If there is no parent, merged override configuration will set equal to current 188 * override config. 189 * @see #mMergedOverrideConfiguration 190 */ onMergedOverrideConfigurationChanged()191 void onMergedOverrideConfigurationChanged() { 192 final ConfigurationContainer parent = getParent(); 193 if (parent != null) { 194 mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration()); 195 mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration); 196 } else { 197 mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration); 198 } 199 for (int i = getChildCount() - 1; i >= 0; --i) { 200 final ConfigurationContainer child = getChildAt(i); 201 child.onMergedOverrideConfigurationChanged(); 202 } 203 } 204 205 /** 206 * Indicates whether this container has not requested any bounds different from its parent. In 207 * this case, it will inherit the bounds of the first ancestor which specifies a bounds subject 208 * to policy constraints. 209 * 210 * @return {@code true} if no explicit bounds have been requested at this container level. 211 * {@code false} otherwise. 212 */ matchParentBounds()213 public boolean matchParentBounds() { 214 return getRequestedOverrideBounds().isEmpty(); 215 } 216 217 /** 218 * Returns whether the bounds specified are considered the same as the existing requested 219 * override bounds. This is either when the two bounds are equal or the requested override 220 * bounds are empty and the specified bounds is null. 221 * 222 * @return {@code true} if the bounds are equivalent, {@code false} otherwise 223 */ equivalentRequestedOverrideBounds(Rect bounds)224 public boolean equivalentRequestedOverrideBounds(Rect bounds) { 225 return equivalentBounds(getRequestedOverrideBounds(), bounds); 226 } 227 228 /** 229 * Returns whether the two bounds are equal to each other or are a combination of null or empty. 230 */ equivalentBounds(Rect bounds, Rect other)231 public static boolean equivalentBounds(Rect bounds, Rect other) { 232 return bounds == other 233 || (bounds != null && (bounds.equals(other) || (bounds.isEmpty() && other == null))) 234 || (other != null && other.isEmpty() && bounds == null); 235 } 236 237 /** 238 * Returns the effective bounds of this container, inheriting the first non-empty bounds set in 239 * its ancestral hierarchy, including itself. 240 * @return 241 */ getBounds()242 public Rect getBounds() { 243 mReturnBounds.set(getConfiguration().windowConfiguration.getBounds()); 244 return mReturnBounds; 245 } 246 getBounds(Rect outBounds)247 public void getBounds(Rect outBounds) { 248 outBounds.set(getBounds()); 249 } 250 251 /** 252 * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}. 253 */ getPosition(Point out)254 public void getPosition(Point out) { 255 Rect bounds = getBounds(); 256 out.set(bounds.left, bounds.top); 257 } 258 getResolvedOverrideBounds()259 Rect getResolvedOverrideBounds() { 260 mReturnBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); 261 return mReturnBounds; 262 } 263 264 /** 265 * Returns the bounds requested on this container. These may not be the actual bounds the 266 * container ends up with due to policy constraints. The {@link Rect} handed back is 267 * shared for all calls to this method and should not be modified. 268 */ getRequestedOverrideBounds()269 public Rect getRequestedOverrideBounds() { 270 mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getBounds()); 271 272 return mReturnBounds; 273 } 274 275 /** 276 * Returns {@code true} if the {@link WindowConfiguration} in the requested override 277 * {@link Configuration} specifies bounds. 278 */ hasOverrideBounds()279 public boolean hasOverrideBounds() { 280 return !getRequestedOverrideBounds().isEmpty(); 281 } 282 283 /** 284 * Sets the passed in {@link Rect} to the current bounds. 285 * @see {@link #getRequestedOverrideBounds()}. 286 */ getRequestedOverrideBounds(Rect outBounds)287 public void getRequestedOverrideBounds(Rect outBounds) { 288 outBounds.set(getRequestedOverrideBounds()); 289 } 290 291 /** 292 * Sets the bounds at the current hierarchy level, overriding any bounds set on an ancestor. 293 * This value will be reported when {@link #getBounds()} and 294 * {@link #getRequestedOverrideBounds()}. If 295 * an empty {@link Rect} or null is specified, this container will be considered to match its 296 * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent. 297 * @param bounds The bounds defining the container size. 298 * @return a bitmask representing the types of changes made to the bounds. 299 */ setBounds(Rect bounds)300 public int setBounds(Rect bounds) { 301 int boundsChange = diffRequestedOverrideBounds(bounds); 302 303 if (boundsChange == BOUNDS_CHANGE_NONE) { 304 return boundsChange; 305 } 306 307 308 mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); 309 mRequestsTmpConfig.windowConfiguration.setBounds(bounds); 310 onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); 311 312 return boundsChange; 313 } 314 setBounds(int left, int top, int right, int bottom)315 public int setBounds(int left, int top, int right, int bottom) { 316 mTmpRect.set(left, top, right, bottom); 317 return setBounds(mTmpRect); 318 } 319 diffRequestedOverrideBounds(Rect bounds)320 int diffRequestedOverrideBounds(Rect bounds) { 321 if (equivalentRequestedOverrideBounds(bounds)) { 322 return BOUNDS_CHANGE_NONE; 323 } 324 325 int boundsChange = BOUNDS_CHANGE_NONE; 326 327 final Rect existingBounds = getRequestedOverrideBounds(); 328 329 if (bounds == null || existingBounds.left != bounds.left 330 || existingBounds.top != bounds.top) { 331 boundsChange |= BOUNDS_CHANGE_POSITION; 332 } 333 334 if (bounds == null || existingBounds.width() != bounds.width() 335 || existingBounds.height() != bounds.height()) { 336 boundsChange |= BOUNDS_CHANGE_SIZE; 337 } 338 339 return boundsChange; 340 } 341 hasOverrideConfiguration()342 boolean hasOverrideConfiguration() { 343 return mHasOverrideConfiguration; 344 } 345 getWindowConfiguration()346 public WindowConfiguration getWindowConfiguration() { 347 return mFullConfiguration.windowConfiguration; 348 } 349 350 /** Returns the windowing mode the configuration container is currently in. */ getWindowingMode()351 public int getWindowingMode() { 352 return mFullConfiguration.windowConfiguration.getWindowingMode(); 353 } 354 355 /** Returns the windowing mode override that is requested by this container. */ getRequestedOverrideWindowingMode()356 public int getRequestedOverrideWindowingMode() { 357 return mRequestedOverrideConfiguration.windowConfiguration.getWindowingMode(); 358 } 359 360 /** Sets the requested windowing mode override for the configuration container. */ setWindowingMode( int windowingMode)361 public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) { 362 mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); 363 mRequestsTmpConfig.windowConfiguration.setWindowingMode(windowingMode); 364 onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); 365 } 366 367 /** Sets the always on top flag for this configuration container. 368 * When you call this function, make sure that the following functions are called as well to 369 * keep proper z-order. 370 * - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)}; 371 * */ setAlwaysOnTop(boolean alwaysOnTop)372 public void setAlwaysOnTop(boolean alwaysOnTop) { 373 mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); 374 mRequestsTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop); 375 onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); 376 } 377 378 /** Sets the windowing mode for the configuration container. */ setDisplayWindowingMode(int windowingMode)379 void setDisplayWindowingMode(int windowingMode) { 380 mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); 381 mRequestsTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode); 382 onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); 383 } 384 385 /** 386 * Returns true if this container is currently in multi-window mode. I.e. sharing the screen 387 * with another activity. 388 */ inMultiWindowMode()389 public boolean inMultiWindowMode() { 390 /*@WindowConfiguration.WindowingMode*/ int windowingMode = 391 mFullConfiguration.windowConfiguration.getWindowingMode(); 392 return WindowConfiguration.inMultiWindowMode(windowingMode); 393 } 394 395 /** Returns true if this container is currently in split-screen windowing mode. */ inSplitScreenWindowingMode()396 public boolean inSplitScreenWindowingMode() { 397 /*@WindowConfiguration.WindowingMode*/ int windowingMode = 398 mFullConfiguration.windowConfiguration.getWindowingMode(); 399 400 return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY 401 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; 402 } 403 404 /** Returns true if this container is currently in split-screen secondary windowing mode. */ inSplitScreenSecondaryWindowingMode()405 public boolean inSplitScreenSecondaryWindowingMode() { 406 /*@WindowConfiguration.WindowingMode*/ int windowingMode = 407 mFullConfiguration.windowConfiguration.getWindowingMode(); 408 409 return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; 410 } 411 inSplitScreenPrimaryWindowingMode()412 public boolean inSplitScreenPrimaryWindowingMode() { 413 return mFullConfiguration.windowConfiguration.getWindowingMode() 414 == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 415 } 416 417 /** 418 * Returns true if this container can be put in either 419 * {@link WindowConfiguration#WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or 420 * {@link WindowConfiguration##WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on 421 * its current state. 422 */ supportsSplitScreenWindowingMode()423 public boolean supportsSplitScreenWindowingMode() { 424 return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode(); 425 } 426 inPinnedWindowingMode()427 public boolean inPinnedWindowingMode() { 428 return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; 429 } 430 inFreeformWindowingMode()431 public boolean inFreeformWindowingMode() { 432 return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM; 433 } 434 435 /** Returns the activity type associated with the the configuration container. */ 436 /*@WindowConfiguration.ActivityType*/ getActivityType()437 public int getActivityType() { 438 return mFullConfiguration.windowConfiguration.getActivityType(); 439 } 440 441 /** Sets the activity type to associate with the configuration container. */ setActivityType( int activityType)442 public void setActivityType(/*@WindowConfiguration.ActivityType*/ int activityType) { 443 int currentActivityType = getActivityType(); 444 if (currentActivityType == activityType) { 445 return; 446 } 447 if (currentActivityType != ACTIVITY_TYPE_UNDEFINED) { 448 throw new IllegalStateException("Can't change activity type once set: " + this 449 + " activityType=" + activityTypeToString(activityType)); 450 } 451 mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); 452 mRequestsTmpConfig.windowConfiguration.setActivityType(activityType); 453 onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); 454 } 455 isActivityTypeHome()456 public boolean isActivityTypeHome() { 457 return getActivityType() == ACTIVITY_TYPE_HOME; 458 } 459 isActivityTypeRecents()460 public boolean isActivityTypeRecents() { 461 return getActivityType() == ACTIVITY_TYPE_RECENTS; 462 } 463 isActivityTypeAssistant()464 public boolean isActivityTypeAssistant() { 465 return getActivityType() == ACTIVITY_TYPE_ASSISTANT; 466 } 467 isActivityTypeDream()468 public boolean isActivityTypeDream() { 469 return getActivityType() == ACTIVITY_TYPE_DREAM; 470 } 471 isActivityTypeStandard()472 public boolean isActivityTypeStandard() { 473 return getActivityType() == ACTIVITY_TYPE_STANDARD; 474 } 475 isActivityTypeStandardOrUndefined()476 public boolean isActivityTypeStandardOrUndefined() { 477 /*@WindowConfiguration.ActivityType*/ final int activityType = getActivityType(); 478 return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED; 479 } 480 hasCompatibleActivityType(ConfigurationContainer other)481 public boolean hasCompatibleActivityType(ConfigurationContainer other) { 482 /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType(); 483 /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType(); 484 485 if (thisType == otherType) { 486 return true; 487 } 488 if (thisType == ACTIVITY_TYPE_ASSISTANT) { 489 // Assistant activities are only compatible with themselves... 490 return false; 491 } 492 // Otherwise we are compatible if us or other is not currently defined. 493 return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED; 494 } 495 496 /** 497 * Returns true if this container is compatible with the input windowing mode and activity type. 498 * The container is compatible: 499 * - If {@param activityType} and {@param windowingMode} match this container activity type and 500 * windowing mode. 501 * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or 502 * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also 503 * standard or undefined and its windowing mode matches {@param windowingMode}. 504 * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or 505 * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't 506 * also standard or undefined and its activity type matches {@param activityType} regardless of 507 * if {@param windowingMode} matches the containers windowing mode. 508 */ isCompatible(int windowingMode, int activityType)509 public boolean isCompatible(int windowingMode, int activityType) { 510 final int thisActivityType = getActivityType(); 511 final int thisWindowingMode = getWindowingMode(); 512 final boolean sameActivityType = thisActivityType == activityType; 513 final boolean sameWindowingMode = thisWindowingMode == windowingMode; 514 515 if (sameActivityType && sameWindowingMode) { 516 return true; 517 } 518 519 if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD) 520 || !isActivityTypeStandardOrUndefined()) { 521 // Only activity type need to match for non-standard activity types that are defined. 522 return sameActivityType; 523 } 524 525 // Otherwise we are compatible if the windowing mode is the same. 526 return sameWindowingMode; 527 } 528 registerConfigurationChangeListener(ConfigurationContainerListener listener)529 void registerConfigurationChangeListener(ConfigurationContainerListener listener) { 530 if (mChangeListeners.contains(listener)) { 531 return; 532 } 533 mChangeListeners.add(listener); 534 listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration); 535 listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration); 536 } 537 unregisterConfigurationChangeListener(ConfigurationContainerListener listener)538 void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) { 539 mChangeListeners.remove(listener); 540 } 541 542 @VisibleForTesting containsListener(ConfigurationContainerListener listener)543 boolean containsListener(ConfigurationContainerListener listener) { 544 return mChangeListeners.contains(listener); 545 } 546 547 /** 548 * Must be called when new parent for the container was set. 549 */ onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent)550 void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { 551 // Removing parent usually means that we've detached this entity to destroy it or to attach 552 // to another parent. In both cases we don't need to update the configuration now. 553 if (newParent != null) { 554 // Update full configuration of this container and all its children. 555 onConfigurationChanged(newParent.mFullConfiguration); 556 // Update merged override configuration of this container and all its children. 557 onMergedOverrideConfigurationChanged(); 558 } 559 } 560 561 /** 562 * Write to a protocol buffer output stream. Protocol buffer message definition is at 563 * {@link com.android.server.wm.ConfigurationContainerProto}. 564 * 565 * @param proto Stream to write the ConfigurationContainer object to. 566 * @param fieldId Field Id of the ConfigurationContainer as defined in the parent 567 * message. 568 * @param logLevel Determines the amount of data to be written to the Protobuf. 569 * @hide 570 */ 571 @CallSuper dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)572 protected void dumpDebug(ProtoOutputStream proto, long fieldId, 573 @WindowTraceLogLevel int logLevel) { 574 // Critical log level logs only visible elements to mitigate performance overheard 575 if (logLevel != WindowTraceLogLevel.ALL && !mHasOverrideConfiguration) { 576 return; 577 } 578 579 final long token = proto.start(fieldId); 580 mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION, 581 logLevel == WindowTraceLogLevel.CRITICAL); 582 if (logLevel == WindowTraceLogLevel.ALL) { 583 mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */); 584 mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION, 585 false /* critical */); 586 } 587 proto.end(token); 588 } 589 590 /** 591 * Dumps the names of this container children in the input print writer indenting each 592 * level with the input prefix. 593 */ dumpChildrenNames(PrintWriter pw, String prefix)594 public void dumpChildrenNames(PrintWriter pw, String prefix) { 595 final String childPrefix = prefix + " "; 596 pw.println(getName() 597 + " type=" + activityTypeToString(getActivityType()) 598 + " mode=" + windowingModeToString(getWindowingMode()) 599 + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode())); 600 for (int i = getChildCount() - 1; i >= 0; --i) { 601 final E cc = getChildAt(i); 602 pw.print(childPrefix + "#" + i + " "); 603 cc.dumpChildrenNames(pw, childPrefix); 604 } 605 } 606 getName()607 String getName() { 608 return toString(); 609 } 610 isAlwaysOnTop()611 public boolean isAlwaysOnTop() { 612 return mFullConfiguration.windowConfiguration.isAlwaysOnTop(); 613 } 614 hasChild()615 boolean hasChild() { 616 return getChildCount() > 0; 617 } 618 getChildCount()619 abstract protected int getChildCount(); 620 getChildAt(int index)621 abstract protected E getChildAt(int index); 622 getParent()623 abstract protected ConfigurationContainer getParent(); 624 625 } 626