WindowContainer.java revision 55ddf8f9e5d8241130aeac6be7b4978860f41bf8
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.util.Pools; 22 23import com.android.internal.util.ToBooleanFunction; 24 25import java.util.Comparator; 26import java.util.LinkedList; 27import java.util.function.Consumer; 28import java.util.function.Predicate; 29 30import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; 31import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 32import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 33import static android.content.res.Configuration.EMPTY; 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> 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 LinkedList<E> mChildren = new LinkedList(); 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.addLast(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 /** Returns the top child container. */ 476 E getTopChild() { 477 return mChildren.peekLast(); 478 } 479 480 /** Returns true if there is still a removal being deferred */ 481 boolean checkCompleteDeferredRemoval() { 482 boolean stillDeferringRemoval = false; 483 484 for (int i = mChildren.size() - 1; i >= 0; --i) { 485 final WindowContainer wc = mChildren.get(i); 486 stillDeferringRemoval |= wc.checkCompleteDeferredRemoval(); 487 } 488 489 return stillDeferringRemoval; 490 } 491 492 /** Checks if all windows in an app are all drawn and shows them if needed. */ 493 void checkAppWindowsReadyToShow() { 494 for (int i = mChildren.size() - 1; i >= 0; --i) { 495 final WindowContainer wc = mChildren.get(i); 496 wc.checkAppWindowsReadyToShow(); 497 } 498 } 499 500 /** Step currently ongoing animation for App window containers. */ 501 void stepAppWindowsAnimation(long currentTime) { 502 for (int i = mChildren.size() - 1; i >= 0; --i) { 503 final WindowContainer wc = mChildren.get(i); 504 wc.stepAppWindowsAnimation(currentTime); 505 } 506 } 507 508 void onAppTransitionDone() { 509 for (int i = mChildren.size() - 1; i >= 0; --i) { 510 final WindowContainer wc = mChildren.get(i); 511 wc.onAppTransitionDone(); 512 } 513 } 514 515 void setOrientation(int orientation) { 516 mOrientation = orientation; 517 } 518 519 /** 520 * Returns the specified orientation for this window container or one of its children is there 521 * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no 522 * specification is set. 523 * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a 524 * specification... 525 */ 526 int getOrientation() { 527 if (!fillsParent()) { 528 // Ignore containers that don't completely fill their parents. 529 return SCREEN_ORIENTATION_UNSET; 530 } 531 532 // The container fills its parent so we can use it orientation if it has one 533 // specified; otherwise we prefer to use the orientation of its topmost child that has one 534 // specified and fall back on this container's unset or unspecified value as a candidate 535 // if none of the children have a better candidate for the orientation. 536 if (mOrientation != SCREEN_ORIENTATION_UNSET 537 && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { 538 return mOrientation; 539 } 540 int candidate = mOrientation; 541 542 for (int i = mChildren.size() - 1; i >= 0; --i) { 543 final WindowContainer wc = mChildren.get(i); 544 545 final int orientation = wc.getOrientation(); 546 if (orientation == SCREEN_ORIENTATION_BEHIND) { 547 // container wants us to use the orientation of the container behind it. See if we 548 // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to 549 // look behind this container. 550 candidate = orientation; 551 continue; 552 } 553 554 if (orientation == SCREEN_ORIENTATION_UNSET) { 555 continue; 556 } 557 558 if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) { 559 // Use the orientation if the container fills its parent or requested an explicit 560 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED. 561 return orientation; 562 } 563 } 564 565 return candidate; 566 } 567 568 /** 569 * Returns true if this container is opaque and fills all the space made available by its parent 570 * container. 571 * 572 * NOTE: It is possible for this container to occupy more space than the parent has (or less), 573 * this is just a signal from the client to window manager stating its intent, but not what it 574 * actually does. 575 */ 576 boolean fillsParent() { 577 return false; 578 } 579 580 // TODO: Users would have their own window containers under the display container? 581 void switchUser() { 582 for (int i = mChildren.size() - 1; i >= 0; --i) { 583 mChildren.get(i).switchUser(); 584 } 585 } 586 587 /** 588 * For all windows at or below this container call the callback. 589 * @param callback Calls the {@link ToBooleanFunction#apply} method for each window found and 590 * stops the search if {@link ToBooleanFunction#apply} returns true. 591 * @param traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of 592 * z-order, else from bottom-to-top. 593 * @return True if the search ended before we reached the end of the hierarchy due to 594 * {@link ToBooleanFunction#apply} returning true. 595 */ 596 boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { 597 if (traverseTopToBottom) { 598 for (int i = mChildren.size() - 1; i >= 0; --i) { 599 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) { 600 return true; 601 } 602 } 603 } else { 604 final int count = mChildren.size(); 605 for (int i = 0; i < count; i++) { 606 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) { 607 return true; 608 } 609 } 610 } 611 return false; 612 } 613 614 void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) { 615 ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback); 616 forAllWindows(wrapper, traverseTopToBottom); 617 wrapper.release(); 618 } 619 620 WindowState getWindow(Predicate<WindowState> callback) { 621 for (int i = mChildren.size() - 1; i >= 0; --i) { 622 final WindowState w = mChildren.get(i).getWindow(callback); 623 if (w != null) { 624 return w; 625 } 626 } 627 628 return null; 629 } 630 631 /** 632 * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than 633 * the input container in terms of z-order. 634 */ 635 @Override 636 public int compareTo(WindowContainer other) { 637 if (this == other) { 638 return 0; 639 } 640 641 if (mParent != null && mParent == other.mParent) { 642 final LinkedList<WindowContainer> list = mParent.mChildren; 643 return list.indexOf(this) > list.indexOf(other) ? 1 : -1; 644 } 645 646 final LinkedList<WindowContainer> thisParentChain = mTmpChain1; 647 final LinkedList<WindowContainer> otherParentChain = mTmpChain2; 648 getParents(thisParentChain); 649 other.getParents(otherParentChain); 650 651 // Find the common ancestor of both containers. 652 WindowContainer commonAncestor = null; 653 WindowContainer thisTop = thisParentChain.peekLast(); 654 WindowContainer otherTop = otherParentChain.peekLast(); 655 while (thisTop != null && otherTop != null && thisTop == otherTop) { 656 commonAncestor = thisParentChain.removeLast(); 657 otherParentChain.removeLast(); 658 thisTop = thisParentChain.peekLast(); 659 otherTop = otherParentChain.peekLast(); 660 } 661 662 // Containers don't belong to the same hierarchy??? 663 if (commonAncestor == null) { 664 throw new IllegalArgumentException("No in the same hierarchy this=" 665 + thisParentChain + " other=" + otherParentChain); 666 } 667 668 // Children are always considered greater than their parents, so if one of the containers 669 // we are comparing it the parent of the other then whichever is the child is greater. 670 if (commonAncestor == this) { 671 return -1; 672 } else if (commonAncestor == other) { 673 return 1; 674 } 675 676 // The position of the first non-common ancestor in the common ancestor list determines 677 // which is greater the which. 678 final LinkedList<WindowContainer> list = commonAncestor.mChildren; 679 return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast()) 680 ? 1 : -1; 681 } 682 683 private void getParents(LinkedList<WindowContainer> parents) { 684 parents.clear(); 685 WindowContainer current = this; 686 do { 687 parents.addLast(current); 688 current = current.mParent; 689 } while (current != null); 690 } 691 692 WindowContainerController getController() { 693 return mController; 694 } 695 696 void setController(WindowContainerController controller) { 697 if (mController != null && controller != null) { 698 throw new IllegalArgumentException("Can't set controller=" + mController 699 + " for container=" + this + " Already set to=" + mController); 700 } 701 if (controller != null) { 702 controller.setContainer(this); 703 } else if (mController != null) { 704 mController.setContainer(null); 705 } 706 mController = controller; 707 } 708 709 /** 710 * Dumps the names of this container children in the input print writer indenting each 711 * level with the input prefix. 712 */ 713 void dumpChildrenNames(StringBuilder out, String prefix) { 714 final String childPrefix = prefix + " "; 715 out.append(getName() + "\n"); 716 for (int i = mChildren.size() - 1; i >= 0; --i) { 717 final WindowContainer wc = mChildren.get(i); 718 out.append(childPrefix + "#" + i + " "); 719 wc.dumpChildrenNames(out, childPrefix); 720 } 721 } 722 723 String getName() { 724 return toString(); 725 } 726 727 private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) { 728 ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire(); 729 if (wrapper == null) { 730 wrapper = new ForAllWindowsConsumerWrapper(); 731 } 732 wrapper.setConsumer(consumer); 733 return wrapper; 734 } 735 736 private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> { 737 738 private Consumer<WindowState> mConsumer; 739 740 void setConsumer(Consumer<WindowState> consumer) { 741 mConsumer = consumer; 742 } 743 744 @Override 745 public boolean apply(WindowState w) { 746 mConsumer.accept(w); 747 return false; 748 } 749 750 void release() { 751 mConsumer = null; 752 mConsumerWrapperPool.release(this); 753 } 754 } 755} 756