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