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