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