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