WindowContainer.java revision e7d2b85068dfda580983a510f60983984c37e7ca
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 static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; 20import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 21import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 22import static android.content.res.Configuration.EMPTY; 23 24import android.annotation.CallSuper; 25import android.content.res.Configuration; 26import android.util.Pools; 27 28import com.android.internal.util.ToBooleanFunction; 29 30import java.util.Comparator; 31import java.util.LinkedList; 32import java.util.function.Consumer; 33import java.util.function.Predicate; 34 35/** 36 * Defines common functionality for classes that can hold windows directly or through their 37 * children in a hierarchy form. 38 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime 39 * changes are made to this class. 40 */ 41class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> 42 implements Comparable<WindowContainer> { 43 44 static final int POSITION_TOP = Integer.MAX_VALUE; 45 static final int POSITION_BOTTOM = Integer.MIN_VALUE; 46 47 /** 48 * The parent of this window container. 49 * For removing or setting new parent {@link #setParent} should be used, because it also 50 * performs configuration updates based on new parent's settings. 51 */ 52 private WindowContainer mParent = null; 53 54 // List of children for this window container. List is in z-order as the children appear on 55 // screen with the top-most window container at the tail of the list. 56 protected final WindowList<E> mChildren = new WindowList<E>(); 57 58 // The specified orientation for this window container. 59 protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 60 61 private final Pools.SynchronizedPool<ForAllWindowsConsumerWrapper> mConsumerWrapperPool = 62 new Pools.SynchronizedPool<>(3); 63 64 // The owner/creator for this container. No controller if null. 65 private WindowContainerController mController; 66 67 @Override 68 final protected WindowContainer getParent() { 69 return mParent; 70 } 71 72 73 @Override 74 final protected int getChildCount() { 75 return mChildren.size(); 76 } 77 78 @Override 79 final protected E getChildAt(int index) { 80 return mChildren.get(index); 81 } 82 83 final protected void setParent(WindowContainer parent) { 84 mParent = parent; 85 // Removing parent usually means that we've detached this entity to destroy it or to attach 86 // to another parent. In both cases we don't need to update the configuration now. 87 if (mParent != null) { 88 // Update full configuration of this container and all its children. 89 onConfigurationChanged(mParent.getConfiguration()); 90 // Update merged override configuration of this container and all its children. 91 onMergedOverrideConfigurationChanged(); 92 } 93 94 onParentSet(); 95 } 96 97 /** 98 * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called. 99 * Supposed to be overridden and contain actions that should be executed after parent was set. 100 */ 101 void onParentSet() { 102 // Do nothing by default. 103 } 104 105 // Temp. holders for a chain of containers we are currently processing. 106 private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList(); 107 private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList(); 108 109 /** 110 * Adds the input window container has a child of this container in order based on the input 111 * comparator. 112 * @param child The window container to add as a child of this window container. 113 * @param comparator Comparator to use in determining the position the child should be added to. 114 * If null, the child will be added to the top. 115 */ 116 @CallSuper 117 protected void addChild(E child, Comparator<E> comparator) { 118 if (child.getParent() != null) { 119 throw new IllegalArgumentException("addChild: container=" + child.getName() 120 + " is already a child of container=" + child.getParent().getName() 121 + " can't add to container=" + getName()); 122 } 123 124 int positionToAdd = -1; 125 if (comparator != null) { 126 final int count = mChildren.size(); 127 for (int i = 0; i < count; i++) { 128 if (comparator.compare(child, mChildren.get(i)) < 0) { 129 positionToAdd = i; 130 break; 131 } 132 } 133 } 134 135 if (positionToAdd == -1) { 136 mChildren.add(child); 137 } else { 138 mChildren.add(positionToAdd, child); 139 } 140 // Set the parent after we've actually added a child in case a subclass depends on this. 141 child.setParent(this); 142 } 143 144 /** Adds the input window container has a child of this container at the input index. */ 145 @CallSuper 146 void addChild(E child, int index) { 147 if (child.getParent() != null) { 148 throw new IllegalArgumentException("addChild: container=" + child.getName() 149 + " is already a child of container=" + child.getParent().getName() 150 + " can't add to container=" + getName()); 151 } 152 mChildren.add(index, child); 153 // Set the parent after we've actually added a child in case a subclass depends on this. 154 child.setParent(this); 155 } 156 157 /** 158 * Removes the input child container from this container which is its parent. 159 * 160 * @return True if the container did contain the input child and it was detached. 161 */ 162 @CallSuper 163 void removeChild(E child) { 164 if (mChildren.remove(child)) { 165 child.setParent(null); 166 } else { 167 throw new IllegalArgumentException("removeChild: container=" + child.getName() 168 + " is not a child of container=" + getName()); 169 } 170 } 171 172 /** 173 * Removes this window container and its children with no regard for what else might be going on 174 * in the system. For example, the container will be removed during animation if this method is 175 * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()} 176 * which allows the system to defer removal until a suitable time. 177 */ 178 @CallSuper 179 void removeImmediately() { 180 while (!mChildren.isEmpty()) { 181 final WindowContainer child = mChildren.peekLast(); 182 child.removeImmediately(); 183 // Need to do this after calling remove on the child because the child might try to 184 // remove/detach itself from its parent which will cause an exception if we remove 185 // it before calling remove on the child. 186 mChildren.remove(child); 187 } 188 189 if (mParent != null) { 190 mParent.removeChild(this); 191 } 192 193 if (mController != null) { 194 setController(null); 195 } 196 } 197 198 /** 199 * Removes this window container and its children taking care not to remove them during a 200 * critical stage in the system. For example, some containers will not be removed during 201 * animation if this method is called. 202 */ 203 // TODO: figure-out implementation that works best for this. 204 // E.g. when do we remove from parent list? maybe not... 205 void removeIfPossible() { 206 for (int i = mChildren.size() - 1; i >= 0; --i) { 207 final WindowContainer wc = mChildren.get(i); 208 wc.removeIfPossible(); 209 } 210 } 211 212 /** Returns true if this window container has the input child. */ 213 boolean hasChild(WindowContainer child) { 214 for (int i = mChildren.size() - 1; i >= 0; --i) { 215 final WindowContainer current = mChildren.get(i); 216 if (current == child || current.hasChild(child)) { 217 return true; 218 } 219 } 220 return false; 221 } 222 223 /** 224 * Move a child from it's current place in siblings list to the specified position, 225 * with an option to move all its parents to top. 226 * @param position Target position to move the child to. 227 * @param child Child to move to selected position. 228 * @param includingParents Flag indicating whether we need to move the entire branch of the 229 * hierarchy when we're moving a child to {@link #POSITION_TOP} or 230 * {@link #POSITION_BOTTOM}. When moving to other intermediate positions 231 * this flag will do nothing. 232 */ 233 @CallSuper 234 void positionChildAt(int position, E child, boolean includingParents) { 235 236 if (child.getParent() != this) { 237 throw new IllegalArgumentException("removeChild: container=" + child.getName() 238 + " is not a child of container=" + getName() 239 + " current parent=" + child.getParent()); 240 } 241 242 if ((position < 0 && position != POSITION_BOTTOM) 243 || (position > mChildren.size() && position != POSITION_TOP)) { 244 throw new IllegalArgumentException("positionAt: invalid position=" + position 245 + ", children number=" + mChildren.size()); 246 } 247 248 if (position >= mChildren.size() - 1) { 249 position = POSITION_TOP; 250 } else if (position == 0) { 251 position = POSITION_BOTTOM; 252 } 253 254 switch (position) { 255 case POSITION_TOP: 256 if (mChildren.peekLast() != child) { 257 mChildren.remove(child); 258 mChildren.add(child); 259 } 260 if (includingParents && getParent() != null) { 261 getParent().positionChildAt(POSITION_TOP, this /* child */, 262 true /* includingParents */); 263 } 264 break; 265 case POSITION_BOTTOM: 266 if (mChildren.peekFirst() != child) { 267 mChildren.remove(child); 268 mChildren.addFirst(child); 269 } 270 if (includingParents && getParent() != null) { 271 getParent().positionChildAt(POSITION_BOTTOM, this /* child */, 272 true /* includingParents */); 273 } 274 break; 275 default: 276 mChildren.remove(child); 277 mChildren.add(position, child); 278 } 279 } 280 281 /** 282 * Update override configuration and recalculate full config. 283 * @see #mOverrideConfiguration 284 * @see #mFullConfiguration 285 */ 286 @Override 287 final public void onOverrideConfigurationChanged(Configuration overrideConfiguration) { 288 super.onOverrideConfigurationChanged(overrideConfiguration); 289 if (mParent != null) { 290 mParent.onDescendantOverrideConfigurationChanged(); 291 } 292 } 293 294 /** 295 * Notify that a descendant's overrideConfiguration has changed. 296 */ 297 void onDescendantOverrideConfigurationChanged() { 298 if (mParent != null) { 299 mParent.onDescendantOverrideConfigurationChanged(); 300 } 301 } 302 303 /** 304 * Notify that the display this container is on has changed. 305 * @param dc The new display this container is on. 306 */ 307 void onDisplayChanged(DisplayContent dc) { 308 for (int i = mChildren.size() - 1; i >= 0; --i) { 309 final WindowContainer child = mChildren.get(i); 310 child.onDisplayChanged(dc); 311 } 312 } 313 314 void setWaitingForDrawnIfResizingChanged() { 315 for (int i = mChildren.size() - 1; i >= 0; --i) { 316 final WindowContainer wc = mChildren.get(i); 317 wc.setWaitingForDrawnIfResizingChanged(); 318 } 319 } 320 321 void onResize() { 322 for (int i = mChildren.size() - 1; i >= 0; --i) { 323 final WindowContainer wc = mChildren.get(i); 324 wc.onResize(); 325 } 326 } 327 328 void onMovedByResize() { 329 for (int i = mChildren.size() - 1; i >= 0; --i) { 330 final WindowContainer wc = mChildren.get(i); 331 wc.onMovedByResize(); 332 } 333 } 334 335 void resetDragResizingChangeReported() { 336 for (int i = mChildren.size() - 1; i >= 0; --i) { 337 final WindowContainer wc = mChildren.get(i); 338 wc.resetDragResizingChangeReported(); 339 } 340 } 341 342 void forceWindowsScaleableInTransaction(boolean force) { 343 for (int i = mChildren.size() - 1; i >= 0; --i) { 344 final WindowContainer wc = mChildren.get(i); 345 wc.forceWindowsScaleableInTransaction(force); 346 } 347 } 348 349 boolean isAnimating() { 350 for (int j = mChildren.size() - 1; j >= 0; j--) { 351 final WindowContainer wc = mChildren.get(j); 352 if (wc.isAnimating()) { 353 return true; 354 } 355 } 356 return false; 357 } 358 359 void sendAppVisibilityToClients() { 360 for (int i = mChildren.size() - 1; i >= 0; --i) { 361 final WindowContainer wc = mChildren.get(i); 362 wc.sendAppVisibilityToClients(); 363 } 364 } 365 366 /** 367 * Returns true if the container or one of its children as some content it can display or wants 368 * to display (e.g. app views or saved surface). 369 * 370 * NOTE: While this method will return true if the there is some content to display, it doesn't 371 * mean the container is visible. Use {@link #isVisible()} to determine if the container is 372 * visible. 373 */ 374 boolean hasContentToDisplay() { 375 for (int i = mChildren.size() - 1; i >= 0; --i) { 376 final WindowContainer wc = mChildren.get(i); 377 if (wc.hasContentToDisplay()) { 378 return true; 379 } 380 } 381 return false; 382 } 383 384 /** 385 * Returns true if the container or one of its children is considered visible from the 386 * WindowManager perspective which usually means valid surface and some other internal state 387 * are true. 388 * 389 * NOTE: While this method will return true if the surface is visible, it doesn't mean the 390 * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if 391 * the container has any content to display. 392 */ 393 boolean isVisible() { 394 // TODO: Will this be more correct if it checks the visibility of its parents? 395 // It depends...For example, Tasks and Stacks are only visible if there children are visible 396 // but, WindowState are not visible if there parent are not visible. Maybe have the 397 // container specify which direction to traverse for visibility? 398 for (int i = mChildren.size() - 1; i >= 0; --i) { 399 final WindowContainer wc = mChildren.get(i); 400 if (wc.isVisible()) { 401 return true; 402 } 403 } 404 return false; 405 } 406 407 /** 408a * Returns whether this child is on top of the window hierarchy. 409 */ 410 boolean isOnTop() { 411 return getParent().getTopChild() == this && getParent().isOnTop(); 412 } 413 414 /** Returns the top child container. */ 415 E getTopChild() { 416 return mChildren.peekLast(); 417 } 418 419 /** Returns true if there is still a removal being deferred */ 420 boolean checkCompleteDeferredRemoval() { 421 boolean stillDeferringRemoval = false; 422 423 for (int i = mChildren.size() - 1; i >= 0; --i) { 424 final WindowContainer wc = mChildren.get(i); 425 stillDeferringRemoval |= wc.checkCompleteDeferredRemoval(); 426 } 427 428 return stillDeferringRemoval; 429 } 430 431 /** Checks if all windows in an app are all drawn and shows them if needed. */ 432 void checkAppWindowsReadyToShow() { 433 for (int i = mChildren.size() - 1; i >= 0; --i) { 434 final WindowContainer wc = mChildren.get(i); 435 wc.checkAppWindowsReadyToShow(); 436 } 437 } 438 439 /** Step currently ongoing animation for App window containers. */ 440 void stepAppWindowsAnimation(long currentTime) { 441 for (int i = mChildren.size() - 1; i >= 0; --i) { 442 final WindowContainer wc = mChildren.get(i); 443 wc.stepAppWindowsAnimation(currentTime); 444 } 445 } 446 447 void onAppTransitionDone() { 448 for (int i = mChildren.size() - 1; i >= 0; --i) { 449 final WindowContainer wc = mChildren.get(i); 450 wc.onAppTransitionDone(); 451 } 452 } 453 454 void setOrientation(int orientation) { 455 mOrientation = orientation; 456 } 457 458 int getOrientation() { 459 return getOrientation(mOrientation); 460 } 461 462 /** 463 * Returns the specified orientation for this window container or one of its children is there 464 * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no 465 * specification is set. 466 * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a 467 * specification... 468 * 469 * @param candidate The current orientation candidate that will be returned if we don't find a 470 * better match. 471 * @return The orientation as specified by this branch or the window hierarchy. 472 */ 473 int getOrientation(int candidate) { 474 if (!fillsParent()) { 475 // Ignore containers that don't completely fill their parents. 476 return SCREEN_ORIENTATION_UNSET; 477 } 478 479 // The container fills its parent so we can use it orientation if it has one 480 // specified; otherwise we prefer to use the orientation of its topmost child that has one 481 // specified and fall back on this container's unset or unspecified value as a candidate 482 // if none of the children have a better candidate for the orientation. 483 if (mOrientation != SCREEN_ORIENTATION_UNSET 484 && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { 485 return mOrientation; 486 } 487 488 for (int i = mChildren.size() - 1; i >= 0; --i) { 489 final WindowContainer wc = mChildren.get(i); 490 491 // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs. 492 // SCREEN_ORIENTATION_UNSPECIFIED? 493 final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND 494 ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET); 495 if (orientation == SCREEN_ORIENTATION_BEHIND) { 496 // container wants us to use the orientation of the container behind it. See if we 497 // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to 498 // look behind this container. 499 candidate = orientation; 500 continue; 501 } 502 503 if (orientation == SCREEN_ORIENTATION_UNSET) { 504 continue; 505 } 506 507 if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) { 508 // Use the orientation if the container fills its parent or requested an explicit 509 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED. 510 return orientation; 511 } 512 } 513 514 return candidate; 515 } 516 517 /** 518 * Returns true if this container is opaque and fills all the space made available by its parent 519 * container. 520 * 521 * NOTE: It is possible for this container to occupy more space than the parent has (or less), 522 * this is just a signal from the client to window manager stating its intent, but not what it 523 * actually does. 524 */ 525 boolean fillsParent() { 526 return false; 527 } 528 529 // TODO: Users would have their own window containers under the display container? 530 void switchUser() { 531 for (int i = mChildren.size() - 1; i >= 0; --i) { 532 mChildren.get(i).switchUser(); 533 } 534 } 535 536 /** 537 * For all windows at or below this container call the callback. 538 * @param callback Calls the {@link ToBooleanFunction#apply} method for each window found and 539 * stops the search if {@link ToBooleanFunction#apply} returns true. 540 * @param traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of 541 * z-order, else from bottom-to-top. 542 * @return True if the search ended before we reached the end of the hierarchy due to 543 * {@link ToBooleanFunction#apply} returning true. 544 */ 545 boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { 546 if (traverseTopToBottom) { 547 for (int i = mChildren.size() - 1; i >= 0; --i) { 548 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) { 549 return true; 550 } 551 } 552 } else { 553 final int count = mChildren.size(); 554 for (int i = 0; i < count; i++) { 555 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) { 556 return true; 557 } 558 } 559 } 560 return false; 561 } 562 563 void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) { 564 ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback); 565 forAllWindows(wrapper, traverseTopToBottom); 566 wrapper.release(); 567 } 568 569 /** 570 * For all tasks at or below this container call the callback. 571 * 572 * @param callback Callback to be called for every task. 573 */ 574 void forAllTasks(Consumer<Task> callback) { 575 for (int i = mChildren.size() - 1; i >= 0; --i) { 576 mChildren.get(i).forAllTasks(callback); 577 } 578 } 579 580 WindowState getWindow(Predicate<WindowState> callback) { 581 for (int i = mChildren.size() - 1; i >= 0; --i) { 582 final WindowState w = mChildren.get(i).getWindow(callback); 583 if (w != null) { 584 return w; 585 } 586 } 587 588 return null; 589 } 590 591 /** 592 * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than 593 * the input container in terms of z-order. 594 */ 595 @Override 596 public int compareTo(WindowContainer other) { 597 if (this == other) { 598 return 0; 599 } 600 601 if (mParent != null && mParent == other.mParent) { 602 final WindowList<WindowContainer> list = mParent.mChildren; 603 return list.indexOf(this) > list.indexOf(other) ? 1 : -1; 604 } 605 606 final LinkedList<WindowContainer> thisParentChain = mTmpChain1; 607 final LinkedList<WindowContainer> otherParentChain = mTmpChain2; 608 try { 609 getParents(thisParentChain); 610 other.getParents(otherParentChain); 611 612 // Find the common ancestor of both containers. 613 WindowContainer commonAncestor = null; 614 WindowContainer thisTop = thisParentChain.peekLast(); 615 WindowContainer otherTop = otherParentChain.peekLast(); 616 while (thisTop != null && otherTop != null && thisTop == otherTop) { 617 commonAncestor = thisParentChain.removeLast(); 618 otherParentChain.removeLast(); 619 thisTop = thisParentChain.peekLast(); 620 otherTop = otherParentChain.peekLast(); 621 } 622 623 // Containers don't belong to the same hierarchy??? 624 if (commonAncestor == null) { 625 throw new IllegalArgumentException("No in the same hierarchy this=" 626 + thisParentChain + " other=" + otherParentChain); 627 } 628 629 // Children are always considered greater than their parents, so if one of the containers 630 // we are comparing it the parent of the other then whichever is the child is greater. 631 if (commonAncestor == this) { 632 return -1; 633 } else if (commonAncestor == other) { 634 return 1; 635 } 636 637 // The position of the first non-common ancestor in the common ancestor list determines 638 // which is greater the which. 639 final WindowList<WindowContainer> list = commonAncestor.mChildren; 640 return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast()) 641 ? 1 : -1; 642 } finally { 643 mTmpChain1.clear(); 644 mTmpChain2.clear(); 645 } 646 } 647 648 private void getParents(LinkedList<WindowContainer> parents) { 649 parents.clear(); 650 WindowContainer current = this; 651 do { 652 parents.addLast(current); 653 current = current.mParent; 654 } while (current != null); 655 } 656 657 WindowContainerController getController() { 658 return mController; 659 } 660 661 void setController(WindowContainerController controller) { 662 if (mController != null && controller != null) { 663 throw new IllegalArgumentException("Can't set controller=" + mController 664 + " for container=" + this + " Already set to=" + mController); 665 } 666 if (controller != null) { 667 controller.setContainer(this); 668 } else if (mController != null) { 669 mController.setContainer(null); 670 } 671 mController = controller; 672 } 673 674 /** 675 * Dumps the names of this container children in the input print writer indenting each 676 * level with the input prefix. 677 */ 678 void dumpChildrenNames(StringBuilder out, String prefix) { 679 final String childPrefix = prefix + " "; 680 out.append(getName() + "\n"); 681 for (int i = mChildren.size() - 1; i >= 0; --i) { 682 final WindowContainer wc = mChildren.get(i); 683 out.append(childPrefix + "#" + i + " "); 684 wc.dumpChildrenNames(out, childPrefix); 685 } 686 } 687 688 String getName() { 689 return toString(); 690 } 691 692 private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) { 693 ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire(); 694 if (wrapper == null) { 695 wrapper = new ForAllWindowsConsumerWrapper(); 696 } 697 wrapper.setConsumer(consumer); 698 return wrapper; 699 } 700 701 private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> { 702 703 private Consumer<WindowState> mConsumer; 704 705 void setConsumer(Consumer<WindowState> consumer) { 706 mConsumer = consumer; 707 } 708 709 @Override 710 public boolean apply(WindowState w) { 711 mConsumer.accept(w); 712 return false; 713 } 714 715 void release() { 716 mConsumer = null; 717 mConsumerWrapperPool.release(this); 718 } 719 } 720} 721