blob: c3f906135a00407262408723c776b47801ff5203 [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;
Evan Rosky69cace42019-09-20 16:28:13 -070048import android.os.RemoteException;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080049import android.os.SystemProperties;
50import android.os.UserHandle;
51import android.provider.Settings;
52import android.util.Slog;
53import android.util.SparseArray;
Evan Rosky69cace42019-09-20 16:28:13 -070054import android.view.IDisplayWindowRotationCallback;
Garfield Tana3f19032019-11-19 18:04:50 -080055import android.view.IWindowManager;
Riddle Hsuad256a12018-07-18 16:11:30 +080056import android.view.Surface;
Wale Ogunwale57946582020-03-21 14:29:07 -070057import android.window.WindowContainerTransaction;
Riddle Hsuad256a12018-07-18 16:11:30 +080058
Riddle Hsuccf09402019-08-13 00:33:06 +080059import com.android.internal.R;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080060import com.android.internal.annotations.VisibleForTesting;
Evan Rosky69cace42019-09-20 16:28:13 -070061import com.android.internal.util.function.pooled.PooledLambda;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080062import com.android.server.LocalServices;
63import com.android.server.UiThread;
Riddle Hsuad256a12018-07-18 16:11:30 +080064import com.android.server.policy.WindowManagerPolicy;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080065import com.android.server.policy.WindowOrientationListener;
Adrian Roosb125e0b2019-10-02 14:55:14 +020066import com.android.server.protolog.common.ProtoLog;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080067import com.android.server.statusbar.StatusBarManagerInternal;
Riddle Hsuad256a12018-07-18 16:11:30 +080068
69import java.io.PrintWriter;
Garfield Tan7fbca052019-02-19 10:45:35 -080070import java.lang.annotation.Retention;
71import java.lang.annotation.RetentionPolicy;
Riddle Hsuad256a12018-07-18 16:11:30 +080072
73/**
74 * Defines the mapping between orientation and rotation of a display.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080075 * Non-public methods are assumed to run inside WM lock.
Riddle Hsuad256a12018-07-18 16:11:30 +080076 */
77public class DisplayRotation {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080078 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
Riddle Hsuad256a12018-07-18 16:11:30 +080079
Riddle Hsuccf09402019-08-13 00:33:06 +080080 private static class RotationAnimationPair {
81 @AnimRes
82 int mEnter;
83 @AnimRes
84 int mExit;
85 }
86
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080087 private final WindowManagerService mService;
Garfield Tan90c90052018-10-08 12:29:41 -070088 private final DisplayContent mDisplayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080089 private final DisplayPolicy mDisplayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -080090 private final DisplayWindowSettings mDisplayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080091 private final Context mContext;
92 private final Object mLock;
93
94 public final boolean isDefaultDisplay;
95 private final boolean mSupportAutoRotation;
96 private final int mLidOpenRotation;
97 private final int mCarDockRotation;
98 private final int mDeskDockRotation;
99 private final int mUndockedHdmiRotation;
Riddle Hsuccf09402019-08-13 00:33:06 +0800100 private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair();
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800101
102 private OrientationListener mOrientationListener;
103 private StatusBarManagerInternal mStatusBarManagerInternal;
104 private SettingsObserver mSettingsObserver;
105
Riddle Hsuccf09402019-08-13 00:33:06 +0800106 @ScreenOrientation
107 private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
108
109 /**
110 * Last applied orientation of the display.
111 *
112 * @see #updateOrientationFromApp
113 */
114 @ScreenOrientation
115 private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
116
117 /**
118 * Current rotation of the display.
119 *
120 * @see #updateRotationUnchecked
121 */
122 @Surface.Rotation
123 private int mRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800124
125 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800126 int mLandscapeRotation; // default landscape
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800127 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800128 int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800129 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800130 int mPortraitRotation; // default portrait
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800131 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +0800132 int mUpsideDownRotation; // "other" portrait
133
Riddle Hsuccf09402019-08-13 00:33:06 +0800134 private boolean mAllowSeamlessRotationDespiteNavBarMoving;
135
136 private int mDeferredRotationPauseCount;
137
138 /**
139 * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
140 * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
141 * so we need to track when this hits zero so we can apply deferred orientation updates.
142 */
143 private int mSeamlessRotationCount;
144
145 /**
146 * True in the interval from starting seamless rotation until the last rotated window draws in
147 * the new orientation.
148 */
149 private boolean mRotatingSeamlessly;
150
151 /**
152 * Behavior of rotation suggestions.
153 *
154 * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
155 */
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800156 private int mShowRotationSuggestions;
157
Riddle Hsuccf09402019-08-13 00:33:06 +0800158 private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1;
159 private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0;
160 private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1;
161
162 @IntDef({ ALLOW_ALL_ROTATIONS_UNDEFINED, ALLOW_ALL_ROTATIONS_DISABLED,
163 ALLOW_ALL_ROTATIONS_ENABLED })
164 @Retention(RetentionPolicy.SOURCE)
165 private @interface AllowAllRotations {}
166
167 /**
168 * Whether to allow the screen to rotate to all rotations (including 180 degree) according to
169 * the sensor even when the current orientation is not
170 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_SENSOR} or
171 * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_USER}.
172 */
173 @AllowAllRotations
174 private int mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
175
176 @WindowManagerPolicy.UserRotationMode
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800177 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
Riddle Hsuccf09402019-08-13 00:33:06 +0800178
179 @Surface.Rotation
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800180 private int mUserRotation = Surface.ROTATION_0;
181
Garfield Tanff362222018-11-14 17:52:32 -0800182 /**
Garfield Tan7fbca052019-02-19 10:45:35 -0800183 * Flag that indicates this is a display that may run better when fixed to user rotation.
184 */
185 private boolean mDefaultFixedToUserRotation;
186
187 /**
Garfield Tanff362222018-11-14 17:52:32 -0800188 * A flag to indicate if the display rotation should be fixed to user specified rotation
189 * regardless of all other states (including app requrested orientation). {@code true} the
190 * display rotation should be fixed to user specified rotation, {@code false} otherwise.
191 */
Garfield Tana3f19032019-11-19 18:04:50 -0800192 private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
Garfield Tanff362222018-11-14 17:52:32 -0800193
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800194 private int mDemoHdmiRotation;
195 private int mDemoRotation;
196 private boolean mDemoHdmiRotationLock;
197 private boolean mDemoRotationLock;
198
Evan Rosky69cace42019-09-20 16:28:13 -0700199 private static final int REMOTE_ROTATION_TIMEOUT_MS = 800;
200
201 private boolean mIsWaitingForRemoteRotation = false;
202
203 private final Runnable mDisplayRotationHandlerTimeout =
204 new Runnable() {
205 @Override
206 public void run() {
207 continueRotation(mRotation, null /* transaction */);
208 }
209 };
210
211 private final IDisplayWindowRotationCallback mRemoteRotationCallback =
212 new IDisplayWindowRotationCallback.Stub() {
213 @Override
214 public void continueRotateDisplay(int targetRotation,
215 WindowContainerTransaction t) {
216 synchronized (mService.getWindowManagerLock()) {
217 mService.mH.sendMessage(PooledLambda.obtainMessage(
218 DisplayRotation::continueRotation, DisplayRotation.this,
219 targetRotation, t));
220 }
221 }
222 };
223
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800224 DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
225 this(service, displayContent, displayContent.getDisplayPolicy(),
Garfield Tanff362222018-11-14 17:52:32 -0800226 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
Riddle Hsuad256a12018-07-18 16:11:30 +0800227 }
228
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800229 @VisibleForTesting
230 DisplayRotation(WindowManagerService service, DisplayContent displayContent,
Garfield Tanff362222018-11-14 17:52:32 -0800231 DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
232 Context context, Object lock) {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800233 mService = service;
Garfield Tan90c90052018-10-08 12:29:41 -0700234 mDisplayContent = displayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800235 mDisplayPolicy = displayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -0800236 mDisplayWindowSettings = displayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800237 mContext = context;
238 mLock = lock;
239 isDefaultDisplay = displayContent.isDefaultDisplay;
Riddle Hsuad256a12018-07-18 16:11:30 +0800240
Riddle Hsuccf09402019-08-13 00:33:06 +0800241 mSupportAutoRotation =
242 mContext.getResources().getBoolean(R.bool.config_supportAutoRotation);
243 mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation);
244 mCarDockRotation = readRotation(R.integer.config_carDockRotation);
245 mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
246 mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800247
248 if (isDefaultDisplay) {
249 final Handler uiHandler = UiThread.getHandler();
250 mOrientationListener = new OrientationListener(mContext, uiHandler);
Riddle Hsuccf09402019-08-13 00:33:06 +0800251 mOrientationListener.setCurrentRotation(mRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800252 mSettingsObserver = new SettingsObserver(uiHandler);
253 mSettingsObserver.observe();
254 }
255 }
256
257 private int readRotation(int resID) {
258 try {
259 final int rotation = mContext.getResources().getInteger(resID);
260 switch (rotation) {
261 case 0:
262 return Surface.ROTATION_0;
263 case 90:
264 return Surface.ROTATION_90;
265 case 180:
266 return Surface.ROTATION_180;
267 case 270:
268 return Surface.ROTATION_270;
269 }
270 } catch (Resources.NotFoundException e) {
271 // fall through
272 }
273 return -1;
274 }
275
Riddle Hsuccf09402019-08-13 00:33:06 +0800276 /**
277 * Updates the configuration which may have different values depending on current user, e.g.
278 * runtime resource overlay.
279 */
280 void updateUserDependentConfiguration(Resources currentUserRes) {
281 mAllowSeamlessRotationDespiteNavBarMoving =
282 currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
283 }
284
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800285 void configure(int width, int height, int shortSizeDp, int longSizeDp) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800286 final Resources res = mContext.getResources();
287 if (width > height) {
288 mLandscapeRotation = Surface.ROTATION_0;
289 mSeascapeRotation = 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 mPortraitRotation = Surface.ROTATION_90;
292 mUpsideDownRotation = Surface.ROTATION_270;
293 } else {
294 mPortraitRotation = Surface.ROTATION_270;
295 mUpsideDownRotation = Surface.ROTATION_90;
296 }
297 } else {
298 mPortraitRotation = Surface.ROTATION_0;
299 mUpsideDownRotation = Surface.ROTATION_180;
Riddle Hsuccf09402019-08-13 00:33:06 +0800300 if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800301 mLandscapeRotation = Surface.ROTATION_270;
302 mSeascapeRotation = Surface.ROTATION_90;
303 } else {
304 mLandscapeRotation = Surface.ROTATION_90;
305 mSeascapeRotation = Surface.ROTATION_270;
306 }
307 }
308
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800309 // For demo purposes, allow the rotation of the HDMI display to be controlled.
310 // By default, HDMI locks rotation to landscape.
311 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
312 mDemoHdmiRotation = mPortraitRotation;
313 } else {
314 mDemoHdmiRotation = mLandscapeRotation;
315 }
316 mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
317
318 // For demo purposes, allow the rotation of the remote display to be controlled.
319 // By default, remote display locks rotation to landscape.
320 if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
321 mDemoRotation = mPortraitRotation;
322 } else {
323 mDemoRotation = mLandscapeRotation;
324 }
325 mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
326
Garfield Tan12b12f7a2019-02-22 16:33:27 -0800327 // It's physically impossible to rotate the car's screen.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800328 final boolean isCar = mContext.getPackageManager().hasSystemFeature(
329 PackageManager.FEATURE_AUTOMOTIVE);
Garfield Tan12b12f7a2019-02-22 16:33:27 -0800330 // It's also not likely to rotate a TV screen.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800331 final boolean isTv = mContext.getPackageManager().hasSystemFeature(
332 PackageManager.FEATURE_LEANBACK);
Garfield Tan7fbca052019-02-19 10:45:35 -0800333 final boolean forceDesktopMode =
334 mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay;
335 mDefaultFixedToUserRotation =
Tiger Huang86e6d072019-05-02 20:23:47 +0800336 (isCar || isTv || mService.mIsPc || forceDesktopMode)
Garfield Tan7fbca052019-02-19 10:45:35 -0800337 // For debug purposes the next line turns this feature off with:
338 // $ adb shell setprop config.override_forced_orient true
339 // $ adb shell wm size reset
340 && !"true".equals(SystemProperties.get("config.override_forced_orient"));
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800341 }
342
Riddle Hsuccf09402019-08-13 00:33:06 +0800343 void applyCurrentRotation(@Surface.Rotation int rotation) {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800344 if (mOrientationListener != null) {
345 mOrientationListener.setCurrentRotation(rotation);
346 }
347 }
348
Riddle Hsuccf09402019-08-13 00:33:06 +0800349 @VisibleForTesting
350 void setRotation(@Surface.Rotation int rotation) {
351 mRotation = rotation;
352 }
353
354 @Surface.Rotation
355 int getRotation() {
356 return mRotation;
357 }
358
359 @ScreenOrientation
360 int getLastOrientation() {
361 return mLastOrientation;
362 }
363
364 boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
365 if (newOrientation == mLastOrientation && !forceUpdate) {
366 return false;
367 }
368 mLastOrientation = newOrientation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800369 if (newOrientation != mCurrentAppOrientation) {
370 mCurrentAppOrientation = newOrientation;
371 if (isDefaultDisplay) {
372 updateOrientationListenerLw();
373 }
374 }
Riddle Hsuccf09402019-08-13 00:33:06 +0800375 return updateRotationUnchecked(forceUpdate);
376 }
377
378 /**
379 * Update rotation of the display and send configuration if the rotation is changed.
380 *
381 * @return {@code true} if the rotation has been changed and the new config is sent.
382 */
383 boolean updateRotationAndSendNewConfigIfChanged() {
384 final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
385 if (changed) {
386 mDisplayContent.sendNewConfiguration();
387 }
388 return changed;
389 }
390
391 /**
392 * Update rotation with an option to force the update. This updates the container's perception
393 * of rotation and, depending on the top activities, will freeze the screen or start seamless
394 * rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}
395 * during {@link DisplayContent#sendNewConfiguration}.
396 *
397 * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
398 * orientation because we're waiting for some rotation to finish or display
399 * to unfreeze, which results in configuration of the previously visible
400 * activity being applied to a newly visible one. Forcing the rotation
401 * update allows to workaround this issue.
402 * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
403 * {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE
404 * THE SCREEN.
405 */
406 boolean updateRotationUnchecked(boolean forceUpdate) {
407 final int displayId = mDisplayContent.getDisplayId();
408 if (!forceUpdate) {
409 if (mDeferredRotationPauseCount > 0) {
410 // Rotation updates have been paused temporarily. Defer the update until updates
411 // have been resumed.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200412 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");
Riddle Hsuccf09402019-08-13 00:33:06 +0800413 return false;
414 }
415
416 final ScreenRotationAnimation screenRotationAnimation =
Vadim Caenb3715832019-08-13 17:06:38 +0200417 mDisplayContent.getRotationAnimation();
Riddle Hsuccf09402019-08-13 00:33:06 +0800418 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
419 // Rotation updates cannot be performed while the previous rotation change animation
420 // is still in progress. Skip this update. We will try updating again after the
421 // animation is finished and the display is unfrozen.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200422 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress.");
Riddle Hsuccf09402019-08-13 00:33:06 +0800423 return false;
424 }
425 if (mService.mDisplayFrozen) {
426 // Even if the screen rotation animation has finished (e.g. isAnimating returns
427 // false), there is still some time where we haven't yet unfrozen the display. We
428 // also need to abort rotation here.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200429 ProtoLog.v(WM_DEBUG_ORIENTATION,
Riddle Hsuccf09402019-08-13 00:33:06 +0800430 "Deferring rotation, still finishing previous rotation");
431 return false;
432 }
433 }
434
435 if (!mService.mDisplayEnabled) {
436 // No point choosing a rotation if the display is not enabled.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200437 ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled.");
Riddle Hsuccf09402019-08-13 00:33:06 +0800438 return false;
439 }
440
441 final int oldRotation = mRotation;
442 final int lastOrientation = mLastOrientation;
443 final int rotation = rotationForOrientation(lastOrientation, oldRotation);
Adrian Roosb125e0b2019-10-02 14:55:14 +0200444 ProtoLog.v(WM_DEBUG_ORIENTATION,
Vadim Caen1096f112019-12-18 11:44:26 +0100445 "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and "
446 + "oldRotation=%s (%d)",
447 Surface.rotationToString(rotation), rotation,
448 displayId,
449 ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
450 Surface.rotationToString(oldRotation), oldRotation);
Riddle Hsuccf09402019-08-13 00:33:06 +0800451
Adrian Roosb125e0b2019-10-02 14:55:14 +0200452 ProtoLog.v(WM_DEBUG_ORIENTATION,
Vadim Caen1096f112019-12-18 11:44:26 +0100453 "Display id=%d selected orientation %s (%d), got rotation %s (%d)", displayId,
454 ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
455 Surface.rotationToString(rotation), rotation);
Riddle Hsuccf09402019-08-13 00:33:06 +0800456
457 if (oldRotation == rotation) {
458 // No change.
459 return false;
460 }
461
Adrian Roosb125e0b2019-10-02 14:55:14 +0200462 ProtoLog.v(WM_DEBUG_ORIENTATION,
463 "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
464 displayId, rotation, oldRotation, lastOrientation);
Riddle Hsuccf09402019-08-13 00:33:06 +0800465
466 if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
467 mDisplayContent.mWaitingForConfig = true;
468 }
469
470 mRotation = rotation;
471
472 mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
473 mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
474 mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
475
476 mDisplayContent.setLayoutNeeded();
477
478 if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
479 // The screen rotation animation uses a screenshot to freeze the screen while windows
480 // resize underneath. When we are rotating seamlessly, we allow the elements to
481 // transition to their rotated state independently and without a freeze required.
482 prepareSeamlessRotation();
483 } else {
484 prepareNormalRotationAnimation();
485 }
486
Riddle Hsu06aeb062020-03-24 23:38:40 +0800487 // Give a remote handler (system ui) some time to reposition things.
488 startRemoteRotation(oldRotation, mRotation);
Evan Rosky69cace42019-09-20 16:28:13 -0700489
Riddle Hsuccf09402019-08-13 00:33:06 +0800490 return true;
491 }
492
Evan Rosky69cace42019-09-20 16:28:13 -0700493 /**
494 * A Remote rotation is when we are waiting for some registered (remote)
495 * {@link IDisplayWindowRotationController} to calculate and return some hierarchy operations
496 * to perform in sync with the rotation.
497 */
498 boolean isWaitingForRemoteRotation() {
499 return mIsWaitingForRemoteRotation;
500 }
501
502 private void startRemoteRotation(int fromRotation, int toRotation) {
503 if (mService.mDisplayRotationController == null) {
504 return;
505 }
506 mIsWaitingForRemoteRotation = true;
507 try {
508 mService.mDisplayRotationController.onRotateDisplay(mDisplayContent.getDisplayId(),
509 fromRotation, toRotation, mRemoteRotationCallback);
510 mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
511 mService.mH.postDelayed(mDisplayRotationHandlerTimeout, REMOTE_ROTATION_TIMEOUT_MS);
512 } catch (RemoteException e) {
513 mIsWaitingForRemoteRotation = false;
514 return;
515 }
516 }
517
518 private void continueRotation(int targetRotation, WindowContainerTransaction t) {
519 synchronized (mService.mGlobalLock) {
520 if (targetRotation != mRotation || !mIsWaitingForRemoteRotation) {
521 // Drop it, this is either coming from an outdated remote rotation; or, we've
522 // already moved on.
523 return;
524 }
525 mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
526 mIsWaitingForRemoteRotation = false;
Evan Roskyaf9f27c2020-02-18 18:58:35 +0000527 mService.mAtmService.deferWindowLayout();
528 try {
529 mDisplayContent.sendNewConfiguration();
530 if (t != null) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700531 mService.mAtmService.mWindowOrganizerController.applyTransaction(t);
Evan Roskyaf9f27c2020-02-18 18:58:35 +0000532 }
533 } finally {
534 mService.mAtmService.continueWindowLayout();
Robert Carre10ee3d2019-11-11 15:03:15 -0800535 }
Evan Rosky69cace42019-09-20 16:28:13 -0700536 }
537 }
538
Riddle Hsuccf09402019-08-13 00:33:06 +0800539 void prepareNormalRotationAnimation() {
Riddle Hsud16620e2020-04-06 20:48:06 +0800540 cancelSeamlessRotation();
Riddle Hsuccf09402019-08-13 00:33:06 +0800541 final RotationAnimationPair anim = selectRotationAnimation();
Riddle Hsud16620e2020-04-06 20:48:06 +0800542 mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
543 }
544
545 /**
546 * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
547 * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
548 * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
549 * and it doesn't choose seamless rotation.
550 */
551 void cancelSeamlessRotation() {
552 if (!mRotatingSeamlessly) {
553 return;
554 }
555 mDisplayContent.forAllWindows(w -> {
556 if (w.mSeamlesslyRotated) {
557 w.finishSeamlessRotation(false /* timeout */);
558 w.mSeamlesslyRotated = false;
559 }
560 }, true /* traverseTopToBottom */);
561 mSeamlessRotationCount = 0;
562 mRotatingSeamlessly = false;
Vadim Caendca5b932020-04-23 18:26:50 +0200563 mDisplayContent.finishFixedRotationAnimation();
Riddle Hsuccf09402019-08-13 00:33:06 +0800564 }
565
566 private void prepareSeamlessRotation() {
567 // We are careful to reset this in case a window was removed before it finished
568 // seamless rotation.
569 mSeamlessRotationCount = 0;
570 mRotatingSeamlessly = true;
571 }
572
573 boolean isRotatingSeamlessly() {
574 return mRotatingSeamlessly;
575 }
576
577 @VisibleForTesting
578 boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
Riddle Hsu6f548e92020-01-13 13:34:09 +0800579 // Display doesn't need to be frozen because application has been started in correct
580 // rotation already, so the rest of the windows can use seamless rotation.
Riddle Hsu9239d7c62020-02-20 01:35:56 +0800581 if (mDisplayContent.mFixedRotationLaunchingApp != null) {
Riddle Hsu6f548e92020-01-13 13:34:09 +0800582 return true;
583 }
Riddle Hsu9239d7c62020-02-20 01:35:56 +0800584
585 final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
586 if (w == null || w != mDisplayContent.mCurrentFocus) {
Riddle Hsuccf09402019-08-13 00:33:06 +0800587 return false;
588 }
589 // We only enable seamless rotation if the top window has requested it and is in the
590 // fullscreen opaque state. Seamless rotation requires freezing various Surface states and
591 // won't work well with animations, so we disable it in the animation case for now.
592 if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {
593 return false;
594 }
595
596 // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
597 // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
598 // will not enter the reverse portrait orientation, so actually the orientation won't change
599 // at all.
600 if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
601 return false;
602 }
603
604 // If the navigation bar can't change sides, then it will jump when we change orientations
605 // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
606 // where the navbar is low-profile enough that this isn't very noticeable.
607 if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {
608 return false;
609 }
610
611 // If the bounds of activity window is different from its parent, then reject to be seamless
612 // because the window position may change after rotation that will look like a sudden jump.
Garfield Tane8d84ab2019-10-11 09:49:40 -0700613 if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) {
Riddle Hsuccf09402019-08-13 00:33:06 +0800614 return false;
615 }
616
617 // In the presence of the PINNED stack or System Alert windows we unfortunately can not
618 // seamlessly rotate.
Andrii Kulian4c0fd0d2020-03-29 13:32:14 -0700619 if (mDisplayContent.getDefaultTaskDisplayArea().hasPinnedTask()
620 || mDisplayContent.hasAlertWindowSurfaces()) {
Riddle Hsuccf09402019-08-13 00:33:06 +0800621 return false;
622 }
623
624 // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
625 // complete (that is, waiting for windows to redraw). It's tempting to check
626 // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
627 if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
628 return false;
629 }
630
631 return true;
632 }
633
634 void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
635 if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
636 return;
637 }
638
639 w.mSeamlesslyRotated = seamlesslyRotated;
640 if (seamlesslyRotated) {
641 mSeamlessRotationCount++;
642 } else {
643 mSeamlessRotationCount--;
644 }
645 if (mSeamlessRotationCount == 0) {
Adrian Roosb125e0b2019-10-02 14:55:14 +0200646 ProtoLog.i(WM_DEBUG_ORIENTATION,
647 "Performing post-rotate rotation after seamless rotation");
Riddle Hsuccf09402019-08-13 00:33:06 +0800648 // Finish seamless rotation.
649 mRotatingSeamlessly = false;
Vadim Caendca5b932020-04-23 18:26:50 +0200650 mDisplayContent.finishFixedRotationAnimation();
Riddle Hsuccf09402019-08-13 00:33:06 +0800651
652 updateRotationAndSendNewConfigIfChanged();
653 }
654 }
655
656 void onSeamlessRotationTimeout() {
657 final boolean[] isLayoutNeeded = { false };
658
659 mDisplayContent.forAllWindows(w -> {
660 if (!w.mSeamlesslyRotated) {
661 return;
662 }
663 isLayoutNeeded[0] = true;
664 w.setDisplayLayoutNeeded();
665 w.finishSeamlessRotation(true /* timeout */);
666 markForSeamlessRotation(w, false /* seamlesslyRotated */);
667 }, true /* traverseTopToBottom */);
668
669 if (isLayoutNeeded[0]) {
670 mService.mWindowPlacerLocked.performSurfacePlacement();
671 }
672 }
673
674 /**
675 * Returns the animation to run for a rotation transition based on the top fullscreen windows
676 * {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently
677 * fullscreen and frontmost.
678 */
679 private RotationAnimationPair selectRotationAnimation() {
680 // If the screen is off or non-interactive, force a jumpcut.
681 final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully()
682 || !mService.mPolicy.okToAnimate();
683 final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow();
684 if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation topFullscreen="
685 + topFullscreen + " rotationAnimation="
686 + (topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation)
687 + " forceJumpcut=" + forceJumpcut);
688 if (forceJumpcut) {
689 mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
690 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
691 return mTmpRotationAnim;
692 }
693 if (topFullscreen != null) {
694 int animationHint = topFullscreen.getRotationAnimationHint();
695 if (animationHint < 0 && mDisplayPolicy.isTopLayoutFullscreen()) {
696 animationHint = topFullscreen.getAttrs().rotationAnimation;
697 }
698 switch (animationHint) {
699 case ROTATION_ANIMATION_CROSSFADE:
700 case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
701 mTmpRotationAnim.mExit = R.anim.rotation_animation_xfade_exit;
702 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
703 break;
704 case ROTATION_ANIMATION_JUMPCUT:
705 mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
706 mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
707 break;
708 case ROTATION_ANIMATION_ROTATE:
709 default:
710 mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
711 break;
712 }
713 } else {
714 mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
715 }
716 return mTmpRotationAnim;
717 }
718
719 /**
720 * Validate whether the current top fullscreen has specified the same
721 * {@link android.view.WindowManager.LayoutParams#rotationAnimation} value as that being passed
722 * in from the previous top fullscreen window.
723 *
724 * @param exitAnimId exiting resource id from the previous window.
725 * @param enterAnimId entering resource id from the previous window.
726 * @param forceDefault For rotation animations only, if true ignore the animation values and
727 * just return false.
728 * @return {@code true} if the previous values are still valid, false if they should be replaced
729 * with the default.
730 */
731 boolean validateRotationAnimation(int exitAnimId, int enterAnimId, boolean forceDefault) {
732 switch (exitAnimId) {
733 case R.anim.rotation_animation_xfade_exit:
734 case R.anim.rotation_animation_jump_exit:
735 // These are the only cases that matter.
736 if (forceDefault) {
737 return false;
738 }
739 final RotationAnimationPair anim = selectRotationAnimation();
740 return exitAnimId == anim.mExit && enterAnimId == anim.mEnter;
741 default:
742 return true;
743 }
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800744 }
745
Garfield Tana3f19032019-11-19 18:04:50 -0800746 void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
Garfield Tanff362222018-11-14 17:52:32 -0800747 mFixedToUserRotation = fixedToUserRotation;
748
749 // We will retrieve user rotation and user rotation mode from settings for default display.
750 if (isDefaultDisplay) {
751 return;
752 }
Garfield Tan90c90052018-10-08 12:29:41 -0700753 if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
754 && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
755 Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
756 + " for " + mDisplayContent);
757 userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
758 }
759 if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) {
760 Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation
761 + " for " + mDisplayContent);
762 userRotation = Surface.ROTATION_0;
763 }
764 mUserRotationMode = userRotationMode;
765 mUserRotation = userRotation;
766 }
767
Garfield Tana3f19032019-11-19 18:04:50 -0800768 void setFixedToUserRotation(int fixedToUserRotation) {
Garfield Tanff362222018-11-14 17:52:32 -0800769 if (mFixedToUserRotation == fixedToUserRotation) {
770 return;
771 }
772
773 mFixedToUserRotation = fixedToUserRotation;
Garfield Tan7fbca052019-02-19 10:45:35 -0800774 mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation);
Garfield Tanff362222018-11-14 17:52:32 -0800775 mService.updateRotation(true /* alwaysSendConfiguration */,
776 false /* forceRelayout */);
777 }
778
Wale Ogunwale8a1860a2019-06-05 08:57:19 -0700779 @VisibleForTesting
780 void setUserRotation(int userRotationMode, int userRotation) {
Garfield Tan90c90052018-10-08 12:29:41 -0700781 if (isDefaultDisplay) {
782 // We'll be notified via settings listener, so we don't need to update internal values.
783 final ContentResolver res = mContext.getContentResolver();
784 final int accelerometerRotation =
785 userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
786 Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
787 accelerometerRotation, UserHandle.USER_CURRENT);
788 Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
789 UserHandle.USER_CURRENT);
790 return;
791 }
792
793 boolean changed = false;
794 if (mUserRotationMode != userRotationMode) {
795 mUserRotationMode = userRotationMode;
796 changed = true;
797 }
798 if (mUserRotation != userRotation) {
799 mUserRotation = userRotation;
800 changed = true;
801 }
Garfield Tanff362222018-11-14 17:52:32 -0800802 mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
Chilun8753ad32018-10-09 15:56:45 +0800803 userRotation);
Garfield Tan90c90052018-10-08 12:29:41 -0700804 if (changed) {
805 mService.updateRotation(true /* alwaysSendConfiguration */,
806 false /* forceRelayout */);
Garfield Tan90c90052018-10-08 12:29:41 -0700807 }
808 }
809
810 void freezeRotation(int rotation) {
Riddle Hsuccf09402019-08-13 00:33:06 +0800811 rotation = (rotation == -1) ? mRotation : rotation;
Garfield Tan90c90052018-10-08 12:29:41 -0700812 setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
813 }
814
815 void thawRotation() {
816 setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
817 }
818
819 boolean isRotationFrozen() {
820 if (!isDefaultDisplay) {
821 return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED;
822 }
823
824 return Settings.System.getIntForUser(mContext.getContentResolver(),
825 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
826 }
827
Garfield Tanff362222018-11-14 17:52:32 -0800828 boolean isFixedToUserRotation() {
Garfield Tan7fbca052019-02-19 10:45:35 -0800829 switch (mFixedToUserRotation) {
Garfield Tana3f19032019-11-19 18:04:50 -0800830 case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED:
Garfield Tan7fbca052019-02-19 10:45:35 -0800831 return false;
Garfield Tana3f19032019-11-19 18:04:50 -0800832 case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED:
Garfield Tan7fbca052019-02-19 10:45:35 -0800833 return true;
834 default:
835 return mDefaultFixedToUserRotation;
836 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800837 }
838
Garfield Tan49dae102019-02-04 09:51:59 -0800839 /**
840 * Returns {@code true} if this display rotation takes app requested orientation into
841 * consideration; {@code false} otherwise. For the time being the only case where this is {@code
842 * false} is when {@link #isFixedToUserRotation()} is {@code true}.
843 */
844 boolean respectAppRequestedOrientation() {
Garfield Tan7fbca052019-02-19 10:45:35 -0800845 return !isFixedToUserRotation();
Garfield Tan49dae102019-02-04 09:51:59 -0800846 }
847
Riddle Hsuad256a12018-07-18 16:11:30 +0800848 public int getLandscapeRotation() {
849 return mLandscapeRotation;
850 }
851
852 public int getSeascapeRotation() {
853 return mSeascapeRotation;
854 }
855
856 public int getPortraitRotation() {
857 return mPortraitRotation;
858 }
859
860 public int getUpsideDownRotation() {
861 return mUpsideDownRotation;
862 }
863
864 public int getCurrentAppOrientation() {
865 return mCurrentAppOrientation;
866 }
867
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800868 public DisplayPolicy getDisplayPolicy() {
869 return mDisplayPolicy;
Riddle Hsuad256a12018-07-18 16:11:30 +0800870 }
871
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800872 public WindowOrientationListener getOrientationListener() {
873 return mOrientationListener;
Riddle Hsuad256a12018-07-18 16:11:30 +0800874 }
875
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800876 public int getUserRotation() {
877 return mUserRotation;
878 }
879
880 public int getUserRotationMode() {
881 return mUserRotationMode;
882 }
883
884 public void updateOrientationListener() {
885 synchronized (mLock) {
886 updateOrientationListenerLw();
Riddle Hsuad256a12018-07-18 16:11:30 +0800887 }
888 }
889
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800890 /**
Riddle Hsuccf09402019-08-13 00:33:06 +0800891 * Temporarily pauses rotation changes until resumed.
892 * <p>
893 * This can be used to prevent rotation changes from occurring while the user is performing
894 * certain operations, such as drag and drop.
895 * <p>
896 * This call nests and must be matched by an equal number of calls to {@link #resume}.
897 */
898 void pause() {
899 mDeferredRotationPauseCount++;
900 }
901
902 /** Resumes normal rotation changes after being paused. */
903 void resume() {
904 if (mDeferredRotationPauseCount <= 0) {
905 return;
906 }
907
908 mDeferredRotationPauseCount--;
909 if (mDeferredRotationPauseCount == 0) {
910 updateRotationAndSendNewConfigIfChanged();
911 }
912 }
913
914 /**
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800915 * Various use cases for invoking this function:
916 * <li>Screen turning off, should always disable listeners if already enabled.</li>
917 * <li>Screen turned on and current app has sensor based orientation, enable listeners
918 * if not already enabled.</li>
919 * <li>Screen turned on and current app does not have sensor orientation, disable listeners
920 * if already enabled.</li>
921 * <li>Screen turning on and current app has sensor based orientation, enable listeners
922 * if needed.</li>
923 * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
924 */
925 private void updateOrientationListenerLw() {
926 if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
927 // If sensor is turned off or nonexistent for some reason.
928 return;
929 }
930
931 final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
932 final boolean awake = mDisplayPolicy.isAwake();
933 final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
934 final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();
935
936 // Could have been invoked due to screen turning on or off or
937 // change of the currently visible window's orientation.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200938 ProtoLog.v(WM_DEBUG_ORIENTATION,
939 "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, "
940 + "orientationSensorEnabled=%b, keyguardDrawComplete=%b, "
941 + "windowManagerDrawComplete=%b",
942 screenOnEarly, awake, mCurrentAppOrientation, mOrientationListener.mEnabled,
943 keyguardDrawComplete, windowManagerDrawComplete);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800944
945 boolean disable = true;
946 // Note: We postpone the rotating of the screen until the keyguard as well as the
947 // window manager have reported a draw complete or the keyguard is going away in dismiss
948 // mode.
949 if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) {
950 if (needSensorRunning()) {
951 disable = false;
952 // Enable listener if not already enabled.
953 if (!mOrientationListener.mEnabled) {
954 // Don't clear the current sensor orientation if the keyguard is going away in
955 // dismiss mode. This allows window manager to use the last sensor reading to
956 // determine the orientation vs. falling back to the last known orientation if
957 // the sensor reading was cleared which can cause it to relaunch the app that
958 // will show in the wrong orientation first before correcting leading to app
959 // launch delays.
960 mOrientationListener.enable(true /* clearCurrentRotation */);
961 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800962 }
963 }
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800964 // Check if sensors need to be disabled.
965 if (disable && mOrientationListener.mEnabled) {
966 mOrientationListener.disable();
967 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800968 }
969
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800970 /**
971 * We always let the sensor be switched on by default except when
972 * the user has explicitly disabled sensor based rotation or when the
973 * screen is switched off.
974 */
975 private boolean needSensorRunning() {
Garfield Tan7fbca052019-02-19 10:45:35 -0800976 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -0800977 // We are sure we only respect user rotation settings, so we are sure we will not
978 // support sensor rotation.
979 return false;
980 }
981
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800982 if (mSupportAutoRotation) {
983 if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
984 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
985 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
986 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
987 // If the application has explicitly requested to follow the
988 // orientation, then we need to turn the sensor on.
989 return true;
990 }
991 }
992
993 final int dockMode = mDisplayPolicy.getDockMode();
994 if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
995 && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
996 || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
997 && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
998 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
999 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
1000 // Enable accelerometer if we are docked in a dock that enables accelerometer
1001 // orientation management.
1002 return true;
1003 }
1004
1005 if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
1006 // If the setting for using the sensor by default is enabled, then
1007 // we will always leave it on. Note that the user could go to
1008 // a window that forces an orientation that does not use the
1009 // sensor and in theory we could turn it off... however, when next
1010 // turning it on we won't have a good value for the current
1011 // orientation for a little bit, which can cause orientation
1012 // changes to lag, so we'd like to keep it always on. (It will
1013 // still be turned off when the screen is off.)
1014
1015 // When locked we can provide rotation suggestions users can approve to change the
1016 // current screen rotation. To do this the sensor needs to be running.
1017 return mSupportAutoRotation &&
1018 mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
1019 }
1020 return mSupportAutoRotation;
1021 }
1022
1023 /**
Riddle Hsuccf09402019-08-13 00:33:06 +08001024 * If this is true we have updated our desired orientation, but not yet changed the real
1025 * orientation our applied our screen rotation animation. For example, because a previous
1026 * screen rotation was in progress.
1027 *
1028 * @return {@code true} if the there is an ongoing rotation change.
1029 */
1030 boolean needsUpdate() {
1031 final int oldRotation = mRotation;
1032 final int rotation = rotationForOrientation(mLastOrientation, oldRotation);
1033 return oldRotation != rotation;
1034 }
1035
1036 /**
1037 * Given an orientation constant, returns the appropriate surface rotation, taking into account
1038 * sensors, docking mode, rotation lock, and other factors.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001039 *
Vadim Caenfc14c662020-01-20 16:00:31 +01001040 * @param orientation An orientation constant, such as
1041 * {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001042 * @param lastRotation The most recently used rotation.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001043 * @return The surface rotation to use.
1044 */
Riddle Hsuccf09402019-08-13 00:33:06 +08001045 @VisibleForTesting
Vadim Caenfc14c662020-01-20 16:00:31 +01001046 @Surface.Rotation
1047 int rotationForOrientation(@ScreenOrientation int orientation,
1048 @Surface.Rotation int lastRotation) {
1049 ProtoLog.v(WM_DEBUG_ORIENTATION,
1050 "rotationForOrientation(orient=%s (%d), last=%s (%d)); user=%s (%d) %s",
1051 ActivityInfo.screenOrientationToString(orientation), orientation,
1052 Surface.rotationToString(lastRotation), lastRotation,
1053 Surface.rotationToString(mUserRotation), mUserRotation,
1054 mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
1055 ? "USER_ROTATION_LOCKED" : "");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001056
Garfield Tan7fbca052019-02-19 10:45:35 -08001057 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -08001058 return mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001059 }
1060
1061 int sensorRotation = mOrientationListener != null
1062 ? mOrientationListener.getProposedRotation() // may be -1
1063 : -1;
1064 if (sensorRotation < 0) {
1065 sensorRotation = lastRotation;
1066 }
1067
1068 final int lidState = mDisplayPolicy.getLidState();
1069 final int dockMode = mDisplayPolicy.getDockMode();
1070 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
1071 final boolean carDockEnablesAccelerometer =
1072 mDisplayPolicy.isCarDockEnablesAccelerometer();
1073 final boolean deskDockEnablesAccelerometer =
1074 mDisplayPolicy.isDeskDockEnablesAccelerometer();
1075
1076 final int preferredRotation;
1077 if (!isDefaultDisplay) {
1078 // For secondary displays we ignore things like displays sensors, docking mode and
Garfield Tan90c90052018-10-08 12:29:41 -07001079 // rotation lock, and always prefer user rotation.
1080 preferredRotation = mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001081 } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
1082 // Ignore sensor when lid switch is open and rotation is forced.
1083 preferredRotation = mLidOpenRotation;
1084 } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
1085 && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
1086 // Ignore sensor when in car dock unless explicitly enabled.
1087 // This case can override the behavior of NOSENSOR, and can also
1088 // enable 180 degree rotation while docked.
1089 preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
1090 } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
1091 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
1092 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
1093 && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
1094 // Ignore sensor when in desk dock unless explicitly enabled.
1095 // This case can override the behavior of NOSENSOR, and can also
1096 // enable 180 degree rotation while docked.
1097 preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
1098 } else if (hdmiPlugged && mDemoHdmiRotationLock) {
1099 // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
1100 // Note that the dock orientation overrides the HDMI orientation.
1101 preferredRotation = mDemoHdmiRotation;
1102 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1103 && mUndockedHdmiRotation >= 0) {
1104 // Ignore sensor when plugged into HDMI and an undocked orientation has
1105 // been specified in the configuration (only for legacy devices without
1106 // full multi-display support).
1107 // Note that the dock orientation overrides the HDMI orientation.
1108 preferredRotation = mUndockedHdmiRotation;
1109 } else if (mDemoRotationLock) {
1110 // Ignore sensor when demo rotation lock is enabled.
1111 // Note that the dock orientation and HDMI rotation lock override this.
1112 preferredRotation = mDemoRotation;
1113 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1114 // While in VR, apps always prefer a portrait rotation. This does not change
1115 // any apps that explicitly set landscape, but does cause sensors be ignored,
1116 // and ignored any orientation lock that the user has set (this conditional
1117 // should remain above the ORIENTATION_LOCKED conditional below).
1118 preferredRotation = mPortraitRotation;
1119 } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
1120 // Application just wants to remain locked in the last rotation.
1121 preferredRotation = lastRotation;
1122 } else if (!mSupportAutoRotation) {
1123 // If we don't support auto-rotation then bail out here and ignore
1124 // the sensor and any rotation lock settings.
1125 preferredRotation = -1;
1126 } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
1127 && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
1128 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
1129 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
1130 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
1131 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
1132 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
1133 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1134 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
1135 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
1136 // Otherwise, use sensor only if requested by the application or enabled
1137 // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
Riddle Hsuccf09402019-08-13 00:33:06 +08001138 if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
1139 // Can't read this during init() because the context doesn't have display metrics at
1140 // that time so we cannot determine tablet vs. phone then.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001141 mAllowAllRotations = mContext.getResources().getBoolean(
Riddle Hsuccf09402019-08-13 00:33:06 +08001142 R.bool.config_allowAllRotations)
1143 ? ALLOW_ALL_ROTATIONS_ENABLED
1144 : ALLOW_ALL_ROTATIONS_DISABLED;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001145 }
1146 if (sensorRotation != Surface.ROTATION_180
Riddle Hsuccf09402019-08-13 00:33:06 +08001147 || mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001148 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1149 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
1150 preferredRotation = sensorRotation;
1151 } else {
1152 preferredRotation = lastRotation;
1153 }
1154 } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
Tetsutoki Shiozawa3e595fe2019-05-07 18:27:25 +09001155 && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
1156 && orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
1157 && orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
1158 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
1159 && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
1160 // Apply rotation lock. Does not apply to NOSENSOR or specific rotations.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001161 // The idea is that the user rotation expresses a weak preference for the direction
1162 // of gravity and as NOSENSOR is never affected by gravity, then neither should
1163 // NOSENSOR be affected by rotation lock (although it will be affected by docks).
Tetsutoki Shiozawa3e595fe2019-05-07 18:27:25 +09001164 // Also avoid setting user rotation when app has preference over one particular rotation
1165 // to avoid leaving the rotation to the reverse of it which has the compatible
1166 // orientation, but isn't what app wants, when the user rotation is the reverse of the
1167 // preferred rotation.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001168 preferredRotation = mUserRotation;
1169 } else {
1170 // No overriding preference.
1171 // We will do exactly what the application asked us to do.
1172 preferredRotation = -1;
1173 }
1174
Riddle Hsuad256a12018-07-18 16:11:30 +08001175 switch (orientation) {
1176 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
1177 // Return portrait unless overridden.
1178 if (isAnyPortrait(preferredRotation)) {
1179 return preferredRotation;
1180 }
1181 return mPortraitRotation;
1182
1183 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
1184 // Return landscape unless overridden.
1185 if (isLandscapeOrSeascape(preferredRotation)) {
1186 return preferredRotation;
1187 }
1188 return mLandscapeRotation;
1189
1190 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
1191 // Return reverse portrait unless overridden.
1192 if (isAnyPortrait(preferredRotation)) {
1193 return preferredRotation;
1194 }
1195 return mUpsideDownRotation;
1196
1197 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
1198 // Return seascape unless overridden.
1199 if (isLandscapeOrSeascape(preferredRotation)) {
1200 return preferredRotation;
1201 }
1202 return mSeascapeRotation;
1203
1204 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
1205 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1206 // Return either landscape rotation.
1207 if (isLandscapeOrSeascape(preferredRotation)) {
1208 return preferredRotation;
1209 }
1210 if (isLandscapeOrSeascape(lastRotation)) {
1211 return lastRotation;
1212 }
1213 return mLandscapeRotation;
1214
1215 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
1216 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1217 // Return either portrait rotation.
1218 if (isAnyPortrait(preferredRotation)) {
1219 return preferredRotation;
1220 }
1221 if (isAnyPortrait(lastRotation)) {
1222 return lastRotation;
1223 }
1224 return mPortraitRotation;
1225
1226 default:
1227 // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
1228 // just return the preferred orientation we already calculated.
1229 if (preferredRotation >= 0) {
1230 return preferredRotation;
1231 }
1232 return Surface.ROTATION_0;
1233 }
1234 }
1235
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001236 private boolean isLandscapeOrSeascape(int rotation) {
1237 return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
1238 }
1239
1240 private boolean isAnyPortrait(int rotation) {
1241 return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
1242 }
1243
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001244 private boolean isValidRotationChoice(final int preferredRotation) {
Riddle Hsuad256a12018-07-18 16:11:30 +08001245 // Determine if the given app orientation is compatible with the provided rotation choice.
1246 switch (mCurrentAppOrientation) {
1247 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1248 // Works with any of the 4 rotations.
1249 return preferredRotation >= 0;
1250
1251 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1252 // It's possible for the user pref to be set at 180 because of FULL_USER. This would
1253 // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
1254 // but never to go to 180.
1255 return preferredRotation == mPortraitRotation;
1256
1257 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1258 // Works landscape or seascape.
1259 return isLandscapeOrSeascape(preferredRotation);
1260
1261 case ActivityInfo.SCREEN_ORIENTATION_USER:
1262 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1263 // Works with any rotation except upside down.
1264 return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
1265 }
1266
1267 return false;
1268 }
1269
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001270 private boolean isRotationChoicePossible(int orientation) {
1271 // Rotation choice is only shown when the user is in locked mode.
1272 if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
1273
1274 // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
1275 // demo, hdmi, vr, etc mode.
1276
1277 // Determine if the rotation is currently forced.
Garfield Tan7fbca052019-02-19 10:45:35 -08001278 if (isFixedToUserRotation()) {
Garfield Tanff362222018-11-14 17:52:32 -08001279 return false; // Rotation is forced to user settings.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001280 }
1281
1282 final int lidState = mDisplayPolicy.getLidState();
1283 if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
1284 return false; // Rotation is forced mLidOpenRotation.
1285 }
1286
1287 final int dockMode = mDisplayPolicy.getDockMode();
1288 final boolean carDockEnablesAccelerometer = false;
1289 if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
1290 return false; // Rotation forced to mCarDockRotation.
1291 }
1292
1293 final boolean deskDockEnablesAccelerometer =
1294 mDisplayPolicy.isDeskDockEnablesAccelerometer();
1295 if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
1296 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
1297 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
1298 && !deskDockEnablesAccelerometer) {
1299 return false; // Rotation forced to mDeskDockRotation.
1300 }
1301
1302 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
1303 if (hdmiPlugged && mDemoHdmiRotationLock) {
1304 return false; // Rotation forced to mDemoHdmiRotation.
1305
1306 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
1307 && mUndockedHdmiRotation >= 0) {
1308 return false; // Rotation forced to mUndockedHdmiRotation.
1309
1310 } else if (mDemoRotationLock) {
1311 return false; // Rotation forced to mDemoRotation.
1312
1313 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
1314 return false; // Rotation forced to mPortraitRotation.
1315
1316 } else if (!mSupportAutoRotation) {
1317 return false;
1318 }
1319
1320 // Ensure that some rotation choice is possible for the given orientation.
1321 switch (orientation) {
1322 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
1323 case ActivityInfo.SCREEN_ORIENTATION_USER:
1324 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1325 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
1326 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
1327 // NOSENSOR description is ambiguous, in reality WM ignores user choice.
1328 return true;
1329 }
1330
1331 // Rotation is forced, should be controlled by system.
1332 return false;
Riddle Hsuad256a12018-07-18 16:11:30 +08001333 }
1334
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001335 /** Notify the StatusBar that system rotation suggestion has changed. */
1336 private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
1337 if (mStatusBarManagerInternal == null) {
1338 mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
1339 }
1340 if (mStatusBarManagerInternal != null) {
1341 mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
1342 }
1343 }
1344
1345 private static String allowAllRotationsToString(int allowAll) {
1346 switch (allowAll) {
1347 case -1:
1348 return "unknown";
1349 case 0:
1350 return "false";
1351 case 1:
1352 return "true";
1353 default:
1354 return Integer.toString(allowAll);
1355 }
1356 }
1357
1358 public void onUserSwitch() {
1359 if (mSettingsObserver != null) {
1360 mSettingsObserver.onChange(false);
1361 }
1362 }
1363
1364 /** Return whether the rotation settings has changed. */
1365 private boolean updateSettings() {
1366 final ContentResolver resolver = mContext.getContentResolver();
1367 boolean shouldUpdateRotation = false;
1368
1369 synchronized (mLock) {
1370 boolean shouldUpdateOrientationListener = false;
1371
1372 // Configure rotation suggestions.
Rajeev Kumarb2ff8e82018-08-06 11:45:05 -07001373 final int showRotationSuggestions =
1374 ActivityManager.isLowRamDeviceStatic()
1375 ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
1376 : Settings.Secure.getIntForUser(resolver,
1377 Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
1378 Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
1379 UserHandle.USER_CURRENT);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001380 if (mShowRotationSuggestions != showRotationSuggestions) {
1381 mShowRotationSuggestions = showRotationSuggestions;
1382 shouldUpdateOrientationListener = true;
1383 }
1384
1385 // Configure rotation lock.
1386 final int userRotation = Settings.System.getIntForUser(resolver,
1387 Settings.System.USER_ROTATION, Surface.ROTATION_0,
1388 UserHandle.USER_CURRENT);
1389 if (mUserRotation != userRotation) {
1390 mUserRotation = userRotation;
1391 shouldUpdateRotation = true;
1392 }
1393
1394 final int userRotationMode = Settings.System.getIntForUser(resolver,
1395 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
1396 ? WindowManagerPolicy.USER_ROTATION_FREE
1397 : WindowManagerPolicy.USER_ROTATION_LOCKED;
1398 if (mUserRotationMode != userRotationMode) {
1399 mUserRotationMode = userRotationMode;
1400 shouldUpdateOrientationListener = true;
1401 shouldUpdateRotation = true;
1402 }
1403
1404 if (shouldUpdateOrientationListener) {
1405 updateOrientationListenerLw(); // Enable or disable the orientation listener.
1406 }
1407 }
1408
1409 return shouldUpdateRotation;
Riddle Hsuad256a12018-07-18 16:11:30 +08001410 }
1411
1412 void dump(String prefix, PrintWriter pw) {
1413 pw.println(prefix + "DisplayRotation");
1414 pw.println(prefix + " mCurrentAppOrientation="
1415 + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
Riddle Hsuccf09402019-08-13 00:33:06 +08001416 pw.println(prefix + " mLastOrientation=" + mLastOrientation);
1417 pw.print(prefix + " mRotation=" + mRotation);
1418 pw.println(" mDeferredRotationPauseCount=" + mDeferredRotationPauseCount);
1419
Riddle Hsuad256a12018-07-18 16:11:30 +08001420 pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
1421 pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
1422 pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
1423 pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001424
Tiger Huang7c610aa2018-10-27 00:01:01 +08001425 pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001426 if (mOrientationListener != null) {
Tiger Huang7c610aa2018-10-27 00:01:01 +08001427 mOrientationListener.dump(pw, prefix + " ");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001428 }
1429 pw.println();
1430
1431 pw.print(prefix + " mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
1432 pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
1433 pw.print(prefix + " mUserRotationMode="
1434 + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
1435 pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
1436 pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
1437
1438 pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
1439 pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
1440 pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
1441 pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
Garfield Tan7fbca052019-02-19 10:45:35 -08001442 pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation());
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001443 }
1444
1445 private class OrientationListener extends WindowOrientationListener {
1446 final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
1447 boolean mEnabled;
1448
1449 OrientationListener(Context context, Handler handler) {
1450 super(context, handler);
1451 }
1452
1453 private class UpdateRunnable implements Runnable {
1454 final int mRotation;
1455
1456 UpdateRunnable(int rotation) {
1457 mRotation = rotation;
1458 }
1459
1460 @Override
1461 public void run() {
1462 // Send interaction hint to improve redraw performance.
1463 mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
1464 if (isRotationChoicePossible(mCurrentAppOrientation)) {
1465 final boolean isValid = isValidRotationChoice(mRotation);
1466 sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
1467 } else {
1468 mService.updateRotation(false /* alwaysSendConfiguration */,
1469 false /* forceRelayout */);
1470 }
1471 }
1472 }
1473
1474 @Override
1475 public void onProposedRotationChanged(int rotation) {
Adrian Roosb125e0b2019-10-02 14:55:14 +02001476 ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001477 Runnable r = mRunnableCache.get(rotation, null);
1478 if (r == null) {
1479 r = new UpdateRunnable(rotation);
1480 mRunnableCache.put(rotation, r);
1481 }
1482 getHandler().post(r);
1483 }
1484
1485 @Override
1486 public void enable(boolean clearCurrentRotation) {
1487 super.enable(clearCurrentRotation);
1488 mEnabled = true;
Adrian Roosb125e0b2019-10-02 14:55:14 +02001489 ProtoLog.v(WM_DEBUG_ORIENTATION, "Enabling listeners");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001490 }
1491
1492 @Override
1493 public void disable() {
1494 super.disable();
1495 mEnabled = false;
Adrian Roosb125e0b2019-10-02 14:55:14 +02001496 ProtoLog.v(WM_DEBUG_ORIENTATION, "Disabling listeners");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +08001497 }
1498 }
1499
1500 private class SettingsObserver extends ContentObserver {
1501 SettingsObserver(Handler handler) {
1502 super(handler);
1503 }
1504
1505 void observe() {
1506 final ContentResolver resolver = mContext.getContentResolver();
1507 resolver.registerContentObserver(Settings.Secure.getUriFor(
1508 Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
1509 UserHandle.USER_ALL);
1510 resolver.registerContentObserver(Settings.System.getUriFor(
1511 Settings.System.ACCELEROMETER_ROTATION), false, this,
1512 UserHandle.USER_ALL);
1513 resolver.registerContentObserver(Settings.System.getUriFor(
1514 Settings.System.USER_ROTATION), false, this,
1515 UserHandle.USER_ALL);
1516 updateSettings();
1517 }
1518
1519 @Override
1520 public void onChange(boolean selfChange) {
1521 if (updateSettings()) {
1522 mService.updateRotation(true /* alwaysSendConfiguration */,
1523 false /* forceRelayout */);
1524 }
1525 }
Riddle Hsuad256a12018-07-18 16:11:30 +08001526 }
Garfield Tanff362222018-11-14 17:52:32 -08001527
1528 @VisibleForTesting
1529 interface ContentObserverRegister {
1530 void registerContentObserver(Uri uri, boolean notifyForDescendants,
1531 ContentObserver observer, @UserIdInt int userHandle);
1532 }
Riddle Hsuad256a12018-07-18 16:11:30 +08001533}