Home | History | Annotate | Download | only in impl
      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