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