blob: 7aabc15d98608ab6094cdc4e95e2ed1d927556d9 [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 Hsu5ce4bb32018-07-18 16:11:30 +080019import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
20import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
22import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
23
Garfield Tanff362222018-11-14 17:52:32 -080024import android.annotation.UserIdInt;
Rajeev Kumarb2ff8e82018-08-06 11:45:05 -070025import android.app.ActivityManager;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080026import android.content.ContentResolver;
Riddle Hsuad256a12018-07-18 16:11:30 +080027import android.content.Context;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080028import android.content.Intent;
Riddle Hsuad256a12018-07-18 16:11:30 +080029import android.content.pm.ActivityInfo;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080030import android.content.pm.PackageManager;
Riddle Hsuad256a12018-07-18 16:11:30 +080031import android.content.res.Resources;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080032import android.database.ContentObserver;
33import android.hardware.power.V1_0.PowerHint;
Garfield Tanff362222018-11-14 17:52:32 -080034import android.net.Uri;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080035import android.os.Handler;
36import android.os.SystemProperties;
37import android.os.UserHandle;
38import android.provider.Settings;
39import android.util.Slog;
40import android.util.SparseArray;
Riddle Hsuad256a12018-07-18 16:11:30 +080041import android.view.Surface;
42
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080043import com.android.internal.annotations.VisibleForTesting;
44import com.android.server.LocalServices;
45import com.android.server.UiThread;
Riddle Hsuad256a12018-07-18 16:11:30 +080046import com.android.server.policy.WindowManagerPolicy;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080047import com.android.server.policy.WindowOrientationListener;
48import com.android.server.statusbar.StatusBarManagerInternal;
Riddle Hsuad256a12018-07-18 16:11:30 +080049
50import java.io.PrintWriter;
51
52/**
53 * Defines the mapping between orientation and rotation of a display.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080054 * Non-public methods are assumed to run inside WM lock.
Riddle Hsuad256a12018-07-18 16:11:30 +080055 */
56public class DisplayRotation {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080057 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
Riddle Hsuad256a12018-07-18 16:11:30 +080058
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080059 private final WindowManagerService mService;
Garfield Tan90c90052018-10-08 12:29:41 -070060 private final DisplayContent mDisplayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080061 private final DisplayPolicy mDisplayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -080062 private final DisplayWindowSettings mDisplayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080063 private final Context mContext;
64 private final Object mLock;
65
66 public final boolean isDefaultDisplay;
67 private final boolean mSupportAutoRotation;
68 private final int mLidOpenRotation;
69 private final int mCarDockRotation;
70 private final int mDeskDockRotation;
71 private final int mUndockedHdmiRotation;
72
73 private OrientationListener mOrientationListener;
74 private StatusBarManagerInternal mStatusBarManagerInternal;
75 private SettingsObserver mSettingsObserver;
76
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080077 private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
78
79 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +080080 int mLandscapeRotation; // default landscape
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080081 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +080082 int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080083 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +080084 int mPortraitRotation; // default portrait
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080085 @VisibleForTesting
Riddle Hsuad256a12018-07-18 16:11:30 +080086 int mUpsideDownRotation; // "other" portrait
87
Riddle Hsu5ce4bb32018-07-18 16:11:30 +080088 // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
89 private int mShowRotationSuggestions;
90
91 private int mAllowAllRotations = -1;
92 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
93 private int mUserRotation = Surface.ROTATION_0;
94
Garfield Tanff362222018-11-14 17:52:32 -080095 /**
96 * A flag to indicate if the display rotation should be fixed to user specified rotation
97 * regardless of all other states (including app requrested orientation). {@code true} the
98 * display rotation should be fixed to user specified rotation, {@code false} otherwise.
99 */
100 private boolean mFixedToUserRotation;
101
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800102 private int mDemoHdmiRotation;
103 private int mDemoRotation;
104 private boolean mDemoHdmiRotationLock;
105 private boolean mDemoRotationLock;
106
107 DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
108 this(service, displayContent, displayContent.getDisplayPolicy(),
Garfield Tanff362222018-11-14 17:52:32 -0800109 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
Riddle Hsuad256a12018-07-18 16:11:30 +0800110 }
111
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800112 @VisibleForTesting
113 DisplayRotation(WindowManagerService service, DisplayContent displayContent,
Garfield Tanff362222018-11-14 17:52:32 -0800114 DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
115 Context context, Object lock) {
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800116 mService = service;
Garfield Tan90c90052018-10-08 12:29:41 -0700117 mDisplayContent = displayContent;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800118 mDisplayPolicy = displayPolicy;
Garfield Tanff362222018-11-14 17:52:32 -0800119 mDisplayWindowSettings = displayWindowSettings;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800120 mContext = context;
121 mLock = lock;
122 isDefaultDisplay = displayContent.isDefaultDisplay;
Riddle Hsuad256a12018-07-18 16:11:30 +0800123
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800124 mSupportAutoRotation = mContext.getResources().getBoolean(
125 com.android.internal.R.bool.config_supportAutoRotation);
126 mLidOpenRotation = readRotation(
127 com.android.internal.R.integer.config_lidOpenRotation);
128 mCarDockRotation = readRotation(
129 com.android.internal.R.integer.config_carDockRotation);
130 mDeskDockRotation = readRotation(
131 com.android.internal.R.integer.config_deskDockRotation);
132 mUndockedHdmiRotation = readRotation(
133 com.android.internal.R.integer.config_undockedHdmiRotation);
134
135 if (isDefaultDisplay) {
136 final Handler uiHandler = UiThread.getHandler();
137 mOrientationListener = new OrientationListener(mContext, uiHandler);
138 mOrientationListener.setCurrentRotation(displayContent.getRotation());
139 mSettingsObserver = new SettingsObserver(uiHandler);
140 mSettingsObserver.observe();
141 }
142 }
143
144 private int readRotation(int resID) {
145 try {
146 final int rotation = mContext.getResources().getInteger(resID);
147 switch (rotation) {
148 case 0:
149 return Surface.ROTATION_0;
150 case 90:
151 return Surface.ROTATION_90;
152 case 180:
153 return Surface.ROTATION_180;
154 case 270:
155 return Surface.ROTATION_270;
156 }
157 } catch (Resources.NotFoundException e) {
158 // fall through
159 }
160 return -1;
161 }
162
163 void configure(int width, int height, int shortSizeDp, int longSizeDp) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800164 final Resources res = mContext.getResources();
165 if (width > height) {
166 mLandscapeRotation = Surface.ROTATION_0;
167 mSeascapeRotation = Surface.ROTATION_180;
168 if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
169 mPortraitRotation = Surface.ROTATION_90;
170 mUpsideDownRotation = Surface.ROTATION_270;
171 } else {
172 mPortraitRotation = Surface.ROTATION_270;
173 mUpsideDownRotation = Surface.ROTATION_90;
174 }
175 } else {
176 mPortraitRotation = Surface.ROTATION_0;
177 mUpsideDownRotation = Surface.ROTATION_180;
178 if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
179 mLandscapeRotation = Surface.ROTATION_270;
180 mSeascapeRotation = Surface.ROTATION_90;
181 } else {
182 mLandscapeRotation = Surface.ROTATION_90;
183 mSeascapeRotation = Surface.ROTATION_270;
184 }
185 }
186
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800187 // For demo purposes, allow the rotation of the HDMI display to be controlled.
188 // By default, HDMI locks rotation to landscape.
189 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
190 mDemoHdmiRotation = mPortraitRotation;
191 } else {
192 mDemoHdmiRotation = mLandscapeRotation;
193 }
194 mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
195
196 // For demo purposes, allow the rotation of the remote display to be controlled.
197 // By default, remote display locks rotation to landscape.
198 if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
199 mDemoRotation = mPortraitRotation;
200 } else {
201 mDemoRotation = mLandscapeRotation;
202 }
203 mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
204
205 // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
206 // http://developer.android.com/guide/practices/screens_support.html#range
207 // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
208 // so if the orientation is forced, we need to respect that no matter what.
209 final boolean isCar = mContext.getPackageManager().hasSystemFeature(
210 PackageManager.FEATURE_AUTOMOTIVE);
211 // For TV, it's usually 960dp x 540dp, ignore the size limitation.
212 // so if the orientation is forced, we need to respect that no matter what.
213 final boolean isTv = mContext.getPackageManager().hasSystemFeature(
214 PackageManager.FEATURE_LEANBACK);
Garfield Tanff362222018-11-14 17:52:32 -0800215 final boolean forceDefaultOrientationInRes =
216 res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
217 final boolean forceDefaultOrienation =
218 ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv)
219 && forceDefaultOrientationInRes
220 // For debug purposes the next line turns this feature off with:
221 // $ adb shell setprop config.override_forced_orient true
222 // $ adb shell wm size reset
223 && !"true".equals(SystemProperties.get("config.override_forced_orient"));
224 // Configuration says we force to use the default orientation. We can fall back to fix
225 // rotation to only user rotation. As long as OEM doesn't change user rotation then the
226 // rotation of this display is effectively stuck at 0 deg.
227 setFixedToUserRotation(forceDefaultOrienation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800228 }
229
230 void setRotation(int rotation) {
231 if (mOrientationListener != null) {
232 mOrientationListener.setCurrentRotation(rotation);
233 }
234 }
235
236 void setCurrentOrientation(int newOrientation) {
237 if (newOrientation != mCurrentAppOrientation) {
238 mCurrentAppOrientation = newOrientation;
239 if (isDefaultDisplay) {
240 updateOrientationListenerLw();
241 }
242 }
243 }
244
Garfield Tanff362222018-11-14 17:52:32 -0800245 void restoreSettings(int userRotationMode, int userRotation,
246 boolean fixedToUserRotation) {
247 mFixedToUserRotation = fixedToUserRotation;
248
249 // We will retrieve user rotation and user rotation mode from settings for default display.
250 if (isDefaultDisplay) {
251 return;
252 }
Garfield Tan90c90052018-10-08 12:29:41 -0700253 if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
254 && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
255 Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
256 + " for " + mDisplayContent);
257 userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
258 }
259 if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) {
260 Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation
261 + " for " + mDisplayContent);
262 userRotation = Surface.ROTATION_0;
263 }
264 mUserRotationMode = userRotationMode;
265 mUserRotation = userRotation;
266 }
267
Garfield Tanff362222018-11-14 17:52:32 -0800268 void setFixedToUserRotation(boolean fixedToUserRotation) {
269 if (mFixedToUserRotation == fixedToUserRotation) {
270 return;
271 }
272
273 mFixedToUserRotation = fixedToUserRotation;
274 mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent,
275 fixedToUserRotation);
276 mService.updateRotation(true /* alwaysSendConfiguration */,
277 false /* forceRelayout */);
278 }
279
Garfield Tan90c90052018-10-08 12:29:41 -0700280 private void setUserRotation(int userRotationMode, int userRotation) {
281 if (isDefaultDisplay) {
282 // We'll be notified via settings listener, so we don't need to update internal values.
283 final ContentResolver res = mContext.getContentResolver();
284 final int accelerometerRotation =
285 userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
286 Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
287 accelerometerRotation, UserHandle.USER_CURRENT);
288 Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
289 UserHandle.USER_CURRENT);
290 return;
291 }
292
293 boolean changed = false;
294 if (mUserRotationMode != userRotationMode) {
295 mUserRotationMode = userRotationMode;
296 changed = true;
297 }
298 if (mUserRotation != userRotation) {
299 mUserRotation = userRotation;
300 changed = true;
301 }
Garfield Tanff362222018-11-14 17:52:32 -0800302 mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
Chilun8753ad32018-10-09 15:56:45 +0800303 userRotation);
Garfield Tan90c90052018-10-08 12:29:41 -0700304 if (changed) {
305 mService.updateRotation(true /* alwaysSendConfiguration */,
306 false /* forceRelayout */);
Garfield Tan90c90052018-10-08 12:29:41 -0700307 }
308 }
309
310 void freezeRotation(int rotation) {
311 rotation = (rotation == -1) ? mDisplayContent.getRotation() : rotation;
312 setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
313 }
314
315 void thawRotation() {
316 setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
317 }
318
319 boolean isRotationFrozen() {
320 if (!isDefaultDisplay) {
321 return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED;
322 }
323
324 return Settings.System.getIntForUser(mContext.getContentResolver(),
325 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
326 }
327
Garfield Tanff362222018-11-14 17:52:32 -0800328 boolean isFixedToUserRotation() {
329 return mFixedToUserRotation;
Riddle Hsuad256a12018-07-18 16:11:30 +0800330 }
331
332 public int getLandscapeRotation() {
333 return mLandscapeRotation;
334 }
335
336 public int getSeascapeRotation() {
337 return mSeascapeRotation;
338 }
339
340 public int getPortraitRotation() {
341 return mPortraitRotation;
342 }
343
344 public int getUpsideDownRotation() {
345 return mUpsideDownRotation;
346 }
347
348 public int getCurrentAppOrientation() {
349 return mCurrentAppOrientation;
350 }
351
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800352 public DisplayPolicy getDisplayPolicy() {
353 return mDisplayPolicy;
Riddle Hsuad256a12018-07-18 16:11:30 +0800354 }
355
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800356 public WindowOrientationListener getOrientationListener() {
357 return mOrientationListener;
Riddle Hsuad256a12018-07-18 16:11:30 +0800358 }
359
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800360 public int getUserRotation() {
361 return mUserRotation;
362 }
363
364 public int getUserRotationMode() {
365 return mUserRotationMode;
366 }
367
368 public void updateOrientationListener() {
369 synchronized (mLock) {
370 updateOrientationListenerLw();
Riddle Hsuad256a12018-07-18 16:11:30 +0800371 }
372 }
373
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800374 /**
375 * Various use cases for invoking this function:
376 * <li>Screen turning off, should always disable listeners if already enabled.</li>
377 * <li>Screen turned on and current app has sensor based orientation, enable listeners
378 * if not already enabled.</li>
379 * <li>Screen turned on and current app does not have sensor orientation, disable listeners
380 * if already enabled.</li>
381 * <li>Screen turning on and current app has sensor based orientation, enable listeners
382 * if needed.</li>
383 * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
384 */
385 private void updateOrientationListenerLw() {
386 if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
387 // If sensor is turned off or nonexistent for some reason.
388 return;
389 }
390
391 final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
392 final boolean awake = mDisplayPolicy.isAwake();
393 final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
394 final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();
395
396 // Could have been invoked due to screen turning on or off or
397 // change of the currently visible window's orientation.
398 if (DEBUG_ORIENTATION) Slog.v(TAG, "screenOnEarly=" + screenOnEarly
399 + ", awake=" + awake + ", currentAppOrientation=" + mCurrentAppOrientation
400 + ", orientationSensorEnabled=" + mOrientationListener.mEnabled
401 + ", keyguardDrawComplete=" + keyguardDrawComplete
402 + ", windowManagerDrawComplete=" + windowManagerDrawComplete);
403
404 boolean disable = true;
405 // Note: We postpone the rotating of the screen until the keyguard as well as the
406 // window manager have reported a draw complete or the keyguard is going away in dismiss
407 // mode.
408 if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) {
409 if (needSensorRunning()) {
410 disable = false;
411 // Enable listener if not already enabled.
412 if (!mOrientationListener.mEnabled) {
413 // Don't clear the current sensor orientation if the keyguard is going away in
414 // dismiss mode. This allows window manager to use the last sensor reading to
415 // determine the orientation vs. falling back to the last known orientation if
416 // the sensor reading was cleared which can cause it to relaunch the app that
417 // will show in the wrong orientation first before correcting leading to app
418 // launch delays.
419 mOrientationListener.enable(true /* clearCurrentRotation */);
420 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800421 }
422 }
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800423 // Check if sensors need to be disabled.
424 if (disable && mOrientationListener.mEnabled) {
425 mOrientationListener.disable();
426 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800427 }
428
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800429 /**
430 * We always let the sensor be switched on by default except when
431 * the user has explicitly disabled sensor based rotation or when the
432 * screen is switched off.
433 */
434 private boolean needSensorRunning() {
Garfield Tanff362222018-11-14 17:52:32 -0800435 if (mFixedToUserRotation) {
436 // We are sure we only respect user rotation settings, so we are sure we will not
437 // support sensor rotation.
438 return false;
439 }
440
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800441 if (mSupportAutoRotation) {
442 if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
443 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
444 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
445 || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
446 // If the application has explicitly requested to follow the
447 // orientation, then we need to turn the sensor on.
448 return true;
449 }
450 }
451
452 final int dockMode = mDisplayPolicy.getDockMode();
453 if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
454 && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
455 || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
456 && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
457 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
458 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
459 // Enable accelerometer if we are docked in a dock that enables accelerometer
460 // orientation management.
461 return true;
462 }
463
464 if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
465 // If the setting for using the sensor by default is enabled, then
466 // we will always leave it on. Note that the user could go to
467 // a window that forces an orientation that does not use the
468 // sensor and in theory we could turn it off... however, when next
469 // turning it on we won't have a good value for the current
470 // orientation for a little bit, which can cause orientation
471 // changes to lag, so we'd like to keep it always on. (It will
472 // still be turned off when the screen is off.)
473
474 // When locked we can provide rotation suggestions users can approve to change the
475 // current screen rotation. To do this the sensor needs to be running.
476 return mSupportAutoRotation &&
477 mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
478 }
479 return mSupportAutoRotation;
480 }
481
482 /**
483 * Given an orientation constant, returns the appropriate surface rotation,
484 * taking into account sensors, docking mode, rotation lock, and other factors.
485 *
486 * @param orientation An orientation constant, such as
487 * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
488 * @param lastRotation The most recently used rotation.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800489 * @return The surface rotation to use.
490 */
491 int rotationForOrientation(int orientation, int lastRotation) {
492 if (DEBUG_ORIENTATION) {
493 Slog.v(TAG, "rotationForOrientation(orient="
494 + orientation + ", last=" + lastRotation
495 + "); user=" + mUserRotation + " "
496 + (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
497 ? "USER_ROTATION_LOCKED" : "")
498 );
499 }
500
Garfield Tanff362222018-11-14 17:52:32 -0800501 if (mFixedToUserRotation) {
502 return mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800503 }
504
505 int sensorRotation = mOrientationListener != null
506 ? mOrientationListener.getProposedRotation() // may be -1
507 : -1;
508 if (sensorRotation < 0) {
509 sensorRotation = lastRotation;
510 }
511
512 final int lidState = mDisplayPolicy.getLidState();
513 final int dockMode = mDisplayPolicy.getDockMode();
514 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
515 final boolean carDockEnablesAccelerometer =
516 mDisplayPolicy.isCarDockEnablesAccelerometer();
517 final boolean deskDockEnablesAccelerometer =
518 mDisplayPolicy.isDeskDockEnablesAccelerometer();
519
520 final int preferredRotation;
521 if (!isDefaultDisplay) {
522 // For secondary displays we ignore things like displays sensors, docking mode and
Garfield Tan90c90052018-10-08 12:29:41 -0700523 // rotation lock, and always prefer user rotation.
524 preferredRotation = mUserRotation;
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800525 } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
526 // Ignore sensor when lid switch is open and rotation is forced.
527 preferredRotation = mLidOpenRotation;
528 } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
529 && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
530 // Ignore sensor when in car dock unless explicitly enabled.
531 // This case can override the behavior of NOSENSOR, and can also
532 // enable 180 degree rotation while docked.
533 preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
534 } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
535 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
536 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
537 && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
538 // Ignore sensor when in desk dock unless explicitly enabled.
539 // This case can override the behavior of NOSENSOR, and can also
540 // enable 180 degree rotation while docked.
541 preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
542 } else if (hdmiPlugged && mDemoHdmiRotationLock) {
543 // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
544 // Note that the dock orientation overrides the HDMI orientation.
545 preferredRotation = mDemoHdmiRotation;
546 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
547 && mUndockedHdmiRotation >= 0) {
548 // Ignore sensor when plugged into HDMI and an undocked orientation has
549 // been specified in the configuration (only for legacy devices without
550 // full multi-display support).
551 // Note that the dock orientation overrides the HDMI orientation.
552 preferredRotation = mUndockedHdmiRotation;
553 } else if (mDemoRotationLock) {
554 // Ignore sensor when demo rotation lock is enabled.
555 // Note that the dock orientation and HDMI rotation lock override this.
556 preferredRotation = mDemoRotation;
557 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
558 // While in VR, apps always prefer a portrait rotation. This does not change
559 // any apps that explicitly set landscape, but does cause sensors be ignored,
560 // and ignored any orientation lock that the user has set (this conditional
561 // should remain above the ORIENTATION_LOCKED conditional below).
562 preferredRotation = mPortraitRotation;
563 } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
564 // Application just wants to remain locked in the last rotation.
565 preferredRotation = lastRotation;
566 } else if (!mSupportAutoRotation) {
567 // If we don't support auto-rotation then bail out here and ignore
568 // the sensor and any rotation lock settings.
569 preferredRotation = -1;
570 } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
571 && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
572 || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
573 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
574 || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
575 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
576 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
577 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
578 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
579 || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
580 // Otherwise, use sensor only if requested by the application or enabled
581 // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
582 if (mAllowAllRotations < 0) {
583 // Can't read this during init() because the context doesn't
584 // have display metrics at that time so we cannot determine
585 // tablet vs. phone then.
586 mAllowAllRotations = mContext.getResources().getBoolean(
587 com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
588 }
589 if (sensorRotation != Surface.ROTATION_180
590 || mAllowAllRotations == 1
591 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
592 || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
593 preferredRotation = sensorRotation;
594 } else {
595 preferredRotation = lastRotation;
596 }
597 } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
598 && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
599 // Apply rotation lock. Does not apply to NOSENSOR.
600 // The idea is that the user rotation expresses a weak preference for the direction
601 // of gravity and as NOSENSOR is never affected by gravity, then neither should
602 // NOSENSOR be affected by rotation lock (although it will be affected by docks).
603 preferredRotation = mUserRotation;
604 } else {
605 // No overriding preference.
606 // We will do exactly what the application asked us to do.
607 preferredRotation = -1;
608 }
609
Riddle Hsuad256a12018-07-18 16:11:30 +0800610 switch (orientation) {
611 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
612 // Return portrait unless overridden.
613 if (isAnyPortrait(preferredRotation)) {
614 return preferredRotation;
615 }
616 return mPortraitRotation;
617
618 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
619 // Return landscape unless overridden.
620 if (isLandscapeOrSeascape(preferredRotation)) {
621 return preferredRotation;
622 }
623 return mLandscapeRotation;
624
625 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
626 // Return reverse portrait unless overridden.
627 if (isAnyPortrait(preferredRotation)) {
628 return preferredRotation;
629 }
630 return mUpsideDownRotation;
631
632 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
633 // Return seascape unless overridden.
634 if (isLandscapeOrSeascape(preferredRotation)) {
635 return preferredRotation;
636 }
637 return mSeascapeRotation;
638
639 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
640 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
641 // Return either landscape rotation.
642 if (isLandscapeOrSeascape(preferredRotation)) {
643 return preferredRotation;
644 }
645 if (isLandscapeOrSeascape(lastRotation)) {
646 return lastRotation;
647 }
648 return mLandscapeRotation;
649
650 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
651 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
652 // Return either portrait rotation.
653 if (isAnyPortrait(preferredRotation)) {
654 return preferredRotation;
655 }
656 if (isAnyPortrait(lastRotation)) {
657 return lastRotation;
658 }
659 return mPortraitRotation;
660
661 default:
662 // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
663 // just return the preferred orientation we already calculated.
664 if (preferredRotation >= 0) {
665 return preferredRotation;
666 }
667 return Surface.ROTATION_0;
668 }
669 }
670
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800671 private boolean isLandscapeOrSeascape(int rotation) {
672 return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
673 }
674
675 private boolean isAnyPortrait(int rotation) {
676 return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
677 }
678
Riddle Hsuad256a12018-07-18 16:11:30 +0800679 /**
680 * Given an orientation constant and a rotation, returns true if the rotation
681 * has compatible metrics to the requested orientation. For example, if
682 * the application requested landscape and got seascape, then the rotation
683 * has compatible metrics; if the application requested portrait and got landscape,
684 * then the rotation has incompatible metrics; if the application did not specify
685 * a preference, then anything goes.
686 *
687 * @param orientation An orientation constant, such as
688 * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
689 * @param rotation The rotation to check.
690 * @return True if the rotation is compatible with the requested orientation.
691 */
692 boolean rotationHasCompatibleMetrics(int orientation, int rotation) {
693 switch (orientation) {
694 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
695 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
696 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
697 return isAnyPortrait(rotation);
698
699 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
700 case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
701 case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
702 return isLandscapeOrSeascape(rotation);
703
704 default:
705 return true;
706 }
707 }
708
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800709 private boolean isValidRotationChoice(final int preferredRotation) {
Riddle Hsuad256a12018-07-18 16:11:30 +0800710 // Determine if the given app orientation is compatible with the provided rotation choice.
711 switch (mCurrentAppOrientation) {
712 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
713 // Works with any of the 4 rotations.
714 return preferredRotation >= 0;
715
716 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
717 // It's possible for the user pref to be set at 180 because of FULL_USER. This would
718 // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
719 // but never to go to 180.
720 return preferredRotation == mPortraitRotation;
721
722 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
723 // Works landscape or seascape.
724 return isLandscapeOrSeascape(preferredRotation);
725
726 case ActivityInfo.SCREEN_ORIENTATION_USER:
727 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
728 // Works with any rotation except upside down.
729 return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
730 }
731
732 return false;
733 }
734
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800735 private boolean isRotationChoicePossible(int orientation) {
736 // Rotation choice is only shown when the user is in locked mode.
737 if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
738
739 // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
740 // demo, hdmi, vr, etc mode.
741
742 // Determine if the rotation is currently forced.
Garfield Tanff362222018-11-14 17:52:32 -0800743 if (mFixedToUserRotation) {
744 return false; // Rotation is forced to user settings.
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800745 }
746
747 final int lidState = mDisplayPolicy.getLidState();
748 if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
749 return false; // Rotation is forced mLidOpenRotation.
750 }
751
752 final int dockMode = mDisplayPolicy.getDockMode();
753 final boolean carDockEnablesAccelerometer = false;
754 if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
755 return false; // Rotation forced to mCarDockRotation.
756 }
757
758 final boolean deskDockEnablesAccelerometer =
759 mDisplayPolicy.isDeskDockEnablesAccelerometer();
760 if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
761 || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
762 || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
763 && !deskDockEnablesAccelerometer) {
764 return false; // Rotation forced to mDeskDockRotation.
765 }
766
767 final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
768 if (hdmiPlugged && mDemoHdmiRotationLock) {
769 return false; // Rotation forced to mDemoHdmiRotation.
770
771 } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
772 && mUndockedHdmiRotation >= 0) {
773 return false; // Rotation forced to mUndockedHdmiRotation.
774
775 } else if (mDemoRotationLock) {
776 return false; // Rotation forced to mDemoRotation.
777
778 } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
779 return false; // Rotation forced to mPortraitRotation.
780
781 } else if (!mSupportAutoRotation) {
782 return false;
783 }
784
785 // Ensure that some rotation choice is possible for the given orientation.
786 switch (orientation) {
787 case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
788 case ActivityInfo.SCREEN_ORIENTATION_USER:
789 case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
790 case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
791 case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
792 // NOSENSOR description is ambiguous, in reality WM ignores user choice.
793 return true;
794 }
795
796 // Rotation is forced, should be controlled by system.
797 return false;
Riddle Hsuad256a12018-07-18 16:11:30 +0800798 }
799
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800800 /** Notify the StatusBar that system rotation suggestion has changed. */
801 private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
802 if (mStatusBarManagerInternal == null) {
803 mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
804 }
805 if (mStatusBarManagerInternal != null) {
806 mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
807 }
808 }
809
810 private static String allowAllRotationsToString(int allowAll) {
811 switch (allowAll) {
812 case -1:
813 return "unknown";
814 case 0:
815 return "false";
816 case 1:
817 return "true";
818 default:
819 return Integer.toString(allowAll);
820 }
821 }
822
823 public void onUserSwitch() {
824 if (mSettingsObserver != null) {
825 mSettingsObserver.onChange(false);
826 }
827 }
828
829 /** Return whether the rotation settings has changed. */
830 private boolean updateSettings() {
831 final ContentResolver resolver = mContext.getContentResolver();
832 boolean shouldUpdateRotation = false;
833
834 synchronized (mLock) {
835 boolean shouldUpdateOrientationListener = false;
836
837 // Configure rotation suggestions.
Rajeev Kumarb2ff8e82018-08-06 11:45:05 -0700838 final int showRotationSuggestions =
839 ActivityManager.isLowRamDeviceStatic()
840 ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
841 : Settings.Secure.getIntForUser(resolver,
842 Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
843 Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
844 UserHandle.USER_CURRENT);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800845 if (mShowRotationSuggestions != showRotationSuggestions) {
846 mShowRotationSuggestions = showRotationSuggestions;
847 shouldUpdateOrientationListener = true;
848 }
849
850 // Configure rotation lock.
851 final int userRotation = Settings.System.getIntForUser(resolver,
852 Settings.System.USER_ROTATION, Surface.ROTATION_0,
853 UserHandle.USER_CURRENT);
854 if (mUserRotation != userRotation) {
855 mUserRotation = userRotation;
856 shouldUpdateRotation = true;
857 }
858
859 final int userRotationMode = Settings.System.getIntForUser(resolver,
860 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
861 ? WindowManagerPolicy.USER_ROTATION_FREE
862 : WindowManagerPolicy.USER_ROTATION_LOCKED;
863 if (mUserRotationMode != userRotationMode) {
864 mUserRotationMode = userRotationMode;
865 shouldUpdateOrientationListener = true;
866 shouldUpdateRotation = true;
867 }
868
869 if (shouldUpdateOrientationListener) {
870 updateOrientationListenerLw(); // Enable or disable the orientation listener.
871 }
872 }
873
874 return shouldUpdateRotation;
Riddle Hsuad256a12018-07-18 16:11:30 +0800875 }
876
877 void dump(String prefix, PrintWriter pw) {
878 pw.println(prefix + "DisplayRotation");
879 pw.println(prefix + " mCurrentAppOrientation="
880 + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
881 pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
882 pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
883 pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
884 pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800885
Tiger Huang7c610aa2018-10-27 00:01:01 +0800886 pw.println(prefix + " mSupportAutoRotation=" + mSupportAutoRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800887 if (mOrientationListener != null) {
Tiger Huang7c610aa2018-10-27 00:01:01 +0800888 mOrientationListener.dump(pw, prefix + " ");
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800889 }
890 pw.println();
891
892 pw.print(prefix + " mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
893 pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
894 pw.print(prefix + " mUserRotationMode="
895 + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
896 pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
897 pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
898
899 pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
900 pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
901 pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
902 pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
Garfield Tanff362222018-11-14 17:52:32 -0800903 pw.println(prefix + " mFixedToUserRotation=" + mFixedToUserRotation);
Riddle Hsu5ce4bb32018-07-18 16:11:30 +0800904 }
905
906 private class OrientationListener extends WindowOrientationListener {
907 final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
908 boolean mEnabled;
909
910 OrientationListener(Context context, Handler handler) {
911 super(context, handler);
912 }
913
914 private class UpdateRunnable implements Runnable {
915 final int mRotation;
916
917 UpdateRunnable(int rotation) {
918 mRotation = rotation;
919 }
920
921 @Override
922 public void run() {
923 // Send interaction hint to improve redraw performance.
924 mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
925 if (isRotationChoicePossible(mCurrentAppOrientation)) {
926 final boolean isValid = isValidRotationChoice(mRotation);
927 sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
928 } else {
929 mService.updateRotation(false /* alwaysSendConfiguration */,
930 false /* forceRelayout */);
931 }
932 }
933 }
934
935 @Override
936 public void onProposedRotationChanged(int rotation) {
937 if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
938 Runnable r = mRunnableCache.get(rotation, null);
939 if (r == null) {
940 r = new UpdateRunnable(rotation);
941 mRunnableCache.put(rotation, r);
942 }
943 getHandler().post(r);
944 }
945
946 @Override
947 public void enable(boolean clearCurrentRotation) {
948 super.enable(clearCurrentRotation);
949 mEnabled = true;
950 if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
951 }
952
953 @Override
954 public void disable() {
955 super.disable();
956 mEnabled = false;
957 if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
958 }
959 }
960
961 private class SettingsObserver extends ContentObserver {
962 SettingsObserver(Handler handler) {
963 super(handler);
964 }
965
966 void observe() {
967 final ContentResolver resolver = mContext.getContentResolver();
968 resolver.registerContentObserver(Settings.Secure.getUriFor(
969 Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
970 UserHandle.USER_ALL);
971 resolver.registerContentObserver(Settings.System.getUriFor(
972 Settings.System.ACCELEROMETER_ROTATION), false, this,
973 UserHandle.USER_ALL);
974 resolver.registerContentObserver(Settings.System.getUriFor(
975 Settings.System.USER_ROTATION), false, this,
976 UserHandle.USER_ALL);
977 updateSettings();
978 }
979
980 @Override
981 public void onChange(boolean selfChange) {
982 if (updateSettings()) {
983 mService.updateRotation(true /* alwaysSendConfiguration */,
984 false /* forceRelayout */);
985 }
986 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800987 }
Garfield Tanff362222018-11-14 17:52:32 -0800988
989 @VisibleForTesting
990 interface ContentObserverRegister {
991 void registerContentObserver(Uri uri, boolean notifyForDescendants,
992 ContentObserver observer, @UserIdInt int userHandle);
993 }
Riddle Hsuad256a12018-07-18 16:11:30 +0800994}