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