1 /* 2 * Copyright (C) 2020 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 17 package com.android.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; 21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 23 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; 24 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; 25 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS; 26 27 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; 28 import static com.android.internal.util.Preconditions.checkState; 29 import static com.android.server.wm.DisplayAreaProto.FEATURE_ID; 30 import static com.android.server.wm.DisplayAreaProto.IS_IGNORING_ORIENTATION_REQUEST; 31 import static com.android.server.wm.DisplayAreaProto.IS_ORGANIZED; 32 import static com.android.server.wm.DisplayAreaProto.IS_ROOT_DISPLAY_AREA; 33 import static com.android.server.wm.DisplayAreaProto.IS_TASK_DISPLAY_AREA; 34 import static com.android.server.wm.DisplayAreaProto.NAME; 35 import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER; 36 import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA; 37 38 import android.annotation.Nullable; 39 import android.content.pm.ActivityInfo; 40 import android.content.pm.ActivityInfo.ScreenOrientation; 41 import android.content.res.Configuration; 42 import android.graphics.Rect; 43 import android.util.proto.ProtoOutputStream; 44 import android.window.DisplayAreaInfo; 45 import android.window.IDisplayAreaOrganizer; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.protolog.common.ProtoLog; 49 import com.android.server.policy.WindowManagerPolicy; 50 51 import java.io.PrintWriter; 52 import java.util.Comparator; 53 import java.util.function.BiFunction; 54 import java.util.function.Consumer; 55 import java.util.function.Function; 56 import java.util.function.Predicate; 57 /** 58 * Container for grouping WindowContainer below DisplayContent. 59 * 60 * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and 61 * can be leashed. 62 * 63 * DisplayAreas can contain nested DisplayAreas. 64 * 65 * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order: 66 * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks. 67 * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks. 68 * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container. 69 * 70 * @param <T> type of the children of the DisplayArea. 71 */ 72 public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { 73 74 protected final Type mType; 75 private final String mName; 76 final int mFeatureId; 77 private final DisplayAreaOrganizerController mOrganizerController; 78 IDisplayAreaOrganizer mOrganizer; 79 private final Configuration mTmpConfiguration = new Configuration(); 80 81 /** 82 * Prevent duplicate calls to onDisplayAreaAppeared, or early call of onDisplayAreaInfoChanged. 83 */ 84 @VisibleForTesting 85 boolean mDisplayAreaAppearedSent; 86 87 /** 88 * Whether this {@link DisplayArea} should ignore fixed-orientation request. If {@code true}, it 89 * can never specify orientation, but shows the fixed-orientation apps below it in the 90 * letterbox; otherwise, it rotates based on the fixed-orientation request. 91 * 92 * <p>Note: use {@link #getIgnoreOrientationRequest} to access outside of {@link 93 * #setIgnoreOrientationRequest} since the value can be overridden at runtime on a device level. 94 */ 95 protected boolean mSetIgnoreOrientationRequest; 96 DisplayArea(WindowManagerService wms, Type type, String name)97 DisplayArea(WindowManagerService wms, Type type, String name) { 98 this(wms, type, name, FEATURE_UNDEFINED); 99 } 100 DisplayArea(WindowManagerService wms, Type type, String name, int featureId)101 DisplayArea(WindowManagerService wms, Type type, String name, int featureId) { 102 super(wms); 103 // TODO(display-area): move this up to ConfigurationContainer 104 setOverrideOrientation(SCREEN_ORIENTATION_UNSET); 105 mType = type; 106 mName = name; 107 mFeatureId = featureId; 108 mRemoteToken = new RemoteToken(this); 109 mOrganizerController = 110 wms.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController; 111 } 112 113 @Override onChildPositionChanged(WindowContainer child)114 void onChildPositionChanged(WindowContainer child) { 115 super.onChildPositionChanged(child); 116 117 // Verify that we have proper ordering 118 Type.checkChild(mType, Type.typeOf(child)); 119 120 if (child instanceof Task) { 121 // TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings. 122 // They might need a separate type. 123 return; 124 } 125 126 for (int i = 1; i < getChildCount(); i++) { 127 final WindowContainer top = getChildAt(i - 1); 128 final WindowContainer bottom = getChildAt(i); 129 if (child == top || child == bottom) { 130 Type.checkSiblings(Type.typeOf(top), Type.typeOf(bottom)); 131 } 132 } 133 } 134 135 @Override positionChildAt(int position, T child, boolean includingParents)136 void positionChildAt(int position, T child, boolean includingParents) { 137 if (child.asDisplayArea() == null) { 138 // Reposition other window containers as normal. 139 super.positionChildAt(position, child, includingParents); 140 return; 141 } 142 143 final int targetPosition = findPositionForChildDisplayArea(position, child.asDisplayArea()); 144 super.positionChildAt(targetPosition, child, false /* includingParents */); 145 146 final WindowContainer parent = getParent(); 147 if (includingParents && parent != null 148 && (position == POSITION_TOP || position == POSITION_BOTTOM)) { 149 parent.positionChildAt(position, this /* child */, true /* includingParents */); 150 } 151 } 152 153 @Override 154 @ScreenOrientation getOrientation(int candidate)155 int getOrientation(int candidate) { 156 final int orientation = super.getOrientation(candidate); 157 if (shouldIgnoreOrientationRequest(orientation)) { 158 // In all the other case, mLastOrientationSource will be reassigned to a new value 159 mLastOrientationSource = null; 160 return SCREEN_ORIENTATION_UNSET; 161 } 162 return orientation; 163 } 164 165 @Override handlesOrientationChangeFromDescendant(@creenOrientation int orientation)166 boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { 167 return !shouldIgnoreOrientationRequest(orientation) 168 && super.handlesOrientationChangeFromDescendant(orientation); 169 } 170 171 @Override onDescendantOrientationChanged(@ullable WindowContainer requestingContainer)172 boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) { 173 // If this is set to ignore the orientation request, we don't propagate descendant 174 // orientation request. 175 final int orientation = requestingContainer != null 176 ? requestingContainer.getOverrideOrientation() 177 : SCREEN_ORIENTATION_UNSET; 178 return !shouldIgnoreOrientationRequest(orientation) 179 && super.onDescendantOrientationChanged(requestingContainer); 180 } 181 182 /** 183 * Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and 184 * windows below it. 185 * 186 * @return Whether the display orientation changed after calling this method. 187 */ setIgnoreOrientationRequest(boolean ignoreOrientationRequest)188 boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) { 189 if (mSetIgnoreOrientationRequest == ignoreOrientationRequest) { 190 return false; 191 } 192 mSetIgnoreOrientationRequest = ignoreOrientationRequest; 193 194 // Check whether we should notify Display to update orientation. 195 if (mDisplayContent == null) { 196 return false; 197 } 198 199 if (mDisplayContent.mFocusedApp != null) { 200 // We record the last focused TDA that respects orientation request, check if this 201 // change may affect it. 202 mDisplayContent.onLastFocusedTaskDisplayAreaChanged( 203 mDisplayContent.mFocusedApp.getDisplayArea()); 204 } 205 206 // The orientation request from this DA may now be respected. 207 if (!ignoreOrientationRequest) { 208 return mDisplayContent.updateOrientation(); 209 } 210 211 final int lastOrientation = mDisplayContent.getLastOrientation(); 212 final WindowContainer lastOrientationSource = mDisplayContent.getLastOrientationSource(); 213 if (lastOrientation == SCREEN_ORIENTATION_UNSET 214 || lastOrientation == SCREEN_ORIENTATION_UNSPECIFIED) { 215 // Orientation won't be changed. 216 return false; 217 } 218 if (lastOrientationSource == null || lastOrientationSource.isDescendantOf(this)) { 219 // Try update if the orientation may be affected. 220 return mDisplayContent.updateOrientation(); 221 } 222 return false; 223 } 224 225 @Override setAlwaysOnTop(boolean alwaysOnTop)226 public void setAlwaysOnTop(boolean alwaysOnTop) { 227 if (isAlwaysOnTop() == alwaysOnTop) { 228 return; 229 } 230 super.setAlwaysOnTop(alwaysOnTop); 231 // positionChildAtTop() must be called even when always on top gets turned off because 232 // we need to make sure that the display area is moved from among always on top containers 233 // to below other always on top containers. Since the position the display area should be 234 // inserted into is calculated properly in {@link DisplayContent#getTopInsertPosition()} 235 // in both cases, we can just request that the root task is put at top here. 236 if (getParent().asDisplayArea() != null) { 237 getParent().asDisplayArea().positionChildAt(POSITION_TOP, this, 238 false /* includingParents */); 239 } 240 } 241 242 /** 243 * @return {@value true} if we need to ignore the orientation in input. 244 */ shouldIgnoreOrientationRequest(@creenOrientation int orientation)245 boolean shouldIgnoreOrientationRequest(@ScreenOrientation int orientation) { 246 // We always respect orientation request for ActivityInfo.SCREEN_ORIENTATION_LOCKED 247 // ActivityInfo.SCREEN_ORIENTATION_NOSENSOR. 248 // Main use case why this is important is Camera apps that rely on those 249 // properties to ensure that they will be able to determine Camera preview 250 // orientation correctly 251 if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED 252 || orientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { 253 return false; 254 } 255 return getIgnoreOrientationRequest() 256 && !shouldRespectOrientationRequestDueToPerAppOverride(); 257 } 258 shouldRespectOrientationRequestDueToPerAppOverride()259 private boolean shouldRespectOrientationRequestDueToPerAppOverride() { 260 if (mDisplayContent == null) { 261 return false; 262 } 263 ActivityRecord activity = mDisplayContent.topRunningActivity( 264 /* considerKeyguardState= */ true); 265 return activity != null && activity.getTaskFragment() != null 266 // Checking TaskFragment rather than ActivityRecord to ensure that transition 267 // between fullscreen and PiP would work well. Checking TaskFragment rather than 268 // Task to ensure that Activity Embedding is excluded. 269 && activity.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN 270 && activity.mLetterboxUiController.isOverrideRespectRequestedOrientationEnabled(); 271 } 272 getIgnoreOrientationRequest()273 boolean getIgnoreOrientationRequest() { 274 // Adding an exception for when ignoreOrientationRequest is overridden at runtime for all 275 // DisplayArea-s. For example, this is needed for the Kids Mode since many Kids apps aren't 276 // optimised to support both orientations and it will be hard for kids to understand the 277 // app compat mode. 278 return mSetIgnoreOrientationRequest && !mWmService.isIgnoreOrientationRequestDisabled(); 279 } 280 281 /** 282 * When a {@link DisplayArea} is repositioned, it should only be moved among its siblings of the 283 * same {@link Type}. 284 * For example, when a {@link DisplayArea} of {@link Type#ANY} is repositioned, it shouldn't be 285 * moved above any {@link Type#ABOVE_TASKS} siblings, or below any {@link Type#BELOW_TASKS} 286 * siblings. 287 */ findPositionForChildDisplayArea(int requestPosition, DisplayArea child)288 private int findPositionForChildDisplayArea(int requestPosition, DisplayArea child) { 289 if (child.getParent() != this) { 290 throw new IllegalArgumentException("positionChildAt: container=" + child.getName() 291 + " is not a child of container=" + getName() 292 + " current parent=" + child.getParent()); 293 } 294 295 // The max possible position we can insert the child at. 296 int maxPosition = findMaxPositionForChildDisplayArea(child); 297 // The min possible position we can insert the child at. 298 int minPosition = findMinPositionForChildDisplayArea(child); 299 300 // Place all non-always-on-top containers below always-on-top ones. 301 int alwaysOnTopCount = 0; 302 for (int i = minPosition; i <= maxPosition; i++) { 303 if (mChildren.get(i).isAlwaysOnTop()) { 304 alwaysOnTopCount++; 305 } 306 } 307 if (child.isAlwaysOnTop()) { 308 minPosition = maxPosition - alwaysOnTopCount + 1; 309 } else { 310 maxPosition -= alwaysOnTopCount; 311 } 312 return Math.max(Math.min(requestPosition, maxPosition), minPosition); 313 } 314 findMaxPositionForChildDisplayArea(DisplayArea child)315 private int findMaxPositionForChildDisplayArea(DisplayArea child) { 316 final Type childType = Type.typeOf(child); 317 for (int i = mChildren.size() - 1; i > 0; i--) { 318 if (Type.typeOf(getChildAt(i)) == childType) { 319 return i; 320 } 321 } 322 return 0; 323 } 324 findMinPositionForChildDisplayArea(DisplayArea child)325 private int findMinPositionForChildDisplayArea(DisplayArea child) { 326 final Type childType = Type.typeOf(child); 327 for (int i = 0; i < mChildren.size(); i++) { 328 if (Type.typeOf(getChildAt(i)) == childType) { 329 return i; 330 } 331 } 332 return mChildren.size() - 1; 333 } 334 335 @Override needsZBoost()336 boolean needsZBoost() { 337 // Z Boost should only happen at or below the ActivityStack level. 338 return false; 339 } 340 341 @Override fillsParent()342 boolean fillsParent() { 343 return true; 344 } 345 346 @Override getName()347 String getName() { 348 return mName; 349 } 350 351 @Override toString()352 public String toString() { 353 return mName + "@" + System.identityHashCode(this); 354 } 355 356 @Override dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel)357 public void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) { 358 if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) { 359 return; 360 } 361 362 final long token = proto.start(fieldId); 363 super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); 364 proto.write(NAME, mName); 365 proto.write(IS_TASK_DISPLAY_AREA, isTaskDisplayArea()); 366 proto.write(IS_ROOT_DISPLAY_AREA, asRootDisplayArea() != null); 367 proto.write(FEATURE_ID, mFeatureId); 368 proto.write(IS_ORGANIZED, isOrganized()); 369 proto.write(IS_IGNORING_ORIENTATION_REQUEST, getIgnoreOrientationRequest()); 370 proto.end(token); 371 } 372 373 @Override dump(PrintWriter pw, String prefix, boolean dumpAll)374 void dump(PrintWriter pw, String prefix, boolean dumpAll) { 375 super.dump(pw, prefix, dumpAll); 376 if (mSetIgnoreOrientationRequest) { 377 pw.println(prefix + "mSetIgnoreOrientationRequest=true"); 378 } 379 if (hasRequestedOverrideConfiguration()) { 380 pw.println(prefix + "overrideConfig=" + getRequestedOverrideConfiguration()); 381 } 382 } 383 dumpChildDisplayArea(PrintWriter pw, String prefix, boolean dumpAll)384 void dumpChildDisplayArea(PrintWriter pw, String prefix, boolean dumpAll) { 385 final String doublePrefix = prefix + " "; 386 for (int i = getChildCount() - 1; i >= 0; i--) { 387 final DisplayArea<?> childArea = getChildAt(i).asDisplayArea(); 388 if (childArea == null) { 389 continue; 390 } 391 pw.print(prefix + "* " + childArea.getName()); 392 if (childArea.isOrganized()) { 393 pw.print(" (organized)"); 394 } 395 pw.println(); 396 if (childArea.isTaskDisplayArea()) { 397 // TaskDisplayArea can only contain task. And it is already printed by display. 398 continue; 399 } 400 childArea.dump(pw, doublePrefix, dumpAll); 401 childArea.dumpChildDisplayArea(pw, doublePrefix, dumpAll); 402 } 403 } 404 405 @Override getProtoFieldId()406 long getProtoFieldId() { 407 return DISPLAY_AREA; 408 } 409 410 @Override asDisplayArea()411 final DisplayArea asDisplayArea() { 412 return this; 413 } 414 415 /** Cheap way of doing cast and instanceof. */ asTokens()416 DisplayArea.Tokens asTokens() { 417 return null; 418 } 419 420 @Override getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom, ActivityRecord boundary)421 ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom, 422 ActivityRecord boundary) { 423 if (mType == Type.ABOVE_TASKS) { 424 return null; 425 } 426 return super.getActivity(callback, traverseTopToBottom, boundary); 427 } 428 429 @Override getTask(Predicate<Task> callback, boolean traverseTopToBottom)430 Task getTask(Predicate<Task> callback, boolean traverseTopToBottom) { 431 if (mType == Type.ABOVE_TASKS) { 432 return null; 433 } 434 return super.getTask(callback, traverseTopToBottom); 435 } 436 437 @Override getRootTask(Predicate<Task> callback, boolean traverseTopToBottom)438 Task getRootTask(Predicate<Task> callback, boolean traverseTopToBottom) { 439 if (mType == Type.ABOVE_TASKS) { 440 return null; 441 } 442 return super.getRootTask(callback, traverseTopToBottom); 443 } 444 445 @Override forAllActivities(Predicate<ActivityRecord> callback, boolean traverseTopToBottom)446 boolean forAllActivities(Predicate<ActivityRecord> callback, boolean traverseTopToBottom) { 447 if (mType == Type.ABOVE_TASKS) { 448 return false; 449 } 450 return super.forAllActivities(callback, traverseTopToBottom); 451 } 452 453 @Override forAllActivities(Consumer<ActivityRecord> callback, boolean traverseTopToBottom)454 void forAllActivities(Consumer<ActivityRecord> callback, boolean traverseTopToBottom) { 455 if (mType == Type.ABOVE_TASKS) { 456 return; 457 } 458 super.forAllActivities(callback, traverseTopToBottom); 459 } 460 461 @Override forAllRootTasks(Predicate<Task> callback, boolean traverseTopToBottom)462 boolean forAllRootTasks(Predicate<Task> callback, boolean traverseTopToBottom) { 463 if (mType == Type.ABOVE_TASKS) { 464 return false; 465 } 466 return super.forAllRootTasks(callback, traverseTopToBottom); 467 } 468 469 @Override forAllTasks(Predicate<Task> callback)470 boolean forAllTasks(Predicate<Task> callback) { 471 if (mType == Type.ABOVE_TASKS) { 472 return false; 473 } 474 return super.forAllTasks(callback); 475 } 476 477 @Override forAllLeafTasks(Predicate<Task> callback)478 boolean forAllLeafTasks(Predicate<Task> callback) { 479 if (mType == Type.ABOVE_TASKS) { 480 return false; 481 } 482 return super.forAllLeafTasks(callback); 483 } 484 485 @Override forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom)486 void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) { 487 if (mType == Type.ABOVE_TASKS) { 488 return; 489 } 490 super.forAllLeafTasks(callback, traverseTopToBottom); 491 } 492 493 @Override forAllLeafTaskFragments(Predicate<TaskFragment> callback)494 boolean forAllLeafTaskFragments(Predicate<TaskFragment> callback) { 495 if (mType == Type.ABOVE_TASKS) { 496 return false; 497 } 498 return super.forAllLeafTaskFragments(callback); 499 } 500 501 @Override forAllDisplayAreas(Consumer<DisplayArea> callback)502 void forAllDisplayAreas(Consumer<DisplayArea> callback) { 503 super.forAllDisplayAreas(callback); 504 callback.accept(this); 505 } 506 507 @Override forAllTaskDisplayAreas(Predicate<TaskDisplayArea> callback, boolean traverseTopToBottom)508 boolean forAllTaskDisplayAreas(Predicate<TaskDisplayArea> callback, 509 boolean traverseTopToBottom) { 510 // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. 511 if (mType != DisplayArea.Type.ANY) { 512 return false; 513 } 514 515 int childCount = mChildren.size(); 516 int i = traverseTopToBottom ? childCount - 1 : 0; 517 while (i >= 0 && i < childCount) { 518 T child = mChildren.get(i); 519 // Only traverse if the child is a DisplayArea. 520 if (child.asDisplayArea() != null && child.asDisplayArea() 521 .forAllTaskDisplayAreas(callback, traverseTopToBottom)) { 522 return true; 523 } 524 i += traverseTopToBottom ? -1 : 1; 525 } 526 return false; 527 } 528 529 @Override forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom)530 void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) { 531 // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. 532 if (mType != DisplayArea.Type.ANY) { 533 return; 534 } 535 536 int childCount = mChildren.size(); 537 int i = traverseTopToBottom ? childCount - 1 : 0; 538 while (i >= 0 && i < childCount) { 539 T child = mChildren.get(i); 540 // Only traverse if the child is a DisplayArea. 541 if (child.asDisplayArea() != null) { 542 child.asDisplayArea().forAllTaskDisplayAreas(callback, traverseTopToBottom); 543 } 544 i += traverseTopToBottom ? -1 : 1; 545 } 546 } 547 548 @Nullable 549 @Override reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator, @Nullable R initValue, boolean traverseTopToBottom)550 <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator, 551 @Nullable R initValue, boolean traverseTopToBottom) { 552 // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. 553 if (mType != DisplayArea.Type.ANY) { 554 return initValue; 555 } 556 557 int childCount = mChildren.size(); 558 int i = traverseTopToBottom ? childCount - 1 : 0; 559 R result = initValue; 560 while (i >= 0 && i < childCount) { 561 T child = mChildren.get(i); 562 // Only traverse if the child is a DisplayArea. 563 if (child.asDisplayArea() != null) { 564 result = (R) child.asDisplayArea() 565 .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom); 566 } 567 i += traverseTopToBottom ? -1 : 1; 568 } 569 return result; 570 } 571 572 @Nullable 573 @Override getItemFromDisplayAreas(Function<DisplayArea, R> callback)574 <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) { 575 final R item = super.getItemFromDisplayAreas(callback); 576 return item != null ? item : callback.apply(this); 577 } 578 579 @Nullable 580 @Override getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean traverseTopToBottom)581 <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, 582 boolean traverseTopToBottom) { 583 // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. 584 if (mType != DisplayArea.Type.ANY) { 585 return null; 586 } 587 588 int childCount = mChildren.size(); 589 int i = traverseTopToBottom ? childCount - 1 : 0; 590 while (i >= 0 && i < childCount) { 591 T child = mChildren.get(i); 592 // Only traverse if the child is a DisplayArea. 593 if (child.asDisplayArea() != null) { 594 R result = (R) child.asDisplayArea() 595 .getItemFromTaskDisplayAreas(callback, traverseTopToBottom); 596 if (result != null) { 597 return result; 598 } 599 } 600 i += traverseTopToBottom ? -1 : 1; 601 } 602 return null; 603 } 604 setOrganizer(IDisplayAreaOrganizer organizer)605 void setOrganizer(IDisplayAreaOrganizer organizer) { 606 setOrganizer(organizer, false /* skipDisplayAreaAppeared */); 607 } 608 setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared)609 void setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared) { 610 if (mOrganizer == organizer) return; 611 if (mDisplayContent == null || !mDisplayContent.isTrusted()) { 612 throw new IllegalStateException( 613 "Don't organize or trigger events for unavailable or untrusted display."); 614 } 615 IDisplayAreaOrganizer lastOrganizer = mOrganizer; 616 // Update the new display area organizer before calling sendDisplayAreaVanished since it 617 // could result in a new SurfaceControl getting created that would notify the old organizer 618 // about it. 619 mOrganizer = organizer; 620 sendDisplayAreaVanished(lastOrganizer); 621 if (!skipDisplayAreaAppeared) { 622 sendDisplayAreaAppeared(); 623 } else if (organizer != null) { 624 // Set as sent since the DisplayAreaAppearedInfo will be sent back when registered. 625 mDisplayAreaAppearedSent = true; 626 } 627 } 628 629 @VisibleForTesting sendDisplayAreaAppeared()630 void sendDisplayAreaAppeared() { 631 if (mOrganizer == null || mDisplayAreaAppearedSent) return; 632 mOrganizerController.onDisplayAreaAppeared(mOrganizer, this); 633 mDisplayAreaAppearedSent = true; 634 } 635 636 @VisibleForTesting sendDisplayAreaInfoChanged()637 void sendDisplayAreaInfoChanged() { 638 if (mOrganizer == null || !mDisplayAreaAppearedSent) return; 639 mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this); 640 } 641 642 @VisibleForTesting sendDisplayAreaVanished(IDisplayAreaOrganizer organizer)643 void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) { 644 if (organizer == null || !mDisplayAreaAppearedSent) return; 645 migrateToNewSurfaceControl(getSyncTransaction()); 646 mOrganizerController.onDisplayAreaVanished(organizer, this); 647 mDisplayAreaAppearedSent = false; 648 } 649 650 @Override onConfigurationChanged(Configuration newParentConfig)651 public void onConfigurationChanged(Configuration newParentConfig) { 652 mTransitionController.collectForDisplayAreaChange(this); 653 mTmpConfiguration.setTo(getConfiguration()); 654 super.onConfigurationChanged(newParentConfig); 655 656 if (mOrganizer != null && getConfiguration().diff(mTmpConfiguration) != 0) { 657 sendDisplayAreaInfoChanged(); 658 } 659 } 660 661 @Override resolveOverrideConfiguration(Configuration newParentConfiguration)662 void resolveOverrideConfiguration(Configuration newParentConfiguration) { 663 super.resolveOverrideConfiguration(newParentConfiguration); 664 final Configuration resolvedConfig = getResolvedOverrideConfiguration(); 665 final Rect overrideBounds = resolvedConfig.windowConfiguration.getBounds(); 666 final Rect overrideAppBounds = resolvedConfig.windowConfiguration.getAppBounds(); 667 final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); 668 669 // If there is no override of appBounds, restrict appBounds to the override bounds. 670 if (!overrideBounds.isEmpty() && (overrideAppBounds == null || overrideAppBounds.isEmpty()) 671 && parentAppBounds != null && !parentAppBounds.isEmpty()) { 672 final Rect appBounds = new Rect(overrideBounds); 673 appBounds.intersect(parentAppBounds); 674 resolvedConfig.windowConfiguration.setAppBounds(appBounds); 675 } 676 } 677 678 @Override isOrganized()679 boolean isOrganized() { 680 return mOrganizer != null; 681 } 682 683 getDisplayAreaInfo()684 DisplayAreaInfo getDisplayAreaInfo() { 685 final DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(), 686 getDisplayContent().getDisplayId(), mFeatureId); 687 final RootDisplayArea root = getRootDisplayArea(); 688 info.rootDisplayAreaId = root == null ? getDisplayContent().mFeatureId : root.mFeatureId; 689 info.configuration.setTo(getConfiguration()); 690 return info; 691 } 692 693 /** 694 * Gets the stable bounds of the DisplayArea, which is the bounds excluding insets for 695 * navigation bar, cutout, and status bar. 696 */ getStableRect(Rect out)697 void getStableRect(Rect out) { 698 if (mDisplayContent == null) { 699 getBounds(out); 700 return; 701 } 702 703 // Intersect with the display stable bounds to get the DisplayArea stable bounds. 704 mDisplayContent.getStableRect(out); 705 out.intersect(getBounds()); 706 } 707 708 @Override providesMaxBounds()709 public boolean providesMaxBounds() { 710 return true; 711 } 712 isTaskDisplayArea()713 boolean isTaskDisplayArea() { 714 return false; 715 } 716 717 @Override removeImmediately()718 void removeImmediately() { 719 setOrganizer(null); 720 super.removeImmediately(); 721 } 722 723 @Override getDisplayArea()724 DisplayArea getDisplayArea() { 725 return this; 726 } 727 728 /** 729 * DisplayArea that contains WindowTokens, and orders them according to their type. 730 */ 731 public static class Tokens extends DisplayArea<WindowToken> { 732 int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 733 734 private final Comparator<WindowToken> mWindowComparator = 735 Comparator.comparingInt(WindowToken::getWindowLayerFromType); 736 737 private final Predicate<WindowState> mGetOrientingWindow = w -> { 738 if (!w.isVisible() || !w.mLegacyPolicyVisibilityAfterAnim) { 739 return false; 740 } 741 final WindowManagerPolicy policy = mWmService.mPolicy; 742 if (policy.isKeyguardHostWindow(w.mAttrs)) { 743 // Ignore the orientation of keyguard if it is going away or is not showing while 744 // the device is fully awake. In other words, use the orientation of keyguard if 745 // its window is visible while the device is going to sleep or is sleeping. 746 if (!mDisplayContent.isKeyguardLocked() 747 && mDisplayContent.getDisplayPolicy().isAwake() 748 // Device is not going to sleep. 749 && policy.okToAnimate(true /* ignoreScreenOn */)) { 750 return false; 751 } 752 // Consider unoccluding only when all unknown visibilities have been 753 // resolved, as otherwise we just may be starting another occluding activity. 754 final boolean isUnoccluding = 755 mDisplayContent.mAppTransition.isUnoccluding() 756 && mDisplayContent.mUnknownAppVisibilityController.allResolved(); 757 // If keyguard is showing, or we're unoccluding, force the keyguard's orientation, 758 // even if SystemUI hasn't updated the attrs yet. 759 if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) { 760 return true; 761 } 762 } 763 final int req = w.mAttrs.screenOrientation; 764 if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND 765 || req == SCREEN_ORIENTATION_UNSET) { 766 return false; 767 } 768 return true; 769 }; 770 Tokens(WindowManagerService wms, Type type, String name)771 Tokens(WindowManagerService wms, Type type, String name) { 772 this(wms, type, name, FEATURE_WINDOW_TOKENS); 773 } 774 Tokens(WindowManagerService wms, Type type, String name, int featureId)775 Tokens(WindowManagerService wms, Type type, String name, int featureId) { 776 super(wms, type, name, featureId); 777 } 778 addChild(WindowToken token)779 void addChild(WindowToken token) { 780 addChild(token, mWindowComparator); 781 } 782 783 @Override 784 @ScreenOrientation getOrientation(int candidate)785 int getOrientation(int candidate) { 786 mLastOrientationSource = null; 787 788 // Find a window requesting orientation. 789 final WindowState win = getWindow(mGetOrientingWindow); 790 791 if (win == null) { 792 return candidate; 793 } 794 int req = win.mAttrs.screenOrientation; 795 ProtoLog.v(WM_DEBUG_ORIENTATION, "%s forcing orientation to %d for display id=%d", 796 win, req, mDisplayContent.getDisplayId()); 797 if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) { 798 // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be 799 // stale. We record / use the last known override. 800 if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) { 801 mLastKeyguardForcedOrientation = req; 802 } else { 803 req = mLastKeyguardForcedOrientation; 804 } 805 } 806 mLastOrientationSource = win; 807 return req; 808 } 809 810 @Override asTokens()811 final DisplayArea.Tokens asTokens() { 812 return this; 813 } 814 } 815 816 /** 817 * DisplayArea that can be dimmed. 818 */ 819 static class Dimmable extends DisplayArea<DisplayArea> { 820 private final Dimmer mDimmer = Dimmer.create(this); 821 Dimmable(WindowManagerService wms, Type type, String name, int featureId)822 Dimmable(WindowManagerService wms, Type type, String name, int featureId) { 823 super(wms, type, name, featureId); 824 } 825 826 @Override getDimmer()827 Dimmer getDimmer() { 828 return mDimmer; 829 } 830 831 @Override prepareSurfaces()832 void prepareSurfaces() { 833 mDimmer.resetDimStates(); 834 super.prepareSurfaces(); 835 final Rect dimBounds = mDimmer.getDimBounds(); 836 if (dimBounds != null) { 837 // Bounds need to be relative, as the dim layer is a child. 838 getBounds(dimBounds); 839 dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */); 840 } 841 842 // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer 843 // on the display level fades out. 844 if (!mTransitionController.isShellTransitionsEnabled() 845 && forAllTasks(task -> !task.canAffectSystemUiFlags())) { 846 mDimmer.resetDimStates(); 847 } 848 849 if (dimBounds != null) { 850 if (mDimmer.updateDims(getSyncTransaction())) { 851 scheduleAnimation(); 852 } 853 } 854 } 855 } 856 857 enum Type { 858 /** Can only contain WindowTokens above the APPLICATION_LAYER. */ 859 ABOVE_TASKS, 860 /** Can only contain WindowTokens below the APPLICATION_LAYER. */ 861 BELOW_TASKS, 862 /** Can contain anything. */ 863 ANY; 864 checkSiblings(Type bottom, Type top)865 static void checkSiblings(Type bottom, Type top) { 866 checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS), 867 bottom + " must be above BELOW_TASKS"); 868 checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS), 869 top + " must be below ABOVE_TASKS"); 870 } 871 checkChild(Type parent, Type child)872 static void checkChild(Type parent, Type child) { 873 switch (parent) { 874 case ABOVE_TASKS: 875 checkState(child == ABOVE_TASKS, "ABOVE_TASKS can only contain ABOVE_TASKS"); 876 break; 877 case BELOW_TASKS: 878 checkState(child == BELOW_TASKS, "BELOW_TASKS can only contain BELOW_TASKS"); 879 break; 880 } 881 } 882 typeOf(WindowContainer c)883 static Type typeOf(WindowContainer c) { 884 if (c.asDisplayArea() != null) { 885 return ((DisplayArea) c).mType; 886 } else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) { 887 return typeOf((WindowToken) c); 888 } else if (c instanceof Task) { 889 return ANY; 890 } else { 891 throw new IllegalArgumentException("Unknown container: " + c); 892 } 893 } 894 typeOf(WindowToken c)895 private static Type typeOf(WindowToken c) { 896 return c.getWindowLayerFromType() < APPLICATION_LAYER ? BELOW_TASKS : ABOVE_TASKS; 897 } 898 } 899 } 900