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