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