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