1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; 20 21 import static com.android.server.wm.WindowOrientationListenerProto.ENABLED; 22 import static com.android.server.wm.WindowOrientationListenerProto.ROTATION; 23 24 import android.app.ActivityThread; 25 import android.content.Context; 26 import android.hardware.Sensor; 27 import android.hardware.SensorEvent; 28 import android.hardware.SensorEventListener; 29 import android.hardware.SensorManager; 30 import android.os.CancellationSignal; 31 import android.os.Handler; 32 import android.os.SystemClock; 33 import android.os.SystemProperties; 34 import android.provider.DeviceConfig; 35 import android.rotationresolver.RotationResolverInternal; 36 import android.util.Slog; 37 import android.util.proto.ProtoOutputStream; 38 import android.view.Surface; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.util.FrameworkStatsLog; 42 import com.android.server.LocalServices; 43 44 import java.io.PrintWriter; 45 import java.util.List; 46 import java.util.Set; 47 48 /** 49 * A special helper class used by the WindowManager 50 * for receiving notifications from the SensorManager when 51 * the orientation of the device has changed. 52 * 53 * NOTE: If changing anything here, please run the API demo 54 * "App/Activity/Screen Orientation" to ensure that all orientation 55 * modes still work correctly. 56 * 57 * You can also visualize the behavior of the WindowOrientationListener. 58 * Refer to frameworks/base/tools/orientationplot/README.txt for details. 59 */ 60 public abstract class WindowOrientationListener { 61 private static final String TAG = "WindowOrientationListener"; 62 private static final boolean LOG = SystemProperties.getBoolean( 63 "debug.orientation.log", false); 64 65 private static final boolean USE_GRAVITY_SENSOR = false; 66 private static final int DEFAULT_BATCH_LATENCY = 100000; 67 private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis"; 68 private static final String KEY_ROTATION_MEMORIZATION_TIMEOUT = 69 "rotation_memorization_timeout_millis"; 70 private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L; 71 private static final long DEFAULT_ROTATION_MEMORIZATION_TIMEOUT_MILLIS = 3_000L; // 3 seconds 72 73 private Handler mHandler; 74 private SensorManager mSensorManager; 75 private boolean mEnabled; 76 private int mRate; 77 private String mSensorType; 78 private Sensor mSensor; 79 80 @VisibleForTesting 81 OrientationJudge mOrientationJudge; 82 83 @VisibleForTesting 84 RotationResolverInternal mRotationResolverService; 85 86 private int mCurrentRotation = -1; 87 private final Context mContext; 88 89 private final Object mLock = new Object(); 90 91 /** 92 * Creates a new WindowOrientationListener. 93 * 94 * @param context for the WindowOrientationListener. 95 * @param handler Provides the Looper for receiving sensor updates. 96 */ WindowOrientationListener(Context context, Handler handler)97 public WindowOrientationListener(Context context, Handler handler) { 98 this(context, handler, SensorManager.SENSOR_DELAY_UI); 99 } 100 101 /** 102 * Creates a new WindowOrientationListener. 103 * 104 * @param context for the WindowOrientationListener. 105 * @param handler Provides the Looper for receiving sensor updates. 106 * @param wmService WindowManagerService to read the device config from. 107 * @param rate at which sensor events are processed (see also 108 * {@link android.hardware.SensorManager SensorManager}). Use the default 109 * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 110 * SENSOR_DELAY_NORMAL} for simple screen orientation change detection. 111 * 112 * This constructor is private since no one uses it. 113 */ WindowOrientationListener( Context context, Handler handler, int rate)114 private WindowOrientationListener( 115 Context context, Handler handler, int rate) { 116 mContext = context; 117 mHandler = handler; 118 mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); 119 mRate = rate; 120 List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION); 121 Sensor wakeUpDeviceOrientationSensor = null; 122 Sensor nonWakeUpDeviceOrientationSensor = null; 123 /** 124 * Prefer the wakeup form of the sensor if implemented. 125 * It's OK to look for just two types of this sensor and use 126 * the last found. Typical devices will only have one sensor of 127 * this type. 128 */ 129 for (Sensor s : l) { 130 if (s.isWakeUpSensor()) { 131 wakeUpDeviceOrientationSensor = s; 132 } else { 133 nonWakeUpDeviceOrientationSensor = s; 134 } 135 } 136 137 if (wakeUpDeviceOrientationSensor != null) { 138 mSensor = wakeUpDeviceOrientationSensor; 139 } else { 140 mSensor = nonWakeUpDeviceOrientationSensor; 141 } 142 143 if (mSensor != null) { 144 mOrientationJudge = new OrientationSensorJudge(); 145 } 146 147 if (mOrientationJudge == null) { 148 mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR 149 ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); 150 if (mSensor != null) { 151 // Create listener only if sensors do exist 152 mOrientationJudge = new AccelSensorJudge(context); 153 } 154 } 155 } 156 157 /** 158 * Enables the WindowOrientationListener so it will monitor the sensor and call 159 * {@link #onProposedRotationChanged(int)} when the device orientation changes. 160 */ enable()161 public void enable() { 162 enable(true /* clearCurrentRotation */); 163 } 164 165 /** 166 * Enables the WindowOrientationListener so it will monitor the sensor and call 167 * {@link #onProposedRotationChanged(int)} when the device orientation changes. 168 * 169 * @param clearCurrentRotation True if the current proposed sensor rotation should be cleared as 170 * part of the reset. 171 */ enable(boolean clearCurrentRotation)172 public void enable(boolean clearCurrentRotation) { 173 synchronized (mLock) { 174 if (mSensor == null) { 175 Slog.w(TAG, "Cannot detect sensors. Not enabled"); 176 return; 177 } 178 if (mEnabled) { 179 return; 180 } 181 if (LOG) { 182 Slog.d(TAG, "WindowOrientationListener enabled clearCurrentRotation=" 183 + clearCurrentRotation); 184 } 185 mOrientationJudge.resetLocked(clearCurrentRotation); 186 if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) { 187 mSensorManager.registerListener( 188 mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler); 189 } else { 190 mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler); 191 } 192 mEnabled = true; 193 } 194 } 195 196 /** 197 * Disables the WindowOrientationListener. 198 */ disable()199 public void disable() { 200 synchronized (mLock) { 201 if (mSensor == null) { 202 Slog.w(TAG, "Cannot detect sensors. Invalid disable"); 203 return; 204 } 205 if (mEnabled == true) { 206 if (LOG) { 207 Slog.d(TAG, "WindowOrientationListener disabled"); 208 } 209 mSensorManager.unregisterListener(mOrientationJudge); 210 mEnabled = false; 211 } 212 } 213 } 214 onTouchStart()215 public void onTouchStart() { 216 synchronized (mLock) { 217 if (mOrientationJudge != null) { 218 mOrientationJudge.onTouchStartLocked(); 219 } 220 } 221 } 222 onTouchEnd()223 public void onTouchEnd() { 224 long whenElapsedNanos = SystemClock.elapsedRealtimeNanos(); 225 226 synchronized (mLock) { 227 if (mOrientationJudge != null) { 228 mOrientationJudge.onTouchEndLocked(whenElapsedNanos); 229 } 230 } 231 } 232 getHandler()233 public Handler getHandler() { 234 return mHandler; 235 } 236 237 /** 238 * Sets the current rotation. 239 * 240 * @param rotation The current rotation. 241 */ setCurrentRotation(int rotation)242 public void setCurrentRotation(int rotation) { 243 synchronized (mLock) { 244 mCurrentRotation = rotation; 245 } 246 } 247 248 /** 249 * Gets the proposed rotation. 250 * 251 * This method only returns a rotation if the orientation listener is certain 252 * of its proposal. If the rotation is indeterminate, returns -1. 253 * 254 * @return The proposed rotation, or -1 if unknown. 255 */ getProposedRotation()256 public int getProposedRotation() { 257 synchronized (mLock) { 258 if (mEnabled) { 259 return mOrientationJudge.getProposedRotationLocked(); 260 } 261 return -1; 262 } 263 } 264 265 /** 266 * Returns true if sensor is enabled and false otherwise 267 */ canDetectOrientation()268 public boolean canDetectOrientation() { 269 synchronized (mLock) { 270 return mSensor != null; 271 } 272 } 273 274 275 /** 276 * Returns true if the rotation resolver feature is enabled by setting. It means {@link 277 * WindowOrientationListener} will then ask {@link RotationResolverInternal} for the appropriate 278 * screen rotation. 279 */ 280 @VisibleForTesting isRotationResolverEnabled()281 abstract boolean isRotationResolverEnabled(); 282 283 /** 284 * Called when the rotation view of the device has changed. 285 * 286 * This method is called whenever the orientation becomes certain of an orientation. 287 * It is called each time the orientation determination transitions from being 288 * uncertain to being certain again, even if it is the same orientation as before. 289 * 290 * This should only be called on the Handler thread. 291 * 292 * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. 293 * @see android.view.Surface 294 */ onProposedRotationChanged(int rotation)295 public abstract void onProposedRotationChanged(int rotation); 296 297 /** 298 * Whether the device is in the lock screen. 299 * @return returns true if the screen is locked. Otherwise, returns false. 300 */ isKeyguardLocked()301 public abstract boolean isKeyguardLocked(); 302 dumpDebug(ProtoOutputStream proto, long fieldId)303 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 304 final long token = proto.start(fieldId); 305 synchronized (mLock) { 306 proto.write(ENABLED, mEnabled); 307 proto.write(ROTATION, mCurrentRotation); 308 } 309 proto.end(token); 310 } 311 dump(PrintWriter pw, String prefix)312 public void dump(PrintWriter pw, String prefix) { 313 synchronized (mLock) { 314 pw.println(prefix + TAG); 315 prefix += " "; 316 pw.println(prefix + "mEnabled=" + mEnabled); 317 pw.println(prefix + "mCurrentRotation=" + Surface.rotationToString(mCurrentRotation)); 318 pw.println(prefix + "mSensorType=" + mSensorType); 319 pw.println(prefix + "mSensor=" + mSensor); 320 pw.println(prefix + "mRate=" + mRate); 321 322 if (mOrientationJudge != null) { 323 mOrientationJudge.dumpLocked(pw, prefix); 324 } 325 } 326 } 327 328 /** 329 * Returns whether this WindowOrientationListener can remain enabled while the device is dozing. 330 * If this returns true, it implies that the underlying sensor can still run while the AP is 331 * asleep, and that the underlying sensor will wake the AP on an event. 332 */ shouldStayEnabledWhileDreaming()333 public boolean shouldStayEnabledWhileDreaming() { 334 if (mContext.getResources().getBoolean( 335 com.android.internal.R.bool.config_forceOrientationListenerEnabledWhileDreaming)) { 336 return true; 337 } 338 return mSensor.getType() == Sensor.TYPE_DEVICE_ORIENTATION && mSensor.isWakeUpSensor(); 339 } 340 341 abstract class OrientationJudge implements SensorEventListener { 342 // Number of nanoseconds per millisecond. 343 protected static final long NANOS_PER_MS = 1000000; 344 345 // Number of milliseconds per nano second. 346 protected static final float MILLIS_PER_NANO = 0.000001f; 347 348 // The minimum amount of time that must have elapsed since the screen was last touched 349 // before the proposed rotation can change. 350 protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS = 351 500 * NANOS_PER_MS; 352 353 /** 354 * Gets the proposed rotation. 355 * 356 * This method only returns a rotation if the orientation listener is certain 357 * of its proposal. If the rotation is indeterminate, returns -1. 358 * 359 * Should only be called when holding WindowOrientationListener lock. 360 * 361 * @return The proposed rotation, or -1 if unknown. 362 */ getProposedRotationLocked()363 public abstract int getProposedRotationLocked(); 364 365 /** 366 * Notifies the orientation judge that the screen is being touched. 367 * 368 * Should only be called when holding WindowOrientationListener lock. 369 */ onTouchStartLocked()370 public abstract void onTouchStartLocked(); 371 372 /** 373 * Notifies the orientation judge that the screen is no longer being touched. 374 * 375 * Should only be called when holding WindowOrientationListener lock. 376 * 377 * @param whenElapsedNanos Given in the elapsed realtime nanos time base. 378 */ onTouchEndLocked(long whenElapsedNanos)379 public abstract void onTouchEndLocked(long whenElapsedNanos); 380 381 /** 382 * Resets the state of the judge. 383 * 384 * Should only be called when holding WindowOrientationListener lock. 385 * 386 * @param clearCurrentRotation True if the current proposed sensor rotation should be 387 * cleared as part of the reset. 388 */ resetLocked(boolean clearCurrentRotation)389 public abstract void resetLocked(boolean clearCurrentRotation); 390 391 /** 392 * Dumps internal state of the orientation judge. 393 * 394 * Should only be called when holding WindowOrientationListener lock. 395 */ dumpLocked(PrintWriter pw, String prefix)396 public abstract void dumpLocked(PrintWriter pw, String prefix); 397 398 @Override onAccuracyChanged(Sensor sensor, int accuracy)399 public abstract void onAccuracyChanged(Sensor sensor, int accuracy); 400 401 @Override onSensorChanged(SensorEvent event)402 public abstract void onSensorChanged(SensorEvent event); 403 } 404 405 /** 406 * This class filters the raw accelerometer data and tries to detect actual changes in 407 * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, 408 * but here's the outline: 409 * 410 * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in 411 * cartesian space because the orientation calculations are sensitive to the 412 * absolute magnitude of the acceleration. In particular, there are singularities 413 * in the calculation as the magnitude approaches 0. By performing the low-pass 414 * filtering early, we can eliminate most spurious high-frequency impulses due to noise. 415 * 416 * - Convert the acceleromter vector from cartesian to spherical coordinates. 417 * Since we're dealing with rotation of the device, this is the sensible coordinate 418 * system to work in. The zenith direction is the Z-axis, the direction the screen 419 * is facing. The radial distance is referred to as the magnitude below. 420 * The elevation angle is referred to as the "tilt" below. 421 * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is 422 * the Y-axis). 423 * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference. 424 * 425 * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing. 426 * The orientation angle is not meaningful when the device is nearly horizontal. 427 * The tilt angle thresholds are set differently for each orientation and different 428 * limits are applied when the device is facing down as opposed to when it is facing 429 * forward or facing up. 430 * 431 * - When the orientation angle reaches a certain threshold, consider transitioning 432 * to the corresponding orientation. These thresholds have some hysteresis built-in 433 * to avoid oscillations between adjacent orientations. 434 * 435 * - Wait for the device to settle for a little bit. Once that happens, issue the 436 * new orientation proposal. 437 * 438 * Details are explained inline. 439 * 440 * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for 441 * signal processing background. 442 */ 443 final class AccelSensorJudge extends OrientationJudge { 444 // We work with all angles in degrees in this class. 445 private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI); 446 447 // Indices into SensorEvent.values for the accelerometer sensor. 448 private static final int ACCELEROMETER_DATA_X = 0; 449 private static final int ACCELEROMETER_DATA_Y = 1; 450 private static final int ACCELEROMETER_DATA_Z = 2; 451 452 // The minimum amount of time that a predicted rotation must be stable before it 453 // is accepted as a valid rotation proposal. This value can be quite small because 454 // the low-pass filter already suppresses most of the noise so we're really just 455 // looking for quick confirmation that the last few samples are in agreement as to 456 // the desired orientation. 457 private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS; 458 459 // The minimum amount of time that must have elapsed since the device last exited 460 // the flat state (time since it was picked up) before the proposed rotation 461 // can change. 462 private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS; 463 464 // The minimum amount of time that must have elapsed since the device stopped 465 // swinging (time since device appeared to be in the process of being put down 466 // or put away into a pocket) before the proposed rotation can change. 467 private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS; 468 469 // The minimum amount of time that must have elapsed since the device stopped 470 // undergoing external acceleration before the proposed rotation can change. 471 private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS = 472 500 * NANOS_PER_MS; 473 474 // If the tilt angle remains greater than the specified angle for a minimum of 475 // the specified time, then the device is deemed to be lying flat 476 // (just chillin' on a table). 477 private static final float FLAT_ANGLE = 80; 478 private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS; 479 480 // If the tilt angle has increased by at least delta degrees within the specified amount 481 // of time, then the device is deemed to be swinging away from the user 482 // down towards flat (tilt = 90). 483 private static final float SWING_AWAY_ANGLE_DELTA = 20; 484 private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS; 485 486 // The maximum sample inter-arrival time in milliseconds. 487 // If the acceleration samples are further apart than this amount in time, we reset the 488 // state of the low-pass filter and orientation properties. This helps to handle 489 // boundary conditions when the device is turned on, wakes from suspend or there is 490 // a significant gap in samples. 491 private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS; 492 493 // The acceleration filter time constant. 494 // 495 // This time constant is used to tune the acceleration filter such that 496 // impulses and vibrational noise (think car dock) is suppressed before we 497 // try to calculate the tilt and orientation angles. 498 // 499 // The filter time constant is related to the filter cutoff frequency, which is the 500 // frequency at which signals are attenuated by 3dB (half the passband power). 501 // Each successive octave beyond this frequency is attenuated by an additional 6dB. 502 // 503 // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz 504 // is given by Fc = 1 / (2pi * t). 505 // 506 // The higher the time constant, the lower the cutoff frequency, so more noise 507 // will be suppressed. 508 // 509 // Filtering adds latency proportional the time constant (inversely proportional 510 // to the cutoff frequency) so we don't want to make the time constant too 511 // large or we can lose responsiveness. Likewise we don't want to make it too 512 // small or we do a poor job suppressing acceleration spikes. 513 // Empirically, 100ms seems to be too small and 500ms is too large. 514 private static final float FILTER_TIME_CONSTANT_MS = 200.0f; 515 516 /* State for orientation detection. */ 517 518 // Thresholds for minimum and maximum allowable deviation from gravity. 519 // 520 // If the device is undergoing external acceleration (being bumped, in a car 521 // that is turning around a corner or a plane taking off) then the magnitude 522 // may be substantially more or less than gravity. This can skew our orientation 523 // detection by making us think that up is pointed in a different direction. 524 // 525 // Conversely, if the device is in freefall, then there will be no gravity to 526 // measure at all. This is problematic because we cannot detect the orientation 527 // without gravity to tell us which way is up. A magnitude near 0 produces 528 // singularities in the tilt and orientation calculations. 529 // 530 // In both cases, we postpone choosing an orientation. 531 // 532 // However, we need to tolerate some acceleration because the angular momentum 533 // of turning the device can skew the observed acceleration for a short period of time. 534 private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2 535 private static final float ACCELERATION_TOLERANCE = 4; // m/s^2 536 private static final float MIN_ACCELERATION_MAGNITUDE = 537 SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE; 538 private static final float MAX_ACCELERATION_MAGNITUDE = 539 SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE; 540 541 // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e. 542 // when screen is facing the sky or ground), we completely ignore orientation data 543 // because it's too unstable. 544 private static final int MAX_TILT = 80; 545 546 // The tilt angle below which we conclude that the user is holding the device 547 // overhead reading in bed and lock into that state. 548 private static final int TILT_OVERHEAD_ENTER = -40; 549 550 // The tilt angle above which we conclude that the user would like a rotation 551 // change to occur and unlock from the overhead state. 552 private static final int TILT_OVERHEAD_EXIT = -15; 553 554 // The gap angle in degrees between adjacent orientation angles for hysteresis. 555 // This creates a "dead zone" between the current orientation and a proposed 556 // adjacent orientation. No orientation proposal is made when the orientation 557 // angle is within the gap between the current orientation and the adjacent 558 // orientation. 559 private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45; 560 561 // The tilt angle range in degrees for each orientation. 562 // Beyond these tilt angles, we don't even consider transitioning into the 563 // specified orientation. We place more stringent requirements on unnatural 564 // orientations than natural ones to make it less likely to accidentally transition 565 // into those states. 566 // The first value of each pair is negative so it applies a limit when the device is 567 // facing down (overhead reading in bed). 568 // The second value of each pair is positive so it applies a limit when the device is 569 // facing up (resting on a table). 570 // The ideal tilt angle is 0 (when the device is vertical) so the limits establish 571 // how close to vertical the device must be in order to change orientation. 572 private final int[][] mTiltToleranceConfig = new int[][] { 573 /* ROTATION_0 */ { -25, 70 }, // note: these are overridden by config.xml 574 /* ROTATION_90 */ { -25, 65 }, 575 /* ROTATION_180 */ { -25, 60 }, 576 /* ROTATION_270 */ { -25, 65 } 577 }; 578 579 // Timestamp and value of the last accelerometer sample. 580 private long mLastFilteredTimestampNanos; 581 private float mLastFilteredX, mLastFilteredY, mLastFilteredZ; 582 583 // The last proposed rotation, -1 if unknown. 584 private int mProposedRotation; 585 586 // Value of the current predicted rotation, -1 if unknown. 587 private int mPredictedRotation; 588 589 // Timestamp of when the predicted rotation most recently changed. 590 private long mPredictedRotationTimestampNanos; 591 592 // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed). 593 private long mFlatTimestampNanos; 594 private boolean mFlat; 595 596 // Timestamp when the device last appeared to be swinging. 597 private long mSwingTimestampNanos; 598 private boolean mSwinging; 599 600 // Timestamp when the device last appeared to be undergoing external acceleration. 601 private long mAccelerationTimestampNanos; 602 private boolean mAccelerating; 603 604 // Timestamp when the last touch to the touch screen ended 605 private long mTouchEndedTimestampNanos = Long.MIN_VALUE; 606 private boolean mTouched; 607 608 // Whether we are locked into an overhead usage mode. 609 private boolean mOverhead; 610 611 // History of observed tilt angles. 612 private static final int TILT_HISTORY_SIZE = 200; 613 private float[] mTiltHistory = new float[TILT_HISTORY_SIZE]; 614 private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE]; 615 private int mTiltHistoryIndex; 616 AccelSensorJudge(Context context)617 public AccelSensorJudge(Context context) { 618 // Load tilt tolerance configuration. 619 int[] tiltTolerance = context.getResources().getIntArray( 620 com.android.internal.R.array.config_autoRotationTiltTolerance); 621 if (tiltTolerance.length == 8) { 622 for (int i = 0; i < 4; i++) { 623 int min = tiltTolerance[i * 2]; 624 int max = tiltTolerance[i * 2 + 1]; 625 if (min >= -90 && min <= max && max <= 90) { 626 mTiltToleranceConfig[i][0] = min; 627 mTiltToleranceConfig[i][1] = max; 628 } else { 629 Slog.wtf(TAG, "config_autoRotationTiltTolerance contains invalid range: " 630 + "min=" + min + ", max=" + max); 631 } 632 } 633 } else { 634 Slog.wtf(TAG, "config_autoRotationTiltTolerance should have exactly 8 elements"); 635 } 636 } 637 638 @Override getProposedRotationLocked()639 public int getProposedRotationLocked() { 640 return mProposedRotation; 641 } 642 643 @Override dumpLocked(PrintWriter pw, String prefix)644 public void dumpLocked(PrintWriter pw, String prefix) { 645 pw.println(prefix + "AccelSensorJudge"); 646 prefix += " "; 647 pw.println(prefix + "mProposedRotation=" + mProposedRotation); 648 pw.println(prefix + "mPredictedRotation=" + mPredictedRotation); 649 pw.println(prefix + "mLastFilteredX=" + mLastFilteredX); 650 pw.println(prefix + "mLastFilteredY=" + mLastFilteredY); 651 pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ); 652 final long delta = SystemClock.elapsedRealtimeNanos() - mLastFilteredTimestampNanos; 653 pw.println(prefix + "mLastFilteredTimestampNanos=" + mLastFilteredTimestampNanos 654 + " (" + (delta * 0.000001f) + "ms ago)"); 655 pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}"); 656 pw.println(prefix + "mFlat=" + mFlat); 657 pw.println(prefix + "mSwinging=" + mSwinging); 658 pw.println(prefix + "mAccelerating=" + mAccelerating); 659 pw.println(prefix + "mOverhead=" + mOverhead); 660 pw.println(prefix + "mTouched=" + mTouched); 661 pw.print(prefix + "mTiltToleranceConfig=["); 662 for (int i = 0; i < 4; i++) { 663 if (i != 0) { 664 pw.print(", "); 665 } 666 pw.print("["); 667 pw.print(mTiltToleranceConfig[i][0]); 668 pw.print(", "); 669 pw.print(mTiltToleranceConfig[i][1]); 670 pw.print("]"); 671 } 672 pw.println("]"); 673 } 674 675 @Override onAccuracyChanged(Sensor sensor, int accuracy)676 public void onAccuracyChanged(Sensor sensor, int accuracy) { 677 } 678 679 @Override onSensorChanged(SensorEvent event)680 public void onSensorChanged(SensorEvent event) { 681 int proposedRotation; 682 int oldProposedRotation; 683 684 synchronized (mLock) { 685 // The vector given in the SensorEvent points straight up (towards the sky) under 686 // ideal conditions (the phone is not accelerating). I'll call this up vector 687 // elsewhere. 688 float x = event.values[ACCELEROMETER_DATA_X]; 689 float y = event.values[ACCELEROMETER_DATA_Y]; 690 float z = event.values[ACCELEROMETER_DATA_Z]; 691 692 if (LOG) { 693 Slog.v(TAG, "Raw acceleration vector: " 694 + "x=" + x + ", y=" + y + ", z=" + z 695 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z)); 696 } 697 698 // Apply a low-pass filter to the acceleration up vector in cartesian space. 699 // Reset the orientation listener state if the samples are too far apart in time 700 // or when we see values of (0, 0, 0) which indicates that we polled the 701 // accelerometer too soon after turning it on and we don't have any data yet. 702 final long now = event.timestamp; 703 final long then = mLastFilteredTimestampNanos; 704 final float timeDeltaMS = (now - then) * 0.000001f; 705 final boolean skipSample; 706 if (now < then 707 || now > then + MAX_FILTER_DELTA_TIME_NANOS 708 || (x == 0 && y == 0 && z == 0)) { 709 if (LOG) { 710 Slog.v(TAG, "Resetting orientation listener."); 711 } 712 resetLocked(true /* clearCurrentRotation */); 713 skipSample = true; 714 } else { 715 final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS); 716 x = alpha * (x - mLastFilteredX) + mLastFilteredX; 717 y = alpha * (y - mLastFilteredY) + mLastFilteredY; 718 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ; 719 if (LOG) { 720 Slog.v(TAG, "Filtered acceleration vector: " 721 + "x=" + x + ", y=" + y + ", z=" + z 722 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z)); 723 } 724 skipSample = false; 725 } 726 mLastFilteredTimestampNanos = now; 727 mLastFilteredX = x; 728 mLastFilteredY = y; 729 mLastFilteredZ = z; 730 731 boolean isAccelerating = false; 732 boolean isFlat = false; 733 boolean isSwinging = false; 734 if (!skipSample) { 735 // Calculate the magnitude of the acceleration vector. 736 final float magnitude = (float) Math.sqrt(x * x + y * y + z * z); 737 if (magnitude < NEAR_ZERO_MAGNITUDE) { 738 if (LOG) { 739 Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero."); 740 } 741 clearPredictedRotationLocked(); 742 } else { 743 // Determine whether the device appears to be undergoing external 744 // acceleration. 745 if (isAcceleratingLocked(magnitude)) { 746 isAccelerating = true; 747 mAccelerationTimestampNanos = now; 748 } 749 750 // Calculate the tilt angle. 751 // This is the angle between the up vector and the x-y plane (the plane of 752 // the screen) in a range of [-90, 90] degrees. 753 // -90 degrees: screen horizontal and facing the ground (overhead) 754 // 0 degrees: screen vertical 755 // 90 degrees: screen horizontal and facing the sky (on table) 756 final int tiltAngle = (int) Math.round( 757 Math.asin(z / magnitude) * RADIANS_TO_DEGREES); 758 addTiltHistoryEntryLocked(now, tiltAngle); 759 760 // Determine whether the device appears to be flat or swinging. 761 if (isFlatLocked(now)) { 762 isFlat = true; 763 mFlatTimestampNanos = now; 764 } 765 if (isSwingingLocked(now, tiltAngle)) { 766 isSwinging = true; 767 mSwingTimestampNanos = now; 768 } 769 770 // If the tilt angle is too close to horizontal then we cannot determine 771 // the orientation angle of the screen. 772 if (tiltAngle <= TILT_OVERHEAD_ENTER) { 773 mOverhead = true; 774 } else if (tiltAngle >= TILT_OVERHEAD_EXIT) { 775 mOverhead = false; 776 } 777 if (mOverhead) { 778 if (LOG) { 779 Slog.v(TAG, "Ignoring sensor data, device is overhead: " 780 + "tiltAngle=" + tiltAngle); 781 } 782 clearPredictedRotationLocked(); 783 } else if (Math.abs(tiltAngle) > MAX_TILT) { 784 if (LOG) { 785 Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " 786 + "tiltAngle=" + tiltAngle); 787 } 788 clearPredictedRotationLocked(); 789 } else { 790 // Calculate the orientation angle. 791 // This is the angle between the x-y projection of the up vector onto 792 // the +y-axis, increasing clockwise in a range of [0, 360] degrees. 793 int orientationAngle = (int) Math.round( 794 -Math.atan2(-x, y) * RADIANS_TO_DEGREES); 795 if (orientationAngle < 0) { 796 // atan2 returns [-180, 180]; normalize to [0, 360] 797 orientationAngle += 360; 798 } 799 800 // Find the nearest rotation. 801 int nearestRotation = (orientationAngle + 45) / 90; 802 if (nearestRotation == 4) { 803 nearestRotation = 0; 804 } 805 806 // Determine the predicted orientation. 807 if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle) 808 && isOrientationAngleAcceptableLocked(nearestRotation, 809 orientationAngle)) { 810 updatePredictedRotationLocked(now, nearestRotation); 811 if (LOG) { 812 Slog.v(TAG, "Predicted: " 813 + "tiltAngle=" + tiltAngle 814 + ", orientationAngle=" + orientationAngle 815 + ", predictedRotation=" + mPredictedRotation 816 + ", predictedRotationAgeMS=" 817 + ((now - mPredictedRotationTimestampNanos) 818 * 0.000001f)); 819 } 820 } else { 821 if (LOG) { 822 Slog.v(TAG, "Ignoring sensor data, no predicted rotation: " 823 + "tiltAngle=" + tiltAngle 824 + ", orientationAngle=" + orientationAngle); 825 } 826 clearPredictedRotationLocked(); 827 } 828 } 829 } 830 } 831 mFlat = isFlat; 832 mSwinging = isSwinging; 833 mAccelerating = isAccelerating; 834 835 // Determine new proposed rotation. 836 oldProposedRotation = mProposedRotation; 837 if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) { 838 mProposedRotation = mPredictedRotation; 839 } 840 proposedRotation = mProposedRotation; 841 842 // Write final statistics about where we are in the orientation detection process. 843 if (LOG) { 844 Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation 845 + ", proposedRotation=" + proposedRotation 846 + ", predictedRotation=" + mPredictedRotation 847 + ", timeDeltaMS=" + timeDeltaMS 848 + ", isAccelerating=" + isAccelerating 849 + ", isFlat=" + isFlat 850 + ", isSwinging=" + isSwinging 851 + ", isOverhead=" + mOverhead 852 + ", isTouched=" + mTouched 853 + ", timeUntilSettledMS=" + remainingMS(now, 854 mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) 855 + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, 856 mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) 857 + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now, 858 mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) 859 + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now, 860 mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) 861 + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now, 862 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS)); 863 } 864 } 865 866 // Tell the listener. 867 if (proposedRotation != oldProposedRotation && proposedRotation >= 0) { 868 if (LOG) { 869 Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation 870 + ", oldProposedRotation=" + oldProposedRotation); 871 } 872 onProposedRotationChanged(proposedRotation); 873 } 874 } 875 876 @Override onTouchStartLocked()877 public void onTouchStartLocked() { 878 mTouched = true; 879 } 880 881 @Override onTouchEndLocked(long whenElapsedNanos)882 public void onTouchEndLocked(long whenElapsedNanos) { 883 mTouched = false; 884 mTouchEndedTimestampNanos = whenElapsedNanos; 885 } 886 887 @Override resetLocked(boolean clearCurrentRotation)888 public void resetLocked(boolean clearCurrentRotation) { 889 mLastFilteredTimestampNanos = Long.MIN_VALUE; 890 if (clearCurrentRotation) { 891 mProposedRotation = -1; 892 } 893 mFlatTimestampNanos = Long.MIN_VALUE; 894 mFlat = false; 895 mSwingTimestampNanos = Long.MIN_VALUE; 896 mSwinging = false; 897 mAccelerationTimestampNanos = Long.MIN_VALUE; 898 mAccelerating = false; 899 mOverhead = false; 900 clearPredictedRotationLocked(); 901 clearTiltHistoryLocked(); 902 } 903 904 905 /** 906 * Returns true if the tilt angle is acceptable for a given predicted rotation. 907 */ isTiltAngleAcceptableLocked(int rotation, int tiltAngle)908 private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) { 909 return tiltAngle >= mTiltToleranceConfig[rotation][0] 910 && tiltAngle <= mTiltToleranceConfig[rotation][1]; 911 } 912 913 /** 914 * Returns true if the orientation angle is acceptable for a given predicted rotation. 915 * 916 * This function takes into account the gap between adjacent orientations 917 * for hysteresis. 918 */ isOrientationAngleAcceptableLocked(int rotation, int orientationAngle)919 private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) { 920 // If there is no current rotation, then there is no gap. 921 // The gap is used only to introduce hysteresis among advertised orientation 922 // changes to avoid flapping. 923 final int currentRotation = mCurrentRotation; 924 if (currentRotation >= 0) { 925 // If the specified rotation is the same or is counter-clockwise adjacent 926 // to the current rotation, then we set a lower bound on the orientation angle. 927 // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90, 928 // then we want to check orientationAngle > 45 + GAP / 2. 929 if (rotation == currentRotation 930 || rotation == (currentRotation + 1) % 4) { 931 int lowerBound = rotation * 90 - 45 932 + ADJACENT_ORIENTATION_ANGLE_GAP / 2; 933 if (rotation == 0) { 934 if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) { 935 return false; 936 } 937 } else { 938 if (orientationAngle < lowerBound) { 939 return false; 940 } 941 } 942 } 943 944 // If the specified rotation is the same or is clockwise adjacent, 945 // then we set an upper bound on the orientation angle. 946 // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270, 947 // then we want to check orientationAngle < 315 - GAP / 2. 948 if (rotation == currentRotation 949 || rotation == (currentRotation + 3) % 4) { 950 int upperBound = rotation * 90 + 45 951 - ADJACENT_ORIENTATION_ANGLE_GAP / 2; 952 if (rotation == 0) { 953 if (orientationAngle <= 45 && orientationAngle > upperBound) { 954 return false; 955 } 956 } else { 957 if (orientationAngle > upperBound) { 958 return false; 959 } 960 } 961 } 962 } 963 return true; 964 } 965 966 /** 967 * Returns true if the predicted rotation is ready to be advertised as a 968 * proposed rotation. 969 */ isPredictedRotationAcceptableLocked(long now)970 private boolean isPredictedRotationAcceptableLocked(long now) { 971 // The predicted rotation must have settled long enough. 972 if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) { 973 return false; 974 } 975 976 // The last flat state (time since picked up) must have been sufficiently long ago. 977 if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) { 978 return false; 979 } 980 981 // The last swing state (time since last movement to put down) must have been 982 // sufficiently long ago. 983 if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) { 984 return false; 985 } 986 987 // The last acceleration state must have been sufficiently long ago. 988 if (now < mAccelerationTimestampNanos 989 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) { 990 return false; 991 } 992 993 // The last touch must have ended sufficiently long ago. 994 if (mTouched || now < mTouchEndedTimestampNanos 995 + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) { 996 return false; 997 } 998 999 // Looks good! 1000 return true; 1001 } 1002 clearPredictedRotationLocked()1003 private void clearPredictedRotationLocked() { 1004 mPredictedRotation = -1; 1005 mPredictedRotationTimestampNanos = Long.MIN_VALUE; 1006 } 1007 updatePredictedRotationLocked(long now, int rotation)1008 private void updatePredictedRotationLocked(long now, int rotation) { 1009 if (mPredictedRotation != rotation) { 1010 mPredictedRotation = rotation; 1011 mPredictedRotationTimestampNanos = now; 1012 } 1013 } 1014 isAcceleratingLocked(float magnitude)1015 private boolean isAcceleratingLocked(float magnitude) { 1016 return magnitude < MIN_ACCELERATION_MAGNITUDE 1017 || magnitude > MAX_ACCELERATION_MAGNITUDE; 1018 } 1019 clearTiltHistoryLocked()1020 private void clearTiltHistoryLocked() { 1021 mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE; 1022 mTiltHistoryIndex = 1; 1023 } 1024 addTiltHistoryEntryLocked(long now, float tilt)1025 private void addTiltHistoryEntryLocked(long now, float tilt) { 1026 mTiltHistory[mTiltHistoryIndex] = tilt; 1027 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now; 1028 mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE; 1029 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE; 1030 } 1031 isFlatLocked(long now)1032 private boolean isFlatLocked(long now) { 1033 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) { 1034 if (mTiltHistory[i] < FLAT_ANGLE) { 1035 break; 1036 } 1037 if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) { 1038 // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS. 1039 return true; 1040 } 1041 } 1042 return false; 1043 } 1044 isSwingingLocked(long now, float tilt)1045 private boolean isSwingingLocked(long now, float tilt) { 1046 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) { 1047 if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) { 1048 break; 1049 } 1050 if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) { 1051 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS. 1052 return true; 1053 } 1054 } 1055 return false; 1056 } 1057 nextTiltHistoryIndexLocked(int index)1058 private int nextTiltHistoryIndexLocked(int index) { 1059 index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1; 1060 return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1; 1061 } 1062 getLastTiltLocked()1063 private float getLastTiltLocked() { 1064 int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex); 1065 return index >= 0 ? mTiltHistory[index] : Float.NaN; 1066 } 1067 remainingMS(long now, long until)1068 private float remainingMS(long now, long until) { 1069 return now >= until ? 0 : (until - now) * 0.000001f; 1070 } 1071 } 1072 1073 final class OrientationSensorJudge extends OrientationJudge { 1074 private static final int ROTATION_UNSET = -1; 1075 private boolean mTouching; 1076 private long mTouchEndedTimestampNanos = Long.MIN_VALUE; 1077 private int mProposedRotation = -1; 1078 private int mDesiredRotation = -1; 1079 private boolean mRotationEvaluationScheduled; 1080 private long mRotationResolverTimeoutMillis; 1081 private long mRotationMemorizationTimeoutMillis; 1082 private final ActivityTaskManagerInternal mActivityTaskManagerInternal; 1083 private long mLastRotationResolutionTimeStamp; 1084 private int mLastRotationResolution = ROTATION_UNSET; 1085 private int mCurrentCallbackId = 0; 1086 private Runnable mCancelRotationResolverRequest; 1087 OrientationSensorJudge()1088 OrientationSensorJudge() { 1089 super(); 1090 setupRotationResolverParameters(); 1091 1092 mActivityTaskManagerInternal = 1093 LocalServices.getService(ActivityTaskManagerInternal.class); 1094 } 1095 setupRotationResolverParameters()1096 private void setupRotationResolverParameters() { 1097 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_WINDOW_MANAGER, 1098 ActivityThread.currentApplication().getMainExecutor(), (properties) -> { 1099 final Set<String> keys = properties.getKeyset(); 1100 if (keys.contains(KEY_ROTATION_RESOLVER_TIMEOUT) 1101 || keys.contains(KEY_ROTATION_MEMORIZATION_TIMEOUT)) { 1102 readRotationResolverParameters(); 1103 } 1104 }); 1105 readRotationResolverParameters(); 1106 } 1107 readRotationResolverParameters()1108 private void readRotationResolverParameters() { 1109 mRotationResolverTimeoutMillis = DeviceConfig.getLong( 1110 NAMESPACE_WINDOW_MANAGER, 1111 KEY_ROTATION_RESOLVER_TIMEOUT, 1112 DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS); 1113 mRotationMemorizationTimeoutMillis = DeviceConfig.getLong( 1114 NAMESPACE_WINDOW_MANAGER, 1115 KEY_ROTATION_MEMORIZATION_TIMEOUT, 1116 DEFAULT_ROTATION_MEMORIZATION_TIMEOUT_MILLIS); 1117 } 1118 1119 @Override getProposedRotationLocked()1120 public int getProposedRotationLocked() { 1121 return mProposedRotation; 1122 } 1123 1124 @Override onTouchStartLocked()1125 public void onTouchStartLocked() { 1126 mTouching = true; 1127 } 1128 1129 @Override onTouchEndLocked(long whenElapsedNanos)1130 public void onTouchEndLocked(long whenElapsedNanos) { 1131 mTouching = false; 1132 mTouchEndedTimestampNanos = whenElapsedNanos; 1133 if (mDesiredRotation != mProposedRotation) { 1134 final long now = SystemClock.elapsedRealtimeNanos(); 1135 scheduleRotationEvaluationIfNecessaryLocked(now); 1136 } 1137 } 1138 1139 1140 @Override onSensorChanged(SensorEvent event)1141 public void onSensorChanged(SensorEvent event) { 1142 int reportedRotation = (int) event.values[0]; 1143 if (reportedRotation < 0 || reportedRotation > 3) { 1144 return; 1145 } 1146 1147 FrameworkStatsLog.write( 1148 FrameworkStatsLog.DEVICE_ROTATED, 1149 event.timestamp, 1150 rotationToLogEnum(reportedRotation), 1151 FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT); 1152 1153 if (isRotationResolverEnabled()) { 1154 if (isKeyguardLocked()) { 1155 if (mLastRotationResolution != ROTATION_UNSET 1156 && SystemClock.uptimeMillis() - mLastRotationResolutionTimeStamp 1157 < mRotationMemorizationTimeoutMillis) { 1158 Slog.d(TAG, 1159 "Reusing the last rotation resolution: " + mLastRotationResolution); 1160 finalizeRotation(mLastRotationResolution); 1161 } else { 1162 finalizeRotation(Surface.ROTATION_0); 1163 } 1164 return; 1165 } 1166 1167 if (mRotationResolverService == null) { 1168 mRotationResolverService = LocalServices.getService( 1169 RotationResolverInternal.class); 1170 } 1171 1172 String packageName = null; 1173 if (mActivityTaskManagerInternal != null) { 1174 final WindowProcessController controller = 1175 mActivityTaskManagerInternal.getTopApp(); 1176 if (controller != null 1177 && controller.mInfo != null 1178 && controller.mInfo.packageName != null) { 1179 packageName = controller.mInfo.packageName; 1180 } 1181 } 1182 1183 mCurrentCallbackId++; 1184 1185 if (mCancelRotationResolverRequest != null) { 1186 getHandler().removeCallbacks(mCancelRotationResolverRequest); 1187 } 1188 final CancellationSignal cancellationSignal = new CancellationSignal(); 1189 mCancelRotationResolverRequest = cancellationSignal::cancel; 1190 getHandler().postDelayed( 1191 mCancelRotationResolverRequest, mRotationResolverTimeoutMillis); 1192 1193 mRotationResolverService.resolveRotation( 1194 new RotationResolverInternal.RotationResolverCallbackInternal() { 1195 private final int mCallbackId = mCurrentCallbackId; 1196 @Override 1197 public void onSuccess(int result) { 1198 finalizeRotationIfFresh(result); 1199 } 1200 1201 @Override 1202 public void onFailure(int error) { 1203 finalizeRotationIfFresh(reportedRotation); 1204 } 1205 1206 private void finalizeRotationIfFresh(int rotation) { 1207 // Ignore the callback if it's not the latest. 1208 if (mCallbackId == mCurrentCallbackId) { 1209 getHandler().removeCallbacks(mCancelRotationResolverRequest); 1210 finalizeRotation(rotation); 1211 } else { 1212 Slog.d(TAG, String.format( 1213 "An outdated callback received [%s vs. %s]. Ignoring " 1214 + "it.", 1215 mCallbackId, mCurrentCallbackId)); 1216 } 1217 } 1218 }, 1219 packageName, 1220 reportedRotation, 1221 mCurrentRotation, 1222 mRotationResolverTimeoutMillis, 1223 cancellationSignal); 1224 } else { 1225 finalizeRotation(reportedRotation); 1226 } 1227 } 1228 1229 @Override onAccuracyChanged(Sensor sensor, int accuracy)1230 public void onAccuracyChanged(Sensor sensor, int accuracy) { } 1231 1232 @Override dumpLocked(PrintWriter pw, String prefix)1233 public void dumpLocked(PrintWriter pw, String prefix) { 1234 pw.println(prefix + "OrientationSensorJudge"); 1235 prefix += " "; 1236 pw.println(prefix + "mDesiredRotation=" + Surface.rotationToString(mDesiredRotation)); 1237 pw.println(prefix + "mProposedRotation=" 1238 + Surface.rotationToString(mProposedRotation)); 1239 pw.println(prefix + "mTouching=" + mTouching); 1240 pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos); 1241 pw.println(prefix + "mLastRotationResolution=" + mLastRotationResolution); 1242 } 1243 1244 @Override resetLocked(boolean clearCurrentRotation)1245 public void resetLocked(boolean clearCurrentRotation) { 1246 if (clearCurrentRotation) { 1247 mProposedRotation = -1; 1248 mDesiredRotation = -1; 1249 } 1250 mTouching = false; 1251 mTouchEndedTimestampNanos = Long.MIN_VALUE; 1252 unscheduleRotationEvaluationLocked(); 1253 } 1254 evaluateRotationChangeLocked()1255 public int evaluateRotationChangeLocked() { 1256 unscheduleRotationEvaluationLocked(); 1257 if (mDesiredRotation == mProposedRotation) { 1258 return -1; 1259 } 1260 final long now = SystemClock.elapsedRealtimeNanos(); 1261 if (isDesiredRotationAcceptableLocked(now)) { 1262 mProposedRotation = mDesiredRotation; 1263 return mProposedRotation; 1264 } else { 1265 scheduleRotationEvaluationIfNecessaryLocked(now); 1266 } 1267 return -1; 1268 } 1269 finalizeRotation(int reportedRotation)1270 private void finalizeRotation(int reportedRotation) { 1271 int newRotation; 1272 synchronized (mLock) { 1273 mDesiredRotation = reportedRotation; 1274 newRotation = evaluateRotationChangeLocked(); 1275 } 1276 if (newRotation >= 0) { 1277 mLastRotationResolution = newRotation; 1278 mLastRotationResolutionTimeStamp = SystemClock.uptimeMillis(); 1279 onProposedRotationChanged(newRotation); 1280 } 1281 } 1282 isDesiredRotationAcceptableLocked(long now)1283 private boolean isDesiredRotationAcceptableLocked(long now) { 1284 if (mTouching) { 1285 return false; 1286 } 1287 if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) { 1288 return false; 1289 } 1290 return true; 1291 } 1292 scheduleRotationEvaluationIfNecessaryLocked(long now)1293 private void scheduleRotationEvaluationIfNecessaryLocked(long now) { 1294 if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) { 1295 if (LOG) { 1296 Slog.d(TAG, "scheduleRotationEvaluationLocked: " + 1297 "ignoring, an evaluation is already scheduled or is unnecessary."); 1298 } 1299 return; 1300 } 1301 if (mTouching) { 1302 if (LOG) { 1303 Slog.d(TAG, "scheduleRotationEvaluationLocked: " + 1304 "ignoring, user is still touching the screen."); 1305 } 1306 return; 1307 } 1308 long timeOfNextPossibleRotationNanos = 1309 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS; 1310 if (now >= timeOfNextPossibleRotationNanos) { 1311 if (LOG) { 1312 Slog.d(TAG, "scheduleRotationEvaluationLocked: " + 1313 "ignoring, already past the next possible time of rotation."); 1314 } 1315 return; 1316 } 1317 // Use a delay instead of an absolute time since handlers are in uptime millis and we 1318 // use elapsed realtime. 1319 final long delayMs = 1320 (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO); 1321 mHandler.postDelayed(mRotationEvaluator, delayMs); 1322 mRotationEvaluationScheduled = true; 1323 } 1324 unscheduleRotationEvaluationLocked()1325 private void unscheduleRotationEvaluationLocked() { 1326 if (!mRotationEvaluationScheduled) { 1327 return; 1328 } 1329 mHandler.removeCallbacks(mRotationEvaluator); 1330 mRotationEvaluationScheduled = false; 1331 } 1332 1333 private Runnable mRotationEvaluator = new Runnable() { 1334 @Override 1335 public void run() { 1336 int newRotation; 1337 synchronized (mLock) { 1338 mRotationEvaluationScheduled = false; 1339 newRotation = evaluateRotationChangeLocked(); 1340 } 1341 if (newRotation >= 0) { 1342 onProposedRotationChanged(newRotation); 1343 } 1344 } 1345 }; 1346 rotationToLogEnum(int rotation)1347 private int rotationToLogEnum(int rotation) { 1348 switch (rotation) { 1349 case 0: 1350 return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_0; 1351 case 1: 1352 return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_90; 1353 case 2: 1354 return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_180; 1355 case 3: 1356 return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_270; 1357 default: 1358 return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__UNKNOWN; 1359 } 1360 } 1361 } 1362 } 1363