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