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