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