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.internal.policy.impl; 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.SystemProperties; 26 import android.util.FloatMath; 27 import android.util.Log; 28 import android.util.Slog; 29 30 /** 31 * A special helper class used by the WindowManager 32 * for receiving notifications from the SensorManager when 33 * the orientation of the device has changed. 34 * 35 * NOTE: If changing anything here, please run the API demo 36 * "App/Activity/Screen Orientation" to ensure that all orientation 37 * modes still work correctly. 38 * 39 * You can also visualize the behavior of the WindowOrientationListener. 40 * Refer to frameworks/base/tools/orientationplot/README.txt for details. 41 * 42 * @hide 43 */ 44 public abstract class WindowOrientationListener { 45 private static final String TAG = "WindowOrientationListener"; 46 private static final boolean LOG = SystemProperties.getBoolean( 47 "debug.orientation.log", false); 48 49 private static final boolean USE_GRAVITY_SENSOR = false; 50 51 private Handler mHandler; 52 private SensorManager mSensorManager; 53 private boolean mEnabled; 54 private int mRate; 55 private Sensor mSensor; 56 private SensorEventListenerImpl mSensorEventListener; 57 private int mCurrentRotation = -1; 58 59 private final Object mLock = new Object(); 60 61 /** 62 * Creates a new WindowOrientationListener. 63 * 64 * @param context for the WindowOrientationListener. 65 * @param handler Provides the Looper for receiving sensor updates. 66 */ 67 public WindowOrientationListener(Context context, Handler handler) { 68 this(context, handler, SensorManager.SENSOR_DELAY_UI); 69 } 70 71 /** 72 * Creates a new WindowOrientationListener. 73 * 74 * @param context for the WindowOrientationListener. 75 * @param handler Provides the Looper for receiving sensor updates. 76 * @param rate at which sensor events are processed (see also 77 * {@link android.hardware.SensorManager SensorManager}). Use the default 78 * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 79 * SENSOR_DELAY_NORMAL} for simple screen orientation change detection. 80 * 81 * This constructor is private since no one uses it. 82 */ 83 private WindowOrientationListener(Context context, Handler handler, int rate) { 84 mHandler = handler; 85 mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 86 mRate = rate; 87 mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR 88 ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); 89 if (mSensor != null) { 90 // Create listener only if sensors do exist 91 mSensorEventListener = new SensorEventListenerImpl(); 92 } 93 } 94 95 /** 96 * Enables the WindowOrientationListener so it will monitor the sensor and call 97 * {@link #onProposedRotationChanged(int)} when the device orientation changes. 98 */ 99 public void enable() { 100 synchronized (mLock) { 101 if (mSensor == null) { 102 Log.w(TAG, "Cannot detect sensors. Not enabled"); 103 return; 104 } 105 if (mEnabled == false) { 106 if (LOG) { 107 Log.d(TAG, "WindowOrientationListener enabled"); 108 } 109 mSensorEventListener.resetLocked(); 110 mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler); 111 mEnabled = true; 112 } 113 } 114 } 115 116 /** 117 * Disables the WindowOrientationListener. 118 */ 119 public void disable() { 120 synchronized (mLock) { 121 if (mSensor == null) { 122 Log.w(TAG, "Cannot detect sensors. Invalid disable"); 123 return; 124 } 125 if (mEnabled == true) { 126 if (LOG) { 127 Log.d(TAG, "WindowOrientationListener disabled"); 128 } 129 mSensorManager.unregisterListener(mSensorEventListener); 130 mEnabled = false; 131 } 132 } 133 } 134 135 /** 136 * Sets the current rotation. 137 * 138 * @param rotation The current rotation. 139 */ 140 public void setCurrentRotation(int rotation) { 141 synchronized (mLock) { 142 mCurrentRotation = rotation; 143 } 144 } 145 146 /** 147 * Gets the proposed rotation. 148 * 149 * This method only returns a rotation if the orientation listener is certain 150 * of its proposal. If the rotation is indeterminate, returns -1. 151 * 152 * @return The proposed rotation, or -1 if unknown. 153 */ 154 public int getProposedRotation() { 155 synchronized (mLock) { 156 if (mEnabled) { 157 return mSensorEventListener.getProposedRotationLocked(); 158 } 159 return -1; 160 } 161 } 162 163 /** 164 * Returns true if sensor is enabled and false otherwise 165 */ 166 public boolean canDetectOrientation() { 167 synchronized (mLock) { 168 return mSensor != null; 169 } 170 } 171 172 /** 173 * Called when the rotation view of the device has changed. 174 * 175 * This method is called whenever the orientation becomes certain of an orientation. 176 * It is called each time the orientation determination transitions from being 177 * uncertain to being certain again, even if it is the same orientation as before. 178 * 179 * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. 180 * @see android.view.Surface 181 */ 182 public abstract void onProposedRotationChanged(int rotation); 183 184 /** 185 * This class filters the raw accelerometer data and tries to detect actual changes in 186 * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, 187 * but here's the outline: 188 * 189 * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in 190 * cartesian space because the orientation calculations are sensitive to the 191 * absolute magnitude of the acceleration. In particular, there are singularities 192 * in the calculation as the magnitude approaches 0. By performing the low-pass 193 * filtering early, we can eliminate most spurious high-frequency impulses due to noise. 194 * 195 * - Convert the acceleromter vector from cartesian to spherical coordinates. 196 * Since we're dealing with rotation of the device, this is the sensible coordinate 197 * system to work in. The zenith direction is the Z-axis, the direction the screen 198 * is facing. The radial distance is referred to as the magnitude below. 199 * The elevation angle is referred to as the "tilt" below. 200 * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is 201 * the Y-axis). 202 * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference. 203 * 204 * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing. 205 * The orientation angle is not meaningful when the device is nearly horizontal. 206 * The tilt angle thresholds are set differently for each orientation and different 207 * limits are applied when the device is facing down as opposed to when it is facing 208 * forward or facing up. 209 * 210 * - When the orientation angle reaches a certain threshold, consider transitioning 211 * to the corresponding orientation. These thresholds have some hysteresis built-in 212 * to avoid oscillations between adjacent orientations. 213 * 214 * - Wait for the device to settle for a little bit. Once that happens, issue the 215 * new orientation proposal. 216 * 217 * Details are explained inline. 218 * 219 * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for 220 * signal processing background. 221 */ 222 final class SensorEventListenerImpl implements SensorEventListener { 223 // We work with all angles in degrees in this class. 224 private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI); 225 226 // Number of nanoseconds per millisecond. 227 private static final long NANOS_PER_MS = 1000000; 228 229 // Indices into SensorEvent.values for the accelerometer sensor. 230 private static final int ACCELEROMETER_DATA_X = 0; 231 private static final int ACCELEROMETER_DATA_Y = 1; 232 private static final int ACCELEROMETER_DATA_Z = 2; 233 234 // The minimum amount of time that a predicted rotation must be stable before it 235 // is accepted as a valid rotation proposal. This value can be quite small because 236 // the low-pass filter already suppresses most of the noise so we're really just 237 // looking for quick confirmation that the last few samples are in agreement as to 238 // the desired orientation. 239 private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS; 240 241 // The minimum amount of time that must have elapsed since the device last exited 242 // the flat state (time since it was picked up) before the proposed rotation 243 // can change. 244 private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS; 245 246 // The minimum amount of time that must have elapsed since the device stopped 247 // swinging (time since device appeared to be in the process of being put down 248 // or put away into a pocket) before the proposed rotation can change. 249 private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS; 250 251 // The minimum amount of time that must have elapsed since the device stopped 252 // undergoing external acceleration before the proposed rotation can change. 253 private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS = 254 500 * NANOS_PER_MS; 255 256 // If the tilt angle remains greater than the specified angle for a minimum of 257 // the specified time, then the device is deemed to be lying flat 258 // (just chillin' on a table). 259 private static final float FLAT_ANGLE = 75; 260 private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS; 261 262 // If the tilt angle has increased by at least delta degrees within the specified amount 263 // of time, then the device is deemed to be swinging away from the user 264 // down towards flat (tilt = 90). 265 private static final float SWING_AWAY_ANGLE_DELTA = 20; 266 private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS; 267 268 // The maximum sample inter-arrival time in milliseconds. 269 // If the acceleration samples are further apart than this amount in time, we reset the 270 // state of the low-pass filter and orientation properties. This helps to handle 271 // boundary conditions when the device is turned on, wakes from suspend or there is 272 // a significant gap in samples. 273 private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS; 274 275 // The acceleration filter time constant. 276 // 277 // This time constant is used to tune the acceleration filter such that 278 // impulses and vibrational noise (think car dock) is suppressed before we 279 // try to calculate the tilt and orientation angles. 280 // 281 // The filter time constant is related to the filter cutoff frequency, which is the 282 // frequency at which signals are attenuated by 3dB (half the passband power). 283 // Each successive octave beyond this frequency is attenuated by an additional 6dB. 284 // 285 // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz 286 // is given by Fc = 1 / (2pi * t). 287 // 288 // The higher the time constant, the lower the cutoff frequency, so more noise 289 // will be suppressed. 290 // 291 // Filtering adds latency proportional the time constant (inversely proportional 292 // to the cutoff frequency) so we don't want to make the time constant too 293 // large or we can lose responsiveness. Likewise we don't want to make it too 294 // small or we do a poor job suppressing acceleration spikes. 295 // Empirically, 100ms seems to be too small and 500ms is too large. 296 private static final float FILTER_TIME_CONSTANT_MS = 200.0f; 297 298 /* State for orientation detection. */ 299 300 // Thresholds for minimum and maximum allowable deviation from gravity. 301 // 302 // If the device is undergoing external acceleration (being bumped, in a car 303 // that is turning around a corner or a plane taking off) then the magnitude 304 // may be substantially more or less than gravity. This can skew our orientation 305 // detection by making us think that up is pointed in a different direction. 306 // 307 // Conversely, if the device is in freefall, then there will be no gravity to 308 // measure at all. This is problematic because we cannot detect the orientation 309 // without gravity to tell us which way is up. A magnitude near 0 produces 310 // singularities in the tilt and orientation calculations. 311 // 312 // In both cases, we postpone choosing an orientation. 313 // 314 // However, we need to tolerate some acceleration because the angular momentum 315 // of turning the device can skew the observed acceleration for a short period of time. 316 private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2 317 private static final float ACCELERATION_TOLERANCE = 4; // m/s^2 318 private static final float MIN_ACCELERATION_MAGNITUDE = 319 SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE; 320 private static final float MAX_ACCELERATION_MAGNITUDE = 321 SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE; 322 323 // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e. 324 // when screen is facing the sky or ground), we completely ignore orientation data. 325 private static final int MAX_TILT = 75; 326 327 // The tilt angle range in degrees for each orientation. 328 // Beyond these tilt angles, we don't even consider transitioning into the 329 // specified orientation. We place more stringent requirements on unnatural 330 // orientations than natural ones to make it less likely to accidentally transition 331 // into those states. 332 // The first value of each pair is negative so it applies a limit when the device is 333 // facing down (overhead reading in bed). 334 // The second value of each pair is positive so it applies a limit when the device is 335 // facing up (resting on a table). 336 // The ideal tilt angle is 0 (when the device is vertical) so the limits establish 337 // how close to vertical the device must be in order to change orientation. 338 private final int[][] TILT_TOLERANCE = new int[][] { 339 /* ROTATION_0 */ { -25, 70 }, 340 /* ROTATION_90 */ { -25, 65 }, 341 /* ROTATION_180 */ { -25, 60 }, 342 /* ROTATION_270 */ { -25, 65 } 343 }; 344 345 // The gap angle in degrees between adjacent orientation angles for hysteresis. 346 // This creates a "dead zone" between the current orientation and a proposed 347 // adjacent orientation. No orientation proposal is made when the orientation 348 // angle is within the gap between the current orientation and the adjacent 349 // orientation. 350 private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45; 351 352 // Timestamp and value of the last accelerometer sample. 353 private long mLastFilteredTimestampNanos; 354 private float mLastFilteredX, mLastFilteredY, mLastFilteredZ; 355 356 // The last proposed rotation, -1 if unknown. 357 private int mProposedRotation; 358 359 // Value of the current predicted rotation, -1 if unknown. 360 private int mPredictedRotation; 361 362 // Timestamp of when the predicted rotation most recently changed. 363 private long mPredictedRotationTimestampNanos; 364 365 // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed). 366 private long mFlatTimestampNanos; 367 368 // Timestamp when the device last appeared to be swinging. 369 private long mSwingTimestampNanos; 370 371 // Timestamp when the device last appeared to be undergoing external acceleration. 372 private long mAccelerationTimestampNanos; 373 374 // History of observed tilt angles. 375 private static final int TILT_HISTORY_SIZE = 40; 376 private float[] mTiltHistory = new float[TILT_HISTORY_SIZE]; 377 private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE]; 378 private int mTiltHistoryIndex; 379 380 public int getProposedRotationLocked() { 381 return mProposedRotation; 382 } 383 384 @Override 385 public void onAccuracyChanged(Sensor sensor, int accuracy) { 386 } 387 388 @Override 389 public void onSensorChanged(SensorEvent event) { 390 int proposedRotation; 391 int oldProposedRotation; 392 393 synchronized (mLock) { 394 // The vector given in the SensorEvent points straight up (towards the sky) under 395 // ideal conditions (the phone is not accelerating). I'll call this up vector 396 // elsewhere. 397 float x = event.values[ACCELEROMETER_DATA_X]; 398 float y = event.values[ACCELEROMETER_DATA_Y]; 399 float z = event.values[ACCELEROMETER_DATA_Z]; 400 401 if (LOG) { 402 Slog.v(TAG, "Raw acceleration vector: " 403 + "x=" + x + ", y=" + y + ", z=" + z 404 + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); 405 } 406 407 // Apply a low-pass filter to the acceleration up vector in cartesian space. 408 // Reset the orientation listener state if the samples are too far apart in time 409 // or when we see values of (0, 0, 0) which indicates that we polled the 410 // accelerometer too soon after turning it on and we don't have any data yet. 411 final long now = event.timestamp; 412 final long then = mLastFilteredTimestampNanos; 413 final float timeDeltaMS = (now - then) * 0.000001f; 414 final boolean skipSample; 415 if (now < then 416 || now > then + MAX_FILTER_DELTA_TIME_NANOS 417 || (x == 0 && y == 0 && z == 0)) { 418 if (LOG) { 419 Slog.v(TAG, "Resetting orientation listener."); 420 } 421 resetLocked(); 422 skipSample = true; 423 } else { 424 final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS); 425 x = alpha * (x - mLastFilteredX) + mLastFilteredX; 426 y = alpha * (y - mLastFilteredY) + mLastFilteredY; 427 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ; 428 if (LOG) { 429 Slog.v(TAG, "Filtered acceleration vector: " 430 + "x=" + x + ", y=" + y + ", z=" + z 431 + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); 432 } 433 skipSample = false; 434 } 435 mLastFilteredTimestampNanos = now; 436 mLastFilteredX = x; 437 mLastFilteredY = y; 438 mLastFilteredZ = z; 439 440 boolean isAccelerating = false; 441 boolean isFlat = false; 442 boolean isSwinging = false; 443 if (!skipSample) { 444 // Calculate the magnitude of the acceleration vector. 445 final float magnitude = FloatMath.sqrt(x * x + y * y + z * z); 446 if (magnitude < NEAR_ZERO_MAGNITUDE) { 447 if (LOG) { 448 Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero."); 449 } 450 clearPredictedRotationLocked(); 451 } else { 452 // Determine whether the device appears to be undergoing external 453 // acceleration. 454 if (isAcceleratingLocked(magnitude)) { 455 isAccelerating = true; 456 mAccelerationTimestampNanos = now; 457 } 458 459 // Calculate the tilt angle. 460 // This is the angle between the up vector and the x-y plane (the plane of 461 // the screen) in a range of [-90, 90] degrees. 462 // -90 degrees: screen horizontal and facing the ground (overhead) 463 // 0 degrees: screen vertical 464 // 90 degrees: screen horizontal and facing the sky (on table) 465 final int tiltAngle = (int) Math.round( 466 Math.asin(z / magnitude) * RADIANS_TO_DEGREES); 467 addTiltHistoryEntryLocked(now, tiltAngle); 468 469 // Determine whether the device appears to be flat or swinging. 470 if (isFlatLocked(now)) { 471 isFlat = true; 472 mFlatTimestampNanos = now; 473 } 474 if (isSwingingLocked(now, tiltAngle)) { 475 isSwinging = true; 476 mSwingTimestampNanos = now; 477 } 478 479 // If the tilt angle is too close to horizontal then we cannot determine 480 // the orientation angle of the screen. 481 if (Math.abs(tiltAngle) > MAX_TILT) { 482 if (LOG) { 483 Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " 484 + "tiltAngle=" + tiltAngle); 485 } 486 clearPredictedRotationLocked(); 487 } else { 488 // Calculate the orientation angle. 489 // This is the angle between the x-y projection of the up vector onto 490 // the +y-axis, increasing clockwise in a range of [0, 360] degrees. 491 int orientationAngle = (int) Math.round( 492 -Math.atan2(-x, y) * RADIANS_TO_DEGREES); 493 if (orientationAngle < 0) { 494 // atan2 returns [-180, 180]; normalize to [0, 360] 495 orientationAngle += 360; 496 } 497 498 // Find the nearest rotation. 499 int nearestRotation = (orientationAngle + 45) / 90; 500 if (nearestRotation == 4) { 501 nearestRotation = 0; 502 } 503 504 // Determine the predicted orientation. 505 if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle) 506 && isOrientationAngleAcceptableLocked(nearestRotation, 507 orientationAngle)) { 508 updatePredictedRotationLocked(now, nearestRotation); 509 if (LOG) { 510 Slog.v(TAG, "Predicted: " 511 + "tiltAngle=" + tiltAngle 512 + ", orientationAngle=" + orientationAngle 513 + ", predictedRotation=" + mPredictedRotation 514 + ", predictedRotationAgeMS=" 515 + ((now - mPredictedRotationTimestampNanos) 516 * 0.000001f)); 517 } 518 } else { 519 if (LOG) { 520 Slog.v(TAG, "Ignoring sensor data, no predicted rotation: " 521 + "tiltAngle=" + tiltAngle 522 + ", orientationAngle=" + orientationAngle); 523 } 524 clearPredictedRotationLocked(); 525 } 526 } 527 } 528 } 529 530 // Determine new proposed rotation. 531 oldProposedRotation = mProposedRotation; 532 if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) { 533 mProposedRotation = mPredictedRotation; 534 } 535 proposedRotation = mProposedRotation; 536 537 // Write final statistics about where we are in the orientation detection process. 538 if (LOG) { 539 Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation 540 + ", proposedRotation=" + proposedRotation 541 + ", predictedRotation=" + mPredictedRotation 542 + ", timeDeltaMS=" + timeDeltaMS 543 + ", isAccelerating=" + isAccelerating 544 + ", isFlat=" + isFlat 545 + ", isSwinging=" + isSwinging 546 + ", timeUntilSettledMS=" + remainingMS(now, 547 mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) 548 + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, 549 mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) 550 + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now, 551 mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) 552 + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now, 553 mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)); 554 } 555 } 556 557 // Tell the listener. 558 if (proposedRotation != oldProposedRotation && proposedRotation >= 0) { 559 if (LOG) { 560 Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation 561 + ", oldProposedRotation=" + oldProposedRotation); 562 } 563 onProposedRotationChanged(proposedRotation); 564 } 565 } 566 567 /** 568 * Returns true if the tilt angle is acceptable for a given predicted rotation. 569 */ 570 private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) { 571 return tiltAngle >= TILT_TOLERANCE[rotation][0] 572 && tiltAngle <= TILT_TOLERANCE[rotation][1]; 573 } 574 575 /** 576 * Returns true if the orientation angle is acceptable for a given predicted rotation. 577 * 578 * This function takes into account the gap between adjacent orientations 579 * for hysteresis. 580 */ 581 private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) { 582 // If there is no current rotation, then there is no gap. 583 // The gap is used only to introduce hysteresis among advertised orientation 584 // changes to avoid flapping. 585 final int currentRotation = mCurrentRotation; 586 if (currentRotation >= 0) { 587 // If the specified rotation is the same or is counter-clockwise adjacent 588 // to the current rotation, then we set a lower bound on the orientation angle. 589 // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90, 590 // then we want to check orientationAngle > 45 + GAP / 2. 591 if (rotation == currentRotation 592 || rotation == (currentRotation + 1) % 4) { 593 int lowerBound = rotation * 90 - 45 594 + ADJACENT_ORIENTATION_ANGLE_GAP / 2; 595 if (rotation == 0) { 596 if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) { 597 return false; 598 } 599 } else { 600 if (orientationAngle < lowerBound) { 601 return false; 602 } 603 } 604 } 605 606 // If the specified rotation is the same or is clockwise adjacent, 607 // then we set an upper bound on the orientation angle. 608 // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270, 609 // then we want to check orientationAngle < 315 - GAP / 2. 610 if (rotation == currentRotation 611 || rotation == (currentRotation + 3) % 4) { 612 int upperBound = rotation * 90 + 45 613 - ADJACENT_ORIENTATION_ANGLE_GAP / 2; 614 if (rotation == 0) { 615 if (orientationAngle <= 45 && orientationAngle > upperBound) { 616 return false; 617 } 618 } else { 619 if (orientationAngle > upperBound) { 620 return false; 621 } 622 } 623 } 624 } 625 return true; 626 } 627 628 /** 629 * Returns true if the predicted rotation is ready to be advertised as a 630 * proposed rotation. 631 */ 632 private boolean isPredictedRotationAcceptableLocked(long now) { 633 // The predicted rotation must have settled long enough. 634 if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) { 635 return false; 636 } 637 638 // The last flat state (time since picked up) must have been sufficiently long ago. 639 if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) { 640 return false; 641 } 642 643 // The last swing state (time since last movement to put down) must have been 644 // sufficiently long ago. 645 if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) { 646 return false; 647 } 648 649 // The last acceleration state must have been sufficiently long ago. 650 if (now < mAccelerationTimestampNanos 651 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) { 652 return false; 653 } 654 655 // Looks good! 656 return true; 657 } 658 659 private void resetLocked() { 660 mLastFilteredTimestampNanos = Long.MIN_VALUE; 661 mProposedRotation = -1; 662 mFlatTimestampNanos = Long.MIN_VALUE; 663 mSwingTimestampNanos = Long.MIN_VALUE; 664 mAccelerationTimestampNanos = Long.MIN_VALUE; 665 clearPredictedRotationLocked(); 666 clearTiltHistoryLocked(); 667 } 668 669 private void clearPredictedRotationLocked() { 670 mPredictedRotation = -1; 671 mPredictedRotationTimestampNanos = Long.MIN_VALUE; 672 } 673 674 private void updatePredictedRotationLocked(long now, int rotation) { 675 if (mPredictedRotation != rotation) { 676 mPredictedRotation = rotation; 677 mPredictedRotationTimestampNanos = now; 678 } 679 } 680 681 private boolean isAcceleratingLocked(float magnitude) { 682 return magnitude < MIN_ACCELERATION_MAGNITUDE 683 || magnitude > MAX_ACCELERATION_MAGNITUDE; 684 } 685 686 private void clearTiltHistoryLocked() { 687 mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE; 688 mTiltHistoryIndex = 1; 689 } 690 691 private void addTiltHistoryEntryLocked(long now, float tilt) { 692 mTiltHistory[mTiltHistoryIndex] = tilt; 693 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now; 694 mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE; 695 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE; 696 } 697 698 private boolean isFlatLocked(long now) { 699 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) { 700 if (mTiltHistory[i] < FLAT_ANGLE) { 701 break; 702 } 703 if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) { 704 // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS. 705 return true; 706 } 707 } 708 return false; 709 } 710 711 private boolean isSwingingLocked(long now, float tilt) { 712 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) { 713 if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) { 714 break; 715 } 716 if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) { 717 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS. 718 return true; 719 } 720 } 721 return false; 722 } 723 724 private int nextTiltHistoryIndexLocked(int index) { 725 index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1; 726 return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1; 727 } 728 729 private float remainingMS(long now, long until) { 730 return now >= until ? 0 : (until - now) * 0.000001f; 731 } 732 } 733 } 734