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