1 /* 2 * Copyright (C) 2018 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 20 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 21 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; 22 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 23 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; 24 25 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; 26 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; 27 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; 28 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 29 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 30 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE; 31 import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION; 32 33 import android.annotation.AnimRes; 34 import android.annotation.IntDef; 35 import android.annotation.UserIdInt; 36 import android.app.ActivityManager; 37 import android.content.ContentResolver; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.pm.ActivityInfo; 41 import android.content.pm.ActivityInfo.ScreenOrientation; 42 import android.content.pm.PackageManager; 43 import android.content.res.Resources; 44 import android.database.ContentObserver; 45 import android.hardware.power.V1_0.PowerHint; 46 import android.net.Uri; 47 import android.os.Handler; 48 import android.os.RemoteException; 49 import android.os.SystemProperties; 50 import android.os.UserHandle; 51 import android.provider.Settings; 52 import android.util.Slog; 53 import android.util.SparseArray; 54 import android.view.IDisplayWindowRotationCallback; 55 import android.view.IWindowManager; 56 import android.view.Surface; 57 import android.window.WindowContainerTransaction; 58 59 import com.android.internal.R; 60 import com.android.internal.annotations.VisibleForTesting; 61 import com.android.internal.util.function.pooled.PooledLambda; 62 import com.android.server.LocalServices; 63 import com.android.server.UiThread; 64 import com.android.server.policy.WindowManagerPolicy; 65 import com.android.server.policy.WindowOrientationListener; 66 import com.android.server.protolog.common.ProtoLog; 67 import com.android.server.statusbar.StatusBarManagerInternal; 68 69 import java.io.PrintWriter; 70 import java.lang.annotation.Retention; 71 import java.lang.annotation.RetentionPolicy; 72 73 /** 74 * Defines the mapping between orientation and rotation of a display. 75 * Non-public methods are assumed to run inside WM lock. 76 */ 77 public class DisplayRotation { 78 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM; 79 80 private static class RotationAnimationPair { 81 @AnimRes 82 int mEnter; 83 @AnimRes 84 int mExit; 85 } 86 87 private final WindowManagerService mService; 88 private final DisplayContent mDisplayContent; 89 private final DisplayPolicy mDisplayPolicy; 90 private final DisplayWindowSettings mDisplayWindowSettings; 91 private final Context mContext; 92 private final Object mLock; 93 94 public final boolean isDefaultDisplay; 95 private final boolean mSupportAutoRotation; 96 private final int mLidOpenRotation; 97 private final int mCarDockRotation; 98 private final int mDeskDockRotation; 99 private final int mUndockedHdmiRotation; 100 private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair(); 101 102 private OrientationListener mOrientationListener; 103 private StatusBarManagerInternal mStatusBarManagerInternal; 104 private SettingsObserver mSettingsObserver; 105 106 @ScreenOrientation 107 private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 108 109 /** 110 * Last applied orientation of the display. 111 * 112 * @see #updateOrientationFromApp 113 */ 114 @ScreenOrientation 115 private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 116 117 /** 118 * Current rotation of the display. 119 * 120 * @see #updateRotationUnchecked 121 */ 122 @Surface.Rotation 123 private int mRotation; 124 125 @VisibleForTesting 126 int mLandscapeRotation; // default landscape 127 @VisibleForTesting 128 int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation 129 @VisibleForTesting 130 int mPortraitRotation; // default portrait 131 @VisibleForTesting 132 int mUpsideDownRotation; // "other" portrait 133 134 private boolean mAllowSeamlessRotationDespiteNavBarMoving; 135 136 private int mDeferredRotationPauseCount; 137 138 /** 139 * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation 140 * is being transformed. We freeze orientation updates while any windows are seamlessly rotated, 141 * so we need to track when this hits zero so we can apply deferred orientation updates. 142 */ 143 private int mSeamlessRotationCount; 144 145 /** 146 * True in the interval from starting seamless rotation until the last rotated window draws in 147 * the new orientation. 148 */ 149 private boolean mRotatingSeamlessly; 150 151 /** 152 * Behavior of rotation suggestions. 153 * 154 * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS 155 */ 156 private int mShowRotationSuggestions; 157 158 private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1; 159 private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0; 160 private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1; 161 162 @IntDef({ ALLOW_ALL_ROTATIONS_UNDEFINED, ALLOW_ALL_ROTATIONS_DISABLED, 163 ALLOW_ALL_ROTATIONS_ENABLED }) 164 @Retention(RetentionPolicy.SOURCE) 165 private @interface AllowAllRotations {} 166 167 /** 168 * Whether to allow the screen to rotate to all rotations (including 180 degree) according to 169 * the sensor even when the current orientation is not 170 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_SENSOR} or 171 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_USER}. 172 */ 173 @AllowAllRotations 174 private int mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED; 175 176 @WindowManagerPolicy.UserRotationMode 177 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; 178 179 @Surface.Rotation 180 private int mUserRotation = Surface.ROTATION_0; 181 182 /** 183 * Flag that indicates this is a display that may run better when fixed to user rotation. 184 */ 185 private boolean mDefaultFixedToUserRotation; 186 187 /** 188 * A flag to indicate if the display rotation should be fixed to user specified rotation 189 * regardless of all other states (including app requrested orientation). {@code true} the 190 * display rotation should be fixed to user specified rotation, {@code false} otherwise. 191 */ 192 private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT; 193 194 private int mDemoHdmiRotation; 195 private int mDemoRotation; 196 private boolean mDemoHdmiRotationLock; 197 private boolean mDemoRotationLock; 198 199 private static final int REMOTE_ROTATION_TIMEOUT_MS = 800; 200 201 private boolean mIsWaitingForRemoteRotation = false; 202 203 private final Runnable mDisplayRotationHandlerTimeout = 204 new Runnable() { 205 @Override 206 public void run() { 207 continueRotation(mRotation, null /* transaction */); 208 } 209 }; 210 211 private final IDisplayWindowRotationCallback mRemoteRotationCallback = 212 new IDisplayWindowRotationCallback.Stub() { 213 @Override 214 public void continueRotateDisplay(int targetRotation, 215 WindowContainerTransaction t) { 216 synchronized (mService.getWindowManagerLock()) { 217 mService.mH.sendMessage(PooledLambda.obtainMessage( 218 DisplayRotation::continueRotation, DisplayRotation.this, 219 targetRotation, t)); 220 } 221 } 222 }; 223 DisplayRotation(WindowManagerService service, DisplayContent displayContent)224 DisplayRotation(WindowManagerService service, DisplayContent displayContent) { 225 this(service, displayContent, displayContent.getDisplayPolicy(), 226 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock()); 227 } 228 229 @VisibleForTesting DisplayRotation(WindowManagerService service, DisplayContent displayContent, DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings, Context context, Object lock)230 DisplayRotation(WindowManagerService service, DisplayContent displayContent, 231 DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings, 232 Context context, Object lock) { 233 mService = service; 234 mDisplayContent = displayContent; 235 mDisplayPolicy = displayPolicy; 236 mDisplayWindowSettings = displayWindowSettings; 237 mContext = context; 238 mLock = lock; 239 isDefaultDisplay = displayContent.isDefaultDisplay; 240 241 mSupportAutoRotation = 242 mContext.getResources().getBoolean(R.bool.config_supportAutoRotation); 243 mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation); 244 mCarDockRotation = readRotation(R.integer.config_carDockRotation); 245 mDeskDockRotation = readRotation(R.integer.config_deskDockRotation); 246 mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation); 247 248 if (isDefaultDisplay) { 249 final Handler uiHandler = UiThread.getHandler(); 250 mOrientationListener = new OrientationListener(mContext, uiHandler); 251 mOrientationListener.setCurrentRotation(mRotation); 252 mSettingsObserver = new SettingsObserver(uiHandler); 253 mSettingsObserver.observe(); 254 } 255 } 256 readRotation(int resID)257 private int readRotation(int resID) { 258 try { 259 final int rotation = mContext.getResources().getInteger(resID); 260 switch (rotation) { 261 case 0: 262 return Surface.ROTATION_0; 263 case 90: 264 return Surface.ROTATION_90; 265 case 180: 266 return Surface.ROTATION_180; 267 case 270: 268 return Surface.ROTATION_270; 269 } 270 } catch (Resources.NotFoundException e) { 271 // fall through 272 } 273 return -1; 274 } 275 276 /** 277 * Updates the configuration which may have different values depending on current user, e.g. 278 * runtime resource overlay. 279 */ updateUserDependentConfiguration(Resources currentUserRes)280 void updateUserDependentConfiguration(Resources currentUserRes) { 281 mAllowSeamlessRotationDespiteNavBarMoving = 282 currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving); 283 } 284 configure(int width, int height, int shortSizeDp, int longSizeDp)285 void configure(int width, int height, int shortSizeDp, int longSizeDp) { 286 final Resources res = mContext.getResources(); 287 if (width > height) { 288 mLandscapeRotation = Surface.ROTATION_0; 289 mSeascapeRotation = Surface.ROTATION_180; 290 if (res.getBoolean(R.bool.config_reverseDefaultRotation)) { 291 mPortraitRotation = Surface.ROTATION_90; 292 mUpsideDownRotation = Surface.ROTATION_270; 293 } else { 294 mPortraitRotation = Surface.ROTATION_270; 295 mUpsideDownRotation = Surface.ROTATION_90; 296 } 297 } else { 298 mPortraitRotation = Surface.ROTATION_0; 299 mUpsideDownRotation = Surface.ROTATION_180; 300 if (res.getBoolean(R.bool.config_reverseDefaultRotation)) { 301 mLandscapeRotation = Surface.ROTATION_270; 302 mSeascapeRotation = Surface.ROTATION_90; 303 } else { 304 mLandscapeRotation = Surface.ROTATION_90; 305 mSeascapeRotation = Surface.ROTATION_270; 306 } 307 } 308 309 // For demo purposes, allow the rotation of the HDMI display to be controlled. 310 // By default, HDMI locks rotation to landscape. 311 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { 312 mDemoHdmiRotation = mPortraitRotation; 313 } else { 314 mDemoHdmiRotation = mLandscapeRotation; 315 } 316 mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false); 317 318 // For demo purposes, allow the rotation of the remote display to be controlled. 319 // By default, remote display locks rotation to landscape. 320 if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) { 321 mDemoRotation = mPortraitRotation; 322 } else { 323 mDemoRotation = mLandscapeRotation; 324 } 325 mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false); 326 327 // It's physically impossible to rotate the car's screen. 328 final boolean isCar = mContext.getPackageManager().hasSystemFeature( 329 PackageManager.FEATURE_AUTOMOTIVE); 330 // It's also not likely to rotate a TV screen. 331 final boolean isTv = mContext.getPackageManager().hasSystemFeature( 332 PackageManager.FEATURE_LEANBACK); 333 mDefaultFixedToUserRotation = 334 (isCar || isTv || mService.mIsPc || mDisplayContent.forceDesktopMode()) 335 // For debug purposes the next line turns this feature off with: 336 // $ adb shell setprop config.override_forced_orient true 337 // $ adb shell wm size reset 338 && !"true".equals(SystemProperties.get("config.override_forced_orient")); 339 } 340 applyCurrentRotation(@urface.Rotation int rotation)341 void applyCurrentRotation(@Surface.Rotation int rotation) { 342 if (mOrientationListener != null) { 343 mOrientationListener.setCurrentRotation(rotation); 344 } 345 } 346 347 @VisibleForTesting setRotation(@urface.Rotation int rotation)348 void setRotation(@Surface.Rotation int rotation) { 349 mRotation = rotation; 350 } 351 352 @Surface.Rotation getRotation()353 int getRotation() { 354 return mRotation; 355 } 356 357 @ScreenOrientation getLastOrientation()358 int getLastOrientation() { 359 return mLastOrientation; 360 } 361 updateOrientation(@creenOrientation int newOrientation, boolean forceUpdate)362 boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) { 363 if (newOrientation == mLastOrientation && !forceUpdate) { 364 return false; 365 } 366 mLastOrientation = newOrientation; 367 if (newOrientation != mCurrentAppOrientation) { 368 mCurrentAppOrientation = newOrientation; 369 if (isDefaultDisplay) { 370 updateOrientationListenerLw(); 371 } 372 } 373 return updateRotationUnchecked(forceUpdate); 374 } 375 376 /** 377 * Update rotation of the display and send configuration if the rotation is changed. 378 * 379 * @return {@code true} if the rotation has been changed and the new config is sent. 380 */ updateRotationAndSendNewConfigIfChanged()381 boolean updateRotationAndSendNewConfigIfChanged() { 382 final boolean changed = updateRotationUnchecked(false /* forceUpdate */); 383 if (changed) { 384 mDisplayContent.sendNewConfiguration(); 385 } 386 return changed; 387 } 388 389 /** 390 * Update rotation with an option to force the update. This updates the container's perception 391 * of rotation and, depending on the top activities, will freeze the screen or start seamless 392 * rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked} 393 * during {@link DisplayContent#sendNewConfiguration}. 394 * 395 * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating 396 * orientation because we're waiting for some rotation to finish or display 397 * to unfreeze, which results in configuration of the previously visible 398 * activity being applied to a newly visible one. Forcing the rotation 399 * update allows to workaround this issue. 400 * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL 401 * {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE 402 * THE SCREEN. 403 */ updateRotationUnchecked(boolean forceUpdate)404 boolean updateRotationUnchecked(boolean forceUpdate) { 405 final int displayId = mDisplayContent.getDisplayId(); 406 if (!forceUpdate) { 407 if (mDeferredRotationPauseCount > 0) { 408 // Rotation updates have been paused temporarily. Defer the update until updates 409 // have been resumed. 410 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused."); 411 return false; 412 } 413 414 final ScreenRotationAnimation screenRotationAnimation = 415 mDisplayContent.getRotationAnimation(); 416 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { 417 // Rotation updates cannot be performed while the previous rotation change animation 418 // is still in progress. Skip this update. We will try updating again after the 419 // animation is finished and the display is unfrozen. 420 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress."); 421 return false; 422 } 423 if (mService.mDisplayFrozen) { 424 // Even if the screen rotation animation has finished (e.g. isAnimating returns 425 // false), there is still some time where we haven't yet unfrozen the display. We 426 // also need to abort rotation here. 427 ProtoLog.v(WM_DEBUG_ORIENTATION, 428 "Deferring rotation, still finishing previous rotation"); 429 return false; 430 } 431 432 if (mDisplayContent.mFixedRotationTransitionListener 433 .isTopFixedOrientationRecentsAnimating()) { 434 // During the recents animation, the closing app might still be considered on top. 435 // In order to ignore its requested orientation to avoid a sensor led rotation (e.g 436 // user rotating the device while the recents animation is running), we ignore 437 // rotation update while the animation is running. 438 return false; 439 } 440 } 441 442 if (!mService.mDisplayEnabled) { 443 // No point choosing a rotation if the display is not enabled. 444 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled."); 445 return false; 446 } 447 448 final int oldRotation = mRotation; 449 final int lastOrientation = mLastOrientation; 450 final int rotation = rotationForOrientation(lastOrientation, oldRotation); 451 ProtoLog.v(WM_DEBUG_ORIENTATION, 452 "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and " 453 + "oldRotation=%s (%d)", 454 Surface.rotationToString(rotation), rotation, 455 displayId, 456 ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation, 457 Surface.rotationToString(oldRotation), oldRotation); 458 459 ProtoLog.v(WM_DEBUG_ORIENTATION, 460 "Display id=%d selected orientation %s (%d), got rotation %s (%d)", displayId, 461 ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation, 462 Surface.rotationToString(rotation), rotation); 463 464 if (oldRotation == rotation) { 465 // No change. 466 return false; 467 } 468 469 ProtoLog.v(WM_DEBUG_ORIENTATION, 470 "Display id=%d rotation changed to %d from %d, lastOrientation=%d", 471 displayId, rotation, oldRotation, lastOrientation); 472 473 if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) { 474 mDisplayContent.mWaitingForConfig = true; 475 } 476 477 mRotation = rotation; 478 479 mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; 480 mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT, 481 mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION); 482 483 mDisplayContent.setLayoutNeeded(); 484 485 if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) { 486 // The screen rotation animation uses a screenshot to freeze the screen while windows 487 // resize underneath. When we are rotating seamlessly, we allow the elements to 488 // transition to their rotated state independently and without a freeze required. 489 prepareSeamlessRotation(); 490 } else { 491 prepareNormalRotationAnimation(); 492 } 493 494 // Give a remote handler (system ui) some time to reposition things. 495 startRemoteRotation(oldRotation, mRotation); 496 497 return true; 498 } 499 500 /** 501 * A Remote rotation is when we are waiting for some registered (remote) 502 * {@link IDisplayWindowRotationController} to calculate and return some hierarchy operations 503 * to perform in sync with the rotation. 504 */ isWaitingForRemoteRotation()505 boolean isWaitingForRemoteRotation() { 506 return mIsWaitingForRemoteRotation; 507 } 508 startRemoteRotation(int fromRotation, int toRotation)509 private void startRemoteRotation(int fromRotation, int toRotation) { 510 if (mService.mDisplayRotationController == null) { 511 return; 512 } 513 mIsWaitingForRemoteRotation = true; 514 try { 515 mService.mDisplayRotationController.onRotateDisplay(mDisplayContent.getDisplayId(), 516 fromRotation, toRotation, mRemoteRotationCallback); 517 mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); 518 mService.mH.postDelayed(mDisplayRotationHandlerTimeout, REMOTE_ROTATION_TIMEOUT_MS); 519 } catch (RemoteException e) { 520 mIsWaitingForRemoteRotation = false; 521 return; 522 } 523 } 524 continueRotation(int targetRotation, WindowContainerTransaction t)525 private void continueRotation(int targetRotation, WindowContainerTransaction t) { 526 synchronized (mService.mGlobalLock) { 527 if (targetRotation != mRotation || !mIsWaitingForRemoteRotation) { 528 // Drop it, this is either coming from an outdated remote rotation; or, we've 529 // already moved on. 530 return; 531 } 532 mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); 533 mIsWaitingForRemoteRotation = false; 534 mService.mAtmService.deferWindowLayout(); 535 try { 536 mDisplayContent.sendNewConfiguration(); 537 if (t != null) { 538 mService.mAtmService.mWindowOrganizerController.applyTransaction(t); 539 } 540 } finally { 541 mService.mAtmService.continueWindowLayout(); 542 } 543 } 544 } 545 prepareNormalRotationAnimation()546 void prepareNormalRotationAnimation() { 547 cancelSeamlessRotation(); 548 final RotationAnimationPair anim = selectRotationAnimation(); 549 mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent); 550 } 551 552 /** 553 * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was 554 * set by previous {@link #updateRotationUnchecked}, but another orientation change happens 555 * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished) 556 * and it doesn't choose seamless rotation. 557 */ cancelSeamlessRotation()558 void cancelSeamlessRotation() { 559 if (!mRotatingSeamlessly) { 560 return; 561 } 562 mDisplayContent.forAllWindows(w -> { 563 if (w.mSeamlesslyRotated) { 564 w.finishSeamlessRotation(false /* timeout */); 565 w.mSeamlesslyRotated = false; 566 } 567 }, true /* traverseTopToBottom */); 568 mSeamlessRotationCount = 0; 569 mRotatingSeamlessly = false; 570 mDisplayContent.finishFixedRotationAnimationIfPossible(); 571 } 572 prepareSeamlessRotation()573 private void prepareSeamlessRotation() { 574 // We are careful to reset this in case a window was removed before it finished 575 // seamless rotation. 576 mSeamlessRotationCount = 0; 577 mRotatingSeamlessly = true; 578 } 579 isRotatingSeamlessly()580 boolean isRotatingSeamlessly() { 581 return mRotatingSeamlessly; 582 } 583 hasSeamlessRotatingWindow()584 boolean hasSeamlessRotatingWindow() { 585 return mSeamlessRotationCount > 0; 586 } 587 588 @VisibleForTesting shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate)589 boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) { 590 // Display doesn't need to be frozen because application has been started in correct 591 // rotation already, so the rest of the windows can use seamless rotation. 592 if (mDisplayContent.hasTopFixedRotationLaunchingApp()) { 593 return true; 594 } 595 596 final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow(); 597 if (w == null || w != mDisplayContent.mCurrentFocus) { 598 return false; 599 } 600 // We only enable seamless rotation if the top window has requested it and is in the 601 // fullscreen opaque state. Seamless rotation requires freezing various Surface states and 602 // won't work well with animations, so we disable it in the animation case for now. 603 if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) { 604 return false; 605 } 606 607 // For the upside down rotation we don't rotate seamlessly as the navigation bar moves 608 // position. Note most apps (using orientation:sensor or user as opposed to fullSensor) 609 // will not enter the reverse portrait orientation, so actually the orientation won't change 610 // at all. 611 if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) { 612 return false; 613 } 614 615 // If the navigation bar can't change sides, then it will jump when we change orientations 616 // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation 617 // where the navbar is low-profile enough that this isn't very noticeable. 618 if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) { 619 return false; 620 } 621 622 // If the bounds of activity window is different from its parent, then reject to be seamless 623 // because the window position may change after rotation that will look like a sudden jump. 624 if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) { 625 return false; 626 } 627 628 // In the presence of the PINNED stack or System Alert windows we unfortunately can not 629 // seamlessly rotate. 630 if (mDisplayContent.getDefaultTaskDisplayArea().hasPinnedTask() 631 || mDisplayContent.hasAlertWindowSurfaces()) { 632 return false; 633 } 634 635 // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to 636 // complete (that is, waiting for windows to redraw). It's tempting to check 637 // mSeamlessRotationCount but that could be incorrect in the case of window-removal. 638 if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) { 639 return false; 640 } 641 642 return true; 643 } 644 markForSeamlessRotation(WindowState w, boolean seamlesslyRotated)645 void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) { 646 if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) { 647 return; 648 } 649 650 w.mSeamlesslyRotated = seamlesslyRotated; 651 if (seamlesslyRotated) { 652 mSeamlessRotationCount++; 653 } else { 654 mSeamlessRotationCount--; 655 } 656 if (mSeamlessRotationCount == 0) { 657 ProtoLog.i(WM_DEBUG_ORIENTATION, 658 "Performing post-rotate rotation after seamless rotation"); 659 // Finish seamless rotation. 660 mRotatingSeamlessly = false; 661 mDisplayContent.finishFixedRotationAnimationIfPossible(); 662 663 updateRotationAndSendNewConfigIfChanged(); 664 } 665 } 666 onSeamlessRotationTimeout()667 void onSeamlessRotationTimeout() { 668 final boolean[] isLayoutNeeded = { false }; 669 670 mDisplayContent.forAllWindows(w -> { 671 if (!w.mSeamlesslyRotated) { 672 return; 673 } 674 isLayoutNeeded[0] = true; 675 w.setDisplayLayoutNeeded(); 676 w.finishSeamlessRotation(true /* timeout */); 677 markForSeamlessRotation(w, false /* seamlesslyRotated */); 678 }, true /* traverseTopToBottom */); 679 680 if (isLayoutNeeded[0]) { 681 mService.mWindowPlacerLocked.performSurfacePlacement(); 682 } 683 } 684 685 /** 686 * Returns the animation to run for a rotation transition based on the top fullscreen windows 687 * {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently 688 * fullscreen and frontmost. 689 */ selectRotationAnimation()690 private RotationAnimationPair selectRotationAnimation() { 691 // If the screen is off or non-interactive, force a jumpcut. 692 final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully() 693 || !mService.mPolicy.okToAnimate(); 694 final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow(); 695 if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation topFullscreen=" 696 + topFullscreen + " rotationAnimation=" 697 + (topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation) 698 + " forceJumpcut=" + forceJumpcut); 699 if (forceJumpcut) { 700 mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit; 701 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter; 702 return mTmpRotationAnim; 703 } 704 if (topFullscreen != null) { 705 int animationHint = topFullscreen.getRotationAnimationHint(); 706 if (animationHint < 0 && mDisplayPolicy.isTopLayoutFullscreen()) { 707 animationHint = topFullscreen.getAttrs().rotationAnimation; 708 } 709 switch (animationHint) { 710 case ROTATION_ANIMATION_CROSSFADE: 711 case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless. 712 mTmpRotationAnim.mExit = R.anim.rotation_animation_xfade_exit; 713 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter; 714 break; 715 case ROTATION_ANIMATION_JUMPCUT: 716 mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit; 717 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter; 718 break; 719 case ROTATION_ANIMATION_ROTATE: 720 default: 721 mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0; 722 break; 723 } 724 } else { 725 mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0; 726 } 727 return mTmpRotationAnim; 728 } 729 730 /** 731 * Validate whether the current top fullscreen has specified the same 732 * {@link android.view.WindowManager.LayoutParams#rotationAnimation} value as that being passed 733 * in from the previous top fullscreen window. 734 * 735 * @param exitAnimId exiting resource id from the previous window. 736 * @param enterAnimId entering resource id from the previous window. 737 * @param forceDefault For rotation animations only, if true ignore the animation values and 738 * just return false. 739 * @return {@code true} if the previous values are still valid, false if they should be replaced 740 * with the default. 741 */ validateRotationAnimation(int exitAnimId, int enterAnimId, boolean forceDefault)742 boolean validateRotationAnimation(int exitAnimId, int enterAnimId, boolean forceDefault) { 743 switch (exitAnimId) { 744 case R.anim.rotation_animation_xfade_exit: 745 case R.anim.rotation_animation_jump_exit: 746 // These are the only cases that matter. 747 if (forceDefault) { 748 return false; 749 } 750 final RotationAnimationPair anim = selectRotationAnimation(); 751 return exitAnimId == anim.mExit && enterAnimId == anim.mEnter; 752 default: 753 return true; 754 } 755 } 756 restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation)757 void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) { 758 mFixedToUserRotation = fixedToUserRotation; 759 760 // We will retrieve user rotation and user rotation mode from settings for default display. 761 if (isDefaultDisplay) { 762 return; 763 } 764 if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE 765 && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) { 766 Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode 767 + " for " + mDisplayContent); 768 userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; 769 } 770 if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) { 771 Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation 772 + " for " + mDisplayContent); 773 userRotation = Surface.ROTATION_0; 774 } 775 mUserRotationMode = userRotationMode; 776 mUserRotation = userRotation; 777 } 778 setFixedToUserRotation(int fixedToUserRotation)779 void setFixedToUserRotation(int fixedToUserRotation) { 780 if (mFixedToUserRotation == fixedToUserRotation) { 781 return; 782 } 783 784 mFixedToUserRotation = fixedToUserRotation; 785 mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation); 786 mService.updateRotation(true /* alwaysSendConfiguration */, 787 false /* forceRelayout */); 788 } 789 790 @VisibleForTesting setUserRotation(int userRotationMode, int userRotation)791 void setUserRotation(int userRotationMode, int userRotation) { 792 if (isDefaultDisplay) { 793 // We'll be notified via settings listener, so we don't need to update internal values. 794 final ContentResolver res = mContext.getContentResolver(); 795 final int accelerometerRotation = 796 userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1; 797 Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION, 798 accelerometerRotation, UserHandle.USER_CURRENT); 799 Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation, 800 UserHandle.USER_CURRENT); 801 return; 802 } 803 804 boolean changed = false; 805 if (mUserRotationMode != userRotationMode) { 806 mUserRotationMode = userRotationMode; 807 changed = true; 808 } 809 if (mUserRotation != userRotation) { 810 mUserRotation = userRotation; 811 changed = true; 812 } 813 mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode, 814 userRotation); 815 if (changed) { 816 mService.updateRotation(true /* alwaysSendConfiguration */, 817 false /* forceRelayout */); 818 } 819 } 820 freezeRotation(int rotation)821 void freezeRotation(int rotation) { 822 rotation = (rotation == -1) ? mRotation : rotation; 823 setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation); 824 } 825 thawRotation()826 void thawRotation() { 827 setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation); 828 } 829 isRotationFrozen()830 boolean isRotationFrozen() { 831 if (!isDefaultDisplay) { 832 return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED; 833 } 834 835 return Settings.System.getIntForUser(mContext.getContentResolver(), 836 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0; 837 } 838 isFixedToUserRotation()839 boolean isFixedToUserRotation() { 840 switch (mFixedToUserRotation) { 841 case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED: 842 return false; 843 case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED: 844 return true; 845 default: 846 return mDefaultFixedToUserRotation; 847 } 848 } 849 850 /** 851 * Returns {@code true} if this display rotation takes app requested orientation into 852 * consideration; {@code false} otherwise. For the time being the only case where this is {@code 853 * false} is when {@link #isFixedToUserRotation()} is {@code true}. 854 */ respectAppRequestedOrientation()855 boolean respectAppRequestedOrientation() { 856 return !isFixedToUserRotation(); 857 } 858 getLandscapeRotation()859 public int getLandscapeRotation() { 860 return mLandscapeRotation; 861 } 862 getSeascapeRotation()863 public int getSeascapeRotation() { 864 return mSeascapeRotation; 865 } 866 getPortraitRotation()867 public int getPortraitRotation() { 868 return mPortraitRotation; 869 } 870 getUpsideDownRotation()871 public int getUpsideDownRotation() { 872 return mUpsideDownRotation; 873 } 874 getCurrentAppOrientation()875 public int getCurrentAppOrientation() { 876 return mCurrentAppOrientation; 877 } 878 getDisplayPolicy()879 public DisplayPolicy getDisplayPolicy() { 880 return mDisplayPolicy; 881 } 882 getOrientationListener()883 public WindowOrientationListener getOrientationListener() { 884 return mOrientationListener; 885 } 886 getUserRotation()887 public int getUserRotation() { 888 return mUserRotation; 889 } 890 getUserRotationMode()891 public int getUserRotationMode() { 892 return mUserRotationMode; 893 } 894 updateOrientationListener()895 public void updateOrientationListener() { 896 synchronized (mLock) { 897 updateOrientationListenerLw(); 898 } 899 } 900 901 /** 902 * Temporarily pauses rotation changes until resumed. 903 * <p> 904 * This can be used to prevent rotation changes from occurring while the user is performing 905 * certain operations, such as drag and drop. 906 * <p> 907 * This call nests and must be matched by an equal number of calls to {@link #resume}. 908 */ pause()909 void pause() { 910 mDeferredRotationPauseCount++; 911 } 912 913 /** Resumes normal rotation changes after being paused. */ resume()914 void resume() { 915 if (mDeferredRotationPauseCount <= 0) { 916 return; 917 } 918 919 mDeferredRotationPauseCount--; 920 if (mDeferredRotationPauseCount == 0) { 921 updateRotationAndSendNewConfigIfChanged(); 922 } 923 } 924 925 /** 926 * Various use cases for invoking this function: 927 * <li>Screen turning off, should always disable listeners if already enabled.</li> 928 * <li>Screen turned on and current app has sensor based orientation, enable listeners 929 * if not already enabled.</li> 930 * <li>Screen turned on and current app does not have sensor orientation, disable listeners 931 * if already enabled.</li> 932 * <li>Screen turning on and current app has sensor based orientation, enable listeners 933 * if needed.</li> 934 * <li>screen turning on and current app has nosensor based orientation, do nothing.</li> 935 */ updateOrientationListenerLw()936 private void updateOrientationListenerLw() { 937 if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) { 938 // If sensor is turned off or nonexistent for some reason. 939 return; 940 } 941 942 final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly(); 943 final boolean awake = mDisplayPolicy.isAwake(); 944 final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete(); 945 final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete(); 946 947 // Could have been invoked due to screen turning on or off or 948 // change of the currently visible window's orientation. 949 ProtoLog.v(WM_DEBUG_ORIENTATION, 950 "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, " 951 + "orientationSensorEnabled=%b, keyguardDrawComplete=%b, " 952 + "windowManagerDrawComplete=%b", 953 screenOnEarly, awake, mCurrentAppOrientation, mOrientationListener.mEnabled, 954 keyguardDrawComplete, windowManagerDrawComplete); 955 956 boolean disable = true; 957 // Note: We postpone the rotating of the screen until the keyguard as well as the 958 // window manager have reported a draw complete or the keyguard is going away in dismiss 959 // mode. 960 if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) { 961 if (needSensorRunning()) { 962 disable = false; 963 // Enable listener if not already enabled. 964 if (!mOrientationListener.mEnabled) { 965 // Don't clear the current sensor orientation if the keyguard is going away in 966 // dismiss mode. This allows window manager to use the last sensor reading to 967 // determine the orientation vs. falling back to the last known orientation if 968 // the sensor reading was cleared which can cause it to relaunch the app that 969 // will show in the wrong orientation first before correcting leading to app 970 // launch delays. 971 mOrientationListener.enable(true /* clearCurrentRotation */); 972 } 973 } 974 } 975 // Check if sensors need to be disabled. 976 if (disable && mOrientationListener.mEnabled) { 977 mOrientationListener.disable(); 978 } 979 } 980 981 /** 982 * We always let the sensor be switched on by default except when 983 * the user has explicitly disabled sensor based rotation or when the 984 * screen is switched off. 985 */ needSensorRunning()986 private boolean needSensorRunning() { 987 if (isFixedToUserRotation()) { 988 // We are sure we only respect user rotation settings, so we are sure we will not 989 // support sensor rotation. 990 return false; 991 } 992 993 if (mSupportAutoRotation) { 994 if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR 995 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR 996 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT 997 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) { 998 // If the application has explicitly requested to follow the 999 // orientation, then we need to turn the sensor on. 1000 return true; 1001 } 1002 } 1003 1004 final int dockMode = mDisplayPolicy.getDockMode(); 1005 if ((mDisplayPolicy.isCarDockEnablesAccelerometer() 1006 && dockMode == Intent.EXTRA_DOCK_STATE_CAR) 1007 || (mDisplayPolicy.isDeskDockEnablesAccelerometer() 1008 && (dockMode == Intent.EXTRA_DOCK_STATE_DESK 1009 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK 1010 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) { 1011 // Enable accelerometer if we are docked in a dock that enables accelerometer 1012 // orientation management. 1013 return true; 1014 } 1015 1016 if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) { 1017 // If the setting for using the sensor by default is enabled, then 1018 // we will always leave it on. Note that the user could go to 1019 // a window that forces an orientation that does not use the 1020 // sensor and in theory we could turn it off... however, when next 1021 // turning it on we won't have a good value for the current 1022 // orientation for a little bit, which can cause orientation 1023 // changes to lag, so we'd like to keep it always on. (It will 1024 // still be turned off when the screen is off.) 1025 1026 // When locked we can provide rotation suggestions users can approve to change the 1027 // current screen rotation. To do this the sensor needs to be running. 1028 return mSupportAutoRotation && 1029 mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED; 1030 } 1031 return mSupportAutoRotation; 1032 } 1033 1034 /** 1035 * If this is true we have updated our desired orientation, but not yet changed the real 1036 * orientation our applied our screen rotation animation. For example, because a previous 1037 * screen rotation was in progress. 1038 * 1039 * @return {@code true} if the there is an ongoing rotation change. 1040 */ needsUpdate()1041 boolean needsUpdate() { 1042 final int oldRotation = mRotation; 1043 final int rotation = rotationForOrientation(mLastOrientation, oldRotation); 1044 return oldRotation != rotation; 1045 } 1046 1047 /** 1048 * Given an orientation constant, returns the appropriate surface rotation, taking into account 1049 * sensors, docking mode, rotation lock, and other factors. 1050 * 1051 * @param orientation An orientation constant, such as 1052 * {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}. 1053 * @param lastRotation The most recently used rotation. 1054 * @return The surface rotation to use. 1055 */ 1056 @VisibleForTesting 1057 @Surface.Rotation rotationForOrientation(@creenOrientation int orientation, @Surface.Rotation int lastRotation)1058 int rotationForOrientation(@ScreenOrientation int orientation, 1059 @Surface.Rotation int lastRotation) { 1060 ProtoLog.v(WM_DEBUG_ORIENTATION, 1061 "rotationForOrientation(orient=%s (%d), last=%s (%d)); user=%s (%d) %s", 1062 ActivityInfo.screenOrientationToString(orientation), orientation, 1063 Surface.rotationToString(lastRotation), lastRotation, 1064 Surface.rotationToString(mUserRotation), mUserRotation, 1065 mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED 1066 ? "USER_ROTATION_LOCKED" : ""); 1067 1068 if (isFixedToUserRotation()) { 1069 return mUserRotation; 1070 } 1071 1072 int sensorRotation = mOrientationListener != null 1073 ? mOrientationListener.getProposedRotation() // may be -1 1074 : -1; 1075 if (sensorRotation < 0) { 1076 sensorRotation = lastRotation; 1077 } 1078 1079 final int lidState = mDisplayPolicy.getLidState(); 1080 final int dockMode = mDisplayPolicy.getDockMode(); 1081 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged(); 1082 final boolean carDockEnablesAccelerometer = 1083 mDisplayPolicy.isCarDockEnablesAccelerometer(); 1084 final boolean deskDockEnablesAccelerometer = 1085 mDisplayPolicy.isDeskDockEnablesAccelerometer(); 1086 1087 final int preferredRotation; 1088 if (!isDefaultDisplay) { 1089 // For secondary displays we ignore things like displays sensors, docking mode and 1090 // rotation lock, and always prefer user rotation. 1091 preferredRotation = mUserRotation; 1092 } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) { 1093 // Ignore sensor when lid switch is open and rotation is forced. 1094 preferredRotation = mLidOpenRotation; 1095 } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR 1096 && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) { 1097 // Ignore sensor when in car dock unless explicitly enabled. 1098 // This case can override the behavior of NOSENSOR, and can also 1099 // enable 180 degree rotation while docked. 1100 preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation; 1101 } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK 1102 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK 1103 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) 1104 && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) { 1105 // Ignore sensor when in desk dock unless explicitly enabled. 1106 // This case can override the behavior of NOSENSOR, and can also 1107 // enable 180 degree rotation while docked. 1108 preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation; 1109 } else if (hdmiPlugged && mDemoHdmiRotationLock) { 1110 // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled. 1111 // Note that the dock orientation overrides the HDMI orientation. 1112 preferredRotation = mDemoHdmiRotation; 1113 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED 1114 && mUndockedHdmiRotation >= 0) { 1115 // Ignore sensor when plugged into HDMI and an undocked orientation has 1116 // been specified in the configuration (only for legacy devices without 1117 // full multi-display support). 1118 // Note that the dock orientation overrides the HDMI orientation. 1119 preferredRotation = mUndockedHdmiRotation; 1120 } else if (mDemoRotationLock) { 1121 // Ignore sensor when demo rotation lock is enabled. 1122 // Note that the dock orientation and HDMI rotation lock override this. 1123 preferredRotation = mDemoRotation; 1124 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) { 1125 // While in VR, apps always prefer a portrait rotation. This does not change 1126 // any apps that explicitly set landscape, but does cause sensors be ignored, 1127 // and ignored any orientation lock that the user has set (this conditional 1128 // should remain above the ORIENTATION_LOCKED conditional below). 1129 preferredRotation = mPortraitRotation; 1130 } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { 1131 // Application just wants to remain locked in the last rotation. 1132 preferredRotation = lastRotation; 1133 } else if (!mSupportAutoRotation) { 1134 // If we don't support auto-rotation then bail out here and ignore 1135 // the sensor and any rotation lock settings. 1136 preferredRotation = -1; 1137 } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE 1138 && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER 1139 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 1140 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE 1141 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT 1142 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER)) 1143 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR 1144 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR 1145 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 1146 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { 1147 // Otherwise, use sensor only if requested by the application or enabled 1148 // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR. 1149 if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) { 1150 // Can't read this during init() because the context doesn't have display metrics at 1151 // that time so we cannot determine tablet vs. phone then. 1152 mAllowAllRotations = mContext.getResources().getBoolean( 1153 R.bool.config_allowAllRotations) 1154 ? ALLOW_ALL_ROTATIONS_ENABLED 1155 : ALLOW_ALL_ROTATIONS_DISABLED; 1156 } 1157 if (sensorRotation != Surface.ROTATION_180 1158 || mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED 1159 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR 1160 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) { 1161 preferredRotation = sensorRotation; 1162 } else { 1163 preferredRotation = lastRotation; 1164 } 1165 } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED 1166 && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR 1167 && orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 1168 && orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 1169 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE 1170 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) { 1171 // Apply rotation lock. Does not apply to NOSENSOR or specific rotations. 1172 // The idea is that the user rotation expresses a weak preference for the direction 1173 // of gravity and as NOSENSOR is never affected by gravity, then neither should 1174 // NOSENSOR be affected by rotation lock (although it will be affected by docks). 1175 // Also avoid setting user rotation when app has preference over one particular rotation 1176 // to avoid leaving the rotation to the reverse of it which has the compatible 1177 // orientation, but isn't what app wants, when the user rotation is the reverse of the 1178 // preferred rotation. 1179 preferredRotation = mUserRotation; 1180 } else { 1181 // No overriding preference. 1182 // We will do exactly what the application asked us to do. 1183 preferredRotation = -1; 1184 } 1185 1186 switch (orientation) { 1187 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: 1188 // Return portrait unless overridden. 1189 if (isAnyPortrait(preferredRotation)) { 1190 return preferredRotation; 1191 } 1192 return mPortraitRotation; 1193 1194 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: 1195 // Return landscape unless overridden. 1196 if (isLandscapeOrSeascape(preferredRotation)) { 1197 return preferredRotation; 1198 } 1199 return mLandscapeRotation; 1200 1201 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: 1202 // Return reverse portrait unless overridden. 1203 if (isAnyPortrait(preferredRotation)) { 1204 return preferredRotation; 1205 } 1206 return mUpsideDownRotation; 1207 1208 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: 1209 // Return seascape unless overridden. 1210 if (isLandscapeOrSeascape(preferredRotation)) { 1211 return preferredRotation; 1212 } 1213 return mSeascapeRotation; 1214 1215 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: 1216 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: 1217 // Return either landscape rotation. 1218 if (isLandscapeOrSeascape(preferredRotation)) { 1219 return preferredRotation; 1220 } 1221 if (isLandscapeOrSeascape(lastRotation)) { 1222 return lastRotation; 1223 } 1224 return mLandscapeRotation; 1225 1226 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: 1227 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: 1228 // Return either portrait rotation. 1229 if (isAnyPortrait(preferredRotation)) { 1230 return preferredRotation; 1231 } 1232 if (isAnyPortrait(lastRotation)) { 1233 return lastRotation; 1234 } 1235 return mPortraitRotation; 1236 1237 default: 1238 // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR, 1239 // just return the preferred orientation we already calculated. 1240 if (preferredRotation >= 0) { 1241 return preferredRotation; 1242 } 1243 return Surface.ROTATION_0; 1244 } 1245 } 1246 isLandscapeOrSeascape(int rotation)1247 private boolean isLandscapeOrSeascape(int rotation) { 1248 return rotation == mLandscapeRotation || rotation == mSeascapeRotation; 1249 } 1250 isAnyPortrait(int rotation)1251 private boolean isAnyPortrait(int rotation) { 1252 return rotation == mPortraitRotation || rotation == mUpsideDownRotation; 1253 } 1254 isValidRotationChoice(final int preferredRotation)1255 private boolean isValidRotationChoice(final int preferredRotation) { 1256 // Determine if the given app orientation is compatible with the provided rotation choice. 1257 switch (mCurrentAppOrientation) { 1258 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: 1259 // Works with any of the 4 rotations. 1260 return preferredRotation >= 0; 1261 1262 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: 1263 // It's possible for the user pref to be set at 180 because of FULL_USER. This would 1264 // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait 1265 // but never to go to 180. 1266 return preferredRotation == mPortraitRotation; 1267 1268 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: 1269 // Works landscape or seascape. 1270 return isLandscapeOrSeascape(preferredRotation); 1271 1272 case ActivityInfo.SCREEN_ORIENTATION_USER: 1273 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: 1274 // Works with any rotation except upside down. 1275 return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation); 1276 } 1277 1278 return false; 1279 } 1280 isRotationChoicePossible(int orientation)1281 private boolean isRotationChoicePossible(int orientation) { 1282 // Rotation choice is only shown when the user is in locked mode. 1283 if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false; 1284 1285 // We should only enable rotation choice if the rotation isn't forced by the lid, dock, 1286 // demo, hdmi, vr, etc mode. 1287 1288 // Determine if the rotation is currently forced. 1289 if (isFixedToUserRotation()) { 1290 return false; // Rotation is forced to user settings. 1291 } 1292 1293 final int lidState = mDisplayPolicy.getLidState(); 1294 if (lidState == LID_OPEN && mLidOpenRotation >= 0) { 1295 return false; // Rotation is forced mLidOpenRotation. 1296 } 1297 1298 final int dockMode = mDisplayPolicy.getDockMode(); 1299 final boolean carDockEnablesAccelerometer = false; 1300 if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) { 1301 return false; // Rotation forced to mCarDockRotation. 1302 } 1303 1304 final boolean deskDockEnablesAccelerometer = 1305 mDisplayPolicy.isDeskDockEnablesAccelerometer(); 1306 if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK 1307 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK 1308 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) 1309 && !deskDockEnablesAccelerometer) { 1310 return false; // Rotation forced to mDeskDockRotation. 1311 } 1312 1313 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged(); 1314 if (hdmiPlugged && mDemoHdmiRotationLock) { 1315 return false; // Rotation forced to mDemoHdmiRotation. 1316 1317 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED 1318 && mUndockedHdmiRotation >= 0) { 1319 return false; // Rotation forced to mUndockedHdmiRotation. 1320 1321 } else if (mDemoRotationLock) { 1322 return false; // Rotation forced to mDemoRotation. 1323 1324 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) { 1325 return false; // Rotation forced to mPortraitRotation. 1326 1327 } else if (!mSupportAutoRotation) { 1328 return false; 1329 } 1330 1331 // Ensure that some rotation choice is possible for the given orientation. 1332 switch (orientation) { 1333 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: 1334 case ActivityInfo.SCREEN_ORIENTATION_USER: 1335 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: 1336 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: 1337 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: 1338 // NOSENSOR description is ambiguous, in reality WM ignores user choice. 1339 return true; 1340 } 1341 1342 // Rotation is forced, should be controlled by system. 1343 return false; 1344 } 1345 1346 /** Notify the StatusBar that system rotation suggestion has changed. */ sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid)1347 private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) { 1348 if (mStatusBarManagerInternal == null) { 1349 mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); 1350 } 1351 if (mStatusBarManagerInternal != null) { 1352 mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid); 1353 } 1354 } 1355 allowAllRotationsToString(int allowAll)1356 private static String allowAllRotationsToString(int allowAll) { 1357 switch (allowAll) { 1358 case -1: 1359 return "unknown"; 1360 case 0: 1361 return "false"; 1362 case 1: 1363 return "true"; 1364 default: 1365 return Integer.toString(allowAll); 1366 } 1367 } 1368 onUserSwitch()1369 public void onUserSwitch() { 1370 if (mSettingsObserver != null) { 1371 mSettingsObserver.onChange(false); 1372 } 1373 } 1374 1375 /** Return whether the rotation settings has changed. */ updateSettings()1376 private boolean updateSettings() { 1377 final ContentResolver resolver = mContext.getContentResolver(); 1378 boolean shouldUpdateRotation = false; 1379 1380 synchronized (mLock) { 1381 boolean shouldUpdateOrientationListener = false; 1382 1383 // Configure rotation suggestions. 1384 final int showRotationSuggestions = 1385 ActivityManager.isLowRamDeviceStatic() 1386 ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED 1387 : Settings.Secure.getIntForUser(resolver, 1388 Settings.Secure.SHOW_ROTATION_SUGGESTIONS, 1389 Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT, 1390 UserHandle.USER_CURRENT); 1391 if (mShowRotationSuggestions != showRotationSuggestions) { 1392 mShowRotationSuggestions = showRotationSuggestions; 1393 shouldUpdateOrientationListener = true; 1394 } 1395 1396 // Configure rotation lock. 1397 final int userRotation = Settings.System.getIntForUser(resolver, 1398 Settings.System.USER_ROTATION, Surface.ROTATION_0, 1399 UserHandle.USER_CURRENT); 1400 if (mUserRotation != userRotation) { 1401 mUserRotation = userRotation; 1402 shouldUpdateRotation = true; 1403 } 1404 1405 final int userRotationMode = Settings.System.getIntForUser(resolver, 1406 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 1407 ? WindowManagerPolicy.USER_ROTATION_FREE 1408 : WindowManagerPolicy.USER_ROTATION_LOCKED; 1409 if (mUserRotationMode != userRotationMode) { 1410 mUserRotationMode = userRotationMode; 1411 shouldUpdateOrientationListener = true; 1412 shouldUpdateRotation = true; 1413 } 1414 1415 if (shouldUpdateOrientationListener) { 1416 updateOrientationListenerLw(); // Enable or disable the orientation listener. 1417 } 1418 } 1419 1420 return shouldUpdateRotation; 1421 } 1422 dump(String prefix, PrintWriter pw)1423 void dump(String prefix, PrintWriter pw) { 1424 pw.println(prefix + "DisplayRotation"); 1425 pw.println(prefix + " mCurrentAppOrientation=" 1426 + ActivityInfo.screenOrientationToString(mCurrentAppOrientation)); 1427 pw.println(prefix + " mLastOrientation=" + mLastOrientation); 1428 pw.print(prefix + " mRotation=" + mRotation); 1429 pw.println(" mDeferredRotationPauseCount=" + mDeferredRotationPauseCount); 1430 1431 pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation)); 1432 pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation)); 1433 pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation)); 1434 pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation)); 1435 1436 pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation); 1437 if (mOrientationListener != null) { 1438 mOrientationListener.dump(pw, prefix + " "); 1439 } 1440 pw.println(); 1441 1442 pw.print(prefix + " mCarDockRotation=" + Surface.rotationToString(mCarDockRotation)); 1443 pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation)); 1444 pw.print(prefix + " mUserRotationMode=" 1445 + WindowManagerPolicy.userRotationModeToString(mUserRotationMode)); 1446 pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation)); 1447 pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations)); 1448 1449 pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation)); 1450 pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock); 1451 pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation)); 1452 pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation)); 1453 pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation()); 1454 } 1455 1456 private class OrientationListener extends WindowOrientationListener { 1457 final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5); 1458 boolean mEnabled; 1459 OrientationListener(Context context, Handler handler)1460 OrientationListener(Context context, Handler handler) { 1461 super(context, handler); 1462 } 1463 1464 private class UpdateRunnable implements Runnable { 1465 final int mRotation; 1466 UpdateRunnable(int rotation)1467 UpdateRunnable(int rotation) { 1468 mRotation = rotation; 1469 } 1470 1471 @Override run()1472 public void run() { 1473 // Send interaction hint to improve redraw performance. 1474 mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0); 1475 if (isRotationChoicePossible(mCurrentAppOrientation)) { 1476 final boolean isValid = isValidRotationChoice(mRotation); 1477 sendProposedRotationChangeToStatusBarInternal(mRotation, isValid); 1478 } else { 1479 mService.updateRotation(false /* alwaysSendConfiguration */, 1480 false /* forceRelayout */); 1481 } 1482 } 1483 } 1484 1485 @Override onProposedRotationChanged(int rotation)1486 public void onProposedRotationChanged(int rotation) { 1487 ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation); 1488 Runnable r = mRunnableCache.get(rotation, null); 1489 if (r == null) { 1490 r = new UpdateRunnable(rotation); 1491 mRunnableCache.put(rotation, r); 1492 } 1493 getHandler().post(r); 1494 } 1495 1496 @Override enable(boolean clearCurrentRotation)1497 public void enable(boolean clearCurrentRotation) { 1498 super.enable(clearCurrentRotation); 1499 mEnabled = true; 1500 ProtoLog.v(WM_DEBUG_ORIENTATION, "Enabling listeners"); 1501 } 1502 1503 @Override disable()1504 public void disable() { 1505 super.disable(); 1506 mEnabled = false; 1507 ProtoLog.v(WM_DEBUG_ORIENTATION, "Disabling listeners"); 1508 } 1509 } 1510 1511 private class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler)1512 SettingsObserver(Handler handler) { 1513 super(handler); 1514 } 1515 observe()1516 void observe() { 1517 final ContentResolver resolver = mContext.getContentResolver(); 1518 resolver.registerContentObserver(Settings.Secure.getUriFor( 1519 Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this, 1520 UserHandle.USER_ALL); 1521 resolver.registerContentObserver(Settings.System.getUriFor( 1522 Settings.System.ACCELEROMETER_ROTATION), false, this, 1523 UserHandle.USER_ALL); 1524 resolver.registerContentObserver(Settings.System.getUriFor( 1525 Settings.System.USER_ROTATION), false, this, 1526 UserHandle.USER_ALL); 1527 updateSettings(); 1528 } 1529 1530 @Override onChange(boolean selfChange)1531 public void onChange(boolean selfChange) { 1532 if (updateSettings()) { 1533 mService.updateRotation(true /* alwaysSendConfiguration */, 1534 false /* forceRelayout */); 1535 } 1536 } 1537 } 1538 1539 @VisibleForTesting 1540 interface ContentObserverRegister { registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer, @UserIdInt int userHandle)1541 void registerContentObserver(Uri uri, boolean notifyForDescendants, 1542 ContentObserver observer, @UserIdInt int userHandle); 1543 } 1544 } 1545