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