blob: 67f1d1b217afaa9d6859b655e5f7486bae816426 [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;
Adrian Roosb125e0b2019-10-02 14:55:14 +020026import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
Riddle Hsuccf09402019-08-13 00:33:06 +080027import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080028import 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;
Adrian Roosb125e0b2019-10-02 14:55:14 +020061import com.android.server.protolog.common.ProtoLog;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080062import com.android.server.statusbar.StatusBarManagerInternal;
Riddle Hsuad256a12018-07-18 16:11:30 +080063
64import java.io.PrintWriter;
Garfield Tan7fbca052019-02-19 10:45:35 -080065import java.lang.annotation.Retention;
66import java.lang.annotation.RetentionPolicy;
Riddle Hsuad256a12018-07-18 16:11:30 +080067
68/**
69 * Defines the mapping between orientation and rotation of a display.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080070 * Non-public methods are assumed to run inside WM lock.
Riddle Hsuad256a12018-07-18 16:11:30 +080071 */
72public class DisplayRotation {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080073 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
Riddle Hsuad256a12018-07-18 16:11:30 +080074
Riddle Hsuccf09402019-08-13 00:33:06 +080075 private static class RotationAnimationPair {
76 @AnimRes
77 int mEnter;
78 @AnimRes
79 int mExit;
80 }
81
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080082 private final WindowManagerService mService;
Garfield Tan90c90052018-10-08 12:29:41 -070083 private final DisplayContent mDisplayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080084 private final DisplayPolicy mDisplayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -080085 private final DisplayWindowSettings mDisplayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080086 private final Context mContext;
87 private final Object mLock;
88
89 public final boolean isDefaultDisplay;
90 private final boolean mSupportAutoRotation;
91 private final int mLidOpenRotation;
92 private final int mCarDockRotation;
93 private final int mDeskDockRotation;
94 private final int mUndockedHdmiRotation;
Riddle Hsuccf09402019-08-13 00:33:06 +080095 private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair();
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080096
97 private OrientationListener mOrientationListener;
98 private StatusBarManagerInternal mStatusBarManagerInternal;
99 private SettingsObserver mSettingsObserver;
100
Riddle Hsuccf09402019-08-13 00:33:06 +0800101 @ScreenOrientation
102 private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
103
104 /**
105 * Last applied orientation of the display.
106 *
107 * @see #updateOrientationFromApp
108 */
109 @ScreenOrientation
110 private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
111
112 /**
113 * Current rotation of the display.
114 *
115 * @see #updateRotationUnchecked
116 */
117 @Surface.Rotation
118 private int mRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800119
120 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800121 int mLandscapeRotation; // default landscape
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800122 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800123 int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800124 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800125 int mPortraitRotation; // default portrait
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800126 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800127 int mUpsideDownRotation; // "other" portrait
128
Riddle Hsuccf09402019-08-13 00:33:06 +0800129 private boolean mAllowSeamlessRotationDespiteNavBarMoving;
130
131 private int mDeferredRotationPauseCount;
132
133 /**
134 * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
135 * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
136 * so we need to track when this hits zero so we can apply deferred orientation updates.
137 */
138 private int mSeamlessRotationCount;
139
140 /**
141 * True in the interval from starting seamless rotation until the last rotated window draws in
142 * the new orientation.
143 */
144 private boolean mRotatingSeamlessly;
145
146 /**
147 * Behavior of rotation suggestions.
148 *
149 * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
150 */
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800151 private int mShowRotationSuggestions;
152
Riddle Hsuccf09402019-08-13 00:33:06 +0800153 private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1;
154 private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0;
155 private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1;
156
157 @IntDef({ ALLOW_ALL_ROTATIONS_UNDEFINED, ALLOW_ALL_ROTATIONS_DISABLED,
158 ALLOW_ALL_ROTATIONS_ENABLED })
159 @Retention(RetentionPolicy.SOURCE)
160 private @interface AllowAllRotations {}
161
162 /**
163 * Whether to allow the screen to rotate to all rotations (including 180 degree) according to
164 * the sensor even when the current orientation is not
165 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_SENSOR} or
166 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_USER}.
167 */
168 @AllowAllRotations
169 private int mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
170
171 @WindowManagerPolicy.UserRotationMode
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800172 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
Riddle Hsuccf09402019-08-13 00:33:06 +0800173
174 @Surface.Rotation
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800175 private int mUserRotation = Surface.ROTATION_0;
176
Garfield Tanff362222018-11-14 17:52:32 -0800177 /**
Garfield Tan7fbca052019-02-19 10:45:35 -0800178 * Flag that indicates this is a display that may run better when fixed to user rotation.
179 */
180 private boolean mDefaultFixedToUserRotation;
181
182 /**
183 * No overridden behavior is provided in terms of fixing rotation to user rotation. Use other
184 * flags to derive the default behavior, such as {@link WindowManagerService#mIsPc} and
185 * {@link WindowManagerService#mForceDesktopModeOnExternalDisplays}.
186 */
187 static final int FIXED_TO_USER_ROTATION_DEFAULT = 0;
188 /**
189 * Don't fix display rotation to {@link #mUserRotation} only. Always allow other factors to play
190 * a role in deciding display rotation.
191 */
192 static final int FIXED_TO_USER_ROTATION_DISABLED = 1;
193 /**
194 * Only use {@link #mUserRotation} as the display rotation.
195 */
196 static final int FIXED_TO_USER_ROTATION_ENABLED = 2;
197 @IntDef({ FIXED_TO_USER_ROTATION_DEFAULT, FIXED_TO_USER_ROTATION_DISABLED,
198 FIXED_TO_USER_ROTATION_ENABLED })
199 @Retention(RetentionPolicy.SOURCE)
200 @interface FixedToUserRotation {}
201
202 /**
Garfield Tanff362222018-11-14 17:52:32 -0800203 * A flag to indicate if the display rotation should be fixed to user specified rotation
204 * regardless of all other states (including app requrested orientation). {@code true} the
205 * display rotation should be fixed to user specified rotation, {@code false} otherwise.
206 */
Riddle Hsuccf09402019-08-13 00:33:06 +0800207 @FixedToUserRotation
Garfield Tan7fbca052019-02-19 10:45:35 -0800208 private int mFixedToUserRotation = FIXED_TO_USER_ROTATION_DEFAULT;
Garfield Tanff362222018-11-14 17:52:32 -0800209
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800210 private int mDemoHdmiRotation;
211 private int mDemoRotation;
212 private boolean mDemoHdmiRotationLock;
213 private boolean mDemoRotationLock;
214
215 DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
216 this(service, displayContent, displayContent.getDisplayPolicy(),
Garfield Tanff362222018-11-14 17:52:32 -0800217 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
Riddle Hsuad256a12018-07-18 16:11:30 +0800218 }
219
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800220 @VisibleForTesting
221 DisplayRotation(WindowManagerService service, DisplayContent displayContent,
Garfield Tanff362222018-11-14 17:52:32 -0800222 DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
223 Context context, Object lock) {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800224 mService = service;
Garfield Tan90c90052018-10-08 12:29:41 -0700225 mDisplayContent = displayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800226 mDisplayPolicy = displayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -0800227 mDisplayWindowSettings = displayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800228 mContext = context;
229 mLock = lock;
230 isDefaultDisplay = displayContent.isDefaultDisplay;
Riddle Hsuad256a12018-07-18 16:11:30 +0800231
Riddle Hsuccf09402019-08-13 00:33:06 +0800232 mSupportAutoRotation =
233 mContext.getResources().getBoolean(R.bool.config_supportAutoRotation);
234 mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation);
235 mCarDockRotation = readRotation(R.integer.config_carDockRotation);
236 mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
237 mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800238
239 if (isDefaultDisplay) {
240 final Handler uiHandler = UiThread.getHandler();
241 mOrientationListener = new OrientationListener(mContext, uiHandler);
Riddle Hsuccf09402019-08-13 00:33:06 +0800242 mOrientationListener.setCurrentRotation(mRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800243 mSettingsObserver = new SettingsObserver(uiHandler);
244 mSettingsObserver.observe();
245 }
246 }
247
248 private int readRotation(int resID) {
249 try {
250 final int rotation = mContext.getResources().getInteger(resID);
251 switch (rotation) {
252 case 0:
253 return Surface.ROTATION_0;
254 case 90:
255 return Surface.ROTATION_90;
256 case 180:
257 return Surface.ROTATION_180;
258 case 270:
259 return Surface.ROTATION_270;
260 }
261 } catch (Resources.NotFoundException e) {
262 // fall through
263 }
264 return -1;
265 }
266
Riddle Hsuccf09402019-08-13 00:33:06 +0800267 /**
268 * Updates the configuration which may have different values depending on current user, e.g.
269 * runtime resource overlay.
270 */
271 void updateUserDependentConfiguration(Resources currentUserRes) {
272 mAllowSeamlessRotationDespiteNavBarMoving =
273 currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
274 }
275
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800276 void configure(int width, int height, int shortSizeDp, int longSizeDp) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800277 final Resources res = mContext.getResources();
278 if (width > height) {
279 mLandscapeRotation = Surface.ROTATION_0;
280 mSeascapeRotation = Surface.ROTATION_180;
Riddle Hsuccf09402019-08-13 00:33:06 +0800281 if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800282 mPortraitRotation = Surface.ROTATION_90;
283 mUpsideDownRotation = Surface.ROTATION_270;
284 } else {
285 mPortraitRotation = Surface.ROTATION_270;
286 mUpsideDownRotation = Surface.ROTATION_90;
287 }
288 } else {
289 mPortraitRotation = Surface.ROTATION_0;
290 mUpsideDownRotation = Surface.ROTATION_180;
Riddle Hsuccf09402019-08-13 00:33:06 +0800291 if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800292 mLandscapeRotation = Surface.ROTATION_270;
293 mSeascapeRotation = Surface.ROTATION_90;
294 } else {
295 mLandscapeRotation = Surface.ROTATION_90;
296 mSeascapeRotation = Surface.ROTATION_270;
297 }
298 }
299
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800300 // For demo purposes, allow the rotation of the HDMI display to be controlled.
301 // By default, HDMI locks rotation to landscape.
302 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
303 mDemoHdmiRotation = mPortraitRotation;
304 } else {
305 mDemoHdmiRotation = mLandscapeRotation;
306 }
307 mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
308
309 // For demo purposes, allow the rotation of the remote display to be controlled.
310 // By default, remote display locks rotation to landscape.
311 if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
312 mDemoRotation = mPortraitRotation;
313 } else {
314 mDemoRotation = mLandscapeRotation;
315 }
316 mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
317
Garfield Tan12b12f7a2019-02-22 16:33:27 -0800318 // It's physically impossible to rotate the car's screen.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800319 final boolean isCar = mContext.getPackageManager().hasSystemFeature(
320 PackageManager.FEATURE_AUTOMOTIVE);
Garfield Tan12b12f7a2019-02-22 16:33:27 -0800321 // It's also not likely to rotate a TV screen.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800322 final boolean isTv = mContext.getPackageManager().hasSystemFeature(
323 PackageManager.FEATURE_LEANBACK);
Garfield Tan7fbca052019-02-19 10:45:35 -0800324 final boolean forceDesktopMode =
325 mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay;
326 mDefaultFixedToUserRotation =
Tiger Huang86e6d072019-05-02 20:23:47 +0800327 (isCar || isTv || mService.mIsPc || forceDesktopMode)
Garfield Tan7fbca052019-02-19 10:45:35 -0800328 // For debug purposes the next line turns this feature off with:
329 // $ adb shell setprop config.override_forced_orient true
330 // $ adb shell wm size reset
331 && !"true".equals(SystemProperties.get("config.override_forced_orient"));
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800332 }
333
Riddle Hsuccf09402019-08-13 00:33:06 +0800334 void applyCurrentRotation(@Surface.Rotation int rotation) {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800335 if (mOrientationListener != null) {
336 mOrientationListener.setCurrentRotation(rotation);
337 }
338 }
339
Riddle Hsuccf09402019-08-13 00:33:06 +0800340 @VisibleForTesting
341 void setRotation(@Surface.Rotation int rotation) {
342 mRotation = rotation;
343 }
344
345 @Surface.Rotation
346 int getRotation() {
347 return mRotation;
348 }
349
350 @ScreenOrientation
351 int getLastOrientation() {
352 return mLastOrientation;
353 }
354
355 boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
356 if (newOrientation == mLastOrientation && !forceUpdate) {
357 return false;
358 }
359 mLastOrientation = newOrientation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800360 if (newOrientation != mCurrentAppOrientation) {
361 mCurrentAppOrientation = newOrientation;
362 if (isDefaultDisplay) {
363 updateOrientationListenerLw();
364 }
365 }
Riddle Hsuccf09402019-08-13 00:33:06 +0800366 return updateRotationUnchecked(forceUpdate);
367 }
368
369 /**
370 * Update rotation of the display and send configuration if the rotation is changed.
371 *
372 * @return {@code true} if the rotation has been changed and the new config is sent.
373 */
374 boolean updateRotationAndSendNewConfigIfChanged() {
375 final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
376 if (changed) {
377 mDisplayContent.sendNewConfiguration();
378 }
379 return changed;
380 }
381
382 /**
383 * Update rotation with an option to force the update. This updates the container's perception
384 * of rotation and, depending on the top activities, will freeze the screen or start seamless
385 * rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}
386 * during {@link DisplayContent#sendNewConfiguration}.
387 *
388 * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
389 * orientation because we're waiting for some rotation to finish or display
390 * to unfreeze, which results in configuration of the previously visible
391 * activity being applied to a newly visible one. Forcing the rotation
392 * update allows to workaround this issue.
393 * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
394 * {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE
395 * THE SCREEN.
396 */
397 boolean updateRotationUnchecked(boolean forceUpdate) {
398 final int displayId = mDisplayContent.getDisplayId();
399 if (!forceUpdate) {
400 if (mDeferredRotationPauseCount > 0) {
401 // Rotation updates have been paused temporarily. Defer the update until updates
402 // have been resumed.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200403 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");
Riddle Hsuccf09402019-08-13 00:33:06 +0800404 return false;
405 }
406
407 final ScreenRotationAnimation screenRotationAnimation =
Vadim Caenb3715832019-08-13 17:06:38 +0200408 mDisplayContent.getRotationAnimation();
Riddle Hsuccf09402019-08-13 00:33:06 +0800409 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
410 // Rotation updates cannot be performed while the previous rotation change animation
411 // is still in progress. Skip this update. We will try updating again after the
412 // animation is finished and the display is unfrozen.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200413 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress.");
Riddle Hsuccf09402019-08-13 00:33:06 +0800414 return false;
415 }
416 if (mService.mDisplayFrozen) {
417 // Even if the screen rotation animation has finished (e.g. isAnimating returns
418 // false), there is still some time where we haven't yet unfrozen the display. We
419 // also need to abort rotation here.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200420 ProtoLog.v(WM_DEBUG_ORIENTATION,
Riddle Hsuccf09402019-08-13 00:33:06 +0800421 "Deferring rotation, still finishing previous rotation");
422 return false;
423 }
424 }
425
426 if (!mService.mDisplayEnabled) {
427 // No point choosing a rotation if the display is not enabled.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200428 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled.");
Riddle Hsuccf09402019-08-13 00:33:06 +0800429 return false;
430 }
431
432 final int oldRotation = mRotation;
433 final int lastOrientation = mLastOrientation;
434 final int rotation = rotationForOrientation(lastOrientation, oldRotation);
Adrian Roosb125e0b2019-10-02 14:55:14 +0200435 ProtoLog.v(WM_DEBUG_ORIENTATION,
436 "Computed rotation=%d for display id=%d based on lastOrientation=%d and "
437 + "oldRotation=%d",
438 rotation, displayId, lastOrientation, oldRotation);
Riddle Hsuccf09402019-08-13 00:33:06 +0800439
Adrian Roosb125e0b2019-10-02 14:55:14 +0200440 ProtoLog.v(WM_DEBUG_ORIENTATION,
441 "Display id=%d selected orientation %d, got rotation %d", displayId,
442 lastOrientation, rotation);
Riddle Hsuccf09402019-08-13 00:33:06 +0800443
444 if (oldRotation == rotation) {
445 // No change.
446 return false;
447 }
448
Adrian Roosb125e0b2019-10-02 14:55:14 +0200449 ProtoLog.v(WM_DEBUG_ORIENTATION,
450 "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
451 displayId, rotation, oldRotation, lastOrientation);
Riddle Hsuccf09402019-08-13 00:33:06 +0800452
453 if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
454 mDisplayContent.mWaitingForConfig = true;
455 }
456
457 mRotation = rotation;
458
459 mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
460 mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
461 mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
462
463 mDisplayContent.setLayoutNeeded();
464
465 if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
466 // The screen rotation animation uses a screenshot to freeze the screen while windows
467 // resize underneath. When we are rotating seamlessly, we allow the elements to
468 // transition to their rotated state independently and without a freeze required.
469 prepareSeamlessRotation();
470 } else {
471 prepareNormalRotationAnimation();
472 }
473
474 return true;
475 }
476
477 void prepareNormalRotationAnimation() {
478 final RotationAnimationPair anim = selectRotationAnimation();
479 mService.startFreezingDisplayLocked(anim.mExit, anim.mEnter, mDisplayContent);
480 }
481
482 private void prepareSeamlessRotation() {
483 // We are careful to reset this in case a window was removed before it finished
484 // seamless rotation.
485 mSeamlessRotationCount = 0;
486 mRotatingSeamlessly = true;
487 }
488
489 boolean isRotatingSeamlessly() {
490 return mRotatingSeamlessly;
491 }
492
493 @VisibleForTesting
494 boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
495 final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
496 if (w == null || w != mDisplayContent.mCurrentFocus) {
497 return false;
498 }
499 // We only enable seamless rotation if the top window has requested it and is in the
500 // fullscreen opaque state. Seamless rotation requires freezing various Surface states and
501 // won't work well with animations, so we disable it in the animation case for now.
502 if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {
503 return false;
504 }
505
506 // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
507 // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
508 // will not enter the reverse portrait orientation, so actually the orientation won't change
509 // at all.
510 if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
511 return false;
512 }
513
514 // If the navigation bar can't change sides, then it will jump when we change orientations
515 // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
516 // where the navbar is low-profile enough that this isn't very noticeable.
517 if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {
518 return false;
519 }
520
521 // If the bounds of activity window is different from its parent, then reject to be seamless
522 // because the window position may change after rotation that will look like a sudden jump.
Garfield Tane8d84ab2019-10-11 09:49:40 -0700523 if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) {
Riddle Hsuccf09402019-08-13 00:33:06 +0800524 return false;
525 }
526
527 // In the presence of the PINNED stack or System Alert windows we unfortunately can not
528 // seamlessly rotate.
529 if (mDisplayContent.hasPinnedStack() || mDisplayContent.hasAlertWindowSurfaces()) {
530 return false;
531 }
532
533 // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
534 // complete (that is, waiting for windows to redraw). It's tempting to check
535 // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
536 if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
537 return false;
538 }
539
540 return true;
541 }
542
543 void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
544 if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
545 return;
546 }
547
548 w.mSeamlesslyRotated = seamlesslyRotated;
549 if (seamlesslyRotated) {
550 mSeamlessRotationCount++;
551 } else {
552 mSeamlessRotationCount--;
553 }
554 if (mSeamlessRotationCount == 0) {
Adrian Roosb125e0b2019-10-02 14:55:14 +0200555 ProtoLog.i(WM_DEBUG_ORIENTATION,
556 "Performing post-rotate rotation after seamless rotation");
Riddle Hsuccf09402019-08-13 00:33:06 +0800557 // 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.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200847 ProtoLog.v(WM_DEBUG_ORIENTATION,
848 "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, "
849 + "orientationSensorEnabled=%b, keyguardDrawComplete=%b, "
850 + "windowManagerDrawComplete=%b",
851 screenOnEarly, awake, mCurrentAppOrientation, mOrientationListener.mEnabled,
852 keyguardDrawComplete, windowManagerDrawComplete);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800853
854 boolean disable = true;
855 // Note: We postpone the rotating of the screen until the keyguard as well as the
856 // window manager have reported a draw complete or the keyguard is going away in dismiss
857 // mode.
858 if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) {
859 if (needSensorRunning()) {
860 disable = false;
861 // Enable listener if not already enabled.
862 if (!mOrientationListener.mEnabled) {
863 // Don't clear the current sensor orientation if the keyguard is going away in
864 // dismiss mode. This allows window manager to use the last sensor reading to
865 // determine the orientation vs. falling back to the last known orientation if
866 // the sensor reading was cleared which can cause it to relaunch the app that
867 // will show in the wrong orientation first before correcting leading to app
868 // launch delays.
869 mOrientationListener.enable(true /* clearCurrentRotation */);
870 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800871 }
872 }
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800873 // Check if sensors need to be disabled.
874 if (disable && mOrientationListener.mEnabled) {
875 mOrientationListener.disable();
876 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800877 }
878
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800879 /**
880 * We always let the sensor be switched on by default except when
881 * the user has explicitly disabled sensor based rotation or when the
882 * screen is switched off.
883 */
884 private boolean needSensorRunning() {
Garfield Tan7fbca052019-02-19 10:45:35 -0800885 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -0800886 // We are sure we only respect user rotation settings, so we are sure we will not
887 // support sensor rotation.
888 return false;
889 }
890
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800891 if (mSupportAutoRotation) {
892 if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
893 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
894 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
895 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
896 // If the application has explicitly requested to follow the
897 // orientation, then we need to turn the sensor on.
898 return true;
899 }
900 }
901
902 final int dockMode = mDisplayPolicy.getDockMode();
903 if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
904 && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
905 || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
906 && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
907 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
908 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
909 // Enable accelerometer if we are docked in a dock that enables accelerometer
910 // orientation management.
911 return true;
912 }
913
914 if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
915 // If the setting for using the sensor by default is enabled, then
916 // we will always leave it on. Note that the user could go to
917 // a window that forces an orientation that does not use the
918 // sensor and in theory we could turn it off... however, when next
919 // turning it on we won't have a good value for the current
920 // orientation for a little bit, which can cause orientation
921 // changes to lag, so we'd like to keep it always on. (It will
922 // still be turned off when the screen is off.)
923
924 // When locked we can provide rotation suggestions users can approve to change the
925 // current screen rotation. To do this the sensor needs to be running.
926 return mSupportAutoRotation &&
927 mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
928 }
929 return mSupportAutoRotation;
930 }
931
932 /**
Riddle Hsuccf09402019-08-13 00:33:06 +0800933 * If this is true we have updated our desired orientation, but not yet changed the real
934 * orientation our applied our screen rotation animation. For example, because a previous
935 * screen rotation was in progress.
936 *
937 * @return {@code true} if the there is an ongoing rotation change.
938 */
939 boolean needsUpdate() {
940 final int oldRotation = mRotation;
941 final int rotation = rotationForOrientation(mLastOrientation, oldRotation);
942 return oldRotation != rotation;
943 }
944
945 /**
946 * Given an orientation constant, returns the appropriate surface rotation, taking into account
947 * sensors, docking mode, rotation lock, and other factors.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800948 *
949 * @param orientation An orientation constant, such as
Riddle Hsuccf09402019-08-13 00:33:06 +0800950 * {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800951 * @param lastRotation The most recently used rotation.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800952 * @return The surface rotation to use.
953 */
Riddle Hsuccf09402019-08-13 00:33:06 +0800954 @VisibleForTesting
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800955 int rotationForOrientation(int orientation, int lastRotation) {
Adrian Roosb125e0b2019-10-02 14:55:14 +0200956 ProtoLog.v(WM_DEBUG_ORIENTATION, "rotationForOrientation(orient=%d, last=%d); user=%d %s",
957 orientation, lastRotation, mUserRotation,
958 mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
959 ? "USER_ROTATION_LOCKED" : ""
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800960 );
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800961
Garfield Tan7fbca052019-02-19 10:45:35 -0800962 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -0800963 return mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800964 }
965
966 int sensorRotation = mOrientationListener != null
967 ? mOrientationListener.getProposedRotation() // may be -1
968 : -1;
969 if (sensorRotation < 0) {
970 sensorRotation = lastRotation;
971 }
972
973 final int lidState = mDisplayPolicy.getLidState();
974 final int dockMode = mDisplayPolicy.getDockMode();
975 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
976 final boolean carDockEnablesAccelerometer =
977 mDisplayPolicy.isCarDockEnablesAccelerometer();
978 final boolean deskDockEnablesAccelerometer =
979 mDisplayPolicy.isDeskDockEnablesAccelerometer();
980
981 final int preferredRotation;
982 if (!isDefaultDisplay) {
983 // For secondary displays we ignore things like displays sensors, docking mode and
Garfield Tan90c90052018-10-08 12:29:41 -0700984 // rotation lock, and always prefer user rotation.
985 preferredRotation = mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800986 } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
987 // Ignore sensor when lid switch is open and rotation is forced.
988 preferredRotation = mLidOpenRotation;
989 } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
990 && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
991 // Ignore sensor when in car dock unless explicitly enabled.
992 // This case can override the behavior of NOSENSOR, and can also
993 // enable 180 degree rotation while docked.
994 preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
995 } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
996 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
997 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
998 && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
999 // Ignore sensor when in desk dock unless explicitly enabled.
1000 // This case can override the behavior of NOSENSOR, and can also
1001 // enable 180 degree rotation while docked.
1002 preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
1003 } else if (hdmiPlugged && mDemoHdmiRotationLock) {
1004 // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
1005 // Note that the dock orientation overrides the HDMI orientation.
1006 preferredRotation = mDemoHdmiRotation;
1007 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1008 && mUndockedHdmiRotation >= 0) {
1009 // Ignore sensor when plugged into HDMI and an undocked orientation has
1010 // been specified in the configuration (only for legacy devices without
1011 // full multi-display support).
1012 // Note that the dock orientation overrides the HDMI orientation.
1013 preferredRotation = mUndockedHdmiRotation;
1014 } else if (mDemoRotationLock) {
1015 // Ignore sensor when demo rotation lock is enabled.
1016 // Note that the dock orientation and HDMI rotation lock override this.
1017 preferredRotation = mDemoRotation;
1018 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1019 // While in VR, apps always prefer a portrait rotation. This does not change
1020 // any apps that explicitly set landscape, but does cause sensors be ignored,
1021 // and ignored any orientation lock that the user has set (this conditional
1022 // should remain above the ORIENTATION_LOCKED conditional below).
1023 preferredRotation = mPortraitRotation;
1024 } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
1025 // Application just wants to remain locked in the last rotation.
1026 preferredRotation = lastRotation;
1027 } else if (!mSupportAutoRotation) {
1028 // If we don't support auto-rotation then bail out here and ignore
1029 // the sensor and any rotation lock settings.
1030 preferredRotation = -1;
1031 } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
1032 && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
1033 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
1034 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
1035 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
1036 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
1037 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
1038 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1039 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
1040 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
1041 // Otherwise, use sensor only if requested by the application or enabled
1042 // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
Riddle Hsuccf09402019-08-13 00:33:06 +08001043 if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
1044 // Can't read this during init() because the context doesn't have display metrics at
1045 // that time so we cannot determine tablet vs. phone then.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001046 mAllowAllRotations = mContext.getResources().getBoolean(
Riddle Hsuccf09402019-08-13 00:33:06 +08001047 R.bool.config_allowAllRotations)
1048 ? ALLOW_ALL_ROTATIONS_ENABLED
1049 : ALLOW_ALL_ROTATIONS_DISABLED;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001050 }
1051 if (sensorRotation != Surface.ROTATION_180
Riddle Hsuccf09402019-08-13 00:33:06 +08001052 || mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001053 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1054 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
1055 preferredRotation = sensorRotation;
1056 } else {
1057 preferredRotation = lastRotation;
1058 }
1059 } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
Tetsutoki Shiozawa3e595fe2019-05-07 18:27:25 +09001060 && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
1061 && orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
1062 && orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
1063 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
1064 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
1065 // Apply rotation lock. Does not apply to NOSENSOR or specific rotations.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001066 // The idea is that the user rotation expresses a weak preference for the direction
1067 // of gravity and as NOSENSOR is never affected by gravity, then neither should
1068 // NOSENSOR be affected by rotation lock (although it will be affected by docks).
Tetsutoki Shiozawa3e595fe2019-05-07 18:27:25 +09001069 // Also avoid setting user rotation when app has preference over one particular rotation
1070 // to avoid leaving the rotation to the reverse of it which has the compatible
1071 // orientation, but isn't what app wants, when the user rotation is the reverse of the
1072 // preferred rotation.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001073 preferredRotation = mUserRotation;
1074 } else {
1075 // No overriding preference.
1076 // We will do exactly what the application asked us to do.
1077 preferredRotation = -1;
1078 }
1079
Riddle Hsuad256a12018-07-18 16:11:30 +08001080 switch (orientation) {
1081 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
1082 // Return portrait unless overridden.
1083 if (isAnyPortrait(preferredRotation)) {
1084 return preferredRotation;
1085 }
1086 return mPortraitRotation;
1087
1088 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
1089 // Return landscape unless overridden.
1090 if (isLandscapeOrSeascape(preferredRotation)) {
1091 return preferredRotation;
1092 }
1093 return mLandscapeRotation;
1094
1095 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
1096 // Return reverse portrait unless overridden.
1097 if (isAnyPortrait(preferredRotation)) {
1098 return preferredRotation;
1099 }
1100 return mUpsideDownRotation;
1101
1102 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
1103 // Return seascape unless overridden.
1104 if (isLandscapeOrSeascape(preferredRotation)) {
1105 return preferredRotation;
1106 }
1107 return mSeascapeRotation;
1108
1109 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
1110 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1111 // Return either landscape rotation.
1112 if (isLandscapeOrSeascape(preferredRotation)) {
1113 return preferredRotation;
1114 }
1115 if (isLandscapeOrSeascape(lastRotation)) {
1116 return lastRotation;
1117 }
1118 return mLandscapeRotation;
1119
1120 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
1121 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1122 // Return either portrait rotation.
1123 if (isAnyPortrait(preferredRotation)) {
1124 return preferredRotation;
1125 }
1126 if (isAnyPortrait(lastRotation)) {
1127 return lastRotation;
1128 }
1129 return mPortraitRotation;
1130
1131 default:
1132 // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
1133 // just return the preferred orientation we already calculated.
1134 if (preferredRotation >= 0) {
1135 return preferredRotation;
1136 }
1137 return Surface.ROTATION_0;
1138 }
1139 }
1140
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001141 private boolean isLandscapeOrSeascape(int rotation) {
1142 return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
1143 }
1144
1145 private boolean isAnyPortrait(int rotation) {
1146 return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
1147 }
1148
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001149 private boolean isValidRotationChoice(final int preferredRotation) {
Riddle Hsuad256a12018-07-18 16:11:30 +08001150 // Determine if the given app orientation is compatible with the provided rotation choice.
1151 switch (mCurrentAppOrientation) {
1152 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1153 // Works with any of the 4 rotations.
1154 return preferredRotation >= 0;
1155
1156 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1157 // It's possible for the user pref to be set at 180 because of FULL_USER. This would
1158 // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
1159 // but never to go to 180.
1160 return preferredRotation == mPortraitRotation;
1161
1162 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1163 // Works landscape or seascape.
1164 return isLandscapeOrSeascape(preferredRotation);
1165
1166 case ActivityInfo.SCREEN_ORIENTATION_USER:
1167 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1168 // Works with any rotation except upside down.
1169 return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
1170 }
1171
1172 return false;
1173 }
1174
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001175 private boolean isRotationChoicePossible(int orientation) {
1176 // Rotation choice is only shown when the user is in locked mode.
1177 if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
1178
1179 // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
1180 // demo, hdmi, vr, etc mode.
1181
1182 // Determine if the rotation is currently forced.
Garfield Tan7fbca052019-02-19 10:45:35 -08001183 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -08001184 return false; // Rotation is forced to user settings.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001185 }
1186
1187 final int lidState = mDisplayPolicy.getLidState();
1188 if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
1189 return false; // Rotation is forced mLidOpenRotation.
1190 }
1191
1192 final int dockMode = mDisplayPolicy.getDockMode();
1193 final boolean carDockEnablesAccelerometer = false;
1194 if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
1195 return false; // Rotation forced to mCarDockRotation.
1196 }
1197
1198 final boolean deskDockEnablesAccelerometer =
1199 mDisplayPolicy.isDeskDockEnablesAccelerometer();
1200 if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
1201 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
1202 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
1203 && !deskDockEnablesAccelerometer) {
1204 return false; // Rotation forced to mDeskDockRotation.
1205 }
1206
1207 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
1208 if (hdmiPlugged && mDemoHdmiRotationLock) {
1209 return false; // Rotation forced to mDemoHdmiRotation.
1210
1211 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1212 && mUndockedHdmiRotation >= 0) {
1213 return false; // Rotation forced to mUndockedHdmiRotation.
1214
1215 } else if (mDemoRotationLock) {
1216 return false; // Rotation forced to mDemoRotation.
1217
1218 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1219 return false; // Rotation forced to mPortraitRotation.
1220
1221 } else if (!mSupportAutoRotation) {
1222 return false;
1223 }
1224
1225 // Ensure that some rotation choice is possible for the given orientation.
1226 switch (orientation) {
1227 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1228 case ActivityInfo.SCREEN_ORIENTATION_USER:
1229 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1230 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1231 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1232 // NOSENSOR description is ambiguous, in reality WM ignores user choice.
1233 return true;
1234 }
1235
1236 // Rotation is forced, should be controlled by system.
1237 return false;
Riddle Hsuad256a12018-07-18 16:11:30 +08001238 }
1239
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001240 /** Notify the StatusBar that system rotation suggestion has changed. */
1241 private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
1242 if (mStatusBarManagerInternal == null) {
1243 mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
1244 }
1245 if (mStatusBarManagerInternal != null) {
1246 mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
1247 }
1248 }
1249
1250 private static String allowAllRotationsToString(int allowAll) {
1251 switch (allowAll) {
1252 case -1:
1253 return "unknown";
1254 case 0:
1255 return "false";
1256 case 1:
1257 return "true";
1258 default:
1259 return Integer.toString(allowAll);
1260 }
1261 }
1262
1263 public void onUserSwitch() {
1264 if (mSettingsObserver != null) {
1265 mSettingsObserver.onChange(false);
1266 }
1267 }
1268
1269 /** Return whether the rotation settings has changed. */
1270 private boolean updateSettings() {
1271 final ContentResolver resolver = mContext.getContentResolver();
1272 boolean shouldUpdateRotation = false;
1273
1274 synchronized (mLock) {
1275 boolean shouldUpdateOrientationListener = false;
1276
1277 // Configure rotation suggestions.
Rajeev Kumarb2ff8e82018-08-06 11:45:05 -07001278 final int showRotationSuggestions =
1279 ActivityManager.isLowRamDeviceStatic()
1280 ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
1281 : Settings.Secure.getIntForUser(resolver,
1282 Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
1283 Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
1284 UserHandle.USER_CURRENT);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001285 if (mShowRotationSuggestions != showRotationSuggestions) {
1286 mShowRotationSuggestions = showRotationSuggestions;
1287 shouldUpdateOrientationListener = true;
1288 }
1289
1290 // Configure rotation lock.
1291 final int userRotation = Settings.System.getIntForUser(resolver,
1292 Settings.System.USER_ROTATION, Surface.ROTATION_0,
1293 UserHandle.USER_CURRENT);
1294 if (mUserRotation != userRotation) {
1295 mUserRotation = userRotation;
1296 shouldUpdateRotation = true;
1297 }
1298
1299 final int userRotationMode = Settings.System.getIntForUser(resolver,
1300 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
1301 ? WindowManagerPolicy.USER_ROTATION_FREE
1302 : WindowManagerPolicy.USER_ROTATION_LOCKED;
1303 if (mUserRotationMode != userRotationMode) {
1304 mUserRotationMode = userRotationMode;
1305 shouldUpdateOrientationListener = true;
1306 shouldUpdateRotation = true;
1307 }
1308
1309 if (shouldUpdateOrientationListener) {
1310 updateOrientationListenerLw(); // Enable or disable the orientation listener.
1311 }
1312 }
1313
1314 return shouldUpdateRotation;
Riddle Hsuad256a12018-07-18 16:11:30 +08001315 }
1316
1317 void dump(String prefix, PrintWriter pw) {
1318 pw.println(prefix + "DisplayRotation");
1319 pw.println(prefix + " mCurrentAppOrientation="
1320 + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
Riddle Hsuccf09402019-08-13 00:33:06 +08001321 pw.println(prefix + " mLastOrientation=" + mLastOrientation);
1322 pw.print(prefix + " mRotation=" + mRotation);
1323 pw.println(" mDeferredRotationPauseCount=" + mDeferredRotationPauseCount);
1324
Riddle Hsuad256a12018-07-18 16:11:30 +08001325 pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
1326 pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
1327 pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
1328 pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001329
Tiger Huang7c610aa2018-10-27 00:01:01 +08001330 pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001331 if (mOrientationListener != null) {
Tiger Huang7c610aa2018-10-27 00:01:01 +08001332 mOrientationListener.dump(pw, prefix + " ");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001333 }
1334 pw.println();
1335
1336 pw.print(prefix + " mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
1337 pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
1338 pw.print(prefix + " mUserRotationMode="
1339 + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
1340 pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
1341 pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
1342
1343 pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
1344 pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
1345 pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
1346 pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
Garfield Tan7fbca052019-02-19 10:45:35 -08001347 pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation());
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001348 }
1349
1350 private class OrientationListener extends WindowOrientationListener {
1351 final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
1352 boolean mEnabled;
1353
1354 OrientationListener(Context context, Handler handler) {
1355 super(context, handler);
1356 }
1357
1358 private class UpdateRunnable implements Runnable {
1359 final int mRotation;
1360
1361 UpdateRunnable(int rotation) {
1362 mRotation = rotation;
1363 }
1364
1365 @Override
1366 public void run() {
1367 // Send interaction hint to improve redraw performance.
1368 mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
1369 if (isRotationChoicePossible(mCurrentAppOrientation)) {
1370 final boolean isValid = isValidRotationChoice(mRotation);
1371 sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
1372 } else {
1373 mService.updateRotation(false /* alwaysSendConfiguration */,
1374 false /* forceRelayout */);
1375 }
1376 }
1377 }
1378
1379 @Override
1380 public void onProposedRotationChanged(int rotation) {
Adrian Roosb125e0b2019-10-02 14:55:14 +02001381 ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001382 Runnable r = mRunnableCache.get(rotation, null);
1383 if (r == null) {
1384 r = new UpdateRunnable(rotation);
1385 mRunnableCache.put(rotation, r);
1386 }
1387 getHandler().post(r);
1388 }
1389
1390 @Override
1391 public void enable(boolean clearCurrentRotation) {
1392 super.enable(clearCurrentRotation);
1393 mEnabled = true;
Adrian Roosb125e0b2019-10-02 14:55:14 +02001394 ProtoLog.v(WM_DEBUG_ORIENTATION, "Enabling listeners");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001395 }
1396
1397 @Override
1398 public void disable() {
1399 super.disable();
1400 mEnabled = false;
Adrian Roosb125e0b2019-10-02 14:55:14 +02001401 ProtoLog.v(WM_DEBUG_ORIENTATION, "Disabling listeners");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001402 }
1403 }
1404
1405 private class SettingsObserver extends ContentObserver {
1406 SettingsObserver(Handler handler) {
1407 super(handler);
1408 }
1409
1410 void observe() {
1411 final ContentResolver resolver = mContext.getContentResolver();
1412 resolver.registerContentObserver(Settings.Secure.getUriFor(
1413 Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
1414 UserHandle.USER_ALL);
1415 resolver.registerContentObserver(Settings.System.getUriFor(
1416 Settings.System.ACCELEROMETER_ROTATION), false, this,
1417 UserHandle.USER_ALL);
1418 resolver.registerContentObserver(Settings.System.getUriFor(
1419 Settings.System.USER_ROTATION), false, this,
1420 UserHandle.USER_ALL);
1421 updateSettings();
1422 }
1423
1424 @Override
1425 public void onChange(boolean selfChange) {
1426 if (updateSettings()) {
1427 mService.updateRotation(true /* alwaysSendConfiguration */,
1428 false /* forceRelayout */);
1429 }
1430 }
Riddle Hsuad256a12018-07-18 16:11:30 +08001431 }
Garfield Tanff362222018-11-14 17:52:32 -08001432
1433 @VisibleForTesting
1434 interface ContentObserverRegister {
1435 void registerContentObserver(Uri uri, boolean notifyForDescendants,
1436 ContentObserver observer, @UserIdInt int userHandle);
1437 }
Riddle Hsuad256a12018-07-18 16:11:30 +08001438}