WindowContainer.java revision 10abe2fe297ce1ec60c15a3bd947757aee5b14b3
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 if ((position < 0 && position != POSITION_BOTTOM) 238 || (position > mChildren.size() && position != POSITION_TOP)) { 239 throw new IllegalArgumentException("positionAt: invalid position=" + position 240 + ", children number=" + mChildren.size()); 241 } 242 243 if (position == mChildren.size() - 1) { 244 position = POSITION_TOP; 245 } else if (position == 0) { 246 position = POSITION_BOTTOM; 247 } 248 249 switch (position) { 250 case POSITION_TOP: 251 if (mChildren.peekLast() != child) { 252 mChildren.remove(child); 253 mChildren.addLast(child); 254 } 255 if (includingParents && getParent() != null) { 256 getParent().positionChildAt(POSITION_TOP, this /* child */, 257 true /* includingParents */); 258 } 259 break; 260 case POSITION_BOTTOM: 261 if (mChildren.peekFirst() != child) { 262 mChildren.remove(child); 263 mChildren.addFirst(child); 264 } 265 if (includingParents && getParent() != null) { 266 getParent().positionChildAt(POSITION_BOTTOM, this /* child */, 267 true /* includingParents */); 268 } 269 break; 270 default: 271 mChildren.remove(child); 272 mChildren.add(position, child); 273 } 274 } 275 276 /** 277 * Returns full configuration applied to this window container. 278 * This method should be used for getting settings applied in each particular level of the 279 * hierarchy. 280 */ 281 Configuration getConfiguration() { 282 return mFullConfiguration; 283 } 284 285 /** 286 * Notify that parent config changed and we need to update full configuration. 287 * @see #mFullConfiguration 288 */ 289 void onConfigurationChanged(Configuration newParentConfig) { 290 mFullConfiguration.setTo(newParentConfig); 291 mFullConfiguration.updateFrom(mOverrideConfiguration); 292 for (int i = mChildren.size() - 1; i >= 0; --i) { 293 final WindowContainer child = mChildren.get(i); 294 child.onConfigurationChanged(mFullConfiguration); 295 } 296 } 297 298 /** Returns override configuration applied to this window container. */ 299 Configuration getOverrideConfiguration() { 300 return mOverrideConfiguration; 301 } 302 303 /** 304 * Update override configuration and recalculate full config. 305 * @see #mOverrideConfiguration 306 * @see #mFullConfiguration 307 */ 308 void onOverrideConfigurationChanged(Configuration overrideConfiguration) { 309 mOverrideConfiguration.setTo(overrideConfiguration); 310 // Update full configuration of this container and all its children. 311 onConfigurationChanged(mParent != null ? mParent.getConfiguration() : Configuration.EMPTY); 312 // Update merged override config of this container and all its children. 313 onMergedOverrideConfigurationChanged(); 314 } 315 316 /** 317 * Get merged override configuration from the top of the hierarchy down to this 318 * particular instance. This should be reported to client as override config. 319 */ 320 Configuration getMergedOverrideConfiguration() { 321 return mMergedOverrideConfiguration; 322 } 323 324 /** 325 * Update merged override configuration based on corresponding parent's config and notify all 326 * its children. If there is no parent, merged override configuration will set equal to current 327 * override config. 328 * @see #mMergedOverrideConfiguration 329 */ 330 private void onMergedOverrideConfigurationChanged() { 331 if (mParent != null) { 332 mMergedOverrideConfiguration.setTo(mParent.getMergedOverrideConfiguration()); 333 mMergedOverrideConfiguration.updateFrom(mOverrideConfiguration); 334 } else { 335 mMergedOverrideConfiguration.setTo(mOverrideConfiguration); 336 } 337 for (int i = mChildren.size() - 1; i >= 0; --i) { 338 final WindowContainer child = mChildren.get(i); 339 child.onMergedOverrideConfigurationChanged(); 340 } 341 } 342 343 /** 344 * Notify that the display this container is on has changed. 345 * @param dc The new display this container is on. 346 */ 347 void onDisplayChanged(DisplayContent dc) { 348 for (int i = mChildren.size() - 1; i >= 0; --i) { 349 final WindowContainer child = mChildren.get(i); 350 child.onDisplayChanged(dc); 351 } 352 } 353 354 void setWaitingForDrawnIfResizingChanged() { 355 for (int i = mChildren.size() - 1; i >= 0; --i) { 356 final WindowContainer wc = mChildren.get(i); 357 wc.setWaitingForDrawnIfResizingChanged(); 358 } 359 } 360 361 void onResize() { 362 for (int i = mChildren.size() - 1; i >= 0; --i) { 363 final WindowContainer wc = mChildren.get(i); 364 wc.onResize(); 365 } 366 } 367 368 void onMovedByResize() { 369 for (int i = mChildren.size() - 1; i >= 0; --i) { 370 final WindowContainer wc = mChildren.get(i); 371 wc.onMovedByResize(); 372 } 373 } 374 375 void resetDragResizingChangeReported() { 376 for (int i = mChildren.size() - 1; i >= 0; --i) { 377 final WindowContainer wc = mChildren.get(i); 378 wc.resetDragResizingChangeReported(); 379 } 380 } 381 382 void forceWindowsScaleableInTransaction(boolean force) { 383 for (int i = mChildren.size() - 1; i >= 0; --i) { 384 final WindowContainer wc = mChildren.get(i); 385 wc.forceWindowsScaleableInTransaction(force); 386 } 387 } 388 389 boolean isAnimating() { 390 for (int j = mChildren.size() - 1; j >= 0; j--) { 391 final WindowContainer wc = mChildren.get(j); 392 if (wc.isAnimating()) { 393 return true; 394 } 395 } 396 return false; 397 } 398 399 void sendAppVisibilityToClients() { 400 for (int i = mChildren.size() - 1; i >= 0; --i) { 401 final WindowContainer wc = mChildren.get(i); 402 wc.sendAppVisibilityToClients(); 403 } 404 } 405 406 void setVisibleBeforeClientHidden() { 407 for (int i = mChildren.size() - 1; i >= 0; --i) { 408 final WindowContainer wc = mChildren.get(i); 409 wc.setVisibleBeforeClientHidden(); 410 } 411 } 412 413 /** 414 * Returns true if the container or one of its children as some content it can display or wants 415 * to display (e.g. app views or saved surface). 416 * 417 * NOTE: While this method will return true if the there is some content to display, it doesn't 418 * mean the container is visible. Use {@link #isVisible()} to determine if the container is 419 * visible. 420 */ 421 boolean hasContentToDisplay() { 422 for (int i = mChildren.size() - 1; i >= 0; --i) { 423 final WindowContainer wc = mChildren.get(i); 424 if (wc.hasContentToDisplay()) { 425 return true; 426 } 427 } 428 return false; 429 } 430 431 /** 432 * Returns true if the container or one of its children is considered visible from the 433 * WindowManager perspective which usually means valid surface and some other internal state 434 * are true. 435 * 436 * NOTE: While this method will return true if the surface is visible, it doesn't mean the 437 * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if 438 * the container has any content to display. 439 */ 440 boolean isVisible() { 441 // TODO: Will this be more correct if it checks the visibility of its parents? 442 // It depends...For example, Tasks and Stacks are only visible if there children are visible 443 // but, WindowState are not visible if there parent are not visible. Maybe have the 444 // container specify which direction to traverse for visibility? 445 for (int i = mChildren.size() - 1; i >= 0; --i) { 446 final WindowContainer wc = mChildren.get(i); 447 if (wc.isVisible()) { 448 return true; 449 } 450 } 451 return false; 452 } 453 454 /** Returns the top child container. */ 455 E getTopChild() { 456 return mChildren.peekLast(); 457 } 458 459 /** Returns true if there is still a removal being deferred */ 460 boolean checkCompleteDeferredRemoval() { 461 boolean stillDeferringRemoval = false; 462 463 for (int i = mChildren.size() - 1; i >= 0; --i) { 464 final WindowContainer wc = mChildren.get(i); 465 stillDeferringRemoval |= wc.checkCompleteDeferredRemoval(); 466 } 467 468 return stillDeferringRemoval; 469 } 470 471 /** Checks if all windows in an app are all drawn and shows them if needed. */ 472 void checkAppWindowsReadyToShow() { 473 for (int i = mChildren.size() - 1; i >= 0; --i) { 474 final WindowContainer wc = mChildren.get(i); 475 wc.checkAppWindowsReadyToShow(); 476 } 477 } 478 479 /** Step currently ongoing animation for App window containers. */ 480 void stepAppWindowsAnimation(long currentTime) { 481 for (int i = mChildren.size() - 1; i >= 0; --i) { 482 final WindowContainer wc = mChildren.get(i); 483 wc.stepAppWindowsAnimation(currentTime); 484 } 485 } 486 487 void onAppTransitionDone() { 488 for (int i = mChildren.size() - 1; i >= 0; --i) { 489 final WindowContainer wc = mChildren.get(i); 490 wc.onAppTransitionDone(); 491 } 492 } 493 494 void setOrientation(int orientation) { 495 mOrientation = orientation; 496 } 497 498 /** 499 * Returns the specified orientation for this window container or one of its children is there 500 * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no 501 * specification is set. 502 * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a 503 * specification... 504 */ 505 int getOrientation() { 506 507 if (!fillsParent() || !isVisible()) { 508 // Ignore invisible containers or containers that don't completely fills their parents. 509 return SCREEN_ORIENTATION_UNSET; 510 } 511 512 // The container fills its parent so we can use it orientation if it has one specified, 513 // otherwise we prefer to use the orientation of its topmost child that has one 514 // specified and fall back on this container's unset or unspecified value as a candidate 515 // if none of the children have a better candidate for the orientation. 516 if (mOrientation != SCREEN_ORIENTATION_UNSET 517 && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { 518 return mOrientation; 519 } 520 int candidate = mOrientation; 521 522 for (int i = mChildren.size() - 1; i >= 0; --i) { 523 final WindowContainer wc = mChildren.get(i); 524 525 final int orientation = wc.getOrientation(); 526 if (orientation == SCREEN_ORIENTATION_BEHIND) { 527 // container wants us to use the orientation of the container behind it. See if we 528 // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to 529 // look behind this container. 530 candidate = orientation; 531 continue; 532 } 533 534 if (orientation == SCREEN_ORIENTATION_UNSET) { 535 continue; 536 } 537 538 if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) { 539 // Use the orientation if the container fills its parent or requested an explicit 540 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED. 541 return orientation; 542 } 543 } 544 545 return candidate; 546 } 547 548 /** 549 * Returns true if this container is opaque and fills all the space made available by its parent 550 * container. 551 * 552 * NOTE: It is possible for this container to occupy more space than the parent has (or less), 553 * this is just a signal from the client to window manager stating its intent, but not what it 554 * actually does. 555 */ 556 boolean fillsParent() { 557 return false; 558 } 559 560 // TODO: Users would have their own window containers under the display container? 561 void switchUser() { 562 for (int i = mChildren.size() - 1; i >= 0; --i) { 563 mChildren.get(i).switchUser(); 564 } 565 } 566 567 /** 568 * For all windows at or below this container call the callback. 569 * @param callback Calls the {@link ToBooleanFunction#apply} method for each window found and 570 * stops the search if {@link ToBooleanFunction#apply} returns true. 571 * @param traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of 572 * z-order, else from bottom-to-top. 573 * @return True if the search ended before we reached the end of the hierarchy due to 574 * {@link ToBooleanFunction#apply} returning true. 575 */ 576 boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { 577 if (traverseTopToBottom) { 578 for (int i = mChildren.size() - 1; i >= 0; --i) { 579 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) { 580 return true; 581 } 582 } 583 } else { 584 final int count = mChildren.size(); 585 for (int i = 0; i < count; i++) { 586 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) { 587 return true; 588 } 589 } 590 } 591 return false; 592 } 593 594 void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) { 595 ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback); 596 forAllWindows(wrapper, traverseTopToBottom); 597 wrapper.release(); 598 } 599 600 WindowState getWindow(Predicate<WindowState> callback) { 601 for (int i = mChildren.size() - 1; i >= 0; --i) { 602 final WindowState w = mChildren.get(i).getWindow(callback); 603 if (w != null) { 604 return w; 605 } 606 } 607 608 return null; 609 } 610 611 /** 612 * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than 613 * the input container in terms of z-order. 614 */ 615 @Override 616 public int compareTo(WindowContainer other) { 617 if (this == other) { 618 return 0; 619 } 620 621 if (mParent != null && mParent == other.mParent) { 622 final LinkedList<WindowContainer> list = mParent.mChildren; 623 return list.indexOf(this) > list.indexOf(other) ? 1 : -1; 624 } 625 626 final LinkedList<WindowContainer> thisParentChain = mTmpChain1; 627 final LinkedList<WindowContainer> otherParentChain = mTmpChain2; 628 getParents(thisParentChain); 629 other.getParents(otherParentChain); 630 631 // Find the common ancestor of both containers. 632 WindowContainer commonAncestor = null; 633 WindowContainer thisTop = thisParentChain.peekLast(); 634 WindowContainer otherTop = otherParentChain.peekLast(); 635 while (thisTop != null && otherTop != null && thisTop == otherTop) { 636 commonAncestor = thisParentChain.removeLast(); 637 otherParentChain.removeLast(); 638 thisTop = thisParentChain.peekLast(); 639 otherTop = otherParentChain.peekLast(); 640 } 641 642 // Containers don't belong to the same hierarchy??? 643 if (commonAncestor == null) { 644 throw new IllegalArgumentException("No in the same hierarchy this=" 645 + thisParentChain + " other=" + otherParentChain); 646 } 647 648 // Children are always considered greater than their parents, so if one of the containers 649 // we are comparing it the parent of the other then whichever is the child is greater. 650 if (commonAncestor == this) { 651 return -1; 652 } else if (commonAncestor == other) { 653 return 1; 654 } 655 656 // The position of the first non-common ancestor in the common ancestor list determines 657 // which is greater the which. 658 final LinkedList<WindowContainer> list = commonAncestor.mChildren; 659 return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast()) 660 ? 1 : -1; 661 } 662 663 private void getParents(LinkedList<WindowContainer> parents) { 664 parents.clear(); 665 WindowContainer current = this; 666 do { 667 parents.addLast(current); 668 current = current.mParent; 669 } while (current != null); 670 } 671 672 WindowContainerController getController() { 673 return mController; 674 } 675 676 void setController(WindowContainerController controller) { 677 if (mController != null && controller != null) { 678 throw new IllegalArgumentException("Can't set controller=" + mController 679 + " for container=" + this + " Already set to=" + mController); 680 } 681 if (controller != null) { 682 controller.setContainer(this); 683 } else if (mController != null) { 684 mController.setContainer(null); 685 } 686 mController = controller; 687 } 688 689 /** 690 * Dumps the names of this container children in the input print writer indenting each 691 * level with the input prefix. 692 */ 693 void dumpChildrenNames(StringBuilder out, String prefix) { 694 final String childPrefix = prefix + " "; 695 out.append(getName() + "\n"); 696 for (int i = mChildren.size() - 1; i >= 0; --i) { 697 final WindowContainer wc = mChildren.get(i); 698 out.append(childPrefix + "#" + i + " "); 699 wc.dumpChildrenNames(out, childPrefix); 700 } 701 } 702 703 String getName() { 704 return toString(); 705 } 706 707 private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) { 708 ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire(); 709 if (wrapper == null) { 710 wrapper = new ForAllWindowsConsumerWrapper(); 711 } 712 wrapper.setConsumer(consumer); 713 return wrapper; 714 } 715 716 private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> { 717 718 private Consumer<WindowState> mConsumer; 719 720 void setConsumer(Consumer<WindowState> consumer) { 721 mConsumer = consumer; 722 } 723 724 @Override 725 public boolean apply(WindowState w) { 726 mConsumer.accept(w); 727 return false; 728 } 729 730 void release() { 731 mConsumer = null; 732 mConsumerWrapperPool.release(this); 733 } 734 } 735} 736