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 com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; 20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; 21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 22 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 23 24 import android.annotation.IntDef; 25 import android.annotation.UserIdInt; 26 import android.app.ActivityManager; 27 import android.content.ContentResolver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.ActivityInfo; 31 import android.content.pm.PackageManager; 32 import android.content.res.Resources; 33 import android.database.ContentObserver; 34 import android.hardware.power.V1_0.PowerHint; 35 import android.net.Uri; 36 import android.os.Handler; 37 import android.os.SystemProperties; 38 import android.os.UserHandle; 39 import android.provider.Settings; 40 import android.util.Slog; 41 import android.util.SparseArray; 42 import android.view.Surface; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.server.LocalServices; 46 import com.android.server.UiThread; 47 import com.android.server.policy.WindowManagerPolicy; 48 import com.android.server.policy.WindowOrientationListener; 49 import com.android.server.statusbar.StatusBarManagerInternal; 50 51 import java.io.PrintWriter; 52 import java.lang.annotation.Retention; 53 import java.lang.annotation.RetentionPolicy; 54 55 /** 56 * Defines the mapping between orientation and rotation of a display. 57 * Non-public methods are assumed to run inside WM lock. 58 */ 59 public class DisplayRotation { 60 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM; 61 62 private final WindowManagerService mService; 63 private final DisplayContent mDisplayContent; 64 private final DisplayPolicy mDisplayPolicy; 65 private final DisplayWindowSettings mDisplayWindowSettings; 66 private final Context mContext; 67 private final Object mLock; 68 69 public final boolean isDefaultDisplay; 70 private final boolean mSupportAutoRotation; 71 private final int mLidOpenRotation; 72 private final int mCarDockRotation; 73 private final int mDeskDockRotation; 74 private final int mUndockedHdmiRotation; 75 76 private OrientationListener mOrientationListener; 77 private StatusBarManagerInternal mStatusBarManagerInternal; 78 private SettingsObserver mSettingsObserver; 79 80 private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 81 82 @VisibleForTesting 83 int mLandscapeRotation; // default landscape 84 @VisibleForTesting 85 int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation 86 @VisibleForTesting 87 int mPortraitRotation; // default portrait 88 @VisibleForTesting 89 int mUpsideDownRotation; // "other" portrait 90 91 // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION) 92 private int mShowRotationSuggestions; 93 94 private int mAllowAllRotations = -1; 95 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; 96 private int mUserRotation = Surface.ROTATION_0; 97 98 /** 99 * Flag that indicates this is a display that may run better when fixed to user rotation. 100 */ 101 private boolean mDefaultFixedToUserRotation; 102 103 /** 104 * No overridden behavior is provided in terms of fixing rotation to user rotation. Use other 105 * flags to derive the default behavior, such as {@link WindowManagerService#mIsPc} and 106 * {@link WindowManagerService#mForceDesktopModeOnExternalDisplays}. 107 */ 108 static final int FIXED_TO_USER_ROTATION_DEFAULT = 0; 109 /** 110 * Don't fix display rotation to {@link #mUserRotation} only. Always allow other factors to play 111 * a role in deciding display rotation. 112 */ 113 static final int FIXED_TO_USER_ROTATION_DISABLED = 1; 114 /** 115 * Only use {@link #mUserRotation} as the display rotation. 116 */ 117 static final int FIXED_TO_USER_ROTATION_ENABLED = 2; 118 @IntDef({ FIXED_TO_USER_ROTATION_DEFAULT, FIXED_TO_USER_ROTATION_DISABLED, 119 FIXED_TO_USER_ROTATION_ENABLED }) 120 @Retention(RetentionPolicy.SOURCE) 121 @interface FixedToUserRotation {} 122 123 /** 124 * A flag to indicate if the display rotation should be fixed to user specified rotation 125 * regardless of all other states (including app requrested orientation). {@code true} the 126 * display rotation should be fixed to user specified rotation, {@code false} otherwise. 127 */ 128 private int mFixedToUserRotation = FIXED_TO_USER_ROTATION_DEFAULT; 129 130 private int mDemoHdmiRotation; 131 private int mDemoRotation; 132 private boolean mDemoHdmiRotationLock; 133 private boolean mDemoRotationLock; 134 DisplayRotation(WindowManagerService service, DisplayContent displayContent)135 DisplayRotation(WindowManagerService service, DisplayContent displayContent) { 136 this(service, displayContent, displayContent.getDisplayPolicy(), 137 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock()); 138 } 139 140 @VisibleForTesting DisplayRotation(WindowManagerService service, DisplayContent displayContent, DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings, Context context, Object lock)141 DisplayRotation(WindowManagerService service, DisplayContent displayContent, 142 DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings, 143 Context context, Object lock) { 144 mService = service; 145 mDisplayContent = displayContent; 146 mDisplayPolicy = displayPolicy; 147 mDisplayWindowSettings = displayWindowSettings; 148 mContext = context; 149 mLock = lock; 150 isDefaultDisplay = displayContent.isDefaultDisplay; 151 152 mSupportAutoRotation = mContext.getResources().getBoolean( 153 com.android.internal.R.bool.config_supportAutoRotation); 154 mLidOpenRotation = readRotation( 155 com.android.internal.R.integer.config_lidOpenRotation); 156 mCarDockRotation = readRotation( 157 com.android.internal.R.integer.config_carDockRotation); 158 mDeskDockRotation = readRotation( 159 com.android.internal.R.integer.config_deskDockRotation); 160 mUndockedHdmiRotation = readRotation( 161 com.android.internal.R.integer.config_undockedHdmiRotation); 162 163 if (isDefaultDisplay) { 164 final Handler uiHandler = UiThread.getHandler(); 165 mOrientationListener = new OrientationListener(mContext, uiHandler); 166 mOrientationListener.setCurrentRotation(displayContent.getRotation()); 167 mSettingsObserver = new SettingsObserver(uiHandler); 168 mSettingsObserver.observe(); 169 } 170 } 171 readRotation(int resID)172 private int readRotation(int resID) { 173 try { 174 final int rotation = mContext.getResources().getInteger(resID); 175 switch (rotation) { 176 case 0: 177 return Surface.ROTATION_0; 178 case 90: 179 return Surface.ROTATION_90; 180 case 180: 181 return Surface.ROTATION_180; 182 case 270: 183 return Surface.ROTATION_270; 184 } 185 } catch (Resources.NotFoundException e) { 186 // fall through 187 } 188 return -1; 189 } 190 configure(int width, int height, int shortSizeDp, int longSizeDp)191 void configure(int width, int height, int shortSizeDp, int longSizeDp) { 192 final Resources res = mContext.getResources(); 193 if (width > height) { 194 mLandscapeRotation = Surface.ROTATION_0; 195 mSeascapeRotation = Surface.ROTATION_180; 196 if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) { 197 mPortraitRotation = Surface.ROTATION_90; 198 mUpsideDownRotation = Surface.ROTATION_270; 199 } else { 200 mPortraitRotation = Surface.ROTATION_270; 201 mUpsideDownRotation = Surface.ROTATION_90; 202 } 203 } else { 204 mPortraitRotation = Surface.ROTATION_0; 205 mUpsideDownRotation = Surface.ROTATION_180; 206 if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) { 207 mLandscapeRotation = Surface.ROTATION_270; 208 mSeascapeRotation = Surface.ROTATION_90; 209 } else { 210 mLandscapeRotation = Surface.ROTATION_90; 211 mSeascapeRotation = Surface.ROTATION_270; 212 } 213 } 214 215 // For demo purposes, allow the rotation of the HDMI display to be controlled. 216 // By default, HDMI locks rotation to landscape. 217 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { 218 mDemoHdmiRotation = mPortraitRotation; 219 } else { 220 mDemoHdmiRotation = mLandscapeRotation; 221 } 222 mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false); 223 224 // For demo purposes, allow the rotation of the remote display to be controlled. 225 // By default, remote display locks rotation to landscape. 226 if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) { 227 mDemoRotation = mPortraitRotation; 228 } else { 229 mDemoRotation = mLandscapeRotation; 230 } 231 mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false); 232 233 // It's physically impossible to rotate the car's screen. 234 final boolean isCar = mContext.getPackageManager().hasSystemFeature( 235 PackageManager.FEATURE_AUTOMOTIVE); 236 // It's also not likely to rotate a TV screen. 237 final boolean isTv = mContext.getPackageManager().hasSystemFeature( 238 PackageManager.FEATURE_LEANBACK); 239 final boolean forceDesktopMode = 240 mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay; 241 mDefaultFixedToUserRotation = 242 (isCar || isTv || mService.mIsPc || forceDesktopMode) 243 // For debug purposes the next line turns this feature off with: 244 // $ adb shell setprop config.override_forced_orient true 245 // $ adb shell wm size reset 246 && !"true".equals(SystemProperties.get("config.override_forced_orient")); 247 } 248 setRotation(int rotation)249 void setRotation(int rotation) { 250 if (mOrientationListener != null) { 251 mOrientationListener.setCurrentRotation(rotation); 252 } 253 } 254 setCurrentOrientation(int newOrientation)255 void setCurrentOrientation(int newOrientation) { 256 if (newOrientation != mCurrentAppOrientation) { 257 mCurrentAppOrientation = newOrientation; 258 if (isDefaultDisplay) { 259 updateOrientationListenerLw(); 260 } 261 } 262 } 263 restoreSettings(int userRotationMode, int userRotation, @FixedToUserRotation int fixedToUserRotation)264 void restoreSettings(int userRotationMode, int userRotation, 265 @FixedToUserRotation int fixedToUserRotation) { 266 mFixedToUserRotation = fixedToUserRotation; 267 268 // We will retrieve user rotation and user rotation mode from settings for default display. 269 if (isDefaultDisplay) { 270 return; 271 } 272 if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE 273 && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) { 274 Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode 275 + " for " + mDisplayContent); 276 userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; 277 } 278 if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) { 279 Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation 280 + " for " + mDisplayContent); 281 userRotation = Surface.ROTATION_0; 282 } 283 mUserRotationMode = userRotationMode; 284 mUserRotation = userRotation; 285 } 286 setFixedToUserRotation(@ixedToUserRotation int fixedToUserRotation)287 void setFixedToUserRotation(@FixedToUserRotation int fixedToUserRotation) { 288 if (mFixedToUserRotation == fixedToUserRotation) { 289 return; 290 } 291 292 mFixedToUserRotation = fixedToUserRotation; 293 mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation); 294 mService.updateRotation(true /* alwaysSendConfiguration */, 295 false /* forceRelayout */); 296 } 297 setUserRotation(int userRotationMode, int userRotation)298 private void setUserRotation(int userRotationMode, int userRotation) { 299 if (isDefaultDisplay) { 300 // We'll be notified via settings listener, so we don't need to update internal values. 301 final ContentResolver res = mContext.getContentResolver(); 302 final int accelerometerRotation = 303 userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1; 304 Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION, 305 accelerometerRotation, UserHandle.USER_CURRENT); 306 Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation, 307 UserHandle.USER_CURRENT); 308 return; 309 } 310 311 boolean changed = false; 312 if (mUserRotationMode != userRotationMode) { 313 mUserRotationMode = userRotationMode; 314 changed = true; 315 } 316 if (mUserRotation != userRotation) { 317 mUserRotation = userRotation; 318 changed = true; 319 } 320 mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode, 321 userRotation); 322 if (changed) { 323 mService.updateRotation(true /* alwaysSendConfiguration */, 324 false /* forceRelayout */); 325 } 326 } 327 freezeRotation(int rotation)328 void freezeRotation(int rotation) { 329 rotation = (rotation == -1) ? mDisplayContent.getRotation() : rotation; 330 setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation); 331 } 332 thawRotation()333 void thawRotation() { 334 setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation); 335 } 336 isRotationFrozen()337 boolean isRotationFrozen() { 338 if (!isDefaultDisplay) { 339 return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED; 340 } 341 342 return Settings.System.getIntForUser(mContext.getContentResolver(), 343 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0; 344 } 345 isFixedToUserRotation()346 boolean isFixedToUserRotation() { 347 switch (mFixedToUserRotation) { 348 case FIXED_TO_USER_ROTATION_DISABLED: 349 return false; 350 case FIXED_TO_USER_ROTATION_ENABLED: 351 return true; 352 default: 353 return mDefaultFixedToUserRotation; 354 } 355 } 356 357 /** 358 * Returns {@code true} if this display rotation takes app requested orientation into 359 * consideration; {@code false} otherwise. For the time being the only case where this is {@code 360 * false} is when {@link #isFixedToUserRotation()} is {@code true}. 361 */ respectAppRequestedOrientation()362 boolean respectAppRequestedOrientation() { 363 return !isFixedToUserRotation(); 364 } 365 getLandscapeRotation()366 public int getLandscapeRotation() { 367 return mLandscapeRotation; 368 } 369 getSeascapeRotation()370 public int getSeascapeRotation() { 371 return mSeascapeRotation; 372 } 373 getPortraitRotation()374 public int getPortraitRotation() { 375 return mPortraitRotation; 376 } 377 getUpsideDownRotation()378 public int getUpsideDownRotation() { 379 return mUpsideDownRotation; 380 } 381 getCurrentAppOrientation()382 public int getCurrentAppOrientation() { 383 return mCurrentAppOrientation; 384 } 385 getDisplayPolicy()386 public DisplayPolicy getDisplayPolicy() { 387 return mDisplayPolicy; 388 } 389 getOrientationListener()390 public WindowOrientationListener getOrientationListener() { 391 return mOrientationListener; 392 } 393 getUserRotation()394 public int getUserRotation() { 395 return mUserRotation; 396 } 397 getUserRotationMode()398 public int getUserRotationMode() { 399 return mUserRotationMode; 400 } 401 updateOrientationListener()402 public void updateOrientationListener() { 403 synchronized (mLock) { 404 updateOrientationListenerLw(); 405 } 406 } 407 408 /** 409 * Various use cases for invoking this function: 410 * <li>Screen turning off, should always disable listeners if already enabled.</li> 411 * <li>Screen turned on and current app has sensor based orientation, enable listeners 412 * if not already enabled.</li> 413 * <li>Screen turned on and current app does not have sensor orientation, disable listeners 414 * if already enabled.</li> 415 * <li>Screen turning on and current app has sensor based orientation, enable listeners 416 * if needed.</li> 417 * <li>screen turning on and current app has nosensor based orientation, do nothing.</li> 418 */ updateOrientationListenerLw()419 private void updateOrientationListenerLw() { 420 if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) { 421 // If sensor is turned off or nonexistent for some reason. 422 return; 423 } 424 425 final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly(); 426 final boolean awake = mDisplayPolicy.isAwake(); 427 final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete(); 428 final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete(); 429 430 // Could have been invoked due to screen turning on or off or 431 // change of the currently visible window's orientation. 432 if (DEBUG_ORIENTATION) Slog.v(TAG, "screenOnEarly=" + screenOnEarly 433 + ", awake=" + awake + ", currentAppOrientation=" + mCurrentAppOrientation 434 + ", orientationSensorEnabled=" + mOrientationListener.mEnabled 435 + ", keyguardDrawComplete=" + keyguardDrawComplete 436 + ", windowManagerDrawComplete=" + windowManagerDrawComplete); 437 438 boolean disable = true; 439 // Note: We postpone the rotating of the screen until the keyguard as well as the 440 // window manager have reported a draw complete or the keyguard is going away in dismiss 441 // mode. 442 if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) { 443 if (needSensorRunning()) { 444 disable = false; 445 // Enable listener if not already enabled. 446 if (!mOrientationListener.mEnabled) { 447 // Don't clear the current sensor orientation if the keyguard is going away in 448 // dismiss mode. This allows window manager to use the last sensor reading to 449 // determine the orientation vs. falling back to the last known orientation if 450 // the sensor reading was cleared which can cause it to relaunch the app that 451 // will show in the wrong orientation first before correcting leading to app 452 // launch delays. 453 mOrientationListener.enable(true /* clearCurrentRotation */); 454 } 455 } 456 } 457 // Check if sensors need to be disabled. 458 if (disable && mOrientationListener.mEnabled) { 459 mOrientationListener.disable(); 460 } 461 } 462 463 /** 464 * We always let the sensor be switched on by default except when 465 * the user has explicitly disabled sensor based rotation or when the 466 * screen is switched off. 467 */ needSensorRunning()468 private boolean needSensorRunning() { 469 if (isFixedToUserRotation()) { 470 // We are sure we only respect user rotation settings, so we are sure we will not 471 // support sensor rotation. 472 return false; 473 } 474 475 if (mSupportAutoRotation) { 476 if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR 477 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR 478 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT 479 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) { 480 // If the application has explicitly requested to follow the 481 // orientation, then we need to turn the sensor on. 482 return true; 483 } 484 } 485 486 final int dockMode = mDisplayPolicy.getDockMode(); 487 if ((mDisplayPolicy.isCarDockEnablesAccelerometer() 488 && dockMode == Intent.EXTRA_DOCK_STATE_CAR) 489 || (mDisplayPolicy.isDeskDockEnablesAccelerometer() 490 && (dockMode == Intent.EXTRA_DOCK_STATE_DESK 491 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK 492 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) { 493 // Enable accelerometer if we are docked in a dock that enables accelerometer 494 // orientation management. 495 return true; 496 } 497 498 if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) { 499 // If the setting for using the sensor by default is enabled, then 500 // we will always leave it on. Note that the user could go to 501 // a window that forces an orientation that does not use the 502 // sensor and in theory we could turn it off... however, when next 503 // turning it on we won't have a good value for the current 504 // orientation for a little bit, which can cause orientation 505 // changes to lag, so we'd like to keep it always on. (It will 506 // still be turned off when the screen is off.) 507 508 // When locked we can provide rotation suggestions users can approve to change the 509 // current screen rotation. To do this the sensor needs to be running. 510 return mSupportAutoRotation && 511 mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED; 512 } 513 return mSupportAutoRotation; 514 } 515 516 /** 517 * Given an orientation constant, returns the appropriate surface rotation, 518 * taking into account sensors, docking mode, rotation lock, and other factors. 519 * 520 * @param orientation An orientation constant, such as 521 * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}. 522 * @param lastRotation The most recently used rotation. 523 * @return The surface rotation to use. 524 */ rotationForOrientation(int orientation, int lastRotation)525 int rotationForOrientation(int orientation, int lastRotation) { 526 if (DEBUG_ORIENTATION) { 527 Slog.v(TAG, "rotationForOrientation(orient=" 528 + orientation + ", last=" + lastRotation 529 + "); user=" + mUserRotation + " " 530 + (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED 531 ? "USER_ROTATION_LOCKED" : "") 532 ); 533 } 534 535 if (isFixedToUserRotation()) { 536 return mUserRotation; 537 } 538 539 int sensorRotation = mOrientationListener != null 540 ? mOrientationListener.getProposedRotation() // may be -1 541 : -1; 542 if (sensorRotation < 0) { 543 sensorRotation = lastRotation; 544 } 545 546 final int lidState = mDisplayPolicy.getLidState(); 547 final int dockMode = mDisplayPolicy.getDockMode(); 548 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged(); 549 final boolean carDockEnablesAccelerometer = 550 mDisplayPolicy.isCarDockEnablesAccelerometer(); 551 final boolean deskDockEnablesAccelerometer = 552 mDisplayPolicy.isDeskDockEnablesAccelerometer(); 553 554 final int preferredRotation; 555 if (!isDefaultDisplay) { 556 // For secondary displays we ignore things like displays sensors, docking mode and 557 // rotation lock, and always prefer user rotation. 558 preferredRotation = mUserRotation; 559 } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) { 560 // Ignore sensor when lid switch is open and rotation is forced. 561 preferredRotation = mLidOpenRotation; 562 } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR 563 && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) { 564 // Ignore sensor when in car dock unless explicitly enabled. 565 // This case can override the behavior of NOSENSOR, and can also 566 // enable 180 degree rotation while docked. 567 preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation; 568 } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK 569 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK 570 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) 571 && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) { 572 // Ignore sensor when in desk dock unless explicitly enabled. 573 // This case can override the behavior of NOSENSOR, and can also 574 // enable 180 degree rotation while docked. 575 preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation; 576 } else if (hdmiPlugged && mDemoHdmiRotationLock) { 577 // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled. 578 // Note that the dock orientation overrides the HDMI orientation. 579 preferredRotation = mDemoHdmiRotation; 580 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED 581 && mUndockedHdmiRotation >= 0) { 582 // Ignore sensor when plugged into HDMI and an undocked orientation has 583 // been specified in the configuration (only for legacy devices without 584 // full multi-display support). 585 // Note that the dock orientation overrides the HDMI orientation. 586 preferredRotation = mUndockedHdmiRotation; 587 } else if (mDemoRotationLock) { 588 // Ignore sensor when demo rotation lock is enabled. 589 // Note that the dock orientation and HDMI rotation lock override this. 590 preferredRotation = mDemoRotation; 591 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) { 592 // While in VR, apps always prefer a portrait rotation. This does not change 593 // any apps that explicitly set landscape, but does cause sensors be ignored, 594 // and ignored any orientation lock that the user has set (this conditional 595 // should remain above the ORIENTATION_LOCKED conditional below). 596 preferredRotation = mPortraitRotation; 597 } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { 598 // Application just wants to remain locked in the last rotation. 599 preferredRotation = lastRotation; 600 } else if (!mSupportAutoRotation) { 601 // If we don't support auto-rotation then bail out here and ignore 602 // the sensor and any rotation lock settings. 603 preferredRotation = -1; 604 } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE 605 && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER 606 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 607 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE 608 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT 609 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER)) 610 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR 611 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR 612 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 613 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { 614 // Otherwise, use sensor only if requested by the application or enabled 615 // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR. 616 if (mAllowAllRotations < 0) { 617 // Can't read this during init() because the context doesn't 618 // have display metrics at that time so we cannot determine 619 // tablet vs. phone then. 620 mAllowAllRotations = mContext.getResources().getBoolean( 621 com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0; 622 } 623 if (sensorRotation != Surface.ROTATION_180 624 || mAllowAllRotations == 1 625 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR 626 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) { 627 preferredRotation = sensorRotation; 628 } else { 629 preferredRotation = lastRotation; 630 } 631 } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED 632 && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { 633 // Apply rotation lock. Does not apply to NOSENSOR. 634 // The idea is that the user rotation expresses a weak preference for the direction 635 // of gravity and as NOSENSOR is never affected by gravity, then neither should 636 // NOSENSOR be affected by rotation lock (although it will be affected by docks). 637 preferredRotation = mUserRotation; 638 } else { 639 // No overriding preference. 640 // We will do exactly what the application asked us to do. 641 preferredRotation = -1; 642 } 643 644 switch (orientation) { 645 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: 646 // Return portrait unless overridden. 647 if (isAnyPortrait(preferredRotation)) { 648 return preferredRotation; 649 } 650 return mPortraitRotation; 651 652 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: 653 // Return landscape unless overridden. 654 if (isLandscapeOrSeascape(preferredRotation)) { 655 return preferredRotation; 656 } 657 return mLandscapeRotation; 658 659 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: 660 // Return reverse portrait unless overridden. 661 if (isAnyPortrait(preferredRotation)) { 662 return preferredRotation; 663 } 664 return mUpsideDownRotation; 665 666 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: 667 // Return seascape unless overridden. 668 if (isLandscapeOrSeascape(preferredRotation)) { 669 return preferredRotation; 670 } 671 return mSeascapeRotation; 672 673 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: 674 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: 675 // Return either landscape rotation. 676 if (isLandscapeOrSeascape(preferredRotation)) { 677 return preferredRotation; 678 } 679 if (isLandscapeOrSeascape(lastRotation)) { 680 return lastRotation; 681 } 682 return mLandscapeRotation; 683 684 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: 685 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: 686 // Return either portrait rotation. 687 if (isAnyPortrait(preferredRotation)) { 688 return preferredRotation; 689 } 690 if (isAnyPortrait(lastRotation)) { 691 return lastRotation; 692 } 693 return mPortraitRotation; 694 695 default: 696 // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR, 697 // just return the preferred orientation we already calculated. 698 if (preferredRotation >= 0) { 699 return preferredRotation; 700 } 701 return Surface.ROTATION_0; 702 } 703 } 704 isLandscapeOrSeascape(int rotation)705 private boolean isLandscapeOrSeascape(int rotation) { 706 return rotation == mLandscapeRotation || rotation == mSeascapeRotation; 707 } 708 isAnyPortrait(int rotation)709 private boolean isAnyPortrait(int rotation) { 710 return rotation == mPortraitRotation || rotation == mUpsideDownRotation; 711 } 712 isValidRotationChoice(final int preferredRotation)713 private boolean isValidRotationChoice(final int preferredRotation) { 714 // Determine if the given app orientation is compatible with the provided rotation choice. 715 switch (mCurrentAppOrientation) { 716 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: 717 // Works with any of the 4 rotations. 718 return preferredRotation >= 0; 719 720 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: 721 // It's possible for the user pref to be set at 180 because of FULL_USER. This would 722 // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait 723 // but never to go to 180. 724 return preferredRotation == mPortraitRotation; 725 726 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: 727 // Works landscape or seascape. 728 return isLandscapeOrSeascape(preferredRotation); 729 730 case ActivityInfo.SCREEN_ORIENTATION_USER: 731 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: 732 // Works with any rotation except upside down. 733 return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation); 734 } 735 736 return false; 737 } 738 isRotationChoicePossible(int orientation)739 private boolean isRotationChoicePossible(int orientation) { 740 // Rotation choice is only shown when the user is in locked mode. 741 if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false; 742 743 // We should only enable rotation choice if the rotation isn't forced by the lid, dock, 744 // demo, hdmi, vr, etc mode. 745 746 // Determine if the rotation is currently forced. 747 if (isFixedToUserRotation()) { 748 return false; // Rotation is forced to user settings. 749 } 750 751 final int lidState = mDisplayPolicy.getLidState(); 752 if (lidState == LID_OPEN && mLidOpenRotation >= 0) { 753 return false; // Rotation is forced mLidOpenRotation. 754 } 755 756 final int dockMode = mDisplayPolicy.getDockMode(); 757 final boolean carDockEnablesAccelerometer = false; 758 if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) { 759 return false; // Rotation forced to mCarDockRotation. 760 } 761 762 final boolean deskDockEnablesAccelerometer = 763 mDisplayPolicy.isDeskDockEnablesAccelerometer(); 764 if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK 765 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK 766 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) 767 && !deskDockEnablesAccelerometer) { 768 return false; // Rotation forced to mDeskDockRotation. 769 } 770 771 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged(); 772 if (hdmiPlugged && mDemoHdmiRotationLock) { 773 return false; // Rotation forced to mDemoHdmiRotation. 774 775 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED 776 && mUndockedHdmiRotation >= 0) { 777 return false; // Rotation forced to mUndockedHdmiRotation. 778 779 } else if (mDemoRotationLock) { 780 return false; // Rotation forced to mDemoRotation. 781 782 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) { 783 return false; // Rotation forced to mPortraitRotation. 784 785 } else if (!mSupportAutoRotation) { 786 return false; 787 } 788 789 // Ensure that some rotation choice is possible for the given orientation. 790 switch (orientation) { 791 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: 792 case ActivityInfo.SCREEN_ORIENTATION_USER: 793 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: 794 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: 795 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: 796 // NOSENSOR description is ambiguous, in reality WM ignores user choice. 797 return true; 798 } 799 800 // Rotation is forced, should be controlled by system. 801 return false; 802 } 803 804 /** Notify the StatusBar that system rotation suggestion has changed. */ sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid)805 private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) { 806 if (mStatusBarManagerInternal == null) { 807 mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); 808 } 809 if (mStatusBarManagerInternal != null) { 810 mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid); 811 } 812 } 813 allowAllRotationsToString(int allowAll)814 private static String allowAllRotationsToString(int allowAll) { 815 switch (allowAll) { 816 case -1: 817 return "unknown"; 818 case 0: 819 return "false"; 820 case 1: 821 return "true"; 822 default: 823 return Integer.toString(allowAll); 824 } 825 } 826 onUserSwitch()827 public void onUserSwitch() { 828 if (mSettingsObserver != null) { 829 mSettingsObserver.onChange(false); 830 } 831 } 832 833 /** Return whether the rotation settings has changed. */ updateSettings()834 private boolean updateSettings() { 835 final ContentResolver resolver = mContext.getContentResolver(); 836 boolean shouldUpdateRotation = false; 837 838 synchronized (mLock) { 839 boolean shouldUpdateOrientationListener = false; 840 841 // Configure rotation suggestions. 842 final int showRotationSuggestions = 843 ActivityManager.isLowRamDeviceStatic() 844 ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED 845 : Settings.Secure.getIntForUser(resolver, 846 Settings.Secure.SHOW_ROTATION_SUGGESTIONS, 847 Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT, 848 UserHandle.USER_CURRENT); 849 if (mShowRotationSuggestions != showRotationSuggestions) { 850 mShowRotationSuggestions = showRotationSuggestions; 851 shouldUpdateOrientationListener = true; 852 } 853 854 // Configure rotation lock. 855 final int userRotation = Settings.System.getIntForUser(resolver, 856 Settings.System.USER_ROTATION, Surface.ROTATION_0, 857 UserHandle.USER_CURRENT); 858 if (mUserRotation != userRotation) { 859 mUserRotation = userRotation; 860 shouldUpdateRotation = true; 861 } 862 863 final int userRotationMode = Settings.System.getIntForUser(resolver, 864 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 865 ? WindowManagerPolicy.USER_ROTATION_FREE 866 : WindowManagerPolicy.USER_ROTATION_LOCKED; 867 if (mUserRotationMode != userRotationMode) { 868 mUserRotationMode = userRotationMode; 869 shouldUpdateOrientationListener = true; 870 shouldUpdateRotation = true; 871 } 872 873 if (shouldUpdateOrientationListener) { 874 updateOrientationListenerLw(); // Enable or disable the orientation listener. 875 } 876 } 877 878 return shouldUpdateRotation; 879 } 880 dump(String prefix, PrintWriter pw)881 void dump(String prefix, PrintWriter pw) { 882 pw.println(prefix + "DisplayRotation"); 883 pw.println(prefix + " mCurrentAppOrientation=" 884 + ActivityInfo.screenOrientationToString(mCurrentAppOrientation)); 885 pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation)); 886 pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation)); 887 pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation)); 888 pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation)); 889 890 pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation); 891 if (mOrientationListener != null) { 892 mOrientationListener.dump(pw, prefix + " "); 893 } 894 pw.println(); 895 896 pw.print(prefix + " mCarDockRotation=" + Surface.rotationToString(mCarDockRotation)); 897 pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation)); 898 pw.print(prefix + " mUserRotationMode=" 899 + WindowManagerPolicy.userRotationModeToString(mUserRotationMode)); 900 pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation)); 901 pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations)); 902 903 pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation)); 904 pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock); 905 pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation)); 906 pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation)); 907 pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation()); 908 } 909 910 private class OrientationListener extends WindowOrientationListener { 911 final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5); 912 boolean mEnabled; 913 OrientationListener(Context context, Handler handler)914 OrientationListener(Context context, Handler handler) { 915 super(context, handler); 916 } 917 918 private class UpdateRunnable implements Runnable { 919 final int mRotation; 920 UpdateRunnable(int rotation)921 UpdateRunnable(int rotation) { 922 mRotation = rotation; 923 } 924 925 @Override run()926 public void run() { 927 // Send interaction hint to improve redraw performance. 928 mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0); 929 if (isRotationChoicePossible(mCurrentAppOrientation)) { 930 final boolean isValid = isValidRotationChoice(mRotation); 931 sendProposedRotationChangeToStatusBarInternal(mRotation, isValid); 932 } else { 933 mService.updateRotation(false /* alwaysSendConfiguration */, 934 false /* forceRelayout */); 935 } 936 } 937 } 938 939 @Override onProposedRotationChanged(int rotation)940 public void onProposedRotationChanged(int rotation) { 941 if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation); 942 Runnable r = mRunnableCache.get(rotation, null); 943 if (r == null) { 944 r = new UpdateRunnable(rotation); 945 mRunnableCache.put(rotation, r); 946 } 947 getHandler().post(r); 948 } 949 950 @Override enable(boolean clearCurrentRotation)951 public void enable(boolean clearCurrentRotation) { 952 super.enable(clearCurrentRotation); 953 mEnabled = true; 954 if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners"); 955 } 956 957 @Override disable()958 public void disable() { 959 super.disable(); 960 mEnabled = false; 961 if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners"); 962 } 963 } 964 965 private class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler)966 SettingsObserver(Handler handler) { 967 super(handler); 968 } 969 observe()970 void observe() { 971 final ContentResolver resolver = mContext.getContentResolver(); 972 resolver.registerContentObserver(Settings.Secure.getUriFor( 973 Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this, 974 UserHandle.USER_ALL); 975 resolver.registerContentObserver(Settings.System.getUriFor( 976 Settings.System.ACCELEROMETER_ROTATION), false, this, 977 UserHandle.USER_ALL); 978 resolver.registerContentObserver(Settings.System.getUriFor( 979 Settings.System.USER_ROTATION), false, this, 980 UserHandle.USER_ALL); 981 updateSettings(); 982 } 983 984 @Override onChange(boolean selfChange)985 public void onChange(boolean selfChange) { 986 if (updateSettings()) { 987 mService.updateRotation(true /* alwaysSendConfiguration */, 988 false /* forceRelayout */); 989 } 990 } 991 } 992 993 @VisibleForTesting 994 interface ContentObserverRegister { registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer, @UserIdInt int userHandle)995 void registerContentObserver(Uri uri, boolean notifyForDescendants, 996 ContentObserver observer, @UserIdInt int userHandle); 997 } 998 } 999