blob: 8ef0acbd380807df7b8ffb87696f17f953736a4a [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() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800112 synchronized (mLock) {
113 if (mSensor == null) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700114 Slog.w(TAG, "Cannot detect sensors. Not enabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800115 return;
Jeff Browndaf5d892012-05-07 18:30:18 -0700116 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800117 if (mEnabled == false) {
118 if (LOG) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700119 Slog.d(TAG, "WindowOrientationListener enabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800120 }
Michael Wright814de9b2015-08-17 23:31:55 +0100121 mOrientationJudge.resetLocked();
Ivan Podogovd49b1ee2016-05-27 13:47:20 +0100122 if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {
123 mSensorManager.registerListener(
124 mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);
125 } else {
126 mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
127 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800128 mEnabled = true;
129 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 }
131 }
132
133 /**
134 * Disables the WindowOrientationListener.
135 */
136 public void disable() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800137 synchronized (mLock) {
138 if (mSensor == null) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700139 Slog.w(TAG, "Cannot detect sensors. Invalid disable");
Craig Mautnereee29c42013-01-17 14:44:34 -0800140 return;
Jeff Browndaf5d892012-05-07 18:30:18 -0700141 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800142 if (mEnabled == true) {
143 if (LOG) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700144 Slog.d(TAG, "WindowOrientationListener disabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800145 }
Michael Wright814de9b2015-08-17 23:31:55 +0100146 mSensorManager.unregisterListener(mOrientationJudge);
Craig Mautnereee29c42013-01-17 14:44:34 -0800147 mEnabled = false;
148 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
150 }
151
Adrian Roos3595be42015-03-05 16:31:15 +0100152 public void onTouchStart() {
153 synchronized (mLock) {
Michael Wright814de9b2015-08-17 23:31:55 +0100154 if (mOrientationJudge != null) {
155 mOrientationJudge.onTouchStartLocked();
Adrian Roos12741962015-03-31 11:20:30 -0700156 }
Adrian Roos3595be42015-03-05 16:31:15 +0100157 }
158 }
159
160 public void onTouchEnd() {
161 long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
162
163 synchronized (mLock) {
Michael Wright814de9b2015-08-17 23:31:55 +0100164 if (mOrientationJudge != null) {
165 mOrientationJudge.onTouchEndLocked(whenElapsedNanos);
Adrian Roos12741962015-03-31 11:20:30 -0700166 }
Adrian Roos3595be42015-03-05 16:31:15 +0100167 }
168 }
169
Jeff Brown4519f072011-01-23 13:16:01 -0800170 /**
Jeff Brownc0347aa2011-09-23 17:26:09 -0700171 * Sets the current rotation.
172 *
173 * @param rotation The current rotation.
Jeff Brown4519f072011-01-23 13:16:01 -0800174 */
Jeff Brownc0347aa2011-09-23 17:26:09 -0700175 public void setCurrentRotation(int rotation) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800176 synchronized (mLock) {
177 mCurrentRotation = rotation;
178 }
Jeff Brownc0347aa2011-09-23 17:26:09 -0700179 }
180
181 /**
182 * Gets the proposed rotation.
183 *
184 * This method only returns a rotation if the orientation listener is certain
185 * of its proposal. If the rotation is indeterminate, returns -1.
186 *
187 * @return The proposed rotation, or -1 if unknown.
188 */
189 public int getProposedRotation() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800190 synchronized (mLock) {
191 if (mEnabled) {
Michael Wright814de9b2015-08-17 23:31:55 +0100192 return mOrientationJudge.getProposedRotationLocked();
Craig Mautnereee29c42013-01-17 14:44:34 -0800193 }
194 return -1;
Suchi Amalapurapu63104ed2009-12-15 14:06:08 -0800195 }
Dianne Hackborne4fbd622009-03-27 18:09:16 -0700196 }
Steve Howard5f531ae2010-08-05 17:14:53 -0700197
198 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 * Returns true if sensor is enabled and false otherwise
200 */
201 public boolean canDetectOrientation() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800202 synchronized (mLock) {
203 return mSensor != null;
204 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 }
Steve Howard1ba101f2010-02-23 14:30:13 -0800206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700208 * Called when the rotation view of the device has changed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 *
Jeff Brownc0347aa2011-09-23 17:26:09 -0700210 * This method is called whenever the orientation becomes certain of an orientation.
211 * It is called each time the orientation determination transitions from being
212 * uncertain to being certain again, even if it is the same orientation as before.
213 *
Michael Wright74f6c792015-08-25 21:15:12 +0100214 * This should only be called on the Handler thread.
215 *
Steve Howard1ba101f2010-02-23 14:30:13 -0800216 * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
Craig Mautnereee29c42013-01-17 14:44:34 -0800217 * @see android.view.Surface
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 */
Jeff Brownc0347aa2011-09-23 17:26:09 -0700219 public abstract void onProposedRotationChanged(int rotation);
Jeff Brown4519f072011-01-23 13:16:01 -0800220
Jeff Brown600f0032014-05-22 17:06:00 -0700221 public void dump(PrintWriter pw, String prefix) {
222 synchronized (mLock) {
223 pw.println(prefix + TAG);
224 prefix += " ";
225 pw.println(prefix + "mEnabled=" + mEnabled);
226 pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
Michael Wright814de9b2015-08-17 23:31:55 +0100227 pw.println(prefix + "mSensorType=" + mSensorType);
Jeff Brown600f0032014-05-22 17:06:00 -0700228 pw.println(prefix + "mSensor=" + mSensor);
229 pw.println(prefix + "mRate=" + mRate);
230
Michael Wright814de9b2015-08-17 23:31:55 +0100231 if (mOrientationJudge != null) {
232 mOrientationJudge.dumpLocked(pw, prefix);
Jaewan Kim0abc28e2014-06-26 09:13:11 +0900233 }
Jeff Brown600f0032014-05-22 17:06:00 -0700234 }
235 }
236
Michael Wright814de9b2015-08-17 23:31:55 +0100237 abstract class OrientationJudge implements SensorEventListener {
238 // Number of nanoseconds per millisecond.
239 protected static final long NANOS_PER_MS = 1000000;
240
241 // Number of milliseconds per nano second.
242 protected static final float MILLIS_PER_NANO = 0.000001f;
243
244 // The minimum amount of time that must have elapsed since the screen was last touched
245 // before the proposed rotation can change.
246 protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
247 500 * NANOS_PER_MS;
248
249 /**
250 * Gets the proposed rotation.
251 *
252 * This method only returns a rotation if the orientation listener is certain
253 * of its proposal. If the rotation is indeterminate, returns -1.
254 *
255 * Should only be called when holding WindowOrientationListener lock.
256 *
257 * @return The proposed rotation, or -1 if unknown.
258 */
259 public abstract int getProposedRotationLocked();
260
261 /**
262 * Notifies the orientation judge that the screen is being touched.
263 *
264 * Should only be called when holding WindowOrientationListener lock.
265 */
266 public abstract void onTouchStartLocked();
267
268 /**
269 * Notifies the orientation judge that the screen is no longer being touched.
270 *
271 * Should only be called when holding WindowOrientationListener lock.
272 *
273 * @param whenElapsedNanos Given in the elapsed realtime nanos time base.
274 */
275 public abstract void onTouchEndLocked(long whenElapsedNanos);
276
277 /**
278 * Resets the state of the judge.
279 *
280 * Should only be called when holding WindowOrientationListener lock.
281 */
282 public abstract void resetLocked();
283
284 /**
285 * Dumps internal state of the orientation judge.
286 *
287 * Should only be called when holding WindowOrientationListener lock.
288 */
289 public abstract void dumpLocked(PrintWriter pw, String prefix);
290
291 @Override
292 public abstract void onAccuracyChanged(Sensor sensor, int accuracy);
293
294 @Override
295 public abstract void onSensorChanged(SensorEvent event);
296 }
297
Jeff Brown4519f072011-01-23 13:16:01 -0800298 /**
Jeff Brown4519f072011-01-23 13:16:01 -0800299 * This class filters the raw accelerometer data and tries to detect actual changes in
300 * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
301 * but here's the outline:
302 *
303 * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in
304 * cartesian space because the orientation calculations are sensitive to the
305 * absolute magnitude of the acceleration. In particular, there are singularities
306 * in the calculation as the magnitude approaches 0. By performing the low-pass
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800307 * filtering early, we can eliminate most spurious high-frequency impulses due to noise.
Jeff Brown4519f072011-01-23 13:16:01 -0800308 *
309 * - Convert the acceleromter vector from cartesian to spherical coordinates.
310 * Since we're dealing with rotation of the device, this is the sensible coordinate
311 * system to work in. The zenith direction is the Z-axis, the direction the screen
312 * is facing. The radial distance is referred to as the magnitude below.
313 * The elevation angle is referred to as the "tilt" below.
314 * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is
315 * the Y-axis).
316 * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference.
317 *
318 * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing.
319 * The orientation angle is not meaningful when the device is nearly horizontal.
320 * The tilt angle thresholds are set differently for each orientation and different
321 * limits are applied when the device is facing down as opposed to when it is facing
322 * forward or facing up.
323 *
324 * - When the orientation angle reaches a certain threshold, consider transitioning
325 * to the corresponding orientation. These thresholds have some hysteresis built-in
326 * to avoid oscillations between adjacent orientations.
327 *
Jeff Brownc0347aa2011-09-23 17:26:09 -0700328 * - Wait for the device to settle for a little bit. Once that happens, issue the
329 * new orientation proposal.
Jeff Brown4519f072011-01-23 13:16:01 -0800330 *
331 * Details are explained inline.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800332 *
333 * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
334 * signal processing background.
Jeff Brown4519f072011-01-23 13:16:01 -0800335 */
Michael Wright814de9b2015-08-17 23:31:55 +0100336 final class AccelSensorJudge extends OrientationJudge {
Jeff Brown4519f072011-01-23 13:16:01 -0800337 // We work with all angles in degrees in this class.
338 private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
339
340 // Indices into SensorEvent.values for the accelerometer sensor.
341 private static final int ACCELEROMETER_DATA_X = 0;
342 private static final int ACCELEROMETER_DATA_Y = 1;
343 private static final int ACCELEROMETER_DATA_Z = 2;
344
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800345 // The minimum amount of time that a predicted rotation must be stable before it
346 // is accepted as a valid rotation proposal. This value can be quite small because
347 // the low-pass filter already suppresses most of the noise so we're really just
348 // looking for quick confirmation that the last few samples are in agreement as to
349 // the desired orientation.
350 private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800351
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800352 // The minimum amount of time that must have elapsed since the device last exited
353 // the flat state (time since it was picked up) before the proposed rotation
354 // can change.
355 private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800356
Jeff Browndaf5d892012-05-07 18:30:18 -0700357 // The minimum amount of time that must have elapsed since the device stopped
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800358 // swinging (time since device appeared to be in the process of being put down
359 // or put away into a pocket) before the proposed rotation can change.
360 private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700361
Jeff Browndaf5d892012-05-07 18:30:18 -0700362 // The minimum amount of time that must have elapsed since the device stopped
363 // undergoing external acceleration before the proposed rotation can change.
364 private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
365 500 * NANOS_PER_MS;
366
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800367 // If the tilt angle remains greater than the specified angle for a minimum of
368 // the specified time, then the device is deemed to be lying flat
369 // (just chillin' on a table).
Jeff Browne97c9a22015-06-10 22:12:54 -0700370 private static final float FLAT_ANGLE = 80;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800371 private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS;
372
373 // If the tilt angle has increased by at least delta degrees within the specified amount
374 // of time, then the device is deemed to be swinging away from the user
375 // down towards flat (tilt = 90).
376 private static final float SWING_AWAY_ANGLE_DELTA = 20;
377 private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700378
Jeff Brown4519f072011-01-23 13:16:01 -0800379 // The maximum sample inter-arrival time in milliseconds.
380 // If the acceleration samples are further apart than this amount in time, we reset the
381 // state of the low-pass filter and orientation properties. This helps to handle
382 // boundary conditions when the device is turned on, wakes from suspend or there is
383 // a significant gap in samples.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800384 private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800385
Jeff Brownc0347aa2011-09-23 17:26:09 -0700386 // The acceleration filter time constant.
387 //
388 // This time constant is used to tune the acceleration filter such that
389 // impulses and vibrational noise (think car dock) is suppressed before we
390 // try to calculate the tilt and orientation angles.
391 //
392 // The filter time constant is related to the filter cutoff frequency, which is the
393 // frequency at which signals are attenuated by 3dB (half the passband power).
Jeff Brown4519f072011-01-23 13:16:01 -0800394 // Each successive octave beyond this frequency is attenuated by an additional 6dB.
395 //
Jeff Brownc0347aa2011-09-23 17:26:09 -0700396 // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
397 // is given by Fc = 1 / (2pi * t).
398 //
399 // The higher the time constant, the lower the cutoff frequency, so more noise
400 // will be suppressed.
401 //
402 // Filtering adds latency proportional the time constant (inversely proportional
403 // to the cutoff frequency) so we don't want to make the time constant too
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800404 // large or we can lose responsiveness. Likewise we don't want to make it too
405 // small or we do a poor job suppressing acceleration spikes.
406 // Empirically, 100ms seems to be too small and 500ms is too large.
407 private static final float FILTER_TIME_CONSTANT_MS = 200.0f;
Jeff Brown4519f072011-01-23 13:16:01 -0800408
409 /* State for orientation detection. */
410
411 // Thresholds for minimum and maximum allowable deviation from gravity.
412 //
413 // If the device is undergoing external acceleration (being bumped, in a car
414 // that is turning around a corner or a plane taking off) then the magnitude
415 // may be substantially more or less than gravity. This can skew our orientation
416 // detection by making us think that up is pointed in a different direction.
417 //
418 // Conversely, if the device is in freefall, then there will be no gravity to
419 // measure at all. This is problematic because we cannot detect the orientation
420 // without gravity to tell us which way is up. A magnitude near 0 produces
421 // singularities in the tilt and orientation calculations.
422 //
423 // In both cases, we postpone choosing an orientation.
Jeff Browndaf5d892012-05-07 18:30:18 -0700424 //
425 // However, we need to tolerate some acceleration because the angular momentum
426 // of turning the device can skew the observed acceleration for a short period of time.
427 private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2
428 private static final float ACCELERATION_TOLERANCE = 4; // m/s^2
Jeff Brown4519f072011-01-23 13:16:01 -0800429 private static final float MIN_ACCELERATION_MAGNITUDE =
Jeff Browndaf5d892012-05-07 18:30:18 -0700430 SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
Jeff Brown4519f072011-01-23 13:16:01 -0800431 private static final float MAX_ACCELERATION_MAGNITUDE =
Jeff Browndaf5d892012-05-07 18:30:18 -0700432 SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
Jeff Brown4519f072011-01-23 13:16:01 -0800433
434 // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
Jeff Browne97c9a22015-06-10 22:12:54 -0700435 // when screen is facing the sky or ground), we completely ignore orientation data
436 // because it's too unstable.
437 private static final int MAX_TILT = 80;
438
439 // The tilt angle below which we conclude that the user is holding the device
440 // overhead reading in bed and lock into that state.
441 private static final int TILT_OVERHEAD_ENTER = -40;
442
443 // The tilt angle above which we conclude that the user would like a rotation
444 // change to occur and unlock from the overhead state.
445 private static final int TILT_OVERHEAD_EXIT = -15;
446
447 // The gap angle in degrees between adjacent orientation angles for hysteresis.
448 // This creates a "dead zone" between the current orientation and a proposed
449 // adjacent orientation. No orientation proposal is made when the orientation
450 // angle is within the gap between the current orientation and the adjacent
451 // orientation.
452 private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
Jeff Brown4519f072011-01-23 13:16:01 -0800453
454 // The tilt angle range in degrees for each orientation.
455 // Beyond these tilt angles, we don't even consider transitioning into the
456 // specified orientation. We place more stringent requirements on unnatural
457 // orientations than natural ones to make it less likely to accidentally transition
458 // into those states.
459 // The first value of each pair is negative so it applies a limit when the device is
460 // facing down (overhead reading in bed).
461 // The second value of each pair is positive so it applies a limit when the device is
462 // facing up (resting on a table).
463 // The ideal tilt angle is 0 (when the device is vertical) so the limits establish
464 // how close to vertical the device must be in order to change orientation.
Jeff Browne97c9a22015-06-10 22:12:54 -0700465 private final int[][] mTiltToleranceConfig = new int[][] {
466 /* ROTATION_0 */ { -25, 70 }, // note: these are overridden by config.xml
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800467 /* ROTATION_90 */ { -25, 65 },
468 /* ROTATION_180 */ { -25, 60 },
469 /* ROTATION_270 */ { -25, 65 }
Jeff Brown4519f072011-01-23 13:16:01 -0800470 };
471
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800472 // Timestamp and value of the last accelerometer sample.
473 private long mLastFilteredTimestampNanos;
474 private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
Jeff Brown4519f072011-01-23 13:16:01 -0800475
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800476 // The last proposed rotation, -1 if unknown.
477 private int mProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800478
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800479 // Value of the current predicted rotation, -1 if unknown.
480 private int mPredictedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800481
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800482 // Timestamp of when the predicted rotation most recently changed.
483 private long mPredictedRotationTimestampNanos;
484
485 // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
486 private long mFlatTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700487 private boolean mFlat;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800488
489 // Timestamp when the device last appeared to be swinging.
490 private long mSwingTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700491 private boolean mSwinging;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800492
Jeff Browndaf5d892012-05-07 18:30:18 -0700493 // Timestamp when the device last appeared to be undergoing external acceleration.
494 private long mAccelerationTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700495 private boolean mAccelerating;
496
Adrian Roos3595be42015-03-05 16:31:15 +0100497 // Timestamp when the last touch to the touch screen ended
498 private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
499 private boolean mTouched;
500
Jeff Brown600f0032014-05-22 17:06:00 -0700501 // Whether we are locked into an overhead usage mode.
502 private boolean mOverhead;
Jeff Browndaf5d892012-05-07 18:30:18 -0700503
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800504 // History of observed tilt angles.
Jeff Browne97c9a22015-06-10 22:12:54 -0700505 private static final int TILT_HISTORY_SIZE = 200;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800506 private float[] mTiltHistory = new float[TILT_HISTORY_SIZE];
507 private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
508 private int mTiltHistoryIndex;
Jeff Brown4519f072011-01-23 13:16:01 -0800509
Michael Wright814de9b2015-08-17 23:31:55 +0100510 public AccelSensorJudge(Context context) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700511 // Load tilt tolerance configuration.
512 int[] tiltTolerance = context.getResources().getIntArray(
513 com.android.internal.R.array.config_autoRotationTiltTolerance);
514 if (tiltTolerance.length == 8) {
515 for (int i = 0; i < 4; i++) {
516 int min = tiltTolerance[i * 2];
517 int max = tiltTolerance[i * 2 + 1];
518 if (min >= -90 && min <= max && max <= 90) {
519 mTiltToleranceConfig[i][0] = min;
520 mTiltToleranceConfig[i][1] = max;
521 } else {
522 Slog.wtf(TAG, "config_autoRotationTiltTolerance contains invalid range: "
523 + "min=" + min + ", max=" + max);
524 }
525 }
526 } else {
527 Slog.wtf(TAG, "config_autoRotationTiltTolerance should have exactly 8 elements");
528 }
529 }
530
Michael Wright814de9b2015-08-17 23:31:55 +0100531 @Override
Craig Mautnereee29c42013-01-17 14:44:34 -0800532 public int getProposedRotationLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800533 return mProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800534 }
535
Michael Wright814de9b2015-08-17 23:31:55 +0100536 @Override
Jeff Brown600f0032014-05-22 17:06:00 -0700537 public void dumpLocked(PrintWriter pw, String prefix) {
Michael Wright814de9b2015-08-17 23:31:55 +0100538 pw.println(prefix + "AccelSensorJudge");
539 prefix += " ";
Jeff Brown600f0032014-05-22 17:06:00 -0700540 pw.println(prefix + "mProposedRotation=" + mProposedRotation);
541 pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
542 pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
543 pw.println(prefix + "mLastFilteredY=" + mLastFilteredY);
544 pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ);
Michael Wright6eabf572015-08-12 15:35:31 +0100545 final long delta = SystemClock.elapsedRealtimeNanos() - mLastFilteredTimestampNanos;
546 pw.println(prefix + "mLastFilteredTimestampNanos=" + mLastFilteredTimestampNanos
547 + " (" + (delta * 0.000001f) + "ms ago)");
Jeff Brown600f0032014-05-22 17:06:00 -0700548 pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}");
549 pw.println(prefix + "mFlat=" + mFlat);
550 pw.println(prefix + "mSwinging=" + mSwinging);
551 pw.println(prefix + "mAccelerating=" + mAccelerating);
552 pw.println(prefix + "mOverhead=" + mOverhead);
Adrian Roos3595be42015-03-05 16:31:15 +0100553 pw.println(prefix + "mTouched=" + mTouched);
Jeff Browne97c9a22015-06-10 22:12:54 -0700554 pw.print(prefix + "mTiltToleranceConfig=[");
555 for (int i = 0; i < 4; i++) {
556 if (i != 0) {
557 pw.print(", ");
558 }
559 pw.print("[");
560 pw.print(mTiltToleranceConfig[i][0]);
561 pw.print(", ");
562 pw.print(mTiltToleranceConfig[i][1]);
563 pw.print("]");
564 }
565 pw.println("]");
Jeff Brown600f0032014-05-22 17:06:00 -0700566 }
567
Jeff Brown4519f072011-01-23 13:16:01 -0800568 @Override
569 public void onAccuracyChanged(Sensor sensor, int accuracy) {
570 }
571
572 @Override
573 public void onSensorChanged(SensorEvent event) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800574 int proposedRotation;
575 int oldProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800576
Craig Mautnereee29c42013-01-17 14:44:34 -0800577 synchronized (mLock) {
578 // The vector given in the SensorEvent points straight up (towards the sky) under
579 // ideal conditions (the phone is not accelerating). I'll call this up vector
580 // elsewhere.
581 float x = event.values[ACCELEROMETER_DATA_X];
582 float y = event.values[ACCELEROMETER_DATA_Y];
583 float z = event.values[ACCELEROMETER_DATA_Z];
Jeff Brown4519f072011-01-23 13:16:01 -0800584
Jeff Browndaf5d892012-05-07 18:30:18 -0700585 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800586 Slog.v(TAG, "Raw acceleration vector: "
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800587 + "x=" + x + ", y=" + y + ", z=" + z
Neil Fuller33253a42014-10-01 11:55:10 +0100588 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z));
Jeff Brown4519f072011-01-23 13:16:01 -0800589 }
Jeff Brown4519f072011-01-23 13:16:01 -0800590
Craig Mautnereee29c42013-01-17 14:44:34 -0800591 // Apply a low-pass filter to the acceleration up vector in cartesian space.
592 // Reset the orientation listener state if the samples are too far apart in time
593 // or when we see values of (0, 0, 0) which indicates that we polled the
594 // accelerometer too soon after turning it on and we don't have any data yet.
595 final long now = event.timestamp;
596 final long then = mLastFilteredTimestampNanos;
597 final float timeDeltaMS = (now - then) * 0.000001f;
598 final boolean skipSample;
599 if (now < then
600 || now > then + MAX_FILTER_DELTA_TIME_NANOS
601 || (x == 0 && y == 0 && z == 0)) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700602 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800603 Slog.v(TAG, "Resetting orientation listener.");
Jeff Brown4519f072011-01-23 13:16:01 -0800604 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800605 resetLocked();
606 skipSample = true;
Jeff Brown4519f072011-01-23 13:16:01 -0800607 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800608 final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
609 x = alpha * (x - mLastFilteredX) + mLastFilteredX;
610 y = alpha * (y - mLastFilteredY) + mLastFilteredY;
611 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
612 if (LOG) {
613 Slog.v(TAG, "Filtered acceleration vector: "
614 + "x=" + x + ", y=" + y + ", z=" + z
Neil Fuller33253a42014-10-01 11:55:10 +0100615 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z));
Jeff Browndaf5d892012-05-07 18:30:18 -0700616 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800617 skipSample = false;
618 }
619 mLastFilteredTimestampNanos = now;
620 mLastFilteredX = x;
621 mLastFilteredY = y;
622 mLastFilteredZ = z;
Jeff Browndaf5d892012-05-07 18:30:18 -0700623
Craig Mautnereee29c42013-01-17 14:44:34 -0800624 boolean isAccelerating = false;
625 boolean isFlat = false;
626 boolean isSwinging = false;
627 if (!skipSample) {
628 // Calculate the magnitude of the acceleration vector.
Neil Fuller33253a42014-10-01 11:55:10 +0100629 final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
Craig Mautnereee29c42013-01-17 14:44:34 -0800630 if (magnitude < NEAR_ZERO_MAGNITUDE) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700631 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800632 Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero.");
Jeff Brown4519f072011-01-23 13:16:01 -0800633 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800634 clearPredictedRotationLocked();
Jeff Brown4519f072011-01-23 13:16:01 -0800635 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800636 // Determine whether the device appears to be undergoing external
637 // acceleration.
638 if (isAcceleratingLocked(magnitude)) {
639 isAccelerating = true;
640 mAccelerationTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700641 }
642
Craig Mautnereee29c42013-01-17 14:44:34 -0800643 // Calculate the tilt angle.
644 // This is the angle between the up vector and the x-y plane (the plane of
645 // the screen) in a range of [-90, 90] degrees.
646 // -90 degrees: screen horizontal and facing the ground (overhead)
647 // 0 degrees: screen vertical
648 // 90 degrees: screen horizontal and facing the sky (on table)
649 final int tiltAngle = (int) Math.round(
650 Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
651 addTiltHistoryEntryLocked(now, tiltAngle);
652
653 // Determine whether the device appears to be flat or swinging.
654 if (isFlatLocked(now)) {
655 isFlat = true;
656 mFlatTimestampNanos = now;
657 }
658 if (isSwingingLocked(now, tiltAngle)) {
659 isSwinging = true;
660 mSwingTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700661 }
662
Craig Mautnereee29c42013-01-17 14:44:34 -0800663 // If the tilt angle is too close to horizontal then we cannot determine
664 // the orientation angle of the screen.
Jeff Brown600f0032014-05-22 17:06:00 -0700665 if (tiltAngle <= TILT_OVERHEAD_ENTER) {
666 mOverhead = true;
667 } else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
668 mOverhead = false;
669 }
670 if (mOverhead) {
671 if (LOG) {
672 Slog.v(TAG, "Ignoring sensor data, device is overhead: "
673 + "tiltAngle=" + tiltAngle);
674 }
675 clearPredictedRotationLocked();
676 } else if (Math.abs(tiltAngle) > MAX_TILT) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700677 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800678 Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
679 + "tiltAngle=" + tiltAngle);
Jeff Brownc0347aa2011-09-23 17:26:09 -0700680 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800681 clearPredictedRotationLocked();
Jeff Brownc0347aa2011-09-23 17:26:09 -0700682 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800683 // Calculate the orientation angle.
684 // This is the angle between the x-y projection of the up vector onto
685 // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
686 int orientationAngle = (int) Math.round(
687 -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
688 if (orientationAngle < 0) {
689 // atan2 returns [-180, 180]; normalize to [0, 360]
690 orientationAngle += 360;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700691 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800692
693 // Find the nearest rotation.
694 int nearestRotation = (orientationAngle + 45) / 90;
695 if (nearestRotation == 4) {
696 nearestRotation = 0;
697 }
698
699 // Determine the predicted orientation.
700 if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle)
701 && isOrientationAngleAcceptableLocked(nearestRotation,
702 orientationAngle)) {
703 updatePredictedRotationLocked(now, nearestRotation);
704 if (LOG) {
705 Slog.v(TAG, "Predicted: "
706 + "tiltAngle=" + tiltAngle
707 + ", orientationAngle=" + orientationAngle
708 + ", predictedRotation=" + mPredictedRotation
709 + ", predictedRotationAgeMS="
710 + ((now - mPredictedRotationTimestampNanos)
711 * 0.000001f));
712 }
713 } else {
714 if (LOG) {
715 Slog.v(TAG, "Ignoring sensor data, no predicted rotation: "
716 + "tiltAngle=" + tiltAngle
717 + ", orientationAngle=" + orientationAngle);
718 }
719 clearPredictedRotationLocked();
720 }
Jeff Brown4519f072011-01-23 13:16:01 -0800721 }
722 }
723 }
Jeff Brown600f0032014-05-22 17:06:00 -0700724 mFlat = isFlat;
725 mSwinging = isSwinging;
726 mAccelerating = isAccelerating;
Jeff Brown4519f072011-01-23 13:16:01 -0800727
Craig Mautnereee29c42013-01-17 14:44:34 -0800728 // Determine new proposed rotation.
729 oldProposedRotation = mProposedRotation;
730 if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) {
731 mProposedRotation = mPredictedRotation;
732 }
733 proposedRotation = mProposedRotation;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800734
Craig Mautnereee29c42013-01-17 14:44:34 -0800735 // Write final statistics about where we are in the orientation detection process.
736 if (LOG) {
737 Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation
738 + ", proposedRotation=" + proposedRotation
739 + ", predictedRotation=" + mPredictedRotation
740 + ", timeDeltaMS=" + timeDeltaMS
741 + ", isAccelerating=" + isAccelerating
742 + ", isFlat=" + isFlat
743 + ", isSwinging=" + isSwinging
Jeff Brown600f0032014-05-22 17:06:00 -0700744 + ", isOverhead=" + mOverhead
Adrian Roos3595be42015-03-05 16:31:15 +0100745 + ", isTouched=" + mTouched
Craig Mautnereee29c42013-01-17 14:44:34 -0800746 + ", timeUntilSettledMS=" + remainingMS(now,
747 mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
748 + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
749 mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS)
750 + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
751 mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
752 + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
Adrian Roos3595be42015-03-05 16:31:15 +0100753 mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)
754 + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now,
755 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS));
Craig Mautnereee29c42013-01-17 14:44:34 -0800756 }
Jeff Brown4519f072011-01-23 13:16:01 -0800757 }
758
759 // Tell the listener.
Craig Mautnereee29c42013-01-17 14:44:34 -0800760 if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700761 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800762 Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation
Jeff Brownc0347aa2011-09-23 17:26:09 -0700763 + ", oldProposedRotation=" + oldProposedRotation);
764 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800765 onProposedRotationChanged(proposedRotation);
Jeff Brown4519f072011-01-23 13:16:01 -0800766 }
767 }
768
Michael Wright814de9b2015-08-17 23:31:55 +0100769 @Override
770 public void onTouchStartLocked() {
771 mTouched = true;
772 }
773
774 @Override
775 public void onTouchEndLocked(long whenElapsedNanos) {
776 mTouched = false;
777 mTouchEndedTimestampNanos = whenElapsedNanos;
778 }
779
780 @Override
781 public void resetLocked() {
782 mLastFilteredTimestampNanos = Long.MIN_VALUE;
783 mProposedRotation = -1;
784 mFlatTimestampNanos = Long.MIN_VALUE;
785 mFlat = false;
786 mSwingTimestampNanos = Long.MIN_VALUE;
787 mSwinging = false;
788 mAccelerationTimestampNanos = Long.MIN_VALUE;
789 mAccelerating = false;
790 mOverhead = false;
791 clearPredictedRotationLocked();
792 clearTiltHistoryLocked();
793 }
794
795
Jeff Brown4519f072011-01-23 13:16:01 -0800796 /**
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800797 * Returns true if the tilt angle is acceptable for a given predicted rotation.
Jeff Brown4519f072011-01-23 13:16:01 -0800798 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800799 private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700800 return tiltAngle >= mTiltToleranceConfig[rotation][0]
801 && tiltAngle <= mTiltToleranceConfig[rotation][1];
Jeff Brown4519f072011-01-23 13:16:01 -0800802 }
803
804 /**
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800805 * Returns true if the orientation angle is acceptable for a given predicted rotation.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700806 *
Jeff Brown4519f072011-01-23 13:16:01 -0800807 * This function takes into account the gap between adjacent orientations
808 * for hysteresis.
809 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800810 private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) {
Jeff Brown4519f072011-01-23 13:16:01 -0800811 // If there is no current rotation, then there is no gap.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700812 // The gap is used only to introduce hysteresis among advertised orientation
813 // changes to avoid flapping.
Craig Mautnereee29c42013-01-17 14:44:34 -0800814 final int currentRotation = mCurrentRotation;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700815 if (currentRotation >= 0) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800816 // If the specified rotation is the same or is counter-clockwise adjacent
817 // to the current rotation, then we set a lower bound on the orientation angle.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700818 // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90,
Jeff Brown4519f072011-01-23 13:16:01 -0800819 // then we want to check orientationAngle > 45 + GAP / 2.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800820 if (rotation == currentRotation
821 || rotation == (currentRotation + 1) % 4) {
822 int lowerBound = rotation * 90 - 45
Jeff Brown4519f072011-01-23 13:16:01 -0800823 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800824 if (rotation == 0) {
Jeff Brown4519f072011-01-23 13:16:01 -0800825 if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
826 return false;
827 }
828 } else {
829 if (orientationAngle < lowerBound) {
830 return false;
831 }
832 }
833 }
834
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800835 // If the specified rotation is the same or is clockwise adjacent,
Jeff Brown4519f072011-01-23 13:16:01 -0800836 // then we set an upper bound on the orientation angle.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800837 // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270,
Jeff Brown4519f072011-01-23 13:16:01 -0800838 // then we want to check orientationAngle < 315 - GAP / 2.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800839 if (rotation == currentRotation
840 || rotation == (currentRotation + 3) % 4) {
841 int upperBound = rotation * 90 + 45
Jeff Brown4519f072011-01-23 13:16:01 -0800842 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800843 if (rotation == 0) {
Jeff Brown4519f072011-01-23 13:16:01 -0800844 if (orientationAngle <= 45 && orientationAngle > upperBound) {
845 return false;
846 }
847 } else {
848 if (orientationAngle > upperBound) {
849 return false;
850 }
851 }
852 }
853 }
854 return true;
855 }
856
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800857 /**
858 * Returns true if the predicted rotation is ready to be advertised as a
859 * proposed rotation.
860 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800861 private boolean isPredictedRotationAcceptableLocked(long now) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800862 // The predicted rotation must have settled long enough.
863 if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
864 return false;
865 }
866
867 // The last flat state (time since picked up) must have been sufficiently long ago.
868 if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
869 return false;
870 }
871
872 // The last swing state (time since last movement to put down) must have been
873 // sufficiently long ago.
874 if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
875 return false;
876 }
877
Jeff Browndaf5d892012-05-07 18:30:18 -0700878 // The last acceleration state must have been sufficiently long ago.
879 if (now < mAccelerationTimestampNanos
880 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
881 return false;
882 }
883
Adrian Roos3595be42015-03-05 16:31:15 +0100884 // The last touch must have ended sufficiently long ago.
885 if (mTouched || now < mTouchEndedTimestampNanos
886 + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
887 return false;
888 }
889
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800890 // Looks good!
891 return true;
Jeff Brown4519f072011-01-23 13:16:01 -0800892 }
893
Craig Mautnereee29c42013-01-17 14:44:34 -0800894 private void clearPredictedRotationLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800895 mPredictedRotation = -1;
896 mPredictedRotationTimestampNanos = Long.MIN_VALUE;
897 }
898
Craig Mautnereee29c42013-01-17 14:44:34 -0800899 private void updatePredictedRotationLocked(long now, int rotation) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800900 if (mPredictedRotation != rotation) {
901 mPredictedRotation = rotation;
902 mPredictedRotationTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700903 }
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800904 }
905
Craig Mautnereee29c42013-01-17 14:44:34 -0800906 private boolean isAcceleratingLocked(float magnitude) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700907 return magnitude < MIN_ACCELERATION_MAGNITUDE
908 || magnitude > MAX_ACCELERATION_MAGNITUDE;
909 }
910
Craig Mautnereee29c42013-01-17 14:44:34 -0800911 private void clearTiltHistoryLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800912 mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE;
913 mTiltHistoryIndex = 1;
914 }
915
Craig Mautnereee29c42013-01-17 14:44:34 -0800916 private void addTiltHistoryEntryLocked(long now, float tilt) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800917 mTiltHistory[mTiltHistoryIndex] = tilt;
918 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now;
919 mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE;
920 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE;
921 }
922
Craig Mautnereee29c42013-01-17 14:44:34 -0800923 private boolean isFlatLocked(long now) {
924 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800925 if (mTiltHistory[i] < FLAT_ANGLE) {
926 break;
927 }
928 if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) {
929 // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
930 return true;
931 }
932 }
933 return false;
934 }
935
Craig Mautnereee29c42013-01-17 14:44:34 -0800936 private boolean isSwingingLocked(long now, float tilt) {
937 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800938 if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) {
939 break;
940 }
941 if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) {
942 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
943 return true;
944 }
945 }
946 return false;
947 }
948
Craig Mautnereee29c42013-01-17 14:44:34 -0800949 private int nextTiltHistoryIndexLocked(int index) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800950 index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
951 return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
952 }
953
Jeff Brown600f0032014-05-22 17:06:00 -0700954 private float getLastTiltLocked() {
955 int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex);
956 return index >= 0 ? mTiltHistory[index] : Float.NaN;
957 }
958
Craig Mautnereee29c42013-01-17 14:44:34 -0800959 private float remainingMS(long now, long until) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800960 return now >= until ? 0 : (until - now) * 0.000001f;
Jeff Brown4519f072011-01-23 13:16:01 -0800961 }
Michael Wright814de9b2015-08-17 23:31:55 +0100962 }
Adrian Roos3595be42015-03-05 16:31:15 +0100963
Michael Wright814de9b2015-08-17 23:31:55 +0100964 final class OrientationSensorJudge extends OrientationJudge {
965 private boolean mTouching;
966 private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
967 private int mProposedRotation = -1;
968 private int mDesiredRotation = -1;
969 private boolean mRotationEvaluationScheduled;
970
971 @Override
972 public int getProposedRotationLocked() {
973 return mProposedRotation;
Adrian Roos3595be42015-03-05 16:31:15 +0100974 }
975
Michael Wright814de9b2015-08-17 23:31:55 +0100976 @Override
977 public void onTouchStartLocked() {
978 mTouching = true;
979 }
980
981 @Override
982 public void onTouchEndLocked(long whenElapsedNanos) {
983 mTouching = false;
Adrian Roos3595be42015-03-05 16:31:15 +0100984 mTouchEndedTimestampNanos = whenElapsedNanos;
Michael Wright814de9b2015-08-17 23:31:55 +0100985 if (mDesiredRotation != mProposedRotation) {
986 final long now = SystemClock.elapsedRealtimeNanos();
987 scheduleRotationEvaluationIfNecessaryLocked(now);
988 }
Adrian Roos3595be42015-03-05 16:31:15 +0100989 }
Michael Wright814de9b2015-08-17 23:31:55 +0100990
991
992 @Override
993 public void onSensorChanged(SensorEvent event) {
Michael Wright74f6c792015-08-25 21:15:12 +0100994 int newRotation;
Michael Wright814de9b2015-08-17 23:31:55 +0100995 synchronized (mLock) {
996 mDesiredRotation = (int) event.values[0];
Michael Wright74f6c792015-08-25 21:15:12 +0100997 newRotation = evaluateRotationChangeLocked();
998 }
999 if (newRotation >=0) {
1000 onProposedRotationChanged(newRotation);
Michael Wright814de9b2015-08-17 23:31:55 +01001001 }
1002 }
1003
1004 @Override
1005 public void onAccuracyChanged(Sensor sensor, int accuracy) { }
1006
1007 @Override
1008 public void dumpLocked(PrintWriter pw, String prefix) {
1009 pw.println(prefix + "OrientationSensorJudge");
1010 prefix += " ";
1011 pw.println(prefix + "mDesiredRotation=" + mDesiredRotation);
1012 pw.println(prefix + "mProposedRotation=" + mProposedRotation);
1013 pw.println(prefix + "mTouching=" + mTouching);
1014 pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos);
1015 }
1016
1017 @Override
1018 public void resetLocked() {
1019 mProposedRotation = -1;
1020 mDesiredRotation = -1;
1021 mTouching = false;
1022 mTouchEndedTimestampNanos = Long.MIN_VALUE;
1023 unscheduleRotationEvaluationLocked();
1024 }
1025
Michael Wright74f6c792015-08-25 21:15:12 +01001026 public int evaluateRotationChangeLocked() {
Michael Wright814de9b2015-08-17 23:31:55 +01001027 unscheduleRotationEvaluationLocked();
1028 if (mDesiredRotation == mProposedRotation) {
Michael Wright74f6c792015-08-25 21:15:12 +01001029 return -1;
Michael Wright814de9b2015-08-17 23:31:55 +01001030 }
1031 final long now = SystemClock.elapsedRealtimeNanos();
1032 if (isDesiredRotationAcceptableLocked(now)) {
1033 mProposedRotation = mDesiredRotation;
Michael Wright74f6c792015-08-25 21:15:12 +01001034 return mProposedRotation;
Michael Wright814de9b2015-08-17 23:31:55 +01001035 } else {
1036 scheduleRotationEvaluationIfNecessaryLocked(now);
1037 }
Michael Wright74f6c792015-08-25 21:15:12 +01001038 return -1;
Michael Wright814de9b2015-08-17 23:31:55 +01001039 }
1040
1041 private boolean isDesiredRotationAcceptableLocked(long now) {
1042 if (mTouching) {
1043 return false;
1044 }
1045 if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
1046 return false;
1047 }
1048 return true;
1049 }
1050
1051 private void scheduleRotationEvaluationIfNecessaryLocked(long now) {
1052 if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) {
1053 if (LOG) {
1054 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1055 "ignoring, an evaluation is already scheduled or is unnecessary.");
1056 }
1057 return;
1058 }
1059 if (mTouching) {
1060 if (LOG) {
1061 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1062 "ignoring, user is still touching the screen.");
1063 }
1064 return;
1065 }
1066 long timeOfNextPossibleRotationNanos =
1067 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS;
1068 if (now >= timeOfNextPossibleRotationNanos) {
1069 if (LOG) {
1070 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1071 "ignoring, already past the next possible time of rotation.");
1072 }
1073 return;
1074 }
1075 // Use a delay instead of an absolute time since handlers are in uptime millis and we
1076 // use elapsed realtime.
1077 final long delayMs =
1078 (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO);
1079 mHandler.postDelayed(mRotationEvaluator, delayMs);
1080 mRotationEvaluationScheduled = true;
1081 }
1082
1083 private void unscheduleRotationEvaluationLocked() {
1084 if (!mRotationEvaluationScheduled) {
1085 return;
1086 }
1087 mHandler.removeCallbacks(mRotationEvaluator);
1088 mRotationEvaluationScheduled = false;
1089 }
1090
1091 private Runnable mRotationEvaluator = new Runnable() {
1092 @Override
1093 public void run() {
Michael Wright74f6c792015-08-25 21:15:12 +01001094 int newRotation;
Michael Wright814de9b2015-08-17 23:31:55 +01001095 synchronized (mLock) {
1096 mRotationEvaluationScheduled = false;
Michael Wright74f6c792015-08-25 21:15:12 +01001097 newRotation = evaluateRotationChangeLocked();
1098 }
1099 if (newRotation >= 0) {
1100 onProposedRotationChanged(newRotation);
Michael Wright814de9b2015-08-17 23:31:55 +01001101 }
1102 }
1103 };
Jeff Brown4519f072011-01-23 13:16:01 -08001104 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105}