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