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