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