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