• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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