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