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