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