WindowContainer.java revision 441e4494682144aec2ec7f19060464af3d29c319
1/* 2 * Copyright (C) 2016 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 17package com.android.server.wm; 18 19import android.annotation.CallSuper; 20import android.content.res.Configuration; 21import android.view.animation.Animation; 22 23import java.util.Comparator; 24import java.util.LinkedList; 25 26import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; 27import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 28import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 29 30/** 31 * Defines common functionality for classes that can hold windows directly or through their 32 * children in a hierarchy form. 33 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime 34 * changes are made to this class. 35 */ 36class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> { 37 38 /** 39 * The parent of this window container. 40 * For removing or setting new parent {@link #setParent} should be used, because it also 41 * performs configuration updates based on new parent's settings. 42 */ 43 private WindowContainer mParent = null; 44 45 // List of children for this window container. List is in z-order as the children appear on 46 // screen with the top-most window container at the tail of the list. 47 protected final LinkedList<E> mChildren = new LinkedList(); 48 49 /** Contains override configuration settings applied to this window container. */ 50 private Configuration mOverrideConfiguration = new Configuration(); 51 52 /** 53 * Contains full configuration applied to this window container. Corresponds to full parent's 54 * config with applied {@link #mOverrideConfiguration}. 55 */ 56 private Configuration mFullConfiguration = new Configuration(); 57 58 /** 59 * Contains merged override configuration settings from the top of the hierarchy down to this 60 * particular instance. It is different from {@link #mFullConfiguration} because it starts from 61 * topmost container's override config instead of global config. 62 */ 63 private Configuration mMergedOverrideConfiguration = new Configuration(); 64 65 // The specified orientation for this window container. 66 protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 67 68 final protected WindowContainer getParent() { 69 return mParent; 70 } 71 72 final protected void setParent(WindowContainer parent) { 73 mParent = parent; 74 // Update full configuration of this container and all its children. 75 onConfigurationChanged(mParent != null ? mParent.mFullConfiguration : Configuration.EMPTY); 76 // Update merged override configuration of this container and all its children. 77 onMergedOverrideConfigurationChanged(); 78 } 79 80 // Temp. holders for a chain of containers we are currently processing. 81 private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList(); 82 private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList(); 83 84 /** 85 * Adds the input window container has a child of this container in order based on the input 86 * comparator. 87 * @param child The window container to add as a child of this window container. 88 * @param comparator Comparator to use in determining the position the child should be added to. 89 * If null, the child will be added to the top. 90 */ 91 @CallSuper 92 protected void addChild(E child, Comparator<E> comparator) { 93 if (child.getParent() != null) { 94 throw new IllegalArgumentException("addChild: container=" + child.getName() 95 + " is already a child of container=" + child.getParent().getName() 96 + " can't add to container=" + getName()); 97 } 98 child.setParent(this); 99 100 if (mChildren.isEmpty() || comparator == null) { 101 mChildren.add(child); 102 return; 103 } 104 105 final int count = mChildren.size(); 106 for (int i = 0; i < count; i++) { 107 if (comparator.compare(child, mChildren.get(i)) < 0) { 108 mChildren.add(i, child); 109 return; 110 } 111 } 112 113 mChildren.add(child); 114 } 115 116 /** Adds the input window container has a child of this container at the input index. */ 117 @CallSuper 118 protected void addChild(E child, int index) { 119 if (child.getParent() != null) { 120 throw new IllegalArgumentException("addChild: container=" + child.getName() 121 + " is already a child of container=" + child.getParent().getName() 122 + " can't add to container=" + getName()); 123 } 124 child.setParent(this); 125 mChildren.add(index, child); 126 } 127 128 /** 129 * Removes the input child container from this container which is its parent. 130 * 131 * @return True if the container did contain the input child and it was detached. 132 */ 133 @CallSuper 134 void removeChild(E child) { 135 if (mChildren.remove(child)) { 136 child.setParent(null); 137 } else { 138 throw new IllegalArgumentException("removeChild: container=" + child.getName() 139 + " is not a child of container=" + getName()); 140 } 141 } 142 143 /** 144 * Removes this window container and its children with no regard for what else might be going on 145 * in the system. For example, the container will be removed during animation if this method is 146 * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()} 147 * which allows the system to defer removal until a suitable time. 148 */ 149 @CallSuper 150 void removeImmediately() { 151 while (!mChildren.isEmpty()) { 152 final WindowContainer child = mChildren.peekLast(); 153 child.removeImmediately(); 154 // Need to do this after calling remove on the child because the child might try to 155 // remove/detach itself from its parent which will cause an exception if we remove 156 // it before calling remove on the child. 157 mChildren.remove(child); 158 } 159 160 if (mParent != null) { 161 mParent.removeChild(this); 162 } 163 } 164 165 /** 166 * Removes this window container and its children taking care not to remove them during a 167 * critical stage in the system. For example, some containers will not be removed during 168 * animation if this method is called. 169 */ 170 // TODO: figure-out implementation that works best for this. 171 // E.g. when do we remove from parent list? maybe not... 172 void removeIfPossible() { 173 for (int i = mChildren.size() - 1; i >= 0; --i) { 174 final WindowContainer wc = mChildren.get(i); 175 wc.removeIfPossible(); 176 } 177 } 178 179 /** Returns true if this window container has the input child. */ 180 boolean hasChild(WindowContainer child) { 181 for (int i = mChildren.size() - 1; i >= 0; --i) { 182 final WindowContainer current = mChildren.get(i); 183 if (current == child || current.hasChild(child)) { 184 return true; 185 } 186 } 187 return false; 188 } 189 190 /** 191 * Returns full configuration applied to this window container. 192 * This method should be used for getting settings applied in each particular level of the 193 * hierarchy. 194 */ 195 Configuration getConfiguration() { 196 return mFullConfiguration; 197 } 198 199 /** 200 * Notify that parent config changed and we need to update full configuration. 201 * @see #mFullConfiguration 202 */ 203 void onConfigurationChanged(Configuration newParentConfig) { 204 mFullConfiguration.setTo(newParentConfig); 205 mFullConfiguration.updateFrom(mOverrideConfiguration); 206 for (int i = mChildren.size() - 1; i >= 0; --i) { 207 final WindowContainer child = mChildren.get(i); 208 child.onConfigurationChanged(mFullConfiguration); 209 } 210 } 211 212 /** Returns override configuration applied to this window container. */ 213 Configuration getOverrideConfiguration() { 214 return mOverrideConfiguration; 215 } 216 217 /** 218 * Update override configuration and recalculate full config. 219 * @see #mOverrideConfiguration 220 * @see #mFullConfiguration 221 */ 222 void onOverrideConfigurationChanged(Configuration overrideConfiguration) { 223 mOverrideConfiguration.setTo(overrideConfiguration); 224 // Update full configuration of this container and all its children. 225 onConfigurationChanged(mParent != null ? mParent.getConfiguration() : Configuration.EMPTY); 226 // Update merged override config of this container and all its children. 227 onMergedOverrideConfigurationChanged(); 228 } 229 230 /** 231 * Get merged override configuration from the top of the hierarchy down to this 232 * particular instance. This should be reported to client as override config. 233 */ 234 Configuration getMergedOverrideConfiguration() { 235 return mMergedOverrideConfiguration; 236 } 237 238 /** 239 * Update merged override configuration based on corresponding parent's config and notify all 240 * its children. If there is no parent, merged override configuration will set equal to current 241 * override config. 242 * @see #mMergedOverrideConfiguration 243 */ 244 private void onMergedOverrideConfigurationChanged() { 245 if (mParent != null) { 246 mMergedOverrideConfiguration.setTo(mParent.getMergedOverrideConfiguration()); 247 mMergedOverrideConfiguration.updateFrom(mOverrideConfiguration); 248 } else { 249 mMergedOverrideConfiguration.setTo(mOverrideConfiguration); 250 } 251 for (int i = mChildren.size() - 1; i >= 0; --i) { 252 final WindowContainer child = mChildren.get(i); 253 child.onMergedOverrideConfigurationChanged(); 254 } 255 } 256 257 void setWaitingForDrawnIfResizingChanged() { 258 for (int i = mChildren.size() - 1; i >= 0; --i) { 259 final WindowContainer wc = mChildren.get(i); 260 wc.setWaitingForDrawnIfResizingChanged(); 261 } 262 } 263 264 void onResize() { 265 for (int i = mChildren.size() - 1; i >= 0; --i) { 266 final WindowContainer wc = mChildren.get(i); 267 wc.onResize(); 268 } 269 } 270 271 void onMovedByResize() { 272 for (int i = mChildren.size() - 1; i >= 0; --i) { 273 final WindowContainer wc = mChildren.get(i); 274 wc.onMovedByResize(); 275 } 276 } 277 278 void resetDragResizingChangeReported() { 279 for (int i = mChildren.size() - 1; i >= 0; --i) { 280 final WindowContainer wc = mChildren.get(i); 281 wc.resetDragResizingChangeReported(); 282 } 283 } 284 285 void forceWindowsScaleableInTransaction(boolean force) { 286 for (int i = mChildren.size() - 1; i >= 0; --i) { 287 final WindowContainer wc = mChildren.get(i); 288 wc.forceWindowsScaleableInTransaction(force); 289 } 290 } 291 292 boolean isAnimating() { 293 for (int j = mChildren.size() - 1; j >= 0; j--) { 294 final WindowContainer wc = mChildren.get(j); 295 if (wc.isAnimating()) { 296 return true; 297 } 298 } 299 return false; 300 } 301 302 void sendAppVisibilityToClients() { 303 for (int i = mChildren.size() - 1; i >= 0; --i) { 304 final WindowContainer wc = mChildren.get(i); 305 wc.sendAppVisibilityToClients(); 306 } 307 } 308 309 void setVisibleBeforeClientHidden() { 310 for (int i = mChildren.size() - 1; i >= 0; --i) { 311 final WindowContainer wc = mChildren.get(i); 312 wc.setVisibleBeforeClientHidden(); 313 } 314 } 315 316 /** 317 * Returns true if the container or one of its children as some content it can display or wants 318 * to display (e.g. app views or saved surface). 319 * 320 * NOTE: While this method will return true if the there is some content to display, it doesn't 321 * mean the container is visible. Use {@link #isVisible()} to determine if the container is 322 * visible. 323 */ 324 boolean hasContentToDisplay() { 325 for (int i = mChildren.size() - 1; i >= 0; --i) { 326 final WindowContainer wc = mChildren.get(i); 327 if (wc.hasContentToDisplay()) { 328 return true; 329 } 330 } 331 return false; 332 } 333 334 /** 335 * Returns true if the container or one of its children is considered visible from the 336 * WindowManager perspective which usually means valid surface and some other internal state 337 * are true. 338 * 339 * NOTE: While this method will return true if the surface is visible, it doesn't mean the 340 * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if 341 * the container has any content to display. 342 */ 343 boolean isVisible() { 344 // TODO: Will this be more correct if it checks the visibility of its parents? 345 // It depends...For example, Tasks and Stacks are only visible if there children are visible 346 // but, WindowState are not visible if there parent are not visible. Maybe have the 347 // container specify which direction to treverse for for visibility? 348 for (int i = mChildren.size() - 1; i >= 0; --i) { 349 final WindowContainer wc = mChildren.get(i); 350 if (wc.isVisible()) { 351 return true; 352 } 353 } 354 return false; 355 } 356 357 /** Returns the top child container or this container if there are no children. */ 358 WindowContainer getTop() { 359 return mChildren.isEmpty() ? this : mChildren.peekLast(); 360 } 361 362 /** Returns true if there is still a removal being deferred */ 363 boolean checkCompleteDeferredRemoval() { 364 boolean stillDeferringRemoval = false; 365 366 for (int i = mChildren.size() - 1; i >= 0; --i) { 367 final WindowContainer wc = mChildren.get(i); 368 stillDeferringRemoval |= wc.checkCompleteDeferredRemoval(); 369 } 370 371 return stillDeferringRemoval; 372 } 373 374 /** Checks if all windows in an app are all drawn and shows them if needed. */ 375 // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one 376 // display. Remove once we migrate DisplayContent to use WindowContainer. 377 void checkAppWindowsReadyToShow(int displayId) { 378 for (int i = mChildren.size() - 1; i >= 0; --i) { 379 final WindowContainer wc = mChildren.get(i); 380 wc.checkAppWindowsReadyToShow(displayId); 381 } 382 } 383 384 /** 385 * Updates the current all drawn status for this container. That is all its children 386 * that should draw something have done so. 387 */ 388 // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one 389 // display. Remove once we migrate DisplayContent to use WindowContainer. 390 void updateAllDrawn(int displayId) { 391 for (int i = mChildren.size() - 1; i >= 0; --i) { 392 final WindowContainer wc = mChildren.get(i); 393 wc.updateAllDrawn(displayId); 394 } 395 } 396 397 /** Step currently ongoing animation for App window containers. */ 398 // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one 399 // display. Remove once we migrate DisplayContent to use WindowContainer. 400 void stepAppWindowsAnimation(long currentTime, int displayId) { 401 for (int i = mChildren.size() - 1; i >= 0; --i) { 402 final WindowContainer wc = mChildren.get(i); 403 wc.stepAppWindowsAnimation(currentTime, displayId); 404 } 405 } 406 407 void onAppTransitionDone() { 408 for (int i = mChildren.size() - 1; i >= 0; --i) { 409 final WindowContainer wc = mChildren.get(i); 410 wc.onAppTransitionDone(); 411 } 412 } 413 414 void overridePlayingAppAnimations(Animation a) { 415 for (int i = mChildren.size() - 1; i >= 0; i--) { 416 mChildren.get(i).overridePlayingAppAnimations(a); 417 } 418 } 419 420 void setOrientation(int orientation) { 421 mOrientation = orientation; 422 } 423 424 /** 425 * Returns the specified orientation for this window container or one of its children is there 426 * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no 427 * specification is set. 428 * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a 429 * specification... 430 */ 431 int getOrientation() { 432 433 if (!fillsParent() || !isVisible()) { 434 // Ignore invisible containers or containers that don't completely fills their parents. 435 return SCREEN_ORIENTATION_UNSET; 436 } 437 438 // The container fills its parent so we can use it orientation if it has one specified, 439 // otherwise we prefer to use the orientation of its topmost child that has one 440 // specified and fall back on this container's unset or unspecified value as a candidate 441 // if none of the children have a better candidate for the orientation. 442 if (mOrientation != SCREEN_ORIENTATION_UNSET 443 && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { 444 return mOrientation; 445 } 446 int candidate = mOrientation; 447 448 for (int i = mChildren.size() - 1; i >= 0; --i) { 449 final WindowContainer wc = mChildren.get(i); 450 451 final int orientation = wc.getOrientation(); 452 if (orientation == SCREEN_ORIENTATION_BEHIND) { 453 // container wants us to use the orientation of the container behind it. See if we 454 // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to 455 // look behind this container. 456 candidate = orientation; 457 continue; 458 } 459 460 if (orientation == SCREEN_ORIENTATION_UNSET) { 461 continue; 462 } 463 464 if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) { 465 // Use the orientation if the container fills its parent or requested an explicit 466 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED. 467 return orientation; 468 } 469 } 470 471 return candidate; 472 } 473 474 /** 475 * Returns true if this container is opaque and fills all the space made available by its parent 476 * container. 477 * 478 * NOTE: It is possible for this container to occupy more space than the parent has (or less), 479 * this is just a signal from the client to window manager stating its intent, but not what it 480 * actually does. 481 */ 482 boolean fillsParent() { 483 return false; 484 } 485 486 /** 487 * Rebuilds the WindowList for the input display content. 488 * @param dc The display content to rebuild the window list for. 489 * @param addIndex The index in the window list to add the next entry to. 490 * @return The next index in the window list to. 491 */ 492 // TODO: Hoping we can get rid of WindowList so this method wouldn't be needed. 493 int rebuildWindowList(DisplayContent dc, int addIndex) { 494 final int count = mChildren.size(); 495 for (int i = 0; i < count; i++) { 496 final WindowContainer wc = mChildren.get(i); 497 addIndex = wc.rebuildWindowList(dc, addIndex); 498 } 499 return addIndex; 500 } 501 502 /** 503 * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than 504 * the input container in terms of z-order. 505 */ 506 @Override 507 public int compareTo(WindowContainer other) { 508 if (this == other) { 509 return 0; 510 } 511 512 if (mParent != null && mParent == other.mParent) { 513 final LinkedList<WindowContainer> list = mParent.mChildren; 514 return list.indexOf(this) > list.indexOf(other) ? 1 : -1; 515 } 516 517 final LinkedList<WindowContainer> thisParentChain = mTmpChain1; 518 final LinkedList<WindowContainer> otherParentChain = mTmpChain2; 519 getParents(thisParentChain); 520 other.getParents(otherParentChain); 521 522 // Find the common ancestor of both containers. 523 WindowContainer commonAncestor = null; 524 WindowContainer thisTop = thisParentChain.peekLast(); 525 WindowContainer otherTop = otherParentChain.peekLast(); 526 while (thisTop != null && otherTop != null && thisTop == otherTop) { 527 commonAncestor = thisParentChain.removeLast(); 528 otherParentChain.removeLast(); 529 thisTop = thisParentChain.peekLast(); 530 otherTop = otherParentChain.peekLast(); 531 } 532 533 // Containers don't belong to the same hierarchy??? 534 if (commonAncestor == null) { 535 throw new IllegalArgumentException("No in the same hierarchy this=" 536 + thisParentChain + " other=" + otherParentChain); 537 } 538 539 // Children are always considered greater than their parents, so if one of the containers 540 // we are comparing it the parent of the other then whichever is the child is greater. 541 if (commonAncestor == this) { 542 return -1; 543 } else if (commonAncestor == other) { 544 return 1; 545 } 546 547 // The position of the first non-common ancestor in the common ancestor list determines 548 // which is greater the which. 549 final LinkedList<WindowContainer> list = commonAncestor.mChildren; 550 return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast()) 551 ? 1 : -1; 552 } 553 554 private void getParents(LinkedList<WindowContainer> parents) { 555 parents.clear(); 556 WindowContainer current = this; 557 do { 558 parents.addLast(current); 559 current = current.mParent; 560 } while (current != null); 561 } 562 563 /** 564 * Dumps the names of this container children in the input print writer indenting each 565 * level with the input prefix. 566 */ 567 void dumpChildrenNames(StringBuilder out, String prefix) { 568 final String childPrefix = prefix + " "; 569 out.append(getName() + "\n"); 570 final int count = mChildren.size(); 571 for (int i = 0; i < count; i++) { 572 final WindowContainer wc = mChildren.get(i); 573 out.append(childPrefix + "#" + i + " "); 574 wc.dumpChildrenNames(out, childPrefix); 575 } 576 } 577 578 String getName() { 579 return toString(); 580 } 581 582} 583