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