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