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