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