blob: 64f64c0d45d615076df64aa952f036b6e6105632 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Jorim Jaggib10e33f2015-02-04 21:57:40 +010017package com.android.server.policy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
19import android.content.Context;
20import android.hardware.Sensor;
21import android.hardware.SensorEvent;
22import android.hardware.SensorEventListener;
23import android.hardware.SensorManager;
Craig Mautnereee29c42013-01-17 14:44:34 -080024import android.os.Handler;
Adrian Roos3595be42015-03-05 16:31:15 +010025import android.os.SystemClock;
Jeff Browndaf5d892012-05-07 18:30:18 -070026import android.os.SystemProperties;
Michael Wright814de9b2015-08-17 23:31:55 +010027import android.text.TextUtils;
Jeff Brown4519f072011-01-23 13:16:01 -080028import android.util.Slog;
Jeff Brown600f0032014-05-22 17:06:00 -070029
30import java.io.PrintWriter;
Jeff Browne97c9a22015-06-10 22:12:54 -070031import java.util.Arrays;
Michael Wright814de9b2015-08-17 23:31:55 +010032import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
34/**
35 * A special helper class used by the WindowManager
Jeff Brownc0347aa2011-09-23 17:26:09 -070036 * for receiving notifications from the SensorManager when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037 * the orientation of the device has changed.
Dianne Hackborne5439f22010-10-02 16:53:50 -070038 *
39 * NOTE: If changing anything here, please run the API demo
40 * "App/Activity/Screen Orientation" to ensure that all orientation
41 * modes still work correctly.
42 *
Jeff Browndaf5d892012-05-07 18:30:18 -070043 * You can also visualize the behavior of the WindowOrientationListener.
44 * Refer to frameworks/base/tools/orientationplot/README.txt for details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 */
46public abstract class WindowOrientationListener {
47 private static final String TAG = "WindowOrientationListener";
Jeff Browndaf5d892012-05-07 18:30:18 -070048 private static final boolean LOG = SystemProperties.getBoolean(
49 "debug.orientation.log", false);
Jeff Brown4519f072011-01-23 13:16:01 -080050
Jeff Brown5aa73ae2012-01-13 15:29:21 -080051 private static final boolean USE_GRAVITY_SENSOR = false;
Ivan Podogovd49b1ee2016-05-27 13:47:20 +010052 private static final int DEFAULT_BATCH_LATENCY = 100000;
Jeff Brown5aa73ae2012-01-13 15:29:21 -080053
Craig Mautnereee29c42013-01-17 14:44:34 -080054 private Handler mHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 private SensorManager mSensorManager;
Jeff Brown4519f072011-01-23 13:16:01 -080056 private boolean mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 private int mRate;
Michael Wright814de9b2015-08-17 23:31:55 +010058 private String mSensorType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 private Sensor mSensor;
Michael Wright814de9b2015-08-17 23:31:55 +010060 private OrientationJudge mOrientationJudge;
Craig Mautnereee29c42013-01-17 14:44:34 -080061 private int mCurrentRotation = -1;
62
63 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65 /**
66 * Creates a new WindowOrientationListener.
67 *
68 * @param context for the WindowOrientationListener.
Craig Mautnereee29c42013-01-17 14:44:34 -080069 * @param handler Provides the Looper for receiving sensor updates.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 */
Craig Mautnereee29c42013-01-17 14:44:34 -080071 public WindowOrientationListener(Context context, Handler handler) {
72 this(context, handler, SensorManager.SENSOR_DELAY_UI);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 }
Michael Wright814de9b2015-08-17 23:31:55 +010074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 /**
76 * Creates a new WindowOrientationListener.
77 *
78 * @param context for the WindowOrientationListener.
Craig Mautnereee29c42013-01-17 14:44:34 -080079 * @param handler Provides the Looper for receiving sensor updates.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 * @param rate at which sensor events are processed (see also
81 * {@link android.hardware.SensorManager SensorManager}). Use the default
82 * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
83 * SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
Steve Howard1ba101f2010-02-23 14:30:13 -080084 *
Jeff Brown4519f072011-01-23 13:16:01 -080085 * This constructor is private since no one uses it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 */
Craig Mautnereee29c42013-01-17 14:44:34 -080087 private WindowOrientationListener(Context context, Handler handler, int rate) {
88 mHandler = handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
90 mRate = rate;
Trevor Bunker8e4591c2016-01-15 09:00:55 -080091 mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DEVICE_ORIENTATION);
Michael Wright814de9b2015-08-17 23:31:55 +010092
Trevor Bunker8e4591c2016-01-15 09:00:55 -080093 if (mSensor != null) {
94 mOrientationJudge = new OrientationSensorJudge();
Michael Wright814de9b2015-08-17 23:31:55 +010095 }
96
97 if (mOrientationJudge == null) {
98 mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
99 ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
100 if (mSensor != null) {
101 // Create listener only if sensors do exist
102 mOrientationJudge = new AccelSensorJudge(context);
103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 }
105 }
106
107 /**
108 * Enables the WindowOrientationListener so it will monitor the sensor and call
Craig Mautnereee29c42013-01-17 14:44:34 -0800109 * {@link #onProposedRotationChanged(int)} when the device orientation changes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 */
111 public void enable() {
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700112 enable(true /* clearCurrentRotation */);
113 }
114
115 /**
116 * Enables the WindowOrientationListener so it will monitor the sensor and call
117 * {@link #onProposedRotationChanged(int)} when the device orientation changes.
118 *
119 * @param clearCurrentRotation True if the current proposed sensor rotation should be cleared as
120 * part of the reset.
121 */
122 public void enable(boolean clearCurrentRotation) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800123 synchronized (mLock) {
124 if (mSensor == null) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700125 Slog.w(TAG, "Cannot detect sensors. Not enabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800126 return;
Jeff Browndaf5d892012-05-07 18:30:18 -0700127 }
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700128 if (mEnabled) {
129 return;
Craig Mautnereee29c42013-01-17 14:44:34 -0800130 }
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700131 if (LOG) {
132 Slog.d(TAG, "WindowOrientationListener enabled clearCurrentRotation="
133 + clearCurrentRotation);
134 }
135 mOrientationJudge.resetLocked(clearCurrentRotation);
136 if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {
137 mSensorManager.registerListener(
138 mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);
139 } else {
140 mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
141 }
142 mEnabled = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 }
144 }
145
146 /**
147 * Disables the WindowOrientationListener.
148 */
149 public void disable() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800150 synchronized (mLock) {
151 if (mSensor == null) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700152 Slog.w(TAG, "Cannot detect sensors. Invalid disable");
Craig Mautnereee29c42013-01-17 14:44:34 -0800153 return;
Jeff Browndaf5d892012-05-07 18:30:18 -0700154 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800155 if (mEnabled == true) {
156 if (LOG) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700157 Slog.d(TAG, "WindowOrientationListener disabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800158 }
Michael Wright814de9b2015-08-17 23:31:55 +0100159 mSensorManager.unregisterListener(mOrientationJudge);
Craig Mautnereee29c42013-01-17 14:44:34 -0800160 mEnabled = false;
161 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 }
163 }
164
Adrian Roos3595be42015-03-05 16:31:15 +0100165 public void onTouchStart() {
166 synchronized (mLock) {
Michael Wright814de9b2015-08-17 23:31:55 +0100167 if (mOrientationJudge != null) {
168 mOrientationJudge.onTouchStartLocked();
Adrian Roos12741962015-03-31 11:20:30 -0700169 }
Adrian Roos3595be42015-03-05 16:31:15 +0100170 }
171 }
172
173 public void onTouchEnd() {
174 long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
175
176 synchronized (mLock) {
Michael Wright814de9b2015-08-17 23:31:55 +0100177 if (mOrientationJudge != null) {
178 mOrientationJudge.onTouchEndLocked(whenElapsedNanos);
Adrian Roos12741962015-03-31 11:20:30 -0700179 }
Adrian Roos3595be42015-03-05 16:31:15 +0100180 }
181 }
182
Jeff Brown4519f072011-01-23 13:16:01 -0800183 /**
Jeff Brownc0347aa2011-09-23 17:26:09 -0700184 * Sets the current rotation.
185 *
186 * @param rotation The current rotation.
Jeff Brown4519f072011-01-23 13:16:01 -0800187 */
Jeff Brownc0347aa2011-09-23 17:26:09 -0700188 public void setCurrentRotation(int rotation) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800189 synchronized (mLock) {
190 mCurrentRotation = rotation;
191 }
Jeff Brownc0347aa2011-09-23 17:26:09 -0700192 }
193
194 /**
195 * Gets the proposed rotation.
196 *
197 * This method only returns a rotation if the orientation listener is certain
198 * of its proposal. If the rotation is indeterminate, returns -1.
199 *
200 * @return The proposed rotation, or -1 if unknown.
201 */
202 public int getProposedRotation() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800203 synchronized (mLock) {
204 if (mEnabled) {
Michael Wright814de9b2015-08-17 23:31:55 +0100205 return mOrientationJudge.getProposedRotationLocked();
Craig Mautnereee29c42013-01-17 14:44:34 -0800206 }
207 return -1;
Suchi Amalapurapu63104ed2009-12-15 14:06:08 -0800208 }
Dianne Hackborne4fbd622009-03-27 18:09:16 -0700209 }
Steve Howard5f531ae2010-08-05 17:14:53 -0700210
211 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 * Returns true if sensor is enabled and false otherwise
213 */
214 public boolean canDetectOrientation() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800215 synchronized (mLock) {
216 return mSensor != null;
217 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
Steve Howard1ba101f2010-02-23 14:30:13 -0800219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700221 * Called when the rotation view of the device has changed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 *
Jeff Brownc0347aa2011-09-23 17:26:09 -0700223 * This method is called whenever the orientation becomes certain of an orientation.
224 * It is called each time the orientation determination transitions from being
225 * uncertain to being certain again, even if it is the same orientation as before.
226 *
Michael Wright74f6c792015-08-25 21:15:12 +0100227 * This should only be called on the Handler thread.
228 *
Steve Howard1ba101f2010-02-23 14:30:13 -0800229 * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
Craig Mautnereee29c42013-01-17 14:44:34 -0800230 * @see android.view.Surface
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 */
Jeff Brownc0347aa2011-09-23 17:26:09 -0700232 public abstract void onProposedRotationChanged(int rotation);
Jeff Brown4519f072011-01-23 13:16:01 -0800233
Jeff Brown600f0032014-05-22 17:06:00 -0700234 public void dump(PrintWriter pw, String prefix) {
235 synchronized (mLock) {
236 pw.println(prefix + TAG);
237 prefix += " ";
238 pw.println(prefix + "mEnabled=" + mEnabled);
239 pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
Michael Wright814de9b2015-08-17 23:31:55 +0100240 pw.println(prefix + "mSensorType=" + mSensorType);
Jeff Brown600f0032014-05-22 17:06:00 -0700241 pw.println(prefix + "mSensor=" + mSensor);
242 pw.println(prefix + "mRate=" + mRate);
243
Michael Wright814de9b2015-08-17 23:31:55 +0100244 if (mOrientationJudge != null) {
245 mOrientationJudge.dumpLocked(pw, prefix);
Jaewan Kim0abc28e2014-06-26 09:13:11 +0900246 }
Jeff Brown600f0032014-05-22 17:06:00 -0700247 }
248 }
249
Michael Wright814de9b2015-08-17 23:31:55 +0100250 abstract class OrientationJudge implements SensorEventListener {
251 // Number of nanoseconds per millisecond.
252 protected static final long NANOS_PER_MS = 1000000;
253
254 // Number of milliseconds per nano second.
255 protected static final float MILLIS_PER_NANO = 0.000001f;
256
257 // The minimum amount of time that must have elapsed since the screen was last touched
258 // before the proposed rotation can change.
259 protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
260 500 * NANOS_PER_MS;
261
262 /**
263 * Gets the proposed rotation.
264 *
265 * This method only returns a rotation if the orientation listener is certain
266 * of its proposal. If the rotation is indeterminate, returns -1.
267 *
268 * Should only be called when holding WindowOrientationListener lock.
269 *
270 * @return The proposed rotation, or -1 if unknown.
271 */
272 public abstract int getProposedRotationLocked();
273
274 /**
275 * Notifies the orientation judge that the screen is being touched.
276 *
277 * Should only be called when holding WindowOrientationListener lock.
278 */
279 public abstract void onTouchStartLocked();
280
281 /**
282 * Notifies the orientation judge that the screen is no longer being touched.
283 *
284 * Should only be called when holding WindowOrientationListener lock.
285 *
286 * @param whenElapsedNanos Given in the elapsed realtime nanos time base.
287 */
288 public abstract void onTouchEndLocked(long whenElapsedNanos);
289
290 /**
291 * Resets the state of the judge.
292 *
293 * Should only be called when holding WindowOrientationListener lock.
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700294 *
295 * @param clearCurrentRotation True if the current proposed sensor rotation should be
296 * cleared as part of the reset.
Michael Wright814de9b2015-08-17 23:31:55 +0100297 */
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700298 public abstract void resetLocked(boolean clearCurrentRotation);
Michael Wright814de9b2015-08-17 23:31:55 +0100299
300 /**
301 * Dumps internal state of the orientation judge.
302 *
303 * Should only be called when holding WindowOrientationListener lock.
304 */
305 public abstract void dumpLocked(PrintWriter pw, String prefix);
306
307 @Override
308 public abstract void onAccuracyChanged(Sensor sensor, int accuracy);
309
310 @Override
311 public abstract void onSensorChanged(SensorEvent event);
312 }
313
Jeff Brown4519f072011-01-23 13:16:01 -0800314 /**
Jeff Brown4519f072011-01-23 13:16:01 -0800315 * This class filters the raw accelerometer data and tries to detect actual changes in
316 * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
317 * but here's the outline:
318 *
319 * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in
320 * cartesian space because the orientation calculations are sensitive to the
321 * absolute magnitude of the acceleration. In particular, there are singularities
322 * in the calculation as the magnitude approaches 0. By performing the low-pass
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800323 * filtering early, we can eliminate most spurious high-frequency impulses due to noise.
Jeff Brown4519f072011-01-23 13:16:01 -0800324 *
325 * - Convert the acceleromter vector from cartesian to spherical coordinates.
326 * Since we're dealing with rotation of the device, this is the sensible coordinate
327 * system to work in. The zenith direction is the Z-axis, the direction the screen
328 * is facing. The radial distance is referred to as the magnitude below.
329 * The elevation angle is referred to as the "tilt" below.
330 * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is
331 * the Y-axis).
332 * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference.
333 *
334 * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing.
335 * The orientation angle is not meaningful when the device is nearly horizontal.
336 * The tilt angle thresholds are set differently for each orientation and different
337 * limits are applied when the device is facing down as opposed to when it is facing
338 * forward or facing up.
339 *
340 * - When the orientation angle reaches a certain threshold, consider transitioning
341 * to the corresponding orientation. These thresholds have some hysteresis built-in
342 * to avoid oscillations between adjacent orientations.
343 *
Jeff Brownc0347aa2011-09-23 17:26:09 -0700344 * - Wait for the device to settle for a little bit. Once that happens, issue the
345 * new orientation proposal.
Jeff Brown4519f072011-01-23 13:16:01 -0800346 *
347 * Details are explained inline.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800348 *
349 * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
350 * signal processing background.
Jeff Brown4519f072011-01-23 13:16:01 -0800351 */
Michael Wright814de9b2015-08-17 23:31:55 +0100352 final class AccelSensorJudge extends OrientationJudge {
Jeff Brown4519f072011-01-23 13:16:01 -0800353 // We work with all angles in degrees in this class.
354 private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
355
356 // Indices into SensorEvent.values for the accelerometer sensor.
357 private static final int ACCELEROMETER_DATA_X = 0;
358 private static final int ACCELEROMETER_DATA_Y = 1;
359 private static final int ACCELEROMETER_DATA_Z = 2;
360
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800361 // The minimum amount of time that a predicted rotation must be stable before it
362 // is accepted as a valid rotation proposal. This value can be quite small because
363 // the low-pass filter already suppresses most of the noise so we're really just
364 // looking for quick confirmation that the last few samples are in agreement as to
365 // the desired orientation.
366 private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800367
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800368 // The minimum amount of time that must have elapsed since the device last exited
369 // the flat state (time since it was picked up) before the proposed rotation
370 // can change.
371 private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800372
Jeff Browndaf5d892012-05-07 18:30:18 -0700373 // The minimum amount of time that must have elapsed since the device stopped
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800374 // swinging (time since device appeared to be in the process of being put down
375 // or put away into a pocket) before the proposed rotation can change.
376 private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700377
Jeff Browndaf5d892012-05-07 18:30:18 -0700378 // The minimum amount of time that must have elapsed since the device stopped
379 // undergoing external acceleration before the proposed rotation can change.
380 private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
381 500 * NANOS_PER_MS;
382
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800383 // If the tilt angle remains greater than the specified angle for a minimum of
384 // the specified time, then the device is deemed to be lying flat
385 // (just chillin' on a table).
Jeff Browne97c9a22015-06-10 22:12:54 -0700386 private static final float FLAT_ANGLE = 80;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800387 private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS;
388
389 // If the tilt angle has increased by at least delta degrees within the specified amount
390 // of time, then the device is deemed to be swinging away from the user
391 // down towards flat (tilt = 90).
392 private static final float SWING_AWAY_ANGLE_DELTA = 20;
393 private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700394
Jeff Brown4519f072011-01-23 13:16:01 -0800395 // The maximum sample inter-arrival time in milliseconds.
396 // If the acceleration samples are further apart than this amount in time, we reset the
397 // state of the low-pass filter and orientation properties. This helps to handle
398 // boundary conditions when the device is turned on, wakes from suspend or there is
399 // a significant gap in samples.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800400 private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800401
Jeff Brownc0347aa2011-09-23 17:26:09 -0700402 // The acceleration filter time constant.
403 //
404 // This time constant is used to tune the acceleration filter such that
405 // impulses and vibrational noise (think car dock) is suppressed before we
406 // try to calculate the tilt and orientation angles.
407 //
408 // The filter time constant is related to the filter cutoff frequency, which is the
409 // frequency at which signals are attenuated by 3dB (half the passband power).
Jeff Brown4519f072011-01-23 13:16:01 -0800410 // Each successive octave beyond this frequency is attenuated by an additional 6dB.
411 //
Jeff Brownc0347aa2011-09-23 17:26:09 -0700412 // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
413 // is given by Fc = 1 / (2pi * t).
414 //
415 // The higher the time constant, the lower the cutoff frequency, so more noise
416 // will be suppressed.
417 //
418 // Filtering adds latency proportional the time constant (inversely proportional
419 // to the cutoff frequency) so we don't want to make the time constant too
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800420 // large or we can lose responsiveness. Likewise we don't want to make it too
421 // small or we do a poor job suppressing acceleration spikes.
422 // Empirically, 100ms seems to be too small and 500ms is too large.
423 private static final float FILTER_TIME_CONSTANT_MS = 200.0f;
Jeff Brown4519f072011-01-23 13:16:01 -0800424
425 /* State for orientation detection. */
426
427 // Thresholds for minimum and maximum allowable deviation from gravity.
428 //
429 // If the device is undergoing external acceleration (being bumped, in a car
430 // that is turning around a corner or a plane taking off) then the magnitude
431 // may be substantially more or less than gravity. This can skew our orientation
432 // detection by making us think that up is pointed in a different direction.
433 //
434 // Conversely, if the device is in freefall, then there will be no gravity to
435 // measure at all. This is problematic because we cannot detect the orientation
436 // without gravity to tell us which way is up. A magnitude near 0 produces
437 // singularities in the tilt and orientation calculations.
438 //
439 // In both cases, we postpone choosing an orientation.
Jeff Browndaf5d892012-05-07 18:30:18 -0700440 //
441 // However, we need to tolerate some acceleration because the angular momentum
442 // of turning the device can skew the observed acceleration for a short period of time.
443 private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2
444 private static final float ACCELERATION_TOLERANCE = 4; // m/s^2
Jeff Brown4519f072011-01-23 13:16:01 -0800445 private static final float MIN_ACCELERATION_MAGNITUDE =
Jeff Browndaf5d892012-05-07 18:30:18 -0700446 SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
Jeff Brown4519f072011-01-23 13:16:01 -0800447 private static final float MAX_ACCELERATION_MAGNITUDE =
Jeff Browndaf5d892012-05-07 18:30:18 -0700448 SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
Jeff Brown4519f072011-01-23 13:16:01 -0800449
450 // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
Jeff Browne97c9a22015-06-10 22:12:54 -0700451 // when screen is facing the sky or ground), we completely ignore orientation data
452 // because it's too unstable.
453 private static final int MAX_TILT = 80;
454
455 // The tilt angle below which we conclude that the user is holding the device
456 // overhead reading in bed and lock into that state.
457 private static final int TILT_OVERHEAD_ENTER = -40;
458
459 // The tilt angle above which we conclude that the user would like a rotation
460 // change to occur and unlock from the overhead state.
461 private static final int TILT_OVERHEAD_EXIT = -15;
462
463 // The gap angle in degrees between adjacent orientation angles for hysteresis.
464 // This creates a "dead zone" between the current orientation and a proposed
465 // adjacent orientation. No orientation proposal is made when the orientation
466 // angle is within the gap between the current orientation and the adjacent
467 // orientation.
468 private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
Jeff Brown4519f072011-01-23 13:16:01 -0800469
470 // The tilt angle range in degrees for each orientation.
471 // Beyond these tilt angles, we don't even consider transitioning into the
472 // specified orientation. We place more stringent requirements on unnatural
473 // orientations than natural ones to make it less likely to accidentally transition
474 // into those states.
475 // The first value of each pair is negative so it applies a limit when the device is
476 // facing down (overhead reading in bed).
477 // The second value of each pair is positive so it applies a limit when the device is
478 // facing up (resting on a table).
479 // The ideal tilt angle is 0 (when the device is vertical) so the limits establish
480 // how close to vertical the device must be in order to change orientation.
Jeff Browne97c9a22015-06-10 22:12:54 -0700481 private final int[][] mTiltToleranceConfig = new int[][] {
482 /* ROTATION_0 */ { -25, 70 }, // note: these are overridden by config.xml
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800483 /* ROTATION_90 */ { -25, 65 },
484 /* ROTATION_180 */ { -25, 60 },
485 /* ROTATION_270 */ { -25, 65 }
Jeff Brown4519f072011-01-23 13:16:01 -0800486 };
487
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800488 // Timestamp and value of the last accelerometer sample.
489 private long mLastFilteredTimestampNanos;
490 private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
Jeff Brown4519f072011-01-23 13:16:01 -0800491
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800492 // The last proposed rotation, -1 if unknown.
493 private int mProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800494
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800495 // Value of the current predicted rotation, -1 if unknown.
496 private int mPredictedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800497
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800498 // Timestamp of when the predicted rotation most recently changed.
499 private long mPredictedRotationTimestampNanos;
500
501 // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
502 private long mFlatTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700503 private boolean mFlat;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800504
505 // Timestamp when the device last appeared to be swinging.
506 private long mSwingTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700507 private boolean mSwinging;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800508
Jeff Browndaf5d892012-05-07 18:30:18 -0700509 // Timestamp when the device last appeared to be undergoing external acceleration.
510 private long mAccelerationTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700511 private boolean mAccelerating;
512
Adrian Roos3595be42015-03-05 16:31:15 +0100513 // Timestamp when the last touch to the touch screen ended
514 private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
515 private boolean mTouched;
516
Jeff Brown600f0032014-05-22 17:06:00 -0700517 // Whether we are locked into an overhead usage mode.
518 private boolean mOverhead;
Jeff Browndaf5d892012-05-07 18:30:18 -0700519
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800520 // History of observed tilt angles.
Jeff Browne97c9a22015-06-10 22:12:54 -0700521 private static final int TILT_HISTORY_SIZE = 200;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800522 private float[] mTiltHistory = new float[TILT_HISTORY_SIZE];
523 private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
524 private int mTiltHistoryIndex;
Jeff Brown4519f072011-01-23 13:16:01 -0800525
Michael Wright814de9b2015-08-17 23:31:55 +0100526 public AccelSensorJudge(Context context) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700527 // Load tilt tolerance configuration.
528 int[] tiltTolerance = context.getResources().getIntArray(
529 com.android.internal.R.array.config_autoRotationTiltTolerance);
530 if (tiltTolerance.length == 8) {
531 for (int i = 0; i < 4; i++) {
532 int min = tiltTolerance[i * 2];
533 int max = tiltTolerance[i * 2 + 1];
534 if (min >= -90 && min <= max && max <= 90) {
535 mTiltToleranceConfig[i][0] = min;
536 mTiltToleranceConfig[i][1] = max;
537 } else {
538 Slog.wtf(TAG, "config_autoRotationTiltTolerance contains invalid range: "
539 + "min=" + min + ", max=" + max);
540 }
541 }
542 } else {
543 Slog.wtf(TAG, "config_autoRotationTiltTolerance should have exactly 8 elements");
544 }
545 }
546
Michael Wright814de9b2015-08-17 23:31:55 +0100547 @Override
Craig Mautnereee29c42013-01-17 14:44:34 -0800548 public int getProposedRotationLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800549 return mProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800550 }
551
Michael Wright814de9b2015-08-17 23:31:55 +0100552 @Override
Jeff Brown600f0032014-05-22 17:06:00 -0700553 public void dumpLocked(PrintWriter pw, String prefix) {
Michael Wright814de9b2015-08-17 23:31:55 +0100554 pw.println(prefix + "AccelSensorJudge");
555 prefix += " ";
Jeff Brown600f0032014-05-22 17:06:00 -0700556 pw.println(prefix + "mProposedRotation=" + mProposedRotation);
557 pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
558 pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
559 pw.println(prefix + "mLastFilteredY=" + mLastFilteredY);
560 pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ);
Michael Wright6eabf572015-08-12 15:35:31 +0100561 final long delta = SystemClock.elapsedRealtimeNanos() - mLastFilteredTimestampNanos;
562 pw.println(prefix + "mLastFilteredTimestampNanos=" + mLastFilteredTimestampNanos
563 + " (" + (delta * 0.000001f) + "ms ago)");
Jeff Brown600f0032014-05-22 17:06:00 -0700564 pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}");
565 pw.println(prefix + "mFlat=" + mFlat);
566 pw.println(prefix + "mSwinging=" + mSwinging);
567 pw.println(prefix + "mAccelerating=" + mAccelerating);
568 pw.println(prefix + "mOverhead=" + mOverhead);
Adrian Roos3595be42015-03-05 16:31:15 +0100569 pw.println(prefix + "mTouched=" + mTouched);
Jeff Browne97c9a22015-06-10 22:12:54 -0700570 pw.print(prefix + "mTiltToleranceConfig=[");
571 for (int i = 0; i < 4; i++) {
572 if (i != 0) {
573 pw.print(", ");
574 }
575 pw.print("[");
576 pw.print(mTiltToleranceConfig[i][0]);
577 pw.print(", ");
578 pw.print(mTiltToleranceConfig[i][1]);
579 pw.print("]");
580 }
581 pw.println("]");
Jeff Brown600f0032014-05-22 17:06:00 -0700582 }
583
Jeff Brown4519f072011-01-23 13:16:01 -0800584 @Override
585 public void onAccuracyChanged(Sensor sensor, int accuracy) {
586 }
587
588 @Override
589 public void onSensorChanged(SensorEvent event) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800590 int proposedRotation;
591 int oldProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800592
Craig Mautnereee29c42013-01-17 14:44:34 -0800593 synchronized (mLock) {
594 // The vector given in the SensorEvent points straight up (towards the sky) under
595 // ideal conditions (the phone is not accelerating). I'll call this up vector
596 // elsewhere.
597 float x = event.values[ACCELEROMETER_DATA_X];
598 float y = event.values[ACCELEROMETER_DATA_Y];
599 float z = event.values[ACCELEROMETER_DATA_Z];
Jeff Brown4519f072011-01-23 13:16:01 -0800600
Jeff Browndaf5d892012-05-07 18:30:18 -0700601 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800602 Slog.v(TAG, "Raw acceleration vector: "
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800603 + "x=" + x + ", y=" + y + ", z=" + z
Neil Fuller33253a42014-10-01 11:55:10 +0100604 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z));
Jeff Brown4519f072011-01-23 13:16:01 -0800605 }
Jeff Brown4519f072011-01-23 13:16:01 -0800606
Craig Mautnereee29c42013-01-17 14:44:34 -0800607 // Apply a low-pass filter to the acceleration up vector in cartesian space.
608 // Reset the orientation listener state if the samples are too far apart in time
609 // or when we see values of (0, 0, 0) which indicates that we polled the
610 // accelerometer too soon after turning it on and we don't have any data yet.
611 final long now = event.timestamp;
612 final long then = mLastFilteredTimestampNanos;
613 final float timeDeltaMS = (now - then) * 0.000001f;
614 final boolean skipSample;
615 if (now < then
616 || now > then + MAX_FILTER_DELTA_TIME_NANOS
617 || (x == 0 && y == 0 && z == 0)) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700618 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800619 Slog.v(TAG, "Resetting orientation listener.");
Jeff Brown4519f072011-01-23 13:16:01 -0800620 }
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700621 resetLocked(true /* clearCurrentRotation */);
Craig Mautnereee29c42013-01-17 14:44:34 -0800622 skipSample = true;
Jeff Brown4519f072011-01-23 13:16:01 -0800623 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800624 final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
625 x = alpha * (x - mLastFilteredX) + mLastFilteredX;
626 y = alpha * (y - mLastFilteredY) + mLastFilteredY;
627 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
628 if (LOG) {
629 Slog.v(TAG, "Filtered acceleration vector: "
630 + "x=" + x + ", y=" + y + ", z=" + z
Neil Fuller33253a42014-10-01 11:55:10 +0100631 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z));
Jeff Browndaf5d892012-05-07 18:30:18 -0700632 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800633 skipSample = false;
634 }
635 mLastFilteredTimestampNanos = now;
636 mLastFilteredX = x;
637 mLastFilteredY = y;
638 mLastFilteredZ = z;
Jeff Browndaf5d892012-05-07 18:30:18 -0700639
Craig Mautnereee29c42013-01-17 14:44:34 -0800640 boolean isAccelerating = false;
641 boolean isFlat = false;
642 boolean isSwinging = false;
643 if (!skipSample) {
644 // Calculate the magnitude of the acceleration vector.
Neil Fuller33253a42014-10-01 11:55:10 +0100645 final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
Craig Mautnereee29c42013-01-17 14:44:34 -0800646 if (magnitude < NEAR_ZERO_MAGNITUDE) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700647 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800648 Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero.");
Jeff Brown4519f072011-01-23 13:16:01 -0800649 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800650 clearPredictedRotationLocked();
Jeff Brown4519f072011-01-23 13:16:01 -0800651 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800652 // Determine whether the device appears to be undergoing external
653 // acceleration.
654 if (isAcceleratingLocked(magnitude)) {
655 isAccelerating = true;
656 mAccelerationTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700657 }
658
Craig Mautnereee29c42013-01-17 14:44:34 -0800659 // Calculate the tilt angle.
660 // This is the angle between the up vector and the x-y plane (the plane of
661 // the screen) in a range of [-90, 90] degrees.
662 // -90 degrees: screen horizontal and facing the ground (overhead)
663 // 0 degrees: screen vertical
664 // 90 degrees: screen horizontal and facing the sky (on table)
665 final int tiltAngle = (int) Math.round(
666 Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
667 addTiltHistoryEntryLocked(now, tiltAngle);
668
669 // Determine whether the device appears to be flat or swinging.
670 if (isFlatLocked(now)) {
671 isFlat = true;
672 mFlatTimestampNanos = now;
673 }
674 if (isSwingingLocked(now, tiltAngle)) {
675 isSwinging = true;
676 mSwingTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700677 }
678
Craig Mautnereee29c42013-01-17 14:44:34 -0800679 // If the tilt angle is too close to horizontal then we cannot determine
680 // the orientation angle of the screen.
Jeff Brown600f0032014-05-22 17:06:00 -0700681 if (tiltAngle <= TILT_OVERHEAD_ENTER) {
682 mOverhead = true;
683 } else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
684 mOverhead = false;
685 }
686 if (mOverhead) {
687 if (LOG) {
688 Slog.v(TAG, "Ignoring sensor data, device is overhead: "
689 + "tiltAngle=" + tiltAngle);
690 }
691 clearPredictedRotationLocked();
692 } else if (Math.abs(tiltAngle) > MAX_TILT) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700693 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800694 Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
695 + "tiltAngle=" + tiltAngle);
Jeff Brownc0347aa2011-09-23 17:26:09 -0700696 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800697 clearPredictedRotationLocked();
Jeff Brownc0347aa2011-09-23 17:26:09 -0700698 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800699 // Calculate the orientation angle.
700 // This is the angle between the x-y projection of the up vector onto
701 // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
702 int orientationAngle = (int) Math.round(
703 -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
704 if (orientationAngle < 0) {
705 // atan2 returns [-180, 180]; normalize to [0, 360]
706 orientationAngle += 360;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700707 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800708
709 // Find the nearest rotation.
710 int nearestRotation = (orientationAngle + 45) / 90;
711 if (nearestRotation == 4) {
712 nearestRotation = 0;
713 }
714
715 // Determine the predicted orientation.
716 if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle)
717 && isOrientationAngleAcceptableLocked(nearestRotation,
718 orientationAngle)) {
719 updatePredictedRotationLocked(now, nearestRotation);
720 if (LOG) {
721 Slog.v(TAG, "Predicted: "
722 + "tiltAngle=" + tiltAngle
723 + ", orientationAngle=" + orientationAngle
724 + ", predictedRotation=" + mPredictedRotation
725 + ", predictedRotationAgeMS="
726 + ((now - mPredictedRotationTimestampNanos)
727 * 0.000001f));
728 }
729 } else {
730 if (LOG) {
731 Slog.v(TAG, "Ignoring sensor data, no predicted rotation: "
732 + "tiltAngle=" + tiltAngle
733 + ", orientationAngle=" + orientationAngle);
734 }
735 clearPredictedRotationLocked();
736 }
Jeff Brown4519f072011-01-23 13:16:01 -0800737 }
738 }
739 }
Jeff Brown600f0032014-05-22 17:06:00 -0700740 mFlat = isFlat;
741 mSwinging = isSwinging;
742 mAccelerating = isAccelerating;
Jeff Brown4519f072011-01-23 13:16:01 -0800743
Craig Mautnereee29c42013-01-17 14:44:34 -0800744 // Determine new proposed rotation.
745 oldProposedRotation = mProposedRotation;
746 if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) {
747 mProposedRotation = mPredictedRotation;
748 }
749 proposedRotation = mProposedRotation;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800750
Craig Mautnereee29c42013-01-17 14:44:34 -0800751 // Write final statistics about where we are in the orientation detection process.
752 if (LOG) {
753 Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation
754 + ", proposedRotation=" + proposedRotation
755 + ", predictedRotation=" + mPredictedRotation
756 + ", timeDeltaMS=" + timeDeltaMS
757 + ", isAccelerating=" + isAccelerating
758 + ", isFlat=" + isFlat
759 + ", isSwinging=" + isSwinging
Jeff Brown600f0032014-05-22 17:06:00 -0700760 + ", isOverhead=" + mOverhead
Adrian Roos3595be42015-03-05 16:31:15 +0100761 + ", isTouched=" + mTouched
Craig Mautnereee29c42013-01-17 14:44:34 -0800762 + ", timeUntilSettledMS=" + remainingMS(now,
763 mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
764 + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
765 mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS)
766 + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
767 mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
768 + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
Adrian Roos3595be42015-03-05 16:31:15 +0100769 mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)
770 + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now,
771 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS));
Craig Mautnereee29c42013-01-17 14:44:34 -0800772 }
Jeff Brown4519f072011-01-23 13:16:01 -0800773 }
774
775 // Tell the listener.
Craig Mautnereee29c42013-01-17 14:44:34 -0800776 if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700777 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800778 Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation
Jeff Brownc0347aa2011-09-23 17:26:09 -0700779 + ", oldProposedRotation=" + oldProposedRotation);
780 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800781 onProposedRotationChanged(proposedRotation);
Jeff Brown4519f072011-01-23 13:16:01 -0800782 }
783 }
784
Michael Wright814de9b2015-08-17 23:31:55 +0100785 @Override
786 public void onTouchStartLocked() {
787 mTouched = true;
788 }
789
790 @Override
791 public void onTouchEndLocked(long whenElapsedNanos) {
792 mTouched = false;
793 mTouchEndedTimestampNanos = whenElapsedNanos;
794 }
795
796 @Override
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700797 public void resetLocked(boolean clearCurrentRotation) {
Michael Wright814de9b2015-08-17 23:31:55 +0100798 mLastFilteredTimestampNanos = Long.MIN_VALUE;
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -0700799 if (clearCurrentRotation) {
800 mProposedRotation = -1;
801 }
Michael Wright814de9b2015-08-17 23:31:55 +0100802 mFlatTimestampNanos = Long.MIN_VALUE;
803 mFlat = false;
804 mSwingTimestampNanos = Long.MIN_VALUE;
805 mSwinging = false;
806 mAccelerationTimestampNanos = Long.MIN_VALUE;
807 mAccelerating = false;
808 mOverhead = false;
809 clearPredictedRotationLocked();
810 clearTiltHistoryLocked();
811 }
812
813
Jeff Brown4519f072011-01-23 13:16:01 -0800814 /**
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800815 * Returns true if the tilt angle is acceptable for a given predicted rotation.
Jeff Brown4519f072011-01-23 13:16:01 -0800816 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800817 private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700818 return tiltAngle >= mTiltToleranceConfig[rotation][0]
819 && tiltAngle <= mTiltToleranceConfig[rotation][1];
Jeff Brown4519f072011-01-23 13:16:01 -0800820 }
821
822 /**
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800823 * Returns true if the orientation angle is acceptable for a given predicted rotation.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700824 *
Jeff Brown4519f072011-01-23 13:16:01 -0800825 * This function takes into account the gap between adjacent orientations
826 * for hysteresis.
827 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800828 private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) {
Jeff Brown4519f072011-01-23 13:16:01 -0800829 // If there is no current rotation, then there is no gap.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700830 // The gap is used only to introduce hysteresis among advertised orientation
831 // changes to avoid flapping.
Craig Mautnereee29c42013-01-17 14:44:34 -0800832 final int currentRotation = mCurrentRotation;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700833 if (currentRotation >= 0) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800834 // If the specified rotation is the same or is counter-clockwise adjacent
835 // to the current rotation, then we set a lower bound on the orientation angle.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700836 // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90,
Jeff Brown4519f072011-01-23 13:16:01 -0800837 // then we want to check orientationAngle > 45 + GAP / 2.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800838 if (rotation == currentRotation
839 || rotation == (currentRotation + 1) % 4) {
840 int lowerBound = rotation * 90 - 45
Jeff Brown4519f072011-01-23 13:16:01 -0800841 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800842 if (rotation == 0) {
Jeff Brown4519f072011-01-23 13:16:01 -0800843 if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
844 return false;
845 }
846 } else {
847 if (orientationAngle < lowerBound) {
848 return false;
849 }
850 }
851 }
852
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800853 // If the specified rotation is the same or is clockwise adjacent,
Jeff Brown4519f072011-01-23 13:16:01 -0800854 // then we set an upper bound on the orientation angle.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800855 // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270,
Jeff Brown4519f072011-01-23 13:16:01 -0800856 // then we want to check orientationAngle < 315 - GAP / 2.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800857 if (rotation == currentRotation
858 || rotation == (currentRotation + 3) % 4) {
859 int upperBound = rotation * 90 + 45
Jeff Brown4519f072011-01-23 13:16:01 -0800860 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800861 if (rotation == 0) {
Jeff Brown4519f072011-01-23 13:16:01 -0800862 if (orientationAngle <= 45 && orientationAngle > upperBound) {
863 return false;
864 }
865 } else {
866 if (orientationAngle > upperBound) {
867 return false;
868 }
869 }
870 }
871 }
872 return true;
873 }
874
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800875 /**
876 * Returns true if the predicted rotation is ready to be advertised as a
877 * proposed rotation.
878 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800879 private boolean isPredictedRotationAcceptableLocked(long now) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800880 // The predicted rotation must have settled long enough.
881 if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
882 return false;
883 }
884
885 // The last flat state (time since picked up) must have been sufficiently long ago.
886 if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
887 return false;
888 }
889
890 // The last swing state (time since last movement to put down) must have been
891 // sufficiently long ago.
892 if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
893 return false;
894 }
895
Jeff Browndaf5d892012-05-07 18:30:18 -0700896 // The last acceleration state must have been sufficiently long ago.
897 if (now < mAccelerationTimestampNanos
898 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
899 return false;
900 }
901
Adrian Roos3595be42015-03-05 16:31:15 +0100902 // The last touch must have ended sufficiently long ago.
903 if (mTouched || now < mTouchEndedTimestampNanos
904 + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
905 return false;
906 }
907
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800908 // Looks good!
909 return true;
Jeff Brown4519f072011-01-23 13:16:01 -0800910 }
911
Craig Mautnereee29c42013-01-17 14:44:34 -0800912 private void clearPredictedRotationLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800913 mPredictedRotation = -1;
914 mPredictedRotationTimestampNanos = Long.MIN_VALUE;
915 }
916
Craig Mautnereee29c42013-01-17 14:44:34 -0800917 private void updatePredictedRotationLocked(long now, int rotation) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800918 if (mPredictedRotation != rotation) {
919 mPredictedRotation = rotation;
920 mPredictedRotationTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700921 }
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800922 }
923
Craig Mautnereee29c42013-01-17 14:44:34 -0800924 private boolean isAcceleratingLocked(float magnitude) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700925 return magnitude < MIN_ACCELERATION_MAGNITUDE
926 || magnitude > MAX_ACCELERATION_MAGNITUDE;
927 }
928
Craig Mautnereee29c42013-01-17 14:44:34 -0800929 private void clearTiltHistoryLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800930 mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE;
931 mTiltHistoryIndex = 1;
932 }
933
Craig Mautnereee29c42013-01-17 14:44:34 -0800934 private void addTiltHistoryEntryLocked(long now, float tilt) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800935 mTiltHistory[mTiltHistoryIndex] = tilt;
936 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now;
937 mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE;
938 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE;
939 }
940
Craig Mautnereee29c42013-01-17 14:44:34 -0800941 private boolean isFlatLocked(long now) {
942 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800943 if (mTiltHistory[i] < FLAT_ANGLE) {
944 break;
945 }
946 if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) {
947 // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
948 return true;
949 }
950 }
951 return false;
952 }
953
Craig Mautnereee29c42013-01-17 14:44:34 -0800954 private boolean isSwingingLocked(long now, float tilt) {
955 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800956 if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) {
957 break;
958 }
959 if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) {
960 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
961 return true;
962 }
963 }
964 return false;
965 }
966
Craig Mautnereee29c42013-01-17 14:44:34 -0800967 private int nextTiltHistoryIndexLocked(int index) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800968 index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
969 return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
970 }
971
Jeff Brown600f0032014-05-22 17:06:00 -0700972 private float getLastTiltLocked() {
973 int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex);
974 return index >= 0 ? mTiltHistory[index] : Float.NaN;
975 }
976
Craig Mautnereee29c42013-01-17 14:44:34 -0800977 private float remainingMS(long now, long until) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800978 return now >= until ? 0 : (until - now) * 0.000001f;
Jeff Brown4519f072011-01-23 13:16:01 -0800979 }
Michael Wright814de9b2015-08-17 23:31:55 +0100980 }
Adrian Roos3595be42015-03-05 16:31:15 +0100981
Michael Wright814de9b2015-08-17 23:31:55 +0100982 final class OrientationSensorJudge extends OrientationJudge {
983 private boolean mTouching;
984 private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
985 private int mProposedRotation = -1;
986 private int mDesiredRotation = -1;
987 private boolean mRotationEvaluationScheduled;
988
989 @Override
990 public int getProposedRotationLocked() {
991 return mProposedRotation;
Adrian Roos3595be42015-03-05 16:31:15 +0100992 }
993
Michael Wright814de9b2015-08-17 23:31:55 +0100994 @Override
995 public void onTouchStartLocked() {
996 mTouching = true;
997 }
998
999 @Override
1000 public void onTouchEndLocked(long whenElapsedNanos) {
1001 mTouching = false;
Adrian Roos3595be42015-03-05 16:31:15 +01001002 mTouchEndedTimestampNanos = whenElapsedNanos;
Michael Wright814de9b2015-08-17 23:31:55 +01001003 if (mDesiredRotation != mProposedRotation) {
1004 final long now = SystemClock.elapsedRealtimeNanos();
1005 scheduleRotationEvaluationIfNecessaryLocked(now);
1006 }
Adrian Roos3595be42015-03-05 16:31:15 +01001007 }
Michael Wright814de9b2015-08-17 23:31:55 +01001008
1009
1010 @Override
1011 public void onSensorChanged(SensorEvent event) {
Michael Wright74f6c792015-08-25 21:15:12 +01001012 int newRotation;
Michael Wright814de9b2015-08-17 23:31:55 +01001013 synchronized (mLock) {
1014 mDesiredRotation = (int) event.values[0];
Michael Wright74f6c792015-08-25 21:15:12 +01001015 newRotation = evaluateRotationChangeLocked();
1016 }
1017 if (newRotation >=0) {
1018 onProposedRotationChanged(newRotation);
Michael Wright814de9b2015-08-17 23:31:55 +01001019 }
1020 }
1021
1022 @Override
1023 public void onAccuracyChanged(Sensor sensor, int accuracy) { }
1024
1025 @Override
1026 public void dumpLocked(PrintWriter pw, String prefix) {
1027 pw.println(prefix + "OrientationSensorJudge");
1028 prefix += " ";
1029 pw.println(prefix + "mDesiredRotation=" + mDesiredRotation);
1030 pw.println(prefix + "mProposedRotation=" + mProposedRotation);
1031 pw.println(prefix + "mTouching=" + mTouching);
1032 pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos);
1033 }
1034
1035 @Override
Wale Ogunwalebfa81ad2017-05-24 15:14:42 -07001036 public void resetLocked(boolean clearCurrentRotation) {
1037 if (clearCurrentRotation) {
1038 mProposedRotation = -1;
1039 mDesiredRotation = -1;
1040 }
Michael Wright814de9b2015-08-17 23:31:55 +01001041 mTouching = false;
1042 mTouchEndedTimestampNanos = Long.MIN_VALUE;
1043 unscheduleRotationEvaluationLocked();
1044 }
1045
Michael Wright74f6c792015-08-25 21:15:12 +01001046 public int evaluateRotationChangeLocked() {
Michael Wright814de9b2015-08-17 23:31:55 +01001047 unscheduleRotationEvaluationLocked();
1048 if (mDesiredRotation == mProposedRotation) {
Michael Wright74f6c792015-08-25 21:15:12 +01001049 return -1;
Michael Wright814de9b2015-08-17 23:31:55 +01001050 }
1051 final long now = SystemClock.elapsedRealtimeNanos();
1052 if (isDesiredRotationAcceptableLocked(now)) {
1053 mProposedRotation = mDesiredRotation;
Michael Wright74f6c792015-08-25 21:15:12 +01001054 return mProposedRotation;
Michael Wright814de9b2015-08-17 23:31:55 +01001055 } else {
1056 scheduleRotationEvaluationIfNecessaryLocked(now);
1057 }
Michael Wright74f6c792015-08-25 21:15:12 +01001058 return -1;
Michael Wright814de9b2015-08-17 23:31:55 +01001059 }
1060
1061 private boolean isDesiredRotationAcceptableLocked(long now) {
1062 if (mTouching) {
1063 return false;
1064 }
1065 if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
1066 return false;
1067 }
1068 return true;
1069 }
1070
1071 private void scheduleRotationEvaluationIfNecessaryLocked(long now) {
1072 if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) {
1073 if (LOG) {
1074 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1075 "ignoring, an evaluation is already scheduled or is unnecessary.");
1076 }
1077 return;
1078 }
1079 if (mTouching) {
1080 if (LOG) {
1081 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1082 "ignoring, user is still touching the screen.");
1083 }
1084 return;
1085 }
1086 long timeOfNextPossibleRotationNanos =
1087 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS;
1088 if (now >= timeOfNextPossibleRotationNanos) {
1089 if (LOG) {
1090 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1091 "ignoring, already past the next possible time of rotation.");
1092 }
1093 return;
1094 }
1095 // Use a delay instead of an absolute time since handlers are in uptime millis and we
1096 // use elapsed realtime.
1097 final long delayMs =
1098 (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO);
1099 mHandler.postDelayed(mRotationEvaluator, delayMs);
1100 mRotationEvaluationScheduled = true;
1101 }
1102
1103 private void unscheduleRotationEvaluationLocked() {
1104 if (!mRotationEvaluationScheduled) {
1105 return;
1106 }
1107 mHandler.removeCallbacks(mRotationEvaluator);
1108 mRotationEvaluationScheduled = false;
1109 }
1110
1111 private Runnable mRotationEvaluator = new Runnable() {
1112 @Override
1113 public void run() {
Michael Wright74f6c792015-08-25 21:15:12 +01001114 int newRotation;
Michael Wright814de9b2015-08-17 23:31:55 +01001115 synchronized (mLock) {
1116 mRotationEvaluationScheduled = false;
Michael Wright74f6c792015-08-25 21:15:12 +01001117 newRotation = evaluateRotationChangeLocked();
1118 }
1119 if (newRotation >= 0) {
1120 onProposedRotationChanged(newRotation);
Michael Wright814de9b2015-08-17 23:31:55 +01001121 }
1122 }
1123 };
Jeff Brown4519f072011-01-23 13:16:01 -08001124 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125}