blob: 3588470f4a7ec2210cac2f21f2ccb6b6745159f4 [file] [log] [blame]
Riddle Hsuad256a12018-07-18 16:11:30 +08001/*
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
17package com.android.server.wm;
18
Riddle Hsuccf09402019-08-13 00:33:06 +080019import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
20import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
21import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
22import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
23import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
24
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080025import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
Riddle Hsuccf09402019-08-13 00:33:06 +080026import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080027import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
28import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
29import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Riddle Hsuccf09402019-08-13 00:33:06 +080030import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
31import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080032
Riddle Hsuccf09402019-08-13 00:33:06 +080033import android.annotation.AnimRes;
Garfield Tan7fbca052019-02-19 10:45:35 -080034import android.annotation.IntDef;
Garfield Tanff362222018-11-14 17:52:32 -080035import android.annotation.UserIdInt;
Rajeev Kumarb2ff8e82018-08-06 11:45:05 -070036import android.app.ActivityManager;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080037import android.content.ContentResolver;
Riddle Hsuad256a12018-07-18 16:11:30 +080038import android.content.Context;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080039import android.content.Intent;
Riddle Hsuad256a12018-07-18 16:11:30 +080040import android.content.pm.ActivityInfo;
Riddle Hsuccf09402019-08-13 00:33:06 +080041import android.content.pm.ActivityInfo.ScreenOrientation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080042import android.content.pm.PackageManager;
Riddle Hsuad256a12018-07-18 16:11:30 +080043import android.content.res.Resources;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080044import android.database.ContentObserver;
45import android.hardware.power.V1_0.PowerHint;
Garfield Tanff362222018-11-14 17:52:32 -080046import android.net.Uri;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080047import android.os.Handler;
48import android.os.SystemProperties;
49import android.os.UserHandle;
50import android.provider.Settings;
51import android.util.Slog;
52import android.util.SparseArray;
Riddle Hsuad256a12018-07-18 16:11:30 +080053import android.view.Surface;
54
Riddle Hsuccf09402019-08-13 00:33:06 +080055import com.android.internal.R;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080056import com.android.internal.annotations.VisibleForTesting;
57import com.android.server.LocalServices;
58import com.android.server.UiThread;
Riddle Hsuad256a12018-07-18 16:11:30 +080059import com.android.server.policy.WindowManagerPolicy;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080060import com.android.server.policy.WindowOrientationListener;
61import com.android.server.statusbar.StatusBarManagerInternal;
Riddle Hsuad256a12018-07-18 16:11:30 +080062
63import java.io.PrintWriter;
Garfield Tan7fbca052019-02-19 10:45:35 -080064import java.lang.annotation.Retention;
65import java.lang.annotation.RetentionPolicy;
Riddle Hsuad256a12018-07-18 16:11:30 +080066
67/**
68 * Defines the mapping between orientation and rotation of a display.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080069 * Non-public methods are assumed to run inside WM lock.
Riddle Hsuad256a12018-07-18 16:11:30 +080070 */
71public class DisplayRotation {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080072 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
Riddle Hsuad256a12018-07-18 16:11:30 +080073
Riddle Hsuccf09402019-08-13 00:33:06 +080074 private static class RotationAnimationPair {
75 @AnimRes
76 int mEnter;
77 @AnimRes
78 int mExit;
79 }
80
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080081 private final WindowManagerService mService;
Garfield Tan90c90052018-10-08 12:29:41 -070082 private final DisplayContent mDisplayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080083 private final DisplayPolicy mDisplayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -080084 private final DisplayWindowSettings mDisplayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080085 private final Context mContext;
86 private final Object mLock;
87
88 public final boolean isDefaultDisplay;
89 private final boolean mSupportAutoRotation;
90 private final int mLidOpenRotation;
91 private final int mCarDockRotation;
92 private final int mDeskDockRotation;
93 private final int mUndockedHdmiRotation;
Riddle Hsuccf09402019-08-13 00:33:06 +080094 private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair();
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080095
96 private OrientationListener mOrientationListener;
97 private StatusBarManagerInternal mStatusBarManagerInternal;
98 private SettingsObserver mSettingsObserver;
99
Riddle Hsuccf09402019-08-13 00:33:06 +0800100 @ScreenOrientation
101 private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
102
103 /**
104 * Last applied orientation of the display.
105 *
106 * @see #updateOrientationFromApp
107 */
108 @ScreenOrientation
109 private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
110
111 /**
112 * Current rotation of the display.
113 *
114 * @see #updateRotationUnchecked
115 */
116 @Surface.Rotation
117 private int mRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800118
119 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800120 int mLandscapeRotation; // default landscape
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800121 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800122 int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800123 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800124 int mPortraitRotation; // default portrait
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800125 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800126 int mUpsideDownRotation; // "other" portrait
127
Riddle Hsuccf09402019-08-13 00:33:06 +0800128 private boolean mAllowSeamlessRotationDespiteNavBarMoving;
129
130 private int mDeferredRotationPauseCount;
131
132 /**
133 * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
134 * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
135 * so we need to track when this hits zero so we can apply deferred orientation updates.
136 */
137 private int mSeamlessRotationCount;
138
139 /**
140 * True in the interval from starting seamless rotation until the last rotated window draws in
141 * the new orientation.
142 */
143 private boolean mRotatingSeamlessly;
144
145 /**
146 * Behavior of rotation suggestions.
147 *
148 * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
149 */
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800150 private int mShowRotationSuggestions;
151
Riddle Hsuccf09402019-08-13 00:33:06 +0800152 private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1;
153 private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0;
154 private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1;
155
156 @IntDef({ ALLOW_ALL_ROTATIONS_UNDEFINED, ALLOW_ALL_ROTATIONS_DISABLED,
157 ALLOW_ALL_ROTATIONS_ENABLED })
158 @Retention(RetentionPolicy.SOURCE)
159 private @interface AllowAllRotations {}
160
161 /**
162 * Whether to allow the screen to rotate to all rotations (including 180 degree) according to
163 * the sensor even when the current orientation is not
164 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_SENSOR} or
165 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_USER}.
166 */
167 @AllowAllRotations
168 private int mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
169
170 @WindowManagerPolicy.UserRotationMode
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800171 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
Riddle Hsuccf09402019-08-13 00:33:06 +0800172
173 @Surface.Rotation
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800174 private int mUserRotation = Surface.ROTATION_0;
175
Garfield Tanff362222018-11-14 17:52:32 -0800176 /**
Garfield Tan7fbca052019-02-19 10:45:35 -0800177 * Flag that indicates this is a display that may run better when fixed to user rotation.
178 */
179 private boolean mDefaultFixedToUserRotation;
180
181 /**
182 * No overridden behavior is provided in terms of fixing rotation to user rotation. Use other
183 * flags to derive the default behavior, such as {@link WindowManagerService#mIsPc} and
184 * {@link WindowManagerService#mForceDesktopModeOnExternalDisplays}.
185 */
186 static final int FIXED_TO_USER_ROTATION_DEFAULT = 0;
187 /**
188 * Don't fix display rotation to {@link #mUserRotation} only. Always allow other factors to play
189 * a role in deciding display rotation.
190 */
191 static final int FIXED_TO_USER_ROTATION_DISABLED = 1;
192 /**
193 * Only use {@link #mUserRotation} as the display rotation.
194 */
195 static final int FIXED_TO_USER_ROTATION_ENABLED = 2;
196 @IntDef({ FIXED_TO_USER_ROTATION_DEFAULT, FIXED_TO_USER_ROTATION_DISABLED,
197 FIXED_TO_USER_ROTATION_ENABLED })
198 @Retention(RetentionPolicy.SOURCE)
199 @interface FixedToUserRotation {}
200
201 /**
Garfield Tanff362222018-11-14 17:52:32 -0800202 * A flag to indicate if the display rotation should be fixed to user specified rotation
203 * regardless of all other states (including app requrested orientation). {@code true} the
204 * display rotation should be fixed to user specified rotation, {@code false} otherwise.
205 */
Riddle Hsuccf09402019-08-13 00:33:06 +0800206 @FixedToUserRotation
Garfield Tan7fbca052019-02-19 10:45:35 -0800207 private int mFixedToUserRotation = FIXED_TO_USER_ROTATION_DEFAULT;
Garfield Tanff362222018-11-14 17:52:32 -0800208
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800209 private int mDemoHdmiRotation;
210 private int mDemoRotation;
211 private boolean mDemoHdmiRotationLock;
212 private boolean mDemoRotationLock;
213
214 DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
215 this(service, displayContent, displayContent.getDisplayPolicy(),
Garfield Tanff362222018-11-14 17:52:32 -0800216 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
Riddle Hsuad256a12018-07-18 16:11:30 +0800217 }
218
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800219 @VisibleForTesting
220 DisplayRotation(WindowManagerService service, DisplayContent displayContent,
Garfield Tanff362222018-11-14 17:52:32 -0800221 DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
222 Context context, Object lock) {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800223 mService = service;
Garfield Tan90c90052018-10-08 12:29:41 -0700224 mDisplayContent = displayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800225 mDisplayPolicy = displayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -0800226 mDisplayWindowSettings = displayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800227 mContext = context;
228 mLock = lock;
229 isDefaultDisplay = displayContent.isDefaultDisplay;
Riddle Hsuad256a12018-07-18 16:11:30 +0800230
Riddle Hsuccf09402019-08-13 00:33:06 +0800231 mSupportAutoRotation =
232 mContext.getResources().getBoolean(R.bool.config_supportAutoRotation);
233 mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation);
234 mCarDockRotation = readRotation(R.integer.config_carDockRotation);
235 mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
236 mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800237
238 if (isDefaultDisplay) {
239 final Handler uiHandler = UiThread.getHandler();
240 mOrientationListener = new OrientationListener(mContext, uiHandler);
Riddle Hsuccf09402019-08-13 00:33:06 +0800241 mOrientationListener.setCurrentRotation(mRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800242 mSettingsObserver = new SettingsObserver(uiHandler);
243 mSettingsObserver.observe();
244 }
245 }
246
247 private int readRotation(int resID) {
248 try {
249 final int rotation = mContext.getResources().getInteger(resID);
250 switch (rotation) {
251 case 0:
252 return Surface.ROTATION_0;
253 case 90:
254 return Surface.ROTATION_90;
255 case 180:
256 return Surface.ROTATION_180;
257 case 270:
258 return Surface.ROTATION_270;
259 }
260 } catch (Resources.NotFoundException e) {
261 // fall through
262 }
263 return -1;
264 }
265
Riddle Hsuccf09402019-08-13 00:33:06 +0800266 /**
267 * Updates the configuration which may have different values depending on current user, e.g.
268 * runtime resource overlay.
269 */
270 void updateUserDependentConfiguration(Resources currentUserRes) {
271 mAllowSeamlessRotationDespiteNavBarMoving =
272 currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
273 }
274
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800275 void configure(int width, int height, int shortSizeDp, int longSizeDp) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800276 final Resources res = mContext.getResources();
277 if (width > height) {
278 mLandscapeRotation = Surface.ROTATION_0;
279 mSeascapeRotation = Surface.ROTATION_180;
Riddle Hsuccf09402019-08-13 00:33:06 +0800280 if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800281 mPortraitRotation = Surface.ROTATION_90;
282 mUpsideDownRotation = Surface.ROTATION_270;
283 } else {
284 mPortraitRotation = Surface.ROTATION_270;
285 mUpsideDownRotation = Surface.ROTATION_90;
286 }
287 } else {
288 mPortraitRotation = Surface.ROTATION_0;
289 mUpsideDownRotation = Surface.ROTATION_180;
Riddle Hsuccf09402019-08-13 00:33:06 +0800290 if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800291 mLandscapeRotation = Surface.ROTATION_270;
292 mSeascapeRotation = Surface.ROTATION_90;
293 } else {
294 mLandscapeRotation = Surface.ROTATION_90;
295 mSeascapeRotation = Surface.ROTATION_270;
296 }
297 }
298
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800299 // For demo purposes, allow the rotation of the HDMI display to be controlled.
300 // By default, HDMI locks rotation to landscape.
301 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
302 mDemoHdmiRotation = mPortraitRotation;
303 } else {
304 mDemoHdmiRotation = mLandscapeRotation;
305 }
306 mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
307
308 // For demo purposes, allow the rotation of the remote display to be controlled.
309 // By default, remote display locks rotation to landscape.
310 if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
311 mDemoRotation = mPortraitRotation;
312 } else {
313 mDemoRotation = mLandscapeRotation;
314 }
315 mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
316
Garfield Tan12b12f7a2019-02-22 16:33:27 -0800317 // It's physically impossible to rotate the car's screen.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800318 final boolean isCar = mContext.getPackageManager().hasSystemFeature(
319 PackageManager.FEATURE_AUTOMOTIVE);
Garfield Tan12b12f7a2019-02-22 16:33:27 -0800320 // It's also not likely to rotate a TV screen.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800321 final boolean isTv = mContext.getPackageManager().hasSystemFeature(
322 PackageManager.FEATURE_LEANBACK);
Garfield Tan7fbca052019-02-19 10:45:35 -0800323 final boolean forceDesktopMode =
324 mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay;
325 mDefaultFixedToUserRotation =
Tiger Huang86e6d072019-05-02 20:23:47 +0800326 (isCar || isTv || mService.mIsPc || forceDesktopMode)
Garfield Tan7fbca052019-02-19 10:45:35 -0800327 // For debug purposes the next line turns this feature off with:
328 // $ adb shell setprop config.override_forced_orient true
329 // $ adb shell wm size reset
330 && !"true".equals(SystemProperties.get("config.override_forced_orient"));
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800331 }
332
Riddle Hsuccf09402019-08-13 00:33:06 +0800333 void applyCurrentRotation(@Surface.Rotation int rotation) {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800334 if (mOrientationListener != null) {
335 mOrientationListener.setCurrentRotation(rotation);
336 }
337 }
338
Riddle Hsuccf09402019-08-13 00:33:06 +0800339 @VisibleForTesting
340 void setRotation(@Surface.Rotation int rotation) {
341 mRotation = rotation;
342 }
343
344 @Surface.Rotation
345 int getRotation() {
346 return mRotation;
347 }
348
349 @ScreenOrientation
350 int getLastOrientation() {
351 return mLastOrientation;
352 }
353
354 boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
355 if (newOrientation == mLastOrientation && !forceUpdate) {
356 return false;
357 }
358 mLastOrientation = newOrientation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800359 if (newOrientation != mCurrentAppOrientation) {
360 mCurrentAppOrientation = newOrientation;
361 if (isDefaultDisplay) {
362 updateOrientationListenerLw();
363 }
364 }
Riddle Hsuccf09402019-08-13 00:33:06 +0800365 return updateRotationUnchecked(forceUpdate);
366 }
367
368 /**
369 * Update rotation of the display and send configuration if the rotation is changed.
370 *
371 * @return {@code true} if the rotation has been changed and the new config is sent.
372 */
373 boolean updateRotationAndSendNewConfigIfChanged() {
374 final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
375 if (changed) {
376 mDisplayContent.sendNewConfiguration();
377 }
378 return changed;
379 }
380
381 /**
382 * Update rotation with an option to force the update. This updates the container's perception
383 * of rotation and, depending on the top activities, will freeze the screen or start seamless
384 * rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}
385 * during {@link DisplayContent#sendNewConfiguration}.
386 *
387 * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
388 * orientation because we're waiting for some rotation to finish or display
389 * to unfreeze, which results in configuration of the previously visible
390 * activity being applied to a newly visible one. Forcing the rotation
391 * update allows to workaround this issue.
392 * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
393 * {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE
394 * THE SCREEN.
395 */
396 boolean updateRotationUnchecked(boolean forceUpdate) {
397 final int displayId = mDisplayContent.getDisplayId();
398 if (!forceUpdate) {
399 if (mDeferredRotationPauseCount > 0) {
400 // Rotation updates have been paused temporarily. Defer the update until updates
401 // have been resumed.
402 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
403 return false;
404 }
405
406 final ScreenRotationAnimation screenRotationAnimation =
Vadim Caenb3715832019-08-13 17:06:38 +0200407 mDisplayContent.getRotationAnimation();
Riddle Hsuccf09402019-08-13 00:33:06 +0800408 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
409 // Rotation updates cannot be performed while the previous rotation change animation
410 // is still in progress. Skip this update. We will try updating again after the
411 // animation is finished and the display is unfrozen.
412 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
413 return false;
414 }
415 if (mService.mDisplayFrozen) {
416 // Even if the screen rotation animation has finished (e.g. isAnimating returns
417 // false), there is still some time where we haven't yet unfrozen the display. We
418 // also need to abort rotation here.
419 if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
420 "Deferring rotation, still finishing previous rotation");
421 return false;
422 }
423 }
424
425 if (!mService.mDisplayEnabled) {
426 // No point choosing a rotation if the display is not enabled.
427 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
428 return false;
429 }
430
431 final int oldRotation = mRotation;
432 final int lastOrientation = mLastOrientation;
433 final int rotation = rotationForOrientation(lastOrientation, oldRotation);
434 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
435 + displayId + " based on lastOrientation=" + lastOrientation
436 + " and oldRotation=" + oldRotation);
437
438 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + displayId
439 + " selected orientation " + lastOrientation
440 + ", got rotation " + rotation);
441
442 if (oldRotation == rotation) {
443 // No change.
444 return false;
445 }
446
447 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + displayId
448 + " rotation changed to " + rotation
449 + " from " + oldRotation
450 + ", lastOrientation=" + lastOrientation);
451
452 if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
453 mDisplayContent.mWaitingForConfig = true;
454 }
455
456 mRotation = rotation;
457
458 mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
459 mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
460 mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
461
462 mDisplayContent.setLayoutNeeded();
463
464 if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
465 // The screen rotation animation uses a screenshot to freeze the screen while windows
466 // resize underneath. When we are rotating seamlessly, we allow the elements to
467 // transition to their rotated state independently and without a freeze required.
468 prepareSeamlessRotation();
469 } else {
470 prepareNormalRotationAnimation();
471 }
472
473 return true;
474 }
475
476 void prepareNormalRotationAnimation() {
477 final RotationAnimationPair anim = selectRotationAnimation();
478 mService.startFreezingDisplayLocked(anim.mExit, anim.mEnter, mDisplayContent);
479 }
480
481 private void prepareSeamlessRotation() {
482 // We are careful to reset this in case a window was removed before it finished
483 // seamless rotation.
484 mSeamlessRotationCount = 0;
485 mRotatingSeamlessly = true;
486 }
487
488 boolean isRotatingSeamlessly() {
489 return mRotatingSeamlessly;
490 }
491
492 @VisibleForTesting
493 boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
494 final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
495 if (w == null || w != mDisplayContent.mCurrentFocus) {
496 return false;
497 }
498 // We only enable seamless rotation if the top window has requested it and is in the
499 // fullscreen opaque state. Seamless rotation requires freezing various Surface states and
500 // won't work well with animations, so we disable it in the animation case for now.
501 if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {
502 return false;
503 }
504
505 // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
506 // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
507 // will not enter the reverse portrait orientation, so actually the orientation won't change
508 // at all.
509 if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
510 return false;
511 }
512
513 // If the navigation bar can't change sides, then it will jump when we change orientations
514 // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
515 // where the navbar is low-profile enough that this isn't very noticeable.
516 if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {
517 return false;
518 }
519
520 // If the bounds of activity window is different from its parent, then reject to be seamless
521 // because the window position may change after rotation that will look like a sudden jump.
522 if (w.mAppToken != null && !w.mAppToken.matchParentBounds()) {
523 return false;
524 }
525
526 // In the presence of the PINNED stack or System Alert windows we unfortunately can not
527 // seamlessly rotate.
528 if (mDisplayContent.hasPinnedStack() || mDisplayContent.hasAlertWindowSurfaces()) {
529 return false;
530 }
531
532 // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
533 // complete (that is, waiting for windows to redraw). It's tempting to check
534 // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
535 if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
536 return false;
537 }
538
539 return true;
540 }
541
542 void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
543 if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
544 return;
545 }
546
547 w.mSeamlesslyRotated = seamlesslyRotated;
548 if (seamlesslyRotated) {
549 mSeamlessRotationCount++;
550 } else {
551 mSeamlessRotationCount--;
552 }
553 if (mSeamlessRotationCount == 0) {
554 if (DEBUG_ORIENTATION) {
555 Slog.i(TAG, "Performing post-rotate rotation after seamless rotation");
556 }
557 // Finish seamless rotation.
558 mRotatingSeamlessly = false;
559
560 updateRotationAndSendNewConfigIfChanged();
561 }
562 }
563
564 void onSeamlessRotationTimeout() {
565 final boolean[] isLayoutNeeded = { false };
566
567 mDisplayContent.forAllWindows(w -> {
568 if (!w.mSeamlesslyRotated) {
569 return;
570 }
571 isLayoutNeeded[0] = true;
572 w.setDisplayLayoutNeeded();
573 w.finishSeamlessRotation(true /* timeout */);
574 markForSeamlessRotation(w, false /* seamlesslyRotated */);
575 }, true /* traverseTopToBottom */);
576
577 if (isLayoutNeeded[0]) {
578 mService.mWindowPlacerLocked.performSurfacePlacement();
579 }
580 }
581
582 /**
583 * Returns the animation to run for a rotation transition based on the top fullscreen windows
584 * {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently
585 * fullscreen and frontmost.
586 */
587 private RotationAnimationPair selectRotationAnimation() {
588 // If the screen is off or non-interactive, force a jumpcut.
589 final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully()
590 || !mService.mPolicy.okToAnimate();
591 final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow();
592 if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation topFullscreen="
593 + topFullscreen + " rotationAnimation="
594 + (topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation)
595 + " forceJumpcut=" + forceJumpcut);
596 if (forceJumpcut) {
597 mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
598 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
599 return mTmpRotationAnim;
600 }
601 if (topFullscreen != null) {
602 int animationHint = topFullscreen.getRotationAnimationHint();
603 if (animationHint < 0 && mDisplayPolicy.isTopLayoutFullscreen()) {
604 animationHint = topFullscreen.getAttrs().rotationAnimation;
605 }
606 switch (animationHint) {
607 case ROTATION_ANIMATION_CROSSFADE:
608 case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
609 mTmpRotationAnim.mExit = R.anim.rotation_animation_xfade_exit;
610 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
611 break;
612 case ROTATION_ANIMATION_JUMPCUT:
613 mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
614 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
615 break;
616 case ROTATION_ANIMATION_ROTATE:
617 default:
618 mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
619 break;
620 }
621 } else {
622 mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
623 }
624 return mTmpRotationAnim;
625 }
626
627 /**
628 * Validate whether the current top fullscreen has specified the same
629 * {@link android.view.WindowManager.LayoutParams#rotationAnimation} value as that being passed
630 * in from the previous top fullscreen window.
631 *
632 * @param exitAnimId exiting resource id from the previous window.
633 * @param enterAnimId entering resource id from the previous window.
634 * @param forceDefault For rotation animations only, if true ignore the animation values and
635 * just return false.
636 * @return {@code true} if the previous values are still valid, false if they should be replaced
637 * with the default.
638 */
639 boolean validateRotationAnimation(int exitAnimId, int enterAnimId, boolean forceDefault) {
640 switch (exitAnimId) {
641 case R.anim.rotation_animation_xfade_exit:
642 case R.anim.rotation_animation_jump_exit:
643 // These are the only cases that matter.
644 if (forceDefault) {
645 return false;
646 }
647 final RotationAnimationPair anim = selectRotationAnimation();
648 return exitAnimId == anim.mExit && enterAnimId == anim.mEnter;
649 default:
650 return true;
651 }
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800652 }
653
Garfield Tanff362222018-11-14 17:52:32 -0800654 void restoreSettings(int userRotationMode, int userRotation,
Garfield Tan7fbca052019-02-19 10:45:35 -0800655 @FixedToUserRotation int fixedToUserRotation) {
Garfield Tanff362222018-11-14 17:52:32 -0800656 mFixedToUserRotation = fixedToUserRotation;
657
658 // We will retrieve user rotation and user rotation mode from settings for default display.
659 if (isDefaultDisplay) {
660 return;
661 }
Garfield Tan90c90052018-10-08 12:29:41 -0700662 if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
663 && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
664 Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
665 + " for " + mDisplayContent);
666 userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
667 }
668 if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) {
669 Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation
670 + " for " + mDisplayContent);
671 userRotation = Surface.ROTATION_0;
672 }
673 mUserRotationMode = userRotationMode;
674 mUserRotation = userRotation;
675 }
676
Garfield Tan7fbca052019-02-19 10:45:35 -0800677 void setFixedToUserRotation(@FixedToUserRotation int fixedToUserRotation) {
Garfield Tanff362222018-11-14 17:52:32 -0800678 if (mFixedToUserRotation == fixedToUserRotation) {
679 return;
680 }
681
682 mFixedToUserRotation = fixedToUserRotation;
Garfield Tan7fbca052019-02-19 10:45:35 -0800683 mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation);
Garfield Tanff362222018-11-14 17:52:32 -0800684 mService.updateRotation(true /* alwaysSendConfiguration */,
685 false /* forceRelayout */);
686 }
687
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700688 @VisibleForTesting
689 void setUserRotation(int userRotationMode, int userRotation) {
Garfield Tan90c90052018-10-08 12:29:41 -0700690 if (isDefaultDisplay) {
691 // We'll be notified via settings listener, so we don't need to update internal values.
692 final ContentResolver res = mContext.getContentResolver();
693 final int accelerometerRotation =
694 userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
695 Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
696 accelerometerRotation, UserHandle.USER_CURRENT);
697 Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
698 UserHandle.USER_CURRENT);
699 return;
700 }
701
702 boolean changed = false;
703 if (mUserRotationMode != userRotationMode) {
704 mUserRotationMode = userRotationMode;
705 changed = true;
706 }
707 if (mUserRotation != userRotation) {
708 mUserRotation = userRotation;
709 changed = true;
710 }
Garfield Tanff362222018-11-14 17:52:32 -0800711 mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
Chilun8753ad32018-10-09 15:56:45 +0800712 userRotation);
Garfield Tan90c90052018-10-08 12:29:41 -0700713 if (changed) {
714 mService.updateRotation(true /* alwaysSendConfiguration */,
715 false /* forceRelayout */);
Garfield Tan90c90052018-10-08 12:29:41 -0700716 }
717 }
718
719 void freezeRotation(int rotation) {
Riddle Hsuccf09402019-08-13 00:33:06 +0800720 rotation = (rotation == -1) ? mRotation : rotation;
Garfield Tan90c90052018-10-08 12:29:41 -0700721 setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
722 }
723
724 void thawRotation() {
725 setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
726 }
727
728 boolean isRotationFrozen() {
729 if (!isDefaultDisplay) {
730 return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED;
731 }
732
733 return Settings.System.getIntForUser(mContext.getContentResolver(),
734 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
735 }
736
Garfield Tanff362222018-11-14 17:52:32 -0800737 boolean isFixedToUserRotation() {
Garfield Tan7fbca052019-02-19 10:45:35 -0800738 switch (mFixedToUserRotation) {
739 case FIXED_TO_USER_ROTATION_DISABLED:
740 return false;
741 case FIXED_TO_USER_ROTATION_ENABLED:
742 return true;
743 default:
744 return mDefaultFixedToUserRotation;
745 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800746 }
747
Garfield Tan49dae102019-02-04 09:51:59 -0800748 /**
749 * Returns {@code true} if this display rotation takes app requested orientation into
750 * consideration; {@code false} otherwise. For the time being the only case where this is {@code
751 * false} is when {@link #isFixedToUserRotation()} is {@code true}.
752 */
753 boolean respectAppRequestedOrientation() {
Garfield Tan7fbca052019-02-19 10:45:35 -0800754 return !isFixedToUserRotation();
Garfield Tan49dae102019-02-04 09:51:59 -0800755 }
756
Riddle Hsuad256a12018-07-18 16:11:30 +0800757 public int getLandscapeRotation() {
758 return mLandscapeRotation;
759 }
760
761 public int getSeascapeRotation() {
762 return mSeascapeRotation;
763 }
764
765 public int getPortraitRotation() {
766 return mPortraitRotation;
767 }
768
769 public int getUpsideDownRotation() {
770 return mUpsideDownRotation;
771 }
772
773 public int getCurrentAppOrientation() {
774 return mCurrentAppOrientation;
775 }
776
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800777 public DisplayPolicy getDisplayPolicy() {
778 return mDisplayPolicy;
Riddle Hsuad256a12018-07-18 16:11:30 +0800779 }
780
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800781 public WindowOrientationListener getOrientationListener() {
782 return mOrientationListener;
Riddle Hsuad256a12018-07-18 16:11:30 +0800783 }
784
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800785 public int getUserRotation() {
786 return mUserRotation;
787 }
788
789 public int getUserRotationMode() {
790 return mUserRotationMode;
791 }
792
793 public void updateOrientationListener() {
794 synchronized (mLock) {
795 updateOrientationListenerLw();
Riddle Hsuad256a12018-07-18 16:11:30 +0800796 }
797 }
798
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800799 /**
Riddle Hsuccf09402019-08-13 00:33:06 +0800800 * Temporarily pauses rotation changes until resumed.
801 * <p>
802 * This can be used to prevent rotation changes from occurring while the user is performing
803 * certain operations, such as drag and drop.
804 * <p>
805 * This call nests and must be matched by an equal number of calls to {@link #resume}.
806 */
807 void pause() {
808 mDeferredRotationPauseCount++;
809 }
810
811 /** Resumes normal rotation changes after being paused. */
812 void resume() {
813 if (mDeferredRotationPauseCount <= 0) {
814 return;
815 }
816
817 mDeferredRotationPauseCount--;
818 if (mDeferredRotationPauseCount == 0) {
819 updateRotationAndSendNewConfigIfChanged();
820 }
821 }
822
823 /**
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800824 * Various use cases for invoking this function:
825 * <li>Screen turning off, should always disable listeners if already enabled.</li>
826 * <li>Screen turned on and current app has sensor based orientation, enable listeners
827 * if not already enabled.</li>
828 * <li>Screen turned on and current app does not have sensor orientation, disable listeners
829 * if already enabled.</li>
830 * <li>Screen turning on and current app has sensor based orientation, enable listeners
831 * if needed.</li>
832 * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
833 */
834 private void updateOrientationListenerLw() {
835 if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
836 // If sensor is turned off or nonexistent for some reason.
837 return;
838 }
839
840 final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
841 final boolean awake = mDisplayPolicy.isAwake();
842 final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
843 final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();
844
845 // Could have been invoked due to screen turning on or off or
846 // change of the currently visible window's orientation.
847 if (DEBUG_ORIENTATION) Slog.v(TAG, "screenOnEarly=" + screenOnEarly
848 + ", awake=" + awake + ", currentAppOrientation=" + mCurrentAppOrientation
849 + ", orientationSensorEnabled=" + mOrientationListener.mEnabled
850 + ", keyguardDrawComplete=" + keyguardDrawComplete
851 + ", windowManagerDrawComplete=" + windowManagerDrawComplete);
852
853 boolean disable = true;
854 // Note: We postpone the rotating of the screen until the keyguard as well as the
855 // window manager have reported a draw complete or the keyguard is going away in dismiss
856 // mode.
857 if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) {
858 if (needSensorRunning()) {
859 disable = false;
860 // Enable listener if not already enabled.
861 if (!mOrientationListener.mEnabled) {
862 // Don't clear the current sensor orientation if the keyguard is going away in
863 // dismiss mode. This allows window manager to use the last sensor reading to
864 // determine the orientation vs. falling back to the last known orientation if
865 // the sensor reading was cleared which can cause it to relaunch the app that
866 // will show in the wrong orientation first before correcting leading to app
867 // launch delays.
868 mOrientationListener.enable(true /* clearCurrentRotation */);
869 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800870 }
871 }
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800872 // Check if sensors need to be disabled.
873 if (disable && mOrientationListener.mEnabled) {
874 mOrientationListener.disable();
875 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800876 }
877
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800878 /**
879 * We always let the sensor be switched on by default except when
880 * the user has explicitly disabled sensor based rotation or when the
881 * screen is switched off.
882 */
883 private boolean needSensorRunning() {
Garfield Tan7fbca052019-02-19 10:45:35 -0800884 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -0800885 // We are sure we only respect user rotation settings, so we are sure we will not
886 // support sensor rotation.
887 return false;
888 }
889
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800890 if (mSupportAutoRotation) {
891 if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
892 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
893 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
894 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
895 // If the application has explicitly requested to follow the
896 // orientation, then we need to turn the sensor on.
897 return true;
898 }
899 }
900
901 final int dockMode = mDisplayPolicy.getDockMode();
902 if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
903 && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
904 || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
905 && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
906 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
907 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
908 // Enable accelerometer if we are docked in a dock that enables accelerometer
909 // orientation management.
910 return true;
911 }
912
913 if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
914 // If the setting for using the sensor by default is enabled, then
915 // we will always leave it on. Note that the user could go to
916 // a window that forces an orientation that does not use the
917 // sensor and in theory we could turn it off... however, when next
918 // turning it on we won't have a good value for the current
919 // orientation for a little bit, which can cause orientation
920 // changes to lag, so we'd like to keep it always on. (It will
921 // still be turned off when the screen is off.)
922
923 // When locked we can provide rotation suggestions users can approve to change the
924 // current screen rotation. To do this the sensor needs to be running.
925 return mSupportAutoRotation &&
926 mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
927 }
928 return mSupportAutoRotation;
929 }
930
931 /**
Riddle Hsuccf09402019-08-13 00:33:06 +0800932 * If this is true we have updated our desired orientation, but not yet changed the real
933 * orientation our applied our screen rotation animation. For example, because a previous
934 * screen rotation was in progress.
935 *
936 * @return {@code true} if the there is an ongoing rotation change.
937 */
938 boolean needsUpdate() {
939 final int oldRotation = mRotation;
940 final int rotation = rotationForOrientation(mLastOrientation, oldRotation);
941 return oldRotation != rotation;
942 }
943
944 /**
945 * Given an orientation constant, returns the appropriate surface rotation, taking into account
946 * sensors, docking mode, rotation lock, and other factors.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800947 *
948 * @param orientation An orientation constant, such as
Riddle Hsuccf09402019-08-13 00:33:06 +0800949 * {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800950 * @param lastRotation The most recently used rotation.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800951 * @return The surface rotation to use.
952 */
Riddle Hsuccf09402019-08-13 00:33:06 +0800953 @VisibleForTesting
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800954 int rotationForOrientation(int orientation, int lastRotation) {
955 if (DEBUG_ORIENTATION) {
956 Slog.v(TAG, "rotationForOrientation(orient="
957 + orientation + ", last=" + lastRotation
958 + "); user=" + mUserRotation + " "
959 + (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
960 ? "USER_ROTATION_LOCKED" : "")
961 );
962 }
963
Garfield Tan7fbca052019-02-19 10:45:35 -0800964 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -0800965 return mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800966 }
967
968 int sensorRotation = mOrientationListener != null
969 ? mOrientationListener.getProposedRotation() // may be -1
970 : -1;
971 if (sensorRotation < 0) {
972 sensorRotation = lastRotation;
973 }
974
975 final int lidState = mDisplayPolicy.getLidState();
976 final int dockMode = mDisplayPolicy.getDockMode();
977 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
978 final boolean carDockEnablesAccelerometer =
979 mDisplayPolicy.isCarDockEnablesAccelerometer();
980 final boolean deskDockEnablesAccelerometer =
981 mDisplayPolicy.isDeskDockEnablesAccelerometer();
982
983 final int preferredRotation;
984 if (!isDefaultDisplay) {
985 // For secondary displays we ignore things like displays sensors, docking mode and
Garfield Tan90c90052018-10-08 12:29:41 -0700986 // rotation lock, and always prefer user rotation.
987 preferredRotation = mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800988 } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
989 // Ignore sensor when lid switch is open and rotation is forced.
990 preferredRotation = mLidOpenRotation;
991 } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
992 && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
993 // Ignore sensor when in car dock unless explicitly enabled.
994 // This case can override the behavior of NOSENSOR, and can also
995 // enable 180 degree rotation while docked.
996 preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
997 } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
998 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
999 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
1000 && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
1001 // Ignore sensor when in desk dock unless explicitly enabled.
1002 // This case can override the behavior of NOSENSOR, and can also
1003 // enable 180 degree rotation while docked.
1004 preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
1005 } else if (hdmiPlugged && mDemoHdmiRotationLock) {
1006 // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
1007 // Note that the dock orientation overrides the HDMI orientation.
1008 preferredRotation = mDemoHdmiRotation;
1009 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1010 && mUndockedHdmiRotation >= 0) {
1011 // Ignore sensor when plugged into HDMI and an undocked orientation has
1012 // been specified in the configuration (only for legacy devices without
1013 // full multi-display support).
1014 // Note that the dock orientation overrides the HDMI orientation.
1015 preferredRotation = mUndockedHdmiRotation;
1016 } else if (mDemoRotationLock) {
1017 // Ignore sensor when demo rotation lock is enabled.
1018 // Note that the dock orientation and HDMI rotation lock override this.
1019 preferredRotation = mDemoRotation;
1020 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1021 // While in VR, apps always prefer a portrait rotation. This does not change
1022 // any apps that explicitly set landscape, but does cause sensors be ignored,
1023 // and ignored any orientation lock that the user has set (this conditional
1024 // should remain above the ORIENTATION_LOCKED conditional below).
1025 preferredRotation = mPortraitRotation;
1026 } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
1027 // Application just wants to remain locked in the last rotation.
1028 preferredRotation = lastRotation;
1029 } else if (!mSupportAutoRotation) {
1030 // If we don't support auto-rotation then bail out here and ignore
1031 // the sensor and any rotation lock settings.
1032 preferredRotation = -1;
1033 } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
1034 && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
1035 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
1036 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
1037 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
1038 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
1039 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
1040 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1041 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
1042 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
1043 // Otherwise, use sensor only if requested by the application or enabled
1044 // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
Riddle Hsuccf09402019-08-13 00:33:06 +08001045 if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
1046 // Can't read this during init() because the context doesn't have display metrics at
1047 // that time so we cannot determine tablet vs. phone then.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001048 mAllowAllRotations = mContext.getResources().getBoolean(
Riddle Hsuccf09402019-08-13 00:33:06 +08001049 R.bool.config_allowAllRotations)
1050 ? ALLOW_ALL_ROTATIONS_ENABLED
1051 : ALLOW_ALL_ROTATIONS_DISABLED;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001052 }
1053 if (sensorRotation != Surface.ROTATION_180
Riddle Hsuccf09402019-08-13 00:33:06 +08001054 || mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001055 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1056 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
1057 preferredRotation = sensorRotation;
1058 } else {
1059 preferredRotation = lastRotation;
1060 }
1061 } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
Tetsutoki Shiozawa3e595fe2019-05-07 18:27:25 +09001062 && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
1063 && orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
1064 && orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
1065 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
1066 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
1067 // Apply rotation lock. Does not apply to NOSENSOR or specific rotations.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001068 // The idea is that the user rotation expresses a weak preference for the direction
1069 // of gravity and as NOSENSOR is never affected by gravity, then neither should
1070 // NOSENSOR be affected by rotation lock (although it will be affected by docks).
Tetsutoki Shiozawa3e595fe2019-05-07 18:27:25 +09001071 // Also avoid setting user rotation when app has preference over one particular rotation
1072 // to avoid leaving the rotation to the reverse of it which has the compatible
1073 // orientation, but isn't what app wants, when the user rotation is the reverse of the
1074 // preferred rotation.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001075 preferredRotation = mUserRotation;
1076 } else {
1077 // No overriding preference.
1078 // We will do exactly what the application asked us to do.
1079 preferredRotation = -1;
1080 }
1081
Riddle Hsuad256a12018-07-18 16:11:30 +08001082 switch (orientation) {
1083 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
1084 // Return portrait unless overridden.
1085 if (isAnyPortrait(preferredRotation)) {
1086 return preferredRotation;
1087 }
1088 return mPortraitRotation;
1089
1090 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
1091 // Return landscape unless overridden.
1092 if (isLandscapeOrSeascape(preferredRotation)) {
1093 return preferredRotation;
1094 }
1095 return mLandscapeRotation;
1096
1097 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
1098 // Return reverse portrait unless overridden.
1099 if (isAnyPortrait(preferredRotation)) {
1100 return preferredRotation;
1101 }
1102 return mUpsideDownRotation;
1103
1104 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
1105 // Return seascape unless overridden.
1106 if (isLandscapeOrSeascape(preferredRotation)) {
1107 return preferredRotation;
1108 }
1109 return mSeascapeRotation;
1110
1111 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
1112 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1113 // Return either landscape rotation.
1114 if (isLandscapeOrSeascape(preferredRotation)) {
1115 return preferredRotation;
1116 }
1117 if (isLandscapeOrSeascape(lastRotation)) {
1118 return lastRotation;
1119 }
1120 return mLandscapeRotation;
1121
1122 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
1123 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1124 // Return either portrait rotation.
1125 if (isAnyPortrait(preferredRotation)) {
1126 return preferredRotation;
1127 }
1128 if (isAnyPortrait(lastRotation)) {
1129 return lastRotation;
1130 }
1131 return mPortraitRotation;
1132
1133 default:
1134 // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
1135 // just return the preferred orientation we already calculated.
1136 if (preferredRotation >= 0) {
1137 return preferredRotation;
1138 }
1139 return Surface.ROTATION_0;
1140 }
1141 }
1142
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001143 private boolean isLandscapeOrSeascape(int rotation) {
1144 return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
1145 }
1146
1147 private boolean isAnyPortrait(int rotation) {
1148 return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
1149 }
1150
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001151 private boolean isValidRotationChoice(final int preferredRotation) {
Riddle Hsuad256a12018-07-18 16:11:30 +08001152 // Determine if the given app orientation is compatible with the provided rotation choice.
1153 switch (mCurrentAppOrientation) {
1154 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1155 // Works with any of the 4 rotations.
1156 return preferredRotation >= 0;
1157
1158 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1159 // It's possible for the user pref to be set at 180 because of FULL_USER. This would
1160 // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
1161 // but never to go to 180.
1162 return preferredRotation == mPortraitRotation;
1163
1164 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1165 // Works landscape or seascape.
1166 return isLandscapeOrSeascape(preferredRotation);
1167
1168 case ActivityInfo.SCREEN_ORIENTATION_USER:
1169 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1170 // Works with any rotation except upside down.
1171 return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
1172 }
1173
1174 return false;
1175 }
1176
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001177 private boolean isRotationChoicePossible(int orientation) {
1178 // Rotation choice is only shown when the user is in locked mode.
1179 if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
1180
1181 // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
1182 // demo, hdmi, vr, etc mode.
1183
1184 // Determine if the rotation is currently forced.
Garfield Tan7fbca052019-02-19 10:45:35 -08001185 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -08001186 return false; // Rotation is forced to user settings.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001187 }
1188
1189 final int lidState = mDisplayPolicy.getLidState();
1190 if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
1191 return false; // Rotation is forced mLidOpenRotation.
1192 }
1193
1194 final int dockMode = mDisplayPolicy.getDockMode();
1195 final boolean carDockEnablesAccelerometer = false;
1196 if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
1197 return false; // Rotation forced to mCarDockRotation.
1198 }
1199
1200 final boolean deskDockEnablesAccelerometer =
1201 mDisplayPolicy.isDeskDockEnablesAccelerometer();
1202 if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
1203 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
1204 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
1205 && !deskDockEnablesAccelerometer) {
1206 return false; // Rotation forced to mDeskDockRotation.
1207 }
1208
1209 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
1210 if (hdmiPlugged && mDemoHdmiRotationLock) {
1211 return false; // Rotation forced to mDemoHdmiRotation.
1212
1213 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1214 && mUndockedHdmiRotation >= 0) {
1215 return false; // Rotation forced to mUndockedHdmiRotation.
1216
1217 } else if (mDemoRotationLock) {
1218 return false; // Rotation forced to mDemoRotation.
1219
1220 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1221 return false; // Rotation forced to mPortraitRotation.
1222
1223 } else if (!mSupportAutoRotation) {
1224 return false;
1225 }
1226
1227 // Ensure that some rotation choice is possible for the given orientation.
1228 switch (orientation) {
1229 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1230 case ActivityInfo.SCREEN_ORIENTATION_USER:
1231 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1232 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1233 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1234 // NOSENSOR description is ambiguous, in reality WM ignores user choice.
1235 return true;
1236 }
1237
1238 // Rotation is forced, should be controlled by system.
1239 return false;
Riddle Hsuad256a12018-07-18 16:11:30 +08001240 }
1241
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001242 /** Notify the StatusBar that system rotation suggestion has changed. */
1243 private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
1244 if (mStatusBarManagerInternal == null) {
1245 mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
1246 }
1247 if (mStatusBarManagerInternal != null) {
1248 mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
1249 }
1250 }
1251
1252 private static String allowAllRotationsToString(int allowAll) {
1253 switch (allowAll) {
1254 case -1:
1255 return "unknown";
1256 case 0:
1257 return "false";
1258 case 1:
1259 return "true";
1260 default:
1261 return Integer.toString(allowAll);
1262 }
1263 }
1264
1265 public void onUserSwitch() {
1266 if (mSettingsObserver != null) {
1267 mSettingsObserver.onChange(false);
1268 }
1269 }
1270
1271 /** Return whether the rotation settings has changed. */
1272 private boolean updateSettings() {
1273 final ContentResolver resolver = mContext.getContentResolver();
1274 boolean shouldUpdateRotation = false;
1275
1276 synchronized (mLock) {
1277 boolean shouldUpdateOrientationListener = false;
1278
1279 // Configure rotation suggestions.
Rajeev Kumarb2ff8e82018-08-06 11:45:05 -07001280 final int showRotationSuggestions =
1281 ActivityManager.isLowRamDeviceStatic()
1282 ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
1283 : Settings.Secure.getIntForUser(resolver,
1284 Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
1285 Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
1286 UserHandle.USER_CURRENT);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001287 if (mShowRotationSuggestions != showRotationSuggestions) {
1288 mShowRotationSuggestions = showRotationSuggestions;
1289 shouldUpdateOrientationListener = true;
1290 }
1291
1292 // Configure rotation lock.
1293 final int userRotation = Settings.System.getIntForUser(resolver,
1294 Settings.System.USER_ROTATION, Surface.ROTATION_0,
1295 UserHandle.USER_CURRENT);
1296 if (mUserRotation != userRotation) {
1297 mUserRotation = userRotation;
1298 shouldUpdateRotation = true;
1299 }
1300
1301 final int userRotationMode = Settings.System.getIntForUser(resolver,
1302 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
1303 ? WindowManagerPolicy.USER_ROTATION_FREE
1304 : WindowManagerPolicy.USER_ROTATION_LOCKED;
1305 if (mUserRotationMode != userRotationMode) {
1306 mUserRotationMode = userRotationMode;
1307 shouldUpdateOrientationListener = true;
1308 shouldUpdateRotation = true;
1309 }
1310
1311 if (shouldUpdateOrientationListener) {
1312 updateOrientationListenerLw(); // Enable or disable the orientation listener.
1313 }
1314 }
1315
1316 return shouldUpdateRotation;
Riddle Hsuad256a12018-07-18 16:11:30 +08001317 }
1318
1319 void dump(String prefix, PrintWriter pw) {
1320 pw.println(prefix + "DisplayRotation");
1321 pw.println(prefix + " mCurrentAppOrientation="
1322 + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
Riddle Hsuccf09402019-08-13 00:33:06 +08001323 pw.println(prefix + " mLastOrientation=" + mLastOrientation);
1324 pw.print(prefix + " mRotation=" + mRotation);
1325 pw.println(" mDeferredRotationPauseCount=" + mDeferredRotationPauseCount);
1326
Riddle Hsuad256a12018-07-18 16:11:30 +08001327 pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
1328 pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
1329 pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
1330 pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001331
Tiger Huang7c610aa2018-10-27 00:01:01 +08001332 pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001333 if (mOrientationListener != null) {
Tiger Huang7c610aa2018-10-27 00:01:01 +08001334 mOrientationListener.dump(pw, prefix + " ");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001335 }
1336 pw.println();
1337
1338 pw.print(prefix + " mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
1339 pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
1340 pw.print(prefix + " mUserRotationMode="
1341 + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
1342 pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
1343 pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
1344
1345 pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
1346 pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
1347 pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
1348 pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
Garfield Tan7fbca052019-02-19 10:45:35 -08001349 pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation());
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001350 }
1351
1352 private class OrientationListener extends WindowOrientationListener {
1353 final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
1354 boolean mEnabled;
1355
1356 OrientationListener(Context context, Handler handler) {
1357 super(context, handler);
1358 }
1359
1360 private class UpdateRunnable implements Runnable {
1361 final int mRotation;
1362
1363 UpdateRunnable(int rotation) {
1364 mRotation = rotation;
1365 }
1366
1367 @Override
1368 public void run() {
1369 // Send interaction hint to improve redraw performance.
1370 mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
1371 if (isRotationChoicePossible(mCurrentAppOrientation)) {
1372 final boolean isValid = isValidRotationChoice(mRotation);
1373 sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
1374 } else {
1375 mService.updateRotation(false /* alwaysSendConfiguration */,
1376 false /* forceRelayout */);
1377 }
1378 }
1379 }
1380
1381 @Override
1382 public void onProposedRotationChanged(int rotation) {
1383 if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
1384 Runnable r = mRunnableCache.get(rotation, null);
1385 if (r == null) {
1386 r = new UpdateRunnable(rotation);
1387 mRunnableCache.put(rotation, r);
1388 }
1389 getHandler().post(r);
1390 }
1391
1392 @Override
1393 public void enable(boolean clearCurrentRotation) {
1394 super.enable(clearCurrentRotation);
1395 mEnabled = true;
1396 if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
1397 }
1398
1399 @Override
1400 public void disable() {
1401 super.disable();
1402 mEnabled = false;
1403 if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
1404 }
1405 }
1406
1407 private class SettingsObserver extends ContentObserver {
1408 SettingsObserver(Handler handler) {
1409 super(handler);
1410 }
1411
1412 void observe() {
1413 final ContentResolver resolver = mContext.getContentResolver();
1414 resolver.registerContentObserver(Settings.Secure.getUriFor(
1415 Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
1416 UserHandle.USER_ALL);
1417 resolver.registerContentObserver(Settings.System.getUriFor(
1418 Settings.System.ACCELEROMETER_ROTATION), false, this,
1419 UserHandle.USER_ALL);
1420 resolver.registerContentObserver(Settings.System.getUriFor(
1421 Settings.System.USER_ROTATION), false, this,
1422 UserHandle.USER_ALL);
1423 updateSettings();
1424 }
1425
1426 @Override
1427 public void onChange(boolean selfChange) {
1428 if (updateSettings()) {
1429 mService.updateRotation(true /* alwaysSendConfiguration */,
1430 false /* forceRelayout */);
1431 }
1432 }
Riddle Hsuad256a12018-07-18 16:11:30 +08001433 }
Garfield Tanff362222018-11-14 17:52:32 -08001434
1435 @VisibleForTesting
1436 interface ContentObserverRegister {
1437 void registerContentObserver(Uri uri, boolean notifyForDescendants,
1438 ContentObserver observer, @UserIdInt int userHandle);
1439 }
Riddle Hsuad256a12018-07-18 16:11:30 +08001440}