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