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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
22 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
23 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
24 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
25 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
26 
27 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
28 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
29 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
30 import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
31 import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION;
32 import static com.android.server.wm.DisplayRotationProto.IS_FIXED_TO_USER_ROTATION;
33 import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
34 import static com.android.server.wm.DisplayRotationProto.ROTATION;
35 import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
36 import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT;
37 import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_HALF_FOLD;
38 import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_NOSENSOR;
39 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
40 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
41 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
42 import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION;
43 
44 import android.annotation.AnimRes;
45 import android.annotation.IntDef;
46 import android.annotation.NonNull;
47 import android.annotation.Nullable;
48 import android.app.ActivityManager;
49 import android.content.ContentResolver;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.pm.ActivityInfo;
53 import android.content.pm.ActivityInfo.ScreenOrientation;
54 import android.content.pm.PackageManager;
55 import android.content.res.Resources;
56 import android.database.ContentObserver;
57 import android.hardware.Sensor;
58 import android.hardware.SensorEvent;
59 import android.hardware.SensorEventListener;
60 import android.hardware.SensorManager;
61 import android.hardware.power.Boost;
62 import android.os.Handler;
63 import android.os.SystemClock;
64 import android.os.SystemProperties;
65 import android.os.UserHandle;
66 import android.provider.Settings;
67 import android.util.ArraySet;
68 import android.util.RotationUtils;
69 import android.util.Slog;
70 import android.util.TimeUtils;
71 import android.util.proto.ProtoOutputStream;
72 import android.view.DisplayAddress;
73 import android.view.IWindowManager;
74 import android.view.Surface;
75 import android.window.TransitionRequestInfo;
76 import android.window.WindowContainerTransaction;
77 
78 import com.android.internal.R;
79 import com.android.internal.annotations.VisibleForTesting;
80 import com.android.internal.protolog.common.ProtoLog;
81 import com.android.server.LocalServices;
82 import com.android.server.UiThread;
83 import com.android.server.policy.WindowManagerPolicy;
84 import com.android.server.statusbar.StatusBarManagerInternal;
85 
86 import java.io.PrintWriter;
87 import java.lang.annotation.Retention;
88 import java.lang.annotation.RetentionPolicy;
89 import java.util.ArrayDeque;
90 import java.util.Set;
91 
92 /**
93  * Defines the mapping between orientation and rotation of a display.
94  * Non-public methods are assumed to run inside WM lock.
95  */
96 public class DisplayRotation {
97     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
98 
99     // Delay in milliseconds when updating config due to folding events. This prevents
100     // config changes and unexpected jumps while folding the device to closed state.
101     private static final int FOLDING_RECOMPUTE_CONFIG_DELAY_MS = 800;
102 
103     private static final int ROTATION_UNDEFINED = -1;
104 
105     private static class RotationAnimationPair {
106         @AnimRes
107         int mEnter;
108         @AnimRes
109         int mExit;
110     }
111 
112     @Nullable
113     final FoldController mFoldController;
114 
115     private final WindowManagerService mService;
116     private final DisplayContent mDisplayContent;
117     private final DisplayPolicy mDisplayPolicy;
118     private final DisplayWindowSettings mDisplayWindowSettings;
119     private final Context mContext;
120     private final Object mLock;
121     @Nullable
122     private final DisplayRotationImmersiveAppCompatPolicy mCompatPolicyForImmersiveApps;
123 
124     public final boolean isDefaultDisplay;
125     private final boolean mSupportAutoRotation;
126     private final boolean mAllowRotationResolver;
127     private final int mLidOpenRotation;
128     private final int mCarDockRotation;
129     private final int mDeskDockRotation;
130     private final int mUndockedHdmiRotation;
131     private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair();
132     private final RotationHistory mRotationHistory = new RotationHistory();
133 
134     private OrientationListener mOrientationListener;
135     private StatusBarManagerInternal mStatusBarManagerInternal;
136     private SettingsObserver mSettingsObserver;
137     @NonNull
138     private final DeviceStateController mDeviceStateController;
139     @NonNull
140     private final DisplayRotationCoordinator mDisplayRotationCoordinator;
141     @NonNull
142     @VisibleForTesting
143     final Runnable mDefaultDisplayRotationChangedCallback;
144 
145     @ScreenOrientation
146     private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
147 
148     /**
149      * Last applied orientation of the display.
150      *
151      * @see #updateOrientationFromApp
152      */
153     @ScreenOrientation
154     private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
155 
156     /**
157      * Current rotation of the display.
158      *
159      * @see #updateRotationUnchecked
160      */
161     @Surface.Rotation
162     private int mRotation;
163 
164     @VisibleForTesting
165     int mLandscapeRotation;  // default landscape
166     @VisibleForTesting
167     int mSeascapeRotation;   // "other" landscape, 180 degrees from mLandscapeRotation
168     @VisibleForTesting
169     int mPortraitRotation;   // default portrait
170     @VisibleForTesting
171     int mUpsideDownRotation; // "other" portrait
172 
173     int mLastSensorRotation = -1;
174 
175     private boolean mAllowSeamlessRotationDespiteNavBarMoving;
176 
177     private int mDeferredRotationPauseCount;
178 
179     /**
180      * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
181      * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
182      * so we need to track when this hits zero so we can apply deferred orientation updates.
183      */
184     private int mSeamlessRotationCount;
185 
186     /**
187      * True in the interval from starting seamless rotation until the last rotated window draws in
188      * the new orientation.
189      */
190     private boolean mRotatingSeamlessly;
191 
192     /**
193      * Behavior of rotation suggestions.
194      *
195      * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
196      */
197     private int mShowRotationSuggestions;
198 
199     /**
200      * The most recent {@link Surface.Rotation} choice shown to the user for confirmation, or
201      * {@link #ROTATION_UNDEFINED}
202      */
203     private int mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
204 
205     private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1;
206     private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0;
207     private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1;
208 
209     @IntDef({ ALLOW_ALL_ROTATIONS_UNDEFINED, ALLOW_ALL_ROTATIONS_DISABLED,
210             ALLOW_ALL_ROTATIONS_ENABLED })
211     @Retention(RetentionPolicy.SOURCE)
212     private @interface AllowAllRotations {}
213 
214     /**
215      * Whether to allow the screen to rotate to all rotations (including 180 degree) according to
216      * the sensor even when the current orientation is not
217      * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_SENSOR} or
218      * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_USER}.
219      */
220     @AllowAllRotations
221     private int mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
222 
223     @WindowManagerPolicy.UserRotationMode
224     private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
225 
226     @Surface.Rotation
227     private int mUserRotation = Surface.ROTATION_0;
228 
229     private static final int CAMERA_ROTATION_DISABLED = 0;
230     private static final int CAMERA_ROTATION_ENABLED = 1;
231     private int mCameraRotationMode = CAMERA_ROTATION_DISABLED;
232 
233     /**
234      * Flag that indicates this is a display that may run better when fixed to user rotation.
235      */
236     private boolean mDefaultFixedToUserRotation;
237 
238     /**
239      * A flag to indicate if the display rotation should be fixed to user specified rotation
240      * regardless of all other states (including app requested orientation). {@code true} the
241      * display rotation should be fixed to user specified rotation, {@code false} otherwise.
242      */
243     private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
244 
245     private int mDemoHdmiRotation;
246     private int mDemoRotation;
247     private boolean mDemoHdmiRotationLock;
248     private boolean mDemoRotationLock;
249 
DisplayRotation(WindowManagerService service, DisplayContent displayContent, DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController, @NonNull DisplayRotationCoordinator displayRotationCoordinator)250     DisplayRotation(WindowManagerService service, DisplayContent displayContent,
251             DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController,
252             @NonNull DisplayRotationCoordinator displayRotationCoordinator) {
253         this(service, displayContent, displayAddress, displayContent.getDisplayPolicy(),
254                 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock(),
255                 deviceStateController, displayRotationCoordinator);
256     }
257 
258     @VisibleForTesting
DisplayRotation(WindowManagerService service, DisplayContent displayContent, DisplayAddress displayAddress, DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings, Context context, Object lock, @NonNull DeviceStateController deviceStateController, @NonNull DisplayRotationCoordinator displayRotationCoordinator)259     DisplayRotation(WindowManagerService service, DisplayContent displayContent,
260             DisplayAddress displayAddress, DisplayPolicy displayPolicy,
261             DisplayWindowSettings displayWindowSettings, Context context, Object lock,
262             @NonNull DeviceStateController deviceStateController,
263             @NonNull DisplayRotationCoordinator displayRotationCoordinator) {
264         mService = service;
265         mDisplayContent = displayContent;
266         mDisplayPolicy = displayPolicy;
267         mDisplayWindowSettings = displayWindowSettings;
268         mContext = context;
269         mLock = lock;
270         mDeviceStateController = deviceStateController;
271         isDefaultDisplay = displayContent.isDefaultDisplay;
272         mCompatPolicyForImmersiveApps = initImmersiveAppCompatPolicy(service, displayContent);
273 
274         mSupportAutoRotation =
275                 mContext.getResources().getBoolean(R.bool.config_supportAutoRotation);
276         mAllowRotationResolver =
277                 mContext.getResources().getBoolean(R.bool.config_allowRotationResolver);
278         mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation);
279         mCarDockRotation = readRotation(R.integer.config_carDockRotation);
280         mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
281         mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
282 
283         int defaultRotation = readDefaultDisplayRotation(displayAddress);
284         mRotation = defaultRotation;
285 
286         mDisplayRotationCoordinator = displayRotationCoordinator;
287         if (isDefaultDisplay) {
288             mDisplayRotationCoordinator.setDefaultDisplayDefaultRotation(mRotation);
289         }
290         mDefaultDisplayRotationChangedCallback = this::updateRotationAndSendNewConfigIfChanged;
291 
292         if (DisplayRotationCoordinator.isSecondaryInternalDisplay(displayContent)
293                 && mDeviceStateController
294                         .shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()) {
295             mDisplayRotationCoordinator.setDefaultDisplayRotationChangedCallback(
296                     mDefaultDisplayRotationChangedCallback);
297         }
298 
299         if (isDefaultDisplay) {
300             final Handler uiHandler = UiThread.getHandler();
301             mOrientationListener =
302                     new OrientationListener(mContext, uiHandler, defaultRotation);
303             mOrientationListener.setCurrentRotation(mRotation);
304             mSettingsObserver = new SettingsObserver(uiHandler);
305             mSettingsObserver.observe();
306             if (mSupportAutoRotation && isFoldable(mContext)) {
307                 mFoldController = new FoldController();
308             } else {
309                 mFoldController = null;
310             }
311         } else {
312             mFoldController = null;
313         }
314     }
315 
isFoldable(Context context)316     private static boolean isFoldable(Context context) {
317         return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length > 0;
318     }
319 
320     @VisibleForTesting
321     @Nullable
initImmersiveAppCompatPolicy( WindowManagerService service, DisplayContent displayContent)322     DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
323                 WindowManagerService service, DisplayContent displayContent) {
324         return DisplayRotationImmersiveAppCompatPolicy.createIfNeeded(
325                 service.mLetterboxConfiguration, this, displayContent);
326     }
327 
328     // Change the default value to the value specified in the sysprop
329     // ro.bootanim.set_orientation_<display_id>. Four values are supported: ORIENTATION_0,
330     // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270.
331     // If the value isn't specified or is ORIENTATION_0, nothing will be changed.
332     // This is needed to support having default orientation different from the natural
333     // device orientation. For example, on tablets that may want to keep natural orientation
334     // portrait for applications compatibility but have landscape orientation as a default choice
335     // from the UX perspective.
336     @Surface.Rotation
readDefaultDisplayRotation(DisplayAddress displayAddress)337     private int readDefaultDisplayRotation(DisplayAddress displayAddress) {
338         if (!(displayAddress instanceof DisplayAddress.Physical)) {
339             return Surface.ROTATION_0;
340         }
341         final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) displayAddress;
342         String syspropValue = SystemProperties.get(
343                 "ro.bootanim.set_orientation_" + physicalAddress.getPhysicalDisplayId(),
344                 "ORIENTATION_0");
345         if (syspropValue.equals("ORIENTATION_90")) {
346             return Surface.ROTATION_90;
347         } else if (syspropValue.equals("ORIENTATION_180")) {
348             return Surface.ROTATION_180;
349         } else if (syspropValue.equals("ORIENTATION_270")) {
350             return Surface.ROTATION_270;
351         }
352         return Surface.ROTATION_0;
353     }
354 
readRotation(int resID)355     private int readRotation(int resID) {
356         try {
357             final int rotation = mContext.getResources().getInteger(resID);
358             switch (rotation) {
359                 case 0:
360                     return Surface.ROTATION_0;
361                 case 90:
362                     return Surface.ROTATION_90;
363                 case 180:
364                     return Surface.ROTATION_180;
365                 case 270:
366                     return Surface.ROTATION_270;
367             }
368         } catch (Resources.NotFoundException e) {
369             // fall through
370         }
371         return -1;
372     }
373 
374     @VisibleForTesting
useDefaultSettingsProvider()375     boolean useDefaultSettingsProvider() {
376         return isDefaultDisplay;
377     }
378 
379     /**
380      * Updates the configuration which may have different values depending on current user, e.g.
381      * runtime resource overlay.
382      */
updateUserDependentConfiguration(Resources currentUserRes)383     void updateUserDependentConfiguration(Resources currentUserRes) {
384         mAllowSeamlessRotationDespiteNavBarMoving =
385                 currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
386     }
387 
configure(int width, int height)388     void configure(int width, int height) {
389         final Resources res = mContext.getResources();
390         if (width > height) {
391             mLandscapeRotation = Surface.ROTATION_0;
392             mSeascapeRotation = Surface.ROTATION_180;
393             if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
394                 mPortraitRotation = Surface.ROTATION_90;
395                 mUpsideDownRotation = Surface.ROTATION_270;
396             } else {
397                 mPortraitRotation = Surface.ROTATION_270;
398                 mUpsideDownRotation = Surface.ROTATION_90;
399             }
400         } else {
401             mPortraitRotation = Surface.ROTATION_0;
402             mUpsideDownRotation = Surface.ROTATION_180;
403             if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
404                 mLandscapeRotation = Surface.ROTATION_270;
405                 mSeascapeRotation = Surface.ROTATION_90;
406             } else {
407                 mLandscapeRotation = Surface.ROTATION_90;
408                 mSeascapeRotation = Surface.ROTATION_270;
409             }
410         }
411 
412         // For demo purposes, allow the rotation of the HDMI display to be controlled.
413         // By default, HDMI locks rotation to landscape.
414         if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
415             mDemoHdmiRotation = mPortraitRotation;
416         } else {
417             mDemoHdmiRotation = mLandscapeRotation;
418         }
419         mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
420 
421         // For demo purposes, allow the rotation of the remote display to be controlled.
422         // By default, remote display locks rotation to landscape.
423         if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
424             mDemoRotation = mPortraitRotation;
425         } else {
426             mDemoRotation = mLandscapeRotation;
427         }
428         mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
429 
430         // It's physically impossible to rotate the car's screen.
431         final boolean isCar = mContext.getPackageManager().hasSystemFeature(
432                 PackageManager.FEATURE_AUTOMOTIVE);
433         // It's also not likely to rotate a TV screen.
434         final boolean isTv = mContext.getPackageManager().hasSystemFeature(
435                 PackageManager.FEATURE_LEANBACK);
436         mDefaultFixedToUserRotation =
437                 (isCar || isTv || mService.mIsPc || mDisplayContent.forceDesktopMode()
438                         || mDisplayContent.isDisplayOrientationFixed())
439                 // For debug purposes the next line turns this feature off with:
440                 // $ adb shell setprop config.override_forced_orient true
441                 // $ adb shell wm size reset
442                 && !"true".equals(SystemProperties.get("config.override_forced_orient"));
443     }
444 
applyCurrentRotation(@urface.Rotation int rotation)445     void applyCurrentRotation(@Surface.Rotation int rotation) {
446         mRotationHistory.addRecord(this, rotation);
447         if (mOrientationListener != null) {
448             mOrientationListener.setCurrentRotation(rotation);
449         }
450     }
451 
452     @VisibleForTesting
setRotation(@urface.Rotation int rotation)453     void setRotation(@Surface.Rotation int rotation) {
454         mRotation = rotation;
455     }
456 
457     @Surface.Rotation
getRotation()458     int getRotation() {
459         return mRotation;
460     }
461 
462     @ScreenOrientation
getLastOrientation()463     int getLastOrientation() {
464         return mLastOrientation;
465     }
466 
updateOrientation(@creenOrientation int newOrientation, boolean forceUpdate)467     boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
468         if (newOrientation == mLastOrientation && !forceUpdate) {
469             return false;
470         }
471         mLastOrientation = newOrientation;
472         if (newOrientation != mCurrentAppOrientation) {
473             mCurrentAppOrientation = newOrientation;
474             if (isDefaultDisplay) {
475                 updateOrientationListenerLw();
476             }
477         }
478         return updateRotationUnchecked(forceUpdate);
479     }
480 
481     /**
482      * Update rotation of the display and send configuration if the rotation is changed.
483      *
484      * @return {@code true} if the rotation has been changed and the new config is sent.
485      */
updateRotationAndSendNewConfigIfChanged()486     boolean updateRotationAndSendNewConfigIfChanged() {
487         final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
488         if (changed) {
489             mDisplayContent.sendNewConfiguration();
490         }
491         return changed;
492     }
493 
494     /**
495      * Update rotation with an option to force the update. This updates the container's perception
496      * of rotation and, depending on the top activities, will freeze the screen or start seamless
497      * rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}
498      * during {@link DisplayContent#sendNewConfiguration}.
499      *
500      * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
501      *                    orientation because we're waiting for some rotation to finish or display
502      *                    to unfreeze, which results in configuration of the previously visible
503      *                    activity being applied to a newly visible one. Forcing the rotation
504      *                    update allows to workaround this issue.
505      * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
506      *         {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE
507      *         THE SCREEN.
508      */
updateRotationUnchecked(boolean forceUpdate)509     boolean updateRotationUnchecked(boolean forceUpdate) {
510         final int displayId = mDisplayContent.getDisplayId();
511         if (!forceUpdate) {
512             if (mDeferredRotationPauseCount > 0) {
513                 // Rotation updates have been paused temporarily. Defer the update until updates
514                 // have been resumed.
515                 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");
516                 return false;
517             }
518 
519             if (mDisplayContent.inTransition()
520                     && mDisplayContent.getDisplayPolicy().isScreenOnFully()
521                     && !mDisplayContent.mTransitionController.useShellTransitionsRotation()) {
522                 // Rotation updates cannot be performed while the previous rotation change animation
523                 // is still in progress. Skip this update. We will try updating again after the
524                 // animation is finished and the display is unfrozen.
525                 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress.");
526                 return false;
527             }
528             if (mService.mDisplayFrozen) {
529                 // Even if the screen rotation animation has finished (e.g. isAnimating returns
530                 // false), there is still some time where we haven't yet unfrozen the display. We
531                 // also need to abort rotation here.
532                 ProtoLog.v(WM_DEBUG_ORIENTATION,
533                         "Deferring rotation, still finishing previous rotation");
534                 return false;
535             }
536 
537             if (mDisplayContent.mFixedRotationTransitionListener.shouldDeferRotation()) {
538                 // Makes sure that after the transition is finished, updateOrientation() can see
539                 // the difference from the latest orientation source.
540                 mLastOrientation = SCREEN_ORIENTATION_UNSET;
541                 // During the recents animation, the closing app might still be considered on top.
542                 // In order to ignore its requested orientation to avoid a sensor led rotation (e.g
543                 // user rotating the device while the recents animation is running), we ignore
544                 // rotation update while the animation is running.
545                 return false;
546             }
547         }
548 
549         if (!mService.mDisplayEnabled) {
550             // No point choosing a rotation if the display is not enabled.
551             ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled.");
552             return false;
553         }
554 
555         @Surface.Rotation
556         final int oldRotation = mRotation;
557         @ScreenOrientation
558         final int lastOrientation = mLastOrientation;
559         @Surface.Rotation
560         int rotation = rotationForOrientation(lastOrientation, oldRotation);
561         // Use the saved rotation for tabletop mode, if set.
562         if (mFoldController != null && mFoldController.shouldRevertOverriddenRotation()) {
563             int prevRotation = rotation;
564             rotation = mFoldController.revertOverriddenRotation();
565             ProtoLog.v(WM_DEBUG_ORIENTATION,
566                     "Reverting orientation. Rotating to %s from %s rather than %s.",
567                     Surface.rotationToString(rotation),
568                     Surface.rotationToString(oldRotation),
569                     Surface.rotationToString(prevRotation));
570         }
571 
572         if (DisplayRotationCoordinator.isSecondaryInternalDisplay(mDisplayContent)
573                 && mDeviceStateController
574                         .shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()) {
575             rotation = RotationUtils.reverseRotationDirectionAroundZAxis(
576                     mDisplayRotationCoordinator.getDefaultDisplayCurrentRotation());
577         }
578 
579         ProtoLog.v(WM_DEBUG_ORIENTATION,
580                 "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and "
581                         + "oldRotation=%s (%d)",
582                 Surface.rotationToString(rotation), rotation,
583                 displayId,
584                 ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
585                 Surface.rotationToString(oldRotation), oldRotation);
586 
587         ProtoLog.v(WM_DEBUG_ORIENTATION,
588                 "Display id=%d selected orientation %s (%d), got rotation %s (%d)", displayId,
589                 ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
590                 Surface.rotationToString(rotation), rotation);
591 
592         if (oldRotation == rotation) {
593             // No change.
594             return false;
595         }
596 
597         if (isDefaultDisplay) {
598             mDisplayRotationCoordinator.onDefaultDisplayRotationChanged(rotation);
599         }
600 
601         // Preemptively cancel the running recents animation -- SysUI can't currently handle this
602         // case properly since the signals it receives all happen post-change. We do this earlier
603         // in the rotation flow, since DisplayContent.updateDisplayOverrideConfigurationLocked seems
604         // to happen too late.
605         final RecentsAnimationController recentsAnimationController =
606                 mService.getRecentsAnimationController();
607         if (recentsAnimationController != null) {
608             recentsAnimationController.cancelAnimationForDisplayChange();
609         }
610 
611         ProtoLog.v(WM_DEBUG_ORIENTATION,
612                 "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
613                         displayId, rotation, oldRotation, lastOrientation);
614 
615         mRotation = rotation;
616 
617         mDisplayContent.setLayoutNeeded();
618         mDisplayContent.mWaitingForConfig = true;
619 
620         if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
621             final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
622             final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
623                     : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
624                             oldRotation, mRotation);
625 
626             mDisplayContent.requestChangeTransitionIfNeeded(
627                     ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);
628             if (wasCollecting) {
629                 // Use remote-rotation infra since the transition has already been requested
630                 // TODO(shell-transitions): Remove this once lifecycle management can cover all
631                 //                          rotation cases.
632                 startRemoteRotation(oldRotation, mRotation);
633             }
634             return true;
635         }
636 
637         mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
638         mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
639                 mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
640 
641         if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
642             // The screen rotation animation uses a screenshot to freeze the screen while windows
643             // resize underneath. When we are rotating seamlessly, we allow the elements to
644             // transition to their rotated state independently and without a freeze required.
645             prepareSeamlessRotation();
646         } else {
647             prepareNormalRotationAnimation();
648         }
649 
650         // Give a remote handler (system ui) some time to reposition things.
651         startRemoteRotation(oldRotation, mRotation);
652 
653         return true;
654     }
655 
startRemoteRotation(int fromRotation, int toRotation)656     private void startRemoteRotation(int fromRotation, int toRotation) {
657         mDisplayContent.mRemoteDisplayChangeController.performRemoteDisplayChange(
658                 fromRotation, toRotation, null /* newDisplayAreaInfo */,
659                 (transaction) -> continueRotation(toRotation, transaction)
660         );
661     }
662 
continueRotation(int targetRotation, WindowContainerTransaction t)663     private void continueRotation(int targetRotation, WindowContainerTransaction t) {
664         if (targetRotation != mRotation) {
665             // Drop it, this is either coming from an outdated remote rotation; or, we've
666             // already moved on.
667             return;
668         }
669 
670         if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
671             if (!mDisplayContent.mTransitionController.isCollecting()) {
672                 // The remote may be too slow to response before transition timeout.
673                 Slog.e(TAG, "Trying to continue rotation outside a transition");
674             }
675             mDisplayContent.mTransitionController.collect(mDisplayContent);
676         }
677         mService.mAtmService.deferWindowLayout();
678         try {
679             mDisplayContent.sendNewConfiguration();
680             if (t != null) {
681                 mService.mAtmService.mWindowOrganizerController.applyTransaction(t);
682             }
683         } finally {
684             mService.mAtmService.continueWindowLayout();
685         }
686     }
687 
prepareNormalRotationAnimation()688     void prepareNormalRotationAnimation() {
689         cancelSeamlessRotation();
690         final RotationAnimationPair anim = selectRotationAnimation();
691         mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
692     }
693 
694     /**
695      * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
696      * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
697      * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
698      * and it doesn't choose seamless rotation.
699      */
cancelSeamlessRotation()700     void cancelSeamlessRotation() {
701         if (!mRotatingSeamlessly) {
702             return;
703         }
704         mDisplayContent.forAllWindows(w -> {
705             if (w.mSeamlesslyRotated) {
706                 w.cancelSeamlessRotation();
707                 w.mSeamlesslyRotated = false;
708             }
709         }, true /* traverseTopToBottom */);
710         mSeamlessRotationCount = 0;
711         mRotatingSeamlessly = false;
712         mDisplayContent.finishAsyncRotationIfPossible();
713     }
714 
prepareSeamlessRotation()715     private void prepareSeamlessRotation() {
716         // We are careful to reset this in case a window was removed before it finished
717         // seamless rotation.
718         mSeamlessRotationCount = 0;
719         mRotatingSeamlessly = true;
720     }
721 
isRotatingSeamlessly()722     boolean isRotatingSeamlessly() {
723         return mRotatingSeamlessly;
724     }
725 
hasSeamlessRotatingWindow()726     boolean hasSeamlessRotatingWindow() {
727         return mSeamlessRotationCount > 0;
728     }
729 
730     @VisibleForTesting
shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate)731     boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
732         // Display doesn't need to be frozen because application has been started in correct
733         // rotation already, so the rest of the windows can use seamless rotation.
734         if (mDisplayContent.hasTopFixedRotationLaunchingApp()) {
735             return true;
736         }
737 
738         final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
739         if (w == null || w != mDisplayContent.mCurrentFocus) {
740             return false;
741         }
742         // We only enable seamless rotation if the top window has requested it and is in the
743         // fullscreen opaque state. Seamless rotation requires freezing various Surface states and
744         // won't work well with animations, so we disable it in the animation case for now.
745         if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.inMultiWindowMode()
746                 || w.isAnimatingLw()) {
747             return false;
748         }
749 
750         if (!canRotateSeamlessly(oldRotation, newRotation)) {
751             return false;
752         }
753 
754         // If the bounds of activity window is different from its parent, then reject to be seamless
755         // because the window position may change after rotation that will look like a sudden jump.
756         if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) {
757             return false;
758         }
759 
760         // In the presence of the PINNED root task or System Alert windows we unfortunately can not
761         // seamlessly rotate.
762         if (mDisplayContent.getDefaultTaskDisplayArea().hasPinnedTask()
763                 || mDisplayContent.hasAlertWindowSurfaces()) {
764             return false;
765         }
766 
767         // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
768         // complete (that is, waiting for windows to redraw). It's tempting to check
769         // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
770         if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
771             return false;
772         }
773 
774         return true;
775     }
776 
canRotateSeamlessly(int oldRotation, int newRotation)777     boolean canRotateSeamlessly(int oldRotation, int newRotation) {
778         // If the navigation bar can't change sides, then it will jump when we change orientations
779         // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
780         // where the navbar is low-profile enough that this isn't very noticeable.
781         if (mAllowSeamlessRotationDespiteNavBarMoving || mDisplayPolicy.navigationBarCanMove()) {
782             return true;
783         }
784         // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
785         // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
786         // will not enter the reverse portrait orientation, so actually the orientation won't change
787         // at all.
788         return oldRotation != Surface.ROTATION_180 && newRotation != Surface.ROTATION_180;
789     }
790 
markForSeamlessRotation(WindowState w, boolean seamlesslyRotated)791     void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
792         if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
793             return;
794         }
795 
796         w.mSeamlesslyRotated = seamlesslyRotated;
797         if (seamlesslyRotated) {
798             mSeamlessRotationCount++;
799         } else {
800             mSeamlessRotationCount--;
801         }
802         if (mSeamlessRotationCount == 0) {
803             ProtoLog.i(WM_DEBUG_ORIENTATION,
804                     "Performing post-rotate rotation after seamless rotation");
805             // Finish seamless rotation.
806             mRotatingSeamlessly = false;
807             mDisplayContent.finishAsyncRotationIfPossible();
808 
809             updateRotationAndSendNewConfigIfChanged();
810         }
811     }
812 
813     /**
814      * Returns the animation to run for a rotation transition based on the top fullscreen windows
815      * {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently
816      * fullscreen and frontmost.
817      */
selectRotationAnimation()818     private RotationAnimationPair selectRotationAnimation() {
819         // If the screen is off or non-interactive, force a jumpcut.
820         final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully()
821                 || !mService.mPolicy.okToAnimate(false /* ignoreScreenOn */);
822         final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow();
823         ProtoLog.i(WM_DEBUG_ANIM, "selectRotationAnimation topFullscreen=%s"
824                 + " rotationAnimation=%d forceJumpcut=%b",
825                 topFullscreen,
826                 topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation,
827                 forceJumpcut);
828         if (forceJumpcut) {
829             mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
830             mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
831             return mTmpRotationAnim;
832         }
833         if (topFullscreen != null) {
834             int animationHint = topFullscreen.getRotationAnimationHint();
835             if (animationHint < 0 && mDisplayPolicy.isTopLayoutFullscreen()) {
836                 animationHint = topFullscreen.getAttrs().rotationAnimation;
837             }
838             switch (animationHint) {
839                 case ROTATION_ANIMATION_CROSSFADE:
840                 case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
841                     mTmpRotationAnim.mExit = R.anim.rotation_animation_xfade_exit;
842                     mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
843                     break;
844                 case ROTATION_ANIMATION_JUMPCUT:
845                     mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
846                     mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
847                     break;
848                 case ROTATION_ANIMATION_ROTATE:
849                 default:
850                     mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
851                     break;
852             }
853         } else {
854             mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
855         }
856         return mTmpRotationAnim;
857     }
858 
859     /**
860      * Validate whether the current top fullscreen has specified the same
861      * {@link android.view.WindowManager.LayoutParams#rotationAnimation} value as that being passed
862      * in from the previous top fullscreen window.
863      *
864      * @param exitAnimId exiting resource id from the previous window.
865      * @param enterAnimId entering resource id from the previous window.
866      * @param forceDefault For rotation animations only, if true ignore the animation values and
867      *                     just return false.
868      * @return {@code true} if the previous values are still valid, false if they should be replaced
869      *         with the default.
870      */
validateRotationAnimation(int exitAnimId, int enterAnimId, boolean forceDefault)871     boolean validateRotationAnimation(int exitAnimId, int enterAnimId, boolean forceDefault) {
872         switch (exitAnimId) {
873             case R.anim.rotation_animation_xfade_exit:
874             case R.anim.rotation_animation_jump_exit:
875                 // These are the only cases that matter.
876                 if (forceDefault) {
877                     return false;
878                 }
879                 final RotationAnimationPair anim = selectRotationAnimation();
880                 return exitAnimId == anim.mExit && enterAnimId == anim.mEnter;
881             default:
882                 return true;
883         }
884     }
885 
restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation)886     void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
887         mFixedToUserRotation = fixedToUserRotation;
888 
889         // We will retrieve user rotation and user rotation mode from settings for default display.
890         if (isDefaultDisplay) {
891             return;
892         }
893         if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
894                 && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
895             Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
896                     + " for " + mDisplayContent);
897             userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
898         }
899         if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) {
900             Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation
901                     + " for " + mDisplayContent);
902             userRotation = Surface.ROTATION_0;
903         }
904         mUserRotationMode = userRotationMode;
905         mUserRotation = userRotation;
906     }
907 
setFixedToUserRotation(int fixedToUserRotation)908     void setFixedToUserRotation(int fixedToUserRotation) {
909         if (mFixedToUserRotation == fixedToUserRotation) {
910             return;
911         }
912 
913         mFixedToUserRotation = fixedToUserRotation;
914         mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation);
915         if (mDisplayContent.mFocusedApp != null) {
916             // We record the last focused TDA that respects orientation request, check if this
917             // change may affect it.
918             mDisplayContent.onLastFocusedTaskDisplayAreaChanged(
919                     mDisplayContent.mFocusedApp.getDisplayArea());
920         }
921         mDisplayContent.updateOrientation();
922     }
923 
924     @VisibleForTesting
setUserRotation(int userRotationMode, int userRotation)925     void setUserRotation(int userRotationMode, int userRotation) {
926         mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
927         if (useDefaultSettingsProvider()) {
928             // We'll be notified via settings listener, so we don't need to update internal values.
929             final ContentResolver res = mContext.getContentResolver();
930             final int accelerometerRotation =
931                     userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
932             Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
933                     accelerometerRotation, UserHandle.USER_CURRENT);
934             Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
935                     UserHandle.USER_CURRENT);
936             return;
937         }
938 
939         boolean changed = false;
940         if (mUserRotationMode != userRotationMode) {
941             mUserRotationMode = userRotationMode;
942             changed = true;
943         }
944         if (mUserRotation != userRotation) {
945             mUserRotation = userRotation;
946             changed = true;
947         }
948         mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
949                 userRotation);
950         if (changed) {
951             mService.updateRotation(true /* alwaysSendConfiguration */,
952                     false /* forceRelayout */);
953         }
954     }
955 
freezeRotation(int rotation)956     void freezeRotation(int rotation) {
957         if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mDisplayContent)) {
958             rotation = RotationUtils.reverseRotationDirectionAroundZAxis(rotation);
959         }
960 
961         rotation = (rotation == -1) ? mRotation : rotation;
962         setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
963     }
964 
thawRotation()965     void thawRotation() {
966         setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
967     }
968 
isRotationFrozen()969     boolean isRotationFrozen() {
970         if (!isDefaultDisplay) {
971             return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED;
972         }
973 
974         return Settings.System.getIntForUser(mContext.getContentResolver(),
975                 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
976     }
977 
isFixedToUserRotation()978     boolean isFixedToUserRotation() {
979         switch (mFixedToUserRotation) {
980             case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED:
981                 return false;
982             case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED:
983                 return true;
984             default:
985                 return mDefaultFixedToUserRotation;
986         }
987     }
988 
getFixedToUserRotationMode()989     int getFixedToUserRotationMode() {
990         return mFixedToUserRotation;
991     }
992 
getLandscapeRotation()993     public int getLandscapeRotation() {
994         return mLandscapeRotation;
995     }
996 
getSeascapeRotation()997     public int getSeascapeRotation() {
998         return mSeascapeRotation;
999     }
1000 
getPortraitRotation()1001     public int getPortraitRotation() {
1002         return mPortraitRotation;
1003     }
1004 
getUpsideDownRotation()1005     public int getUpsideDownRotation() {
1006         return mUpsideDownRotation;
1007     }
1008 
getCurrentAppOrientation()1009     public int getCurrentAppOrientation() {
1010         return mCurrentAppOrientation;
1011     }
1012 
getDisplayPolicy()1013     public DisplayPolicy getDisplayPolicy() {
1014         return mDisplayPolicy;
1015     }
1016 
getOrientationListener()1017     public WindowOrientationListener getOrientationListener() {
1018         return mOrientationListener;
1019     }
1020 
getUserRotation()1021     public int getUserRotation() {
1022         return mUserRotation;
1023     }
1024 
getUserRotationMode()1025     public int getUserRotationMode() {
1026         return mUserRotationMode;
1027     }
1028 
updateOrientationListener()1029     public void updateOrientationListener() {
1030         synchronized (mLock) {
1031             updateOrientationListenerLw();
1032         }
1033     }
1034 
1035     /**
1036      * Temporarily pauses rotation changes until resumed.
1037      * <p>
1038      * This can be used to prevent rotation changes from occurring while the user is performing
1039      * certain operations, such as drag and drop.
1040      * <p>
1041      * This call nests and must be matched by an equal number of calls to {@link #resume}.
1042      */
pause()1043     void pause() {
1044         mDeferredRotationPauseCount++;
1045     }
1046 
1047     /** Resumes normal rotation changes after being paused. */
resume()1048     void resume() {
1049         if (mDeferredRotationPauseCount <= 0) {
1050             return;
1051         }
1052 
1053         mDeferredRotationPauseCount--;
1054         if (mDeferredRotationPauseCount == 0) {
1055             updateRotationAndSendNewConfigIfChanged();
1056         }
1057     }
1058 
1059     /**
1060      * Various use cases for invoking this function:
1061      * <li>Screen turning off, should always disable listeners if already enabled.</li>
1062      * <li>Screen turned on and current app has sensor based orientation, enable listeners
1063      *     if not already enabled.</li>
1064      * <li>Screen turned on and current app does not have sensor orientation, disable listeners
1065      *     if already enabled.</li>
1066      * <li>Screen turning on and current app has sensor based orientation, enable listeners
1067      *     if needed.</li>
1068      * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
1069      */
updateOrientationListenerLw()1070     private void updateOrientationListenerLw() {
1071         if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
1072             // If sensor is turned off or nonexistent for some reason.
1073             return;
1074         }
1075 
1076         final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
1077         final boolean awake = mDisplayPolicy.isAwake();
1078         final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
1079         final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();
1080 
1081         // Could have been invoked due to screen turning on or off or
1082         // change of the currently visible window's orientation.
1083         ProtoLog.v(WM_DEBUG_ORIENTATION,
1084                 "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, "
1085                         + "orientationSensorEnabled=%b, keyguardDrawComplete=%b, "
1086                         + "windowManagerDrawComplete=%b",
1087                 screenOnEarly, awake, mCurrentAppOrientation, mOrientationListener.mEnabled,
1088                 keyguardDrawComplete, windowManagerDrawComplete);
1089 
1090         boolean disable = true;
1091 
1092         // If the orientation listener uses a wake sensor, keep the orientation listener on if the
1093         // screen is on (regardless of wake state). This allows the AoD to rotate.
1094         //
1095         // Note: We postpone the rotating of the screen until the keyguard as well as the
1096         // window manager have reported a draw complete or the keyguard is going away in dismiss
1097         // mode.
1098         if (screenOnEarly
1099                 && (awake || mOrientationListener.shouldStayEnabledWhileDreaming())
1100                 && ((keyguardDrawComplete && windowManagerDrawComplete))) {
1101             if (needSensorRunning()) {
1102                 disable = false;
1103                 // Enable listener if not already enabled.
1104                 if (!mOrientationListener.mEnabled) {
1105                     mOrientationListener.enable();
1106                 }
1107             }
1108         }
1109         // Check if sensors need to be disabled.
1110         if (disable) {
1111             mOrientationListener.disable();
1112         }
1113     }
1114 
1115     /**
1116      * We always let the sensor be switched on by default except when
1117      * the user has explicitly disabled sensor based rotation or when the
1118      * screen is switched off.
1119      */
needSensorRunning()1120     private boolean needSensorRunning() {
1121         if (isFixedToUserRotation()) {
1122             // We are sure we only respect user rotation settings, so we are sure we will not
1123             // support sensor rotation.
1124             return false;
1125         }
1126 
1127         if (mFoldController != null && mFoldController.shouldDisableRotationSensor()) {
1128             return false;
1129         }
1130 
1131         if (mSupportAutoRotation) {
1132             if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
1133                     || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1134                     || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
1135                     || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
1136                 // If the application has explicitly requested to follow the
1137                 // orientation, then we need to turn the sensor on.
1138                 return true;
1139             }
1140         }
1141 
1142         final int dockMode = mDisplayPolicy.getDockMode();
1143         if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
1144                 && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
1145                 || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
1146                         && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
1147                                 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
1148                                 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
1149             // Enable accelerometer if we are docked in a dock that enables accelerometer
1150             // orientation management.
1151             return true;
1152         }
1153 
1154         if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
1155             // If the setting for using the sensor by default is enabled, then
1156             // we will always leave it on.  Note that the user could go to
1157             // a window that forces an orientation that does not use the
1158             // sensor and in theory we could turn it off... however, when next
1159             // turning it on we won't have a good value for the current
1160             // orientation for a little bit, which can cause orientation
1161             // changes to lag, so we'd like to keep it always on.  (It will
1162             // still be turned off when the screen is off.)
1163 
1164             // When locked we can provide rotation suggestions users can approve to change the
1165             // current screen rotation. To do this the sensor needs to be running.
1166             return mSupportAutoRotation &&
1167                     mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
1168         }
1169         return mSupportAutoRotation;
1170     }
1171 
1172     /**
1173      * If this is true we have updated our desired orientation, but not yet changed the real
1174      * orientation our applied our screen rotation animation. For example, because a previous
1175      * screen rotation was in progress.
1176      *
1177      * @return {@code true} if the there is an ongoing rotation change.
1178      */
needsUpdate()1179     boolean needsUpdate() {
1180         final int oldRotation = mRotation;
1181         final int rotation = rotationForOrientation(mLastOrientation, oldRotation);
1182         return oldRotation != rotation;
1183     }
1184 
1185 
1186     /**
1187      * Resets whether the screen can be rotated via the accelerometer in all 4 rotations as the
1188      * default behavior.
1189      *
1190      * To be called if there is potential that the value changed. For example if the active display
1191      * changed.
1192      *
1193      * At the moment it is called from
1194      * {@link DisplayWindowSettings#applyRotationSettingsToDisplayLocked}.
1195      */
resetAllowAllRotations()1196     void resetAllowAllRotations() {
1197         mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
1198     }
1199 
1200     /**
1201      * Given an orientation constant, returns the appropriate surface rotation, taking into account
1202      * sensors, docking mode, rotation lock, and other factors.
1203      *
1204      * @param orientation  An orientation constant, such as
1205      *                     {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
1206      * @param lastRotation The most recently used rotation.
1207      * @return The surface rotation to use.
1208      */
1209     @VisibleForTesting
1210     @Surface.Rotation
rotationForOrientation(@creenOrientation int orientation, @Surface.Rotation int lastRotation)1211     int rotationForOrientation(@ScreenOrientation int orientation,
1212             @Surface.Rotation int lastRotation) {
1213         ProtoLog.v(WM_DEBUG_ORIENTATION,
1214                 "rotationForOrientation(orient=%s (%d), last=%s (%d)); user=%s (%d) %s",
1215                 ActivityInfo.screenOrientationToString(orientation), orientation,
1216                 Surface.rotationToString(lastRotation), lastRotation,
1217                 Surface.rotationToString(mUserRotation), mUserRotation,
1218                 mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
1219                         ? "USER_ROTATION_LOCKED" : "");
1220 
1221         if (isFixedToUserRotation()) {
1222             return mUserRotation;
1223         }
1224 
1225         @Surface.Rotation
1226         int sensorRotation = mOrientationListener != null
1227                 ? mOrientationListener.getProposedRotation() // may be -1
1228                 : -1;
1229         if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
1230             sensorRotation = -1;
1231         }
1232         if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mDisplayContent)) {
1233             sensorRotation = RotationUtils.reverseRotationDirectionAroundZAxis(sensorRotation);
1234         }
1235         mLastSensorRotation = sensorRotation;
1236         if (sensorRotation < 0) {
1237             sensorRotation = lastRotation;
1238         }
1239 
1240         final int lidState = mDisplayPolicy.getLidState();
1241         final int dockMode = mDisplayPolicy.getDockMode();
1242         final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
1243         final boolean carDockEnablesAccelerometer =
1244                 mDisplayPolicy.isCarDockEnablesAccelerometer();
1245         final boolean deskDockEnablesAccelerometer =
1246                 mDisplayPolicy.isDeskDockEnablesAccelerometer();
1247 
1248         @Surface.Rotation
1249         final int preferredRotation;
1250         if (!isDefaultDisplay) {
1251             // For secondary displays we ignore things like displays sensors, docking mode and
1252             // rotation lock, and always prefer user rotation.
1253             preferredRotation = mUserRotation;
1254         } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
1255             // Ignore sensor when lid switch is open and rotation is forced.
1256             preferredRotation = mLidOpenRotation;
1257         } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
1258                 && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
1259             // Ignore sensor when in car dock unless explicitly enabled.
1260             // This case can override the behavior of NOSENSOR, and can also
1261             // enable 180 degree rotation while docked.
1262             preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
1263         } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
1264                 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
1265                 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
1266                 && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)
1267                 && !(orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED
1268                         || orientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR)) {
1269             // Ignore sensor when in desk dock unless explicitly enabled.
1270             // This case can enable 180 degree rotation while docked.
1271             preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
1272         } else if (hdmiPlugged && mDemoHdmiRotationLock) {
1273             // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
1274             // Note that the dock orientation overrides the HDMI orientation.
1275             preferredRotation = mDemoHdmiRotation;
1276         } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1277                 && mUndockedHdmiRotation >= 0) {
1278             // Ignore sensor when plugged into HDMI and an undocked orientation has
1279             // been specified in the configuration (only for legacy devices without
1280             // full multi-display support).
1281             // Note that the dock orientation overrides the HDMI orientation.
1282             preferredRotation = mUndockedHdmiRotation;
1283         } else if (mDemoRotationLock) {
1284             // Ignore sensor when demo rotation lock is enabled.
1285             // Note that the dock orientation and HDMI rotation lock override this.
1286             preferredRotation = mDemoRotation;
1287         } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1288             // While in VR, apps always prefer a portrait rotation. This does not change
1289             // any apps that explicitly set landscape, but does cause sensors be ignored,
1290             // and ignored any orientation lock that the user has set (this conditional
1291             // should remain above the ORIENTATION_LOCKED conditional below).
1292             preferredRotation = mPortraitRotation;
1293         } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
1294             // Application just wants to remain locked in the last rotation.
1295             preferredRotation = lastRotation;
1296         } else if (!mSupportAutoRotation) {
1297             // If we don't support auto-rotation then bail out here and ignore
1298             // the sensor and any rotation lock settings.
1299             preferredRotation = -1;
1300         } else if (((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
1301                             || isTabletopAutoRotateOverrideEnabled())
1302                         && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
1303                                 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
1304                                 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
1305                                 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
1306                                 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
1307                 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
1308                 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1309                 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
1310                 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
1311             // Otherwise, use sensor only if requested by the application or enabled
1312             // by default for USER or UNSPECIFIED modes.  Does not apply to NOSENSOR.
1313             if (sensorRotation != Surface.ROTATION_180
1314                     || getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED
1315                     || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1316                     || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
1317                 preferredRotation = sensorRotation;
1318             } else {
1319                 preferredRotation = lastRotation;
1320             }
1321         } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
1322                 && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
1323                 && orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
1324                 && orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
1325                 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
1326                 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
1327             // Apply rotation lock. Does not apply to NOSENSOR or specific rotations.
1328             // The idea is that the user rotation expresses a weak preference for the direction
1329             // of gravity and as NOSENSOR is never affected by gravity, then neither should
1330             // NOSENSOR be affected by rotation lock (although it will be affected by docks).
1331             // Also avoid setting user rotation when app has preference over one particular rotation
1332             // to avoid leaving the rotation to the reverse of it which has the compatible
1333             // orientation, but isn't what app wants, when the user rotation is the reverse of the
1334             // preferred rotation.
1335             preferredRotation = mUserRotation;
1336         } else {
1337             // No overriding preference.
1338             // We will do exactly what the application asked us to do.
1339             preferredRotation = -1;
1340         }
1341 
1342         switch (orientation) {
1343             case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
1344                 // Return portrait unless overridden.
1345                 if (isAnyPortrait(preferredRotation)) {
1346                     return preferredRotation;
1347                 }
1348                 return mPortraitRotation;
1349 
1350             case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
1351                 // Return landscape unless overridden.
1352                 if (isLandscapeOrSeascape(preferredRotation)) {
1353                     return preferredRotation;
1354                 }
1355                 return mLandscapeRotation;
1356 
1357             case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
1358                 // Return reverse portrait unless overridden.
1359                 if (isAnyPortrait(preferredRotation)) {
1360                     return preferredRotation;
1361                 }
1362                 return mUpsideDownRotation;
1363 
1364             case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
1365                 // Return seascape unless overridden.
1366                 if (isLandscapeOrSeascape(preferredRotation)) {
1367                     return preferredRotation;
1368                 }
1369                 return mSeascapeRotation;
1370 
1371             case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
1372             case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1373                 // Return either landscape rotation.
1374                 if (isLandscapeOrSeascape(preferredRotation)) {
1375                     return preferredRotation;
1376                 }
1377                 if (isLandscapeOrSeascape(lastRotation)) {
1378                     return lastRotation;
1379                 }
1380                 return mLandscapeRotation;
1381 
1382             case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
1383             case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1384                 // Return either portrait rotation.
1385                 if (isAnyPortrait(preferredRotation)) {
1386                     return preferredRotation;
1387                 }
1388                 if (isAnyPortrait(lastRotation)) {
1389                     return lastRotation;
1390                 }
1391                 return mPortraitRotation;
1392 
1393             default:
1394                 // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
1395                 // just return the preferred orientation we already calculated.
1396                 if (preferredRotation >= 0) {
1397                     return preferredRotation;
1398                 }
1399                 return Surface.ROTATION_0;
1400         }
1401     }
1402 
getAllowAllRotations()1403     private int getAllowAllRotations() {
1404         if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
1405             // Can't read this during init() because the context doesn't have display metrics at
1406             // that time so we cannot determine tablet vs. phone then.
1407             mAllowAllRotations = mContext.getResources().getBoolean(
1408                     R.bool.config_allowAllRotations)
1409                     ? ALLOW_ALL_ROTATIONS_ENABLED
1410                     : ALLOW_ALL_ROTATIONS_DISABLED;
1411         }
1412 
1413         return mAllowAllRotations;
1414     }
1415 
isLandscapeOrSeascape(@urface.Rotation final int rotation)1416     boolean isLandscapeOrSeascape(@Surface.Rotation final int rotation) {
1417         return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
1418     }
1419 
isAnyPortrait(@urface.Rotation final int rotation)1420     boolean isAnyPortrait(@Surface.Rotation final int rotation) {
1421         return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
1422     }
1423 
isValidRotationChoice(final int preferredRotation)1424     private boolean isValidRotationChoice(final int preferredRotation) {
1425         // Determine if the given app orientation is compatible with the provided rotation choice.
1426         switch (mCurrentAppOrientation) {
1427             case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1428                 // Works with any of the 4 rotations.
1429                 return preferredRotation >= 0;
1430 
1431             case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1432                 // It's possible for the user pref to be set at 180 because of FULL_USER. This would
1433                 // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
1434                 // but never to go to 180.
1435                 return preferredRotation == mPortraitRotation;
1436 
1437             case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1438                 // Works landscape or seascape.
1439                 return isLandscapeOrSeascape(preferredRotation);
1440 
1441             case ActivityInfo.SCREEN_ORIENTATION_USER:
1442             case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1443                 // When all rotations enabled it works with any of the 4 rotations
1444                 if (getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED) {
1445                     return preferredRotation >= 0;
1446                 }
1447 
1448                 // Works with any rotation except upside down.
1449                 return (preferredRotation >= 0) && (preferredRotation != Surface.ROTATION_180);
1450         }
1451 
1452         return false;
1453     }
1454 
isTabletopAutoRotateOverrideEnabled()1455     private boolean isTabletopAutoRotateOverrideEnabled() {
1456         return mFoldController != null && mFoldController.overrideFrozenRotation();
1457     }
1458 
isRotationChoiceAllowed(@urface.Rotation final int proposedRotation)1459     private boolean isRotationChoiceAllowed(@Surface.Rotation final int proposedRotation) {
1460         final boolean isRotationLockEnforced = mCompatPolicyForImmersiveApps != null
1461                 && mCompatPolicyForImmersiveApps.isRotationLockEnforced(proposedRotation);
1462 
1463         // Don't show rotation choice button if
1464         if (!isRotationLockEnforced // not enforcing locked rotation
1465                 // and the screen rotation is not locked by the user.
1466                 && mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
1467             return false;
1468         }
1469 
1470         // Don't show rotation choice if we are in tabletop or book modes.
1471         if (isTabletopAutoRotateOverrideEnabled()) return false;
1472 
1473         // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
1474         // demo, hdmi, vr, etc mode.
1475 
1476         // Determine if the rotation is currently forced.
1477         if (isFixedToUserRotation()) {
1478             return false; // Rotation is forced to user settings.
1479         }
1480 
1481         final int lidState = mDisplayPolicy.getLidState();
1482         if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
1483             return false; // Rotation is forced mLidOpenRotation.
1484         }
1485 
1486         final int dockMode = mDisplayPolicy.getDockMode();
1487         final boolean carDockEnablesAccelerometer = false;
1488         if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
1489             return false; // Rotation forced to mCarDockRotation.
1490         }
1491 
1492         final boolean deskDockEnablesAccelerometer =
1493                 mDisplayPolicy.isDeskDockEnablesAccelerometer();
1494         if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
1495                 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
1496                 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
1497                 && !deskDockEnablesAccelerometer) {
1498             return false; // Rotation forced to mDeskDockRotation.
1499         }
1500 
1501         final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
1502         if (hdmiPlugged && mDemoHdmiRotationLock) {
1503             return false; // Rotation forced to mDemoHdmiRotation.
1504 
1505         } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1506                 && mUndockedHdmiRotation >= 0) {
1507             return false; // Rotation forced to mUndockedHdmiRotation.
1508 
1509         } else if (mDemoRotationLock) {
1510             return false; // Rotation forced to mDemoRotation.
1511 
1512         } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1513             return false; // Rotation forced to mPortraitRotation.
1514 
1515         } else if (!mSupportAutoRotation) {
1516             return false;
1517         }
1518 
1519         // Ensure that some rotation choice is possible for the given orientation.
1520         switch (mCurrentAppOrientation) {
1521             case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1522             case ActivityInfo.SCREEN_ORIENTATION_USER:
1523             case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1524             case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1525             case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1526                 // NOSENSOR description is ambiguous, in reality WM ignores user choice.
1527                 return true;
1528         }
1529 
1530         // Rotation is forced, should be controlled by system.
1531         return false;
1532     }
1533 
1534     /** Notify the StatusBar that system rotation suggestion has changed. */
sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid)1535     private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
1536         if (mStatusBarManagerInternal == null) {
1537             mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
1538         }
1539         if (mStatusBarManagerInternal != null) {
1540             mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
1541         }
1542     }
1543 
dispatchProposedRotation(@urface.Rotation int rotation)1544     void dispatchProposedRotation(@Surface.Rotation int rotation) {
1545         if (mService.mRotationWatcherController.hasProposedRotationListeners()) {
1546             synchronized (mLock) {
1547                 mService.mRotationWatcherController.dispatchProposedRotation(
1548                         mDisplayContent, rotation);
1549             }
1550         }
1551     }
1552 
allowAllRotationsToString(int allowAll)1553     private static String allowAllRotationsToString(int allowAll) {
1554         switch (allowAll) {
1555             case -1:
1556                 return "unknown";
1557             case 0:
1558                 return "false";
1559             case 1:
1560                 return "true";
1561             default:
1562                 return Integer.toString(allowAll);
1563         }
1564     }
1565 
onUserSwitch()1566     public void onUserSwitch() {
1567         if (mSettingsObserver != null) {
1568             mSettingsObserver.onChange(false);
1569         }
1570     }
1571 
onDisplayRemoved()1572     void onDisplayRemoved() {
1573         removeDefaultDisplayRotationChangedCallback();
1574         if (mFoldController != null) {
1575             mFoldController.onDisplayRemoved();
1576         }
1577     }
1578 
1579     /** Return whether the rotation settings has changed. */
updateSettings()1580     private boolean updateSettings() {
1581         final ContentResolver resolver = mContext.getContentResolver();
1582         boolean shouldUpdateRotation = false;
1583 
1584         synchronized (mLock) {
1585             boolean shouldUpdateOrientationListener = false;
1586 
1587             // Configure rotation suggestions.
1588             final int showRotationSuggestions =
1589                     ActivityManager.isLowRamDeviceStatic()
1590                             ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
1591                             : Settings.Secure.getIntForUser(resolver,
1592                             Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
1593                             Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
1594                             UserHandle.USER_CURRENT);
1595             if (mShowRotationSuggestions != showRotationSuggestions) {
1596                 mShowRotationSuggestions = showRotationSuggestions;
1597                 shouldUpdateOrientationListener = true;
1598             }
1599 
1600             // Configure rotation lock.
1601             final int userRotation = Settings.System.getIntForUser(resolver,
1602                     Settings.System.USER_ROTATION, Surface.ROTATION_0,
1603                     UserHandle.USER_CURRENT);
1604             if (mUserRotation != userRotation) {
1605                 mUserRotation = userRotation;
1606                 shouldUpdateRotation = true;
1607             }
1608 
1609             final int userRotationMode = Settings.System.getIntForUser(resolver,
1610                     Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
1611                             ? WindowManagerPolicy.USER_ROTATION_FREE
1612                             : WindowManagerPolicy.USER_ROTATION_LOCKED;
1613             if (mUserRotationMode != userRotationMode) {
1614                 mUserRotationMode = userRotationMode;
1615                 shouldUpdateOrientationListener = true;
1616                 shouldUpdateRotation = true;
1617             }
1618 
1619             if (shouldUpdateOrientationListener) {
1620                 updateOrientationListenerLw(); // Enable or disable the orientation listener.
1621             }
1622 
1623             final int cameraRotationMode = Settings.Secure.getIntForUser(resolver,
1624                     Settings.Secure.CAMERA_AUTOROTATE, 0,
1625                     UserHandle.USER_CURRENT);
1626             if (mCameraRotationMode != cameraRotationMode) {
1627                 mCameraRotationMode = cameraRotationMode;
1628                 shouldUpdateRotation = true;
1629             }
1630         }
1631 
1632         return shouldUpdateRotation;
1633     }
1634 
removeDefaultDisplayRotationChangedCallback()1635     void removeDefaultDisplayRotationChangedCallback() {
1636         if (DisplayRotationCoordinator.isSecondaryInternalDisplay(mDisplayContent)) {
1637             mDisplayRotationCoordinator.removeDefaultDisplayRotationChangedCallback();
1638         }
1639     }
1640 
1641     /**
1642      * Called from {@link ActivityRecord#setRequestedOrientation(int)}
1643      */
onSetRequestedOrientation()1644     void onSetRequestedOrientation() {
1645         if (mCompatPolicyForImmersiveApps == null
1646                 || mRotationChoiceShownToUserForConfirmation == ROTATION_UNDEFINED) {
1647             return;
1648         }
1649         mOrientationListener.onProposedRotationChanged(mRotationChoiceShownToUserForConfirmation);
1650     }
1651 
dump(String prefix, PrintWriter pw)1652     void dump(String prefix, PrintWriter pw) {
1653         pw.println(prefix + "DisplayRotation");
1654         pw.println(prefix + "  mCurrentAppOrientation="
1655                 + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
1656         pw.println(prefix + "  mLastOrientation=" + mLastOrientation);
1657         pw.print(prefix + "  mRotation=" + mRotation);
1658         pw.println(" mDeferredRotationPauseCount=" + mDeferredRotationPauseCount);
1659 
1660         pw.print(prefix + "  mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
1661         pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
1662         pw.print(prefix + "  mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
1663         pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
1664 
1665         pw.println(prefix + "  mSupportAutoRotation=" + mSupportAutoRotation);
1666         if (mOrientationListener != null) {
1667             mOrientationListener.dump(pw, prefix + "  ");
1668         }
1669         pw.println();
1670 
1671         pw.print(prefix + "  mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
1672         pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
1673         pw.print(prefix + "  mUserRotationMode="
1674                 + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
1675         pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
1676         pw.print(" mCameraRotationMode=" + mCameraRotationMode);
1677         pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
1678 
1679         pw.print(prefix + "  mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
1680         pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
1681         pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
1682         pw.println(prefix + "  mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
1683         pw.println(prefix + "  mFixedToUserRotation=" + isFixedToUserRotation());
1684 
1685         if (mFoldController != null) {
1686             pw.println(prefix + "FoldController");
1687             pw.println(prefix + "  mPauseAutorotationDuringUnfolding="
1688                     + mFoldController.mPauseAutorotationDuringUnfolding);
1689             pw.println(prefix + "  mShouldDisableRotationSensor="
1690                     + mFoldController.mShouldDisableRotationSensor);
1691             pw.println(prefix + "  mShouldIgnoreSensorRotation="
1692                     + mFoldController.mShouldIgnoreSensorRotation);
1693             pw.println(prefix + "  mLastDisplaySwitchTime="
1694                     + mFoldController.mLastDisplaySwitchTime);
1695             pw.println(prefix + "  mLastHingeAngleEventTime="
1696                     + mFoldController.mLastHingeAngleEventTime);
1697             pw.println(prefix + "  mDeviceState="
1698                     + mFoldController.mDeviceState);
1699         }
1700 
1701         if (!mRotationHistory.mRecords.isEmpty()) {
1702             pw.println();
1703             pw.println(prefix + "  RotationHistory");
1704             prefix = "    " + prefix;
1705             for (RotationHistory.Record r : mRotationHistory.mRecords) {
1706                 r.dump(prefix, pw);
1707             }
1708         }
1709     }
1710 
dumpDebug(ProtoOutputStream proto, long fieldId)1711     void dumpDebug(ProtoOutputStream proto, long fieldId) {
1712         final long token = proto.start(fieldId);
1713         proto.write(ROTATION, getRotation());
1714         proto.write(FROZEN_TO_USER_ROTATION, isRotationFrozen());
1715         proto.write(USER_ROTATION, getUserRotation());
1716         proto.write(FIXED_TO_USER_ROTATION_MODE, mFixedToUserRotation);
1717         proto.write(LAST_ORIENTATION, mLastOrientation);
1718         proto.write(IS_FIXED_TO_USER_ROTATION, isFixedToUserRotation());
1719         proto.end(token);
1720     }
1721 
isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop)1722     boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
1723         if (mFoldController == null) return false;
1724         return mFoldController.isDeviceInPosture(state, isTabletop);
1725     }
1726 
isDisplaySeparatingHinge()1727     boolean isDisplaySeparatingHinge() {
1728         return mFoldController != null && mFoldController.isSeparatingHinge();
1729     }
1730 
1731     /**
1732      * Called by the display manager just before it applied the device state, it is guaranteed
1733      * that in case of physical display change the {@link DisplayRotation#physicalDisplayChanged}
1734      * method will be invoked *after* this one.
1735      */
foldStateChanged(DeviceStateController.DeviceState deviceState)1736     void foldStateChanged(DeviceStateController.DeviceState deviceState) {
1737         if (mFoldController != null) {
1738             synchronized (mLock) {
1739                 mFoldController.foldStateChanged(deviceState);
1740             }
1741         }
1742     }
1743 
1744     /**
1745      * Called by the DisplayContent when the physical display changes
1746      */
physicalDisplayChanged()1747     void physicalDisplayChanged() {
1748         if (mFoldController != null) {
1749             mFoldController.onPhysicalDisplayChanged();
1750         }
1751     }
1752 
1753     @VisibleForTesting
uptimeMillis()1754     long uptimeMillis() {
1755         return SystemClock.uptimeMillis();
1756     }
1757 
1758     class FoldController {
1759         private final boolean mPauseAutorotationDuringUnfolding;
1760         @Surface.Rotation
1761         private int mHalfFoldSavedRotation = -1; // No saved rotation
1762         private DeviceStateController.DeviceState mDeviceState =
1763                 DeviceStateController.DeviceState.UNKNOWN;
1764         private long mLastHingeAngleEventTime = 0;
1765         private long mLastDisplaySwitchTime = 0;
1766         private boolean mShouldIgnoreSensorRotation;
1767         private boolean mShouldDisableRotationSensor;
1768         private boolean mInHalfFoldTransition = false;
1769         private int mDisplaySwitchRotationBlockTimeMs;
1770         private int mHingeAngleRotationBlockTimeMs;
1771         private int mMaxHingeAngle;
1772         private final boolean mIsDisplayAlwaysSeparatingHinge;
1773         private SensorManager mSensorManager;
1774         private SensorEventListener mHingeAngleSensorEventListener;
1775         private final Set<Integer> mTabletopRotations;
1776         private final Runnable mActivityBoundsUpdateCallback;
1777         private final boolean mAllowHalfFoldAutoRotationOverride;
1778 
FoldController()1779         FoldController() {
1780             mAllowHalfFoldAutoRotationOverride = mContext.getResources().getBoolean(
1781                     R.bool.config_windowManagerHalfFoldAutoRotateOverride);
1782             mTabletopRotations = new ArraySet<>();
1783             int[] tabletop_rotations = mContext.getResources().getIntArray(
1784                     R.array.config_deviceTabletopRotations);
1785             if (tabletop_rotations != null) {
1786                 for (int angle : tabletop_rotations) {
1787                     switch (angle) {
1788                         case 0:
1789                             mTabletopRotations.add(Surface.ROTATION_0);
1790                             break;
1791                         case 90:
1792                             mTabletopRotations.add(Surface.ROTATION_90);
1793                             break;
1794                         case 180:
1795                             mTabletopRotations.add(Surface.ROTATION_180);
1796                             break;
1797                         case 270:
1798                             mTabletopRotations.add(Surface.ROTATION_270);
1799                             break;
1800                         default:
1801                             ProtoLog.e(WM_DEBUG_ORIENTATION,
1802                                     "Invalid surface rotation angle in "
1803                                             + "config_deviceTabletopRotations: %d",
1804                                     angle);
1805                     }
1806                 }
1807             } else {
1808                 ProtoLog.w(WM_DEBUG_ORIENTATION,
1809                         "config_deviceTabletopRotations is not defined. Half-fold "
1810                                 + "letterboxing will work inconsistently.");
1811             }
1812             mIsDisplayAlwaysSeparatingHinge = mContext.getResources().getBoolean(
1813                     R.bool.config_isDisplayHingeAlwaysSeparating);
1814 
1815             mActivityBoundsUpdateCallback = new Runnable() {
1816                 public void run() {
1817                     if (mDeviceState == DeviceStateController.DeviceState.OPEN
1818                             || mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
1819                         synchronized (mLock) {
1820                             final Task topFullscreenTask =
1821                                     mDisplayContent.getTask(
1822                                             t -> t.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
1823                             if (topFullscreenTask != null) {
1824                                 final ActivityRecord top =
1825                                         topFullscreenTask.topRunningActivity();
1826                                 if (top != null) {
1827                                     top.recomputeConfiguration();
1828                                 }
1829                             }
1830                         }
1831                     }
1832                 }
1833             };
1834 
1835             mPauseAutorotationDuringUnfolding = mContext.getResources().getBoolean(
1836                     R.bool.config_windowManagerPauseRotationWhenUnfolding);
1837 
1838             if (mPauseAutorotationDuringUnfolding) {
1839                 mDisplaySwitchRotationBlockTimeMs = mContext.getResources().getInteger(
1840                         R.integer.config_pauseRotationWhenUnfolding_displaySwitchTimeout);
1841                 mHingeAngleRotationBlockTimeMs = mContext.getResources().getInteger(
1842                         R.integer.config_pauseRotationWhenUnfolding_hingeEventTimeout);
1843                 mMaxHingeAngle = mContext.getResources().getInteger(
1844                         R.integer.config_pauseRotationWhenUnfolding_maxHingeAngle);
1845                 registerSensorManager();
1846             }
1847         }
1848 
registerSensorManager()1849         private void registerSensorManager() {
1850             mSensorManager = mContext.getSystemService(SensorManager.class);
1851             if (mSensorManager != null) {
1852                 final Sensor hingeAngleSensor = mSensorManager
1853                         .getDefaultSensor(Sensor.TYPE_HINGE_ANGLE);
1854 
1855                 if (hingeAngleSensor != null) {
1856                     mHingeAngleSensorEventListener = new SensorEventListener() {
1857                         @Override
1858                         public void onSensorChanged(SensorEvent event) {
1859                             onHingeAngleChanged(event.values[0]);
1860                         }
1861 
1862                         @Override
1863                         public void onAccuracyChanged(Sensor sensor, int accuracy) {
1864                         }
1865                     };
1866                     mSensorManager.registerListener(mHingeAngleSensorEventListener,
1867                             hingeAngleSensor, SensorManager.SENSOR_DELAY_FASTEST, getHandler());
1868                 }
1869             }
1870         }
1871 
onDisplayRemoved()1872         void onDisplayRemoved() {
1873             if (mSensorManager != null && mHingeAngleSensorEventListener != null) {
1874                 mSensorManager.unregisterListener(mHingeAngleSensorEventListener);
1875             }
1876         }
1877 
isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop)1878         boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
1879             if (state != mDeviceState) {
1880                 return false;
1881             }
1882             if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
1883                 return isTabletop == mTabletopRotations.contains(mRotation);
1884             }
1885             return true;
1886         }
1887 
getFoldState()1888         DeviceStateController.DeviceState getFoldState() {
1889             return mDeviceState;
1890         }
1891 
isSeparatingHinge()1892         boolean isSeparatingHinge() {
1893             return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED
1894                     || (mDeviceState == DeviceStateController.DeviceState.OPEN
1895                         && mIsDisplayAlwaysSeparatingHinge);
1896         }
1897 
overrideFrozenRotation()1898         boolean overrideFrozenRotation() {
1899             return mAllowHalfFoldAutoRotationOverride
1900                     && mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
1901         }
1902 
shouldRevertOverriddenRotation()1903         boolean shouldRevertOverriddenRotation() {
1904             // When transitioning to open.
1905             return mAllowHalfFoldAutoRotationOverride
1906                     && mDeviceState == DeviceStateController.DeviceState.OPEN
1907                     && !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
1908                     && mInHalfFoldTransition
1909                     && mDisplayContent.getRotationReversionController().isOverrideActive(
1910                         REVERSION_TYPE_HALF_FOLD)
1911                     && mUserRotationMode
1912                         == WindowManagerPolicy.USER_ROTATION_LOCKED; // Ignore if we're unlocked.
1913         }
1914 
revertOverriddenRotation()1915         int revertOverriddenRotation() {
1916             int savedRotation = mHalfFoldSavedRotation;
1917             mHalfFoldSavedRotation = -1;
1918             mDisplayContent.getRotationReversionController()
1919                     .revertOverride(REVERSION_TYPE_HALF_FOLD);
1920             mInHalfFoldTransition = false;
1921             return savedRotation;
1922         }
1923 
foldStateChanged(DeviceStateController.DeviceState newState)1924         void foldStateChanged(DeviceStateController.DeviceState newState) {
1925             ProtoLog.v(WM_DEBUG_ORIENTATION,
1926                     "foldStateChanged: displayId %d, halfFoldStateChanged %s, "
1927                     + "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
1928                     + "mLastOrientation: %d, mRotation: %d",
1929                     mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
1930                     mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
1931             if (mDeviceState == DeviceStateController.DeviceState.UNKNOWN) {
1932                 mDeviceState = newState;
1933                 return;
1934             }
1935             if (newState == DeviceStateController.DeviceState.HALF_FOLDED
1936                     && mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
1937                 // The device has transitioned to HALF_FOLDED state: save the current rotation and
1938                 // update the device rotation.
1939                 mDisplayContent.getRotationReversionController().beforeOverrideApplied(
1940                         REVERSION_TYPE_HALF_FOLD);
1941                 mHalfFoldSavedRotation = mRotation;
1942                 mDeviceState = newState;
1943                 // Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
1944                 // return true, so rotation is unlocked.
1945                 mService.updateRotation(false /* alwaysSendConfiguration */,
1946                         false /* forceRelayout */);
1947             } else {
1948                 mInHalfFoldTransition = true;
1949                 mDeviceState = newState;
1950                 // Tell the device to update its orientation.
1951                 mService.updateRotation(false /* alwaysSendConfiguration */,
1952                         false /* forceRelayout */);
1953             }
1954             // Alert the activity of possible new bounds.
1955             UiThread.getHandler().removeCallbacks(mActivityBoundsUpdateCallback);
1956             UiThread.getHandler().postDelayed(mActivityBoundsUpdateCallback,
1957                     FOLDING_RECOMPUTE_CONFIG_DELAY_MS);
1958         }
1959 
shouldIgnoreSensorRotation()1960         boolean shouldIgnoreSensorRotation() {
1961             return mShouldIgnoreSensorRotation;
1962         }
1963 
shouldDisableRotationSensor()1964         boolean shouldDisableRotationSensor() {
1965             return mShouldDisableRotationSensor;
1966         }
1967 
updateSensorRotationBlockIfNeeded()1968         private void updateSensorRotationBlockIfNeeded() {
1969             final long currentTime = uptimeMillis();
1970             final boolean newShouldIgnoreRotation =
1971                     currentTime - mLastDisplaySwitchTime < mDisplaySwitchRotationBlockTimeMs
1972                     || currentTime - mLastHingeAngleEventTime < mHingeAngleRotationBlockTimeMs;
1973 
1974             if (newShouldIgnoreRotation != mShouldIgnoreSensorRotation) {
1975                 mShouldIgnoreSensorRotation = newShouldIgnoreRotation;
1976 
1977                 // Resuming the autorotation
1978                 if (!mShouldIgnoreSensorRotation) {
1979                     if (mShouldDisableRotationSensor) {
1980                         // Sensor was disabled, let's re-enable it
1981                         mShouldDisableRotationSensor = false;
1982                         updateOrientationListenerLw();
1983                     } else {
1984                         // Sensor was not disabled, let's update the rotation in case if we received
1985                         // some rotation sensor updates when autorotate was disabled
1986                         updateRotationAndSendNewConfigIfChanged();
1987                     }
1988                 }
1989             }
1990         }
1991 
1992         void onPhysicalDisplayChanged() {
1993             if (!mPauseAutorotationDuringUnfolding) return;
1994 
1995             mLastDisplaySwitchTime = uptimeMillis();
1996 
1997             final boolean isUnfolding =
1998                     mDeviceState == DeviceStateController.DeviceState.OPEN
1999                     || mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
2000 
2001             if (isUnfolding) {
2002                 // Temporary disable rotation sensor updates when unfolding
2003                 mShouldDisableRotationSensor = true;
2004                 updateOrientationListenerLw();
2005             }
2006 
2007             updateSensorRotationBlockIfNeeded();
2008             getHandler().postDelayed(() -> {
2009                 synchronized (mLock) {
2010                     updateSensorRotationBlockIfNeeded();
2011                 };
2012             }, mDisplaySwitchRotationBlockTimeMs);
2013         }
2014 
2015         void onHingeAngleChanged(float hingeAngle) {
2016             if (hingeAngle < mMaxHingeAngle) {
2017                 mLastHingeAngleEventTime = uptimeMillis();
2018 
2019                 updateSensorRotationBlockIfNeeded();
2020 
2021                 getHandler().postDelayed(() -> {
2022                     synchronized (mLock) {
2023                         updateSensorRotationBlockIfNeeded();
2024                     };
2025                 }, mHingeAngleRotationBlockTimeMs);
2026             }
2027         }
2028     }
2029 
2030     @VisibleForTesting
2031     Handler getHandler() {
2032         return mService.mH;
2033     }
2034 
2035     private class OrientationListener extends WindowOrientationListener implements Runnable {
2036         transient boolean mEnabled;
2037 
2038         OrientationListener(Context context, Handler handler,
2039                 @Surface.Rotation int defaultRotation) {
2040             super(context, handler, defaultRotation);
2041         }
2042 
2043         @Override
2044         public boolean isKeyguardShowingAndNotOccluded() {
2045             return mService.isKeyguardShowingAndNotOccluded();
2046         }
2047 
2048         @Override
2049         public boolean isRotationResolverEnabled() {
2050             return mAllowRotationResolver
2051                     && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
2052                     && mCameraRotationMode == CAMERA_ROTATION_ENABLED
2053                     && !mService.mPowerManager.isPowerSaveMode();
2054         }
2055 
2056 
2057         @Override
2058         public void onProposedRotationChanged(@Surface.Rotation int rotation) {
2059             ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
2060             // Send interaction power boost to improve redraw performance.
2061             mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
2062             dispatchProposedRotation(rotation);
2063             if (isRotationChoiceAllowed(rotation)) {
2064                 mRotationChoiceShownToUserForConfirmation = rotation;
2065                 final boolean isValid = isValidRotationChoice(rotation);
2066                 sendProposedRotationChangeToStatusBarInternal(rotation, isValid);
2067             } else {
2068                 mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
2069                 mService.updateRotation(false /* alwaysSendConfiguration */,
2070                         false /* forceRelayout */);
2071             }
2072         }
2073 
2074         @Override
2075         public void enable() {
2076             mEnabled = true;
2077             getHandler().post(this);
2078             ProtoLog.v(WM_DEBUG_ORIENTATION, "Enabling listeners");
2079         }
2080 
2081         @Override
2082         public void disable() {
2083             mEnabled = false;
2084             getHandler().post(this);
2085             ProtoLog.v(WM_DEBUG_ORIENTATION, "Disabling listeners");
2086         }
2087 
2088         @Override
2089         public void run() {
2090             if (mEnabled) {
2091                 super.enable();
2092             } else {
2093                 super.disable();
2094             }
2095         }
2096     }
2097 
2098     private class SettingsObserver extends ContentObserver {
2099         SettingsObserver(Handler handler) {
2100             super(handler);
2101         }
2102 
2103         void observe() {
2104             final ContentResolver resolver = mContext.getContentResolver();
2105             resolver.registerContentObserver(Settings.Secure.getUriFor(
2106                     Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
2107                     UserHandle.USER_ALL);
2108             resolver.registerContentObserver(Settings.System.getUriFor(
2109                     Settings.System.ACCELEROMETER_ROTATION), false, this,
2110                     UserHandle.USER_ALL);
2111             resolver.registerContentObserver(Settings.System.getUriFor(
2112                     Settings.System.USER_ROTATION), false, this,
2113                     UserHandle.USER_ALL);
2114             resolver.registerContentObserver(
2115                     Settings.Secure.getUriFor(Settings.Secure.CAMERA_AUTOROTATE), false, this,
2116                     UserHandle.USER_ALL);
2117 
2118             updateSettings();
2119         }
2120 
2121         @Override
2122         public void onChange(boolean selfChange) {
2123             if (updateSettings()) {
2124                 mService.updateRotation(true /* alwaysSendConfiguration */,
2125                         false /* forceRelayout */);
2126             }
2127         }
2128     }
2129 
2130     private static class RotationHistory {
2131         private static final int MAX_SIZE = 8;
2132         private static final int NO_FOLD_CONTROLLER = -2;
2133         private static class Record {
2134             final @Surface.Rotation int mFromRotation;
2135             final @Surface.Rotation int mToRotation;
2136             final @Surface.Rotation int mUserRotation;
2137             final @WindowManagerPolicy.UserRotationMode int mUserRotationMode;
2138             final int mSensorRotation;
2139             final boolean mIgnoreOrientationRequest;
2140             final String mNonDefaultRequestingTaskDisplayArea;
2141             final String mLastOrientationSource;
2142             final @ActivityInfo.ScreenOrientation int mSourceOrientation;
2143             final long mTimestamp = System.currentTimeMillis();
2144             final int mHalfFoldSavedRotation;
2145             final boolean mInHalfFoldTransition;
2146             final DeviceStateController.DeviceState mDeviceState;
2147             @Nullable final boolean[] mRotationReversionSlots;
2148 
2149             @Nullable final String mDisplayRotationCompatPolicySummary;
2150 
2151             Record(DisplayRotation dr, int fromRotation, int toRotation) {
2152                 mFromRotation = fromRotation;
2153                 mToRotation = toRotation;
2154                 mUserRotation = dr.mUserRotation;
2155                 mUserRotationMode = dr.mUserRotationMode;
2156                 final OrientationListener listener = dr.mOrientationListener;
2157                 mSensorRotation = (listener == null || !listener.mEnabled)
2158                         ? -2 /* disabled */ : dr.mLastSensorRotation;
2159                 final DisplayContent dc = dr.mDisplayContent;
2160                 mIgnoreOrientationRequest = dc.getIgnoreOrientationRequest();
2161                 final TaskDisplayArea requestingTda = dc.getOrientationRequestingTaskDisplayArea();
2162                 mNonDefaultRequestingTaskDisplayArea = requestingTda == null
2163                         ? "none" : requestingTda != dc.getDefaultTaskDisplayArea()
2164                         ? requestingTda.toString() : null;
2165                 final WindowContainer<?> source = dc.getLastOrientationSource();
2166                 if (source != null) {
2167                     mLastOrientationSource = source.toString();
2168                     final WindowState w = source.asWindowState();
2169                     mSourceOrientation = w != null
2170                             ? w.mAttrs.screenOrientation
2171                             : source.getOverrideOrientation();
2172                 } else {
2173                     mLastOrientationSource = null;
2174                     mSourceOrientation = SCREEN_ORIENTATION_UNSET;
2175                 }
2176                 if (dr.mFoldController != null) {
2177                     mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
2178                     mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
2179                     mDeviceState = dr.mFoldController.mDeviceState;
2180                 } else {
2181                     mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
2182                     mInHalfFoldTransition = false;
2183                     mDeviceState = DeviceStateController.DeviceState.UNKNOWN;
2184                 }
2185                 mDisplayRotationCompatPolicySummary = dc.mDisplayRotationCompatPolicy == null
2186                         ? null
2187                         : dc.mDisplayRotationCompatPolicy
2188                                 .getSummaryForDisplayRotationHistoryRecord();
2189                 mRotationReversionSlots =
2190                         dr.mDisplayContent.getRotationReversionController().getSlotsCopy();
2191             }
2192 
2193             void dump(String prefix, PrintWriter pw) {
2194                 pw.println(prefix + TimeUtils.logTimeOfDay(mTimestamp)
2195                         + " " + Surface.rotationToString(mFromRotation)
2196                         + " to " + Surface.rotationToString(mToRotation));
2197                 pw.println(prefix + "  source=" + mLastOrientationSource
2198                         + " " + ActivityInfo.screenOrientationToString(mSourceOrientation));
2199                 pw.println(prefix + "  mode="
2200                         + WindowManagerPolicy.userRotationModeToString(mUserRotationMode)
2201                         + " user=" + Surface.rotationToString(mUserRotation)
2202                         + " sensor=" + Surface.rotationToString(mSensorRotation));
2203                 if (mIgnoreOrientationRequest) pw.println(prefix + "  ignoreRequest=true");
2204                 if (mNonDefaultRequestingTaskDisplayArea != null) {
2205                     pw.println(prefix + "  requestingTda=" + mNonDefaultRequestingTaskDisplayArea);
2206                 }
2207                 if (mHalfFoldSavedRotation != NO_FOLD_CONTROLLER) {
2208                     pw.println(prefix + " halfFoldSavedRotation="
2209                             + mHalfFoldSavedRotation
2210                             + " mInHalfFoldTransition=" + mInHalfFoldTransition
2211                             + " mFoldState=" + mDeviceState);
2212                 }
2213                 if (mDisplayRotationCompatPolicySummary != null) {
2214                     pw.println(prefix + mDisplayRotationCompatPolicySummary);
2215                 }
2216                 if (mRotationReversionSlots != null) {
2217                     pw.println(prefix + " reversionSlots= NOSENSOR "
2218                             + mRotationReversionSlots[REVERSION_TYPE_NOSENSOR] + ", CAMERA "
2219                             + mRotationReversionSlots[REVERSION_TYPE_CAMERA_COMPAT] + " HALF_FOLD "
2220                             + mRotationReversionSlots[REVERSION_TYPE_HALF_FOLD]);
2221                 }
2222             }
2223         }
2224 
2225         final ArrayDeque<Record> mRecords = new ArrayDeque<>(MAX_SIZE);
2226 
2227         void addRecord(DisplayRotation dr, int toRotation) {
2228             if (mRecords.size() >= MAX_SIZE) {
2229                 mRecords.removeFirst();
2230             }
2231             final int fromRotation = dr.mDisplayContent.getWindowConfiguration().getRotation();
2232             mRecords.addLast(new Record(dr, fromRotation, toRotation));
2233         }
2234     }
2235 }
2236