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