blob: a32c017c42975474ed89bf3d957e1c56d62d3e32 [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;
52
Craig Mautnereee29c42013-01-17 14:44:34 -080053 private Handler mHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 private SensorManager mSensorManager;
Jeff Brown4519f072011-01-23 13:16:01 -080055 private boolean mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private int mRate;
Michael Wright814de9b2015-08-17 23:31:55 +010057 private String mSensorType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 private Sensor mSensor;
Michael Wright814de9b2015-08-17 23:31:55 +010059 private OrientationJudge mOrientationJudge;
Craig Mautnereee29c42013-01-17 14:44:34 -080060 private int mCurrentRotation = -1;
61
62 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
64 /**
65 * Creates a new WindowOrientationListener.
66 *
67 * @param context for the WindowOrientationListener.
Craig Mautnereee29c42013-01-17 14:44:34 -080068 * @param handler Provides the Looper for receiving sensor updates.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 */
Craig Mautnereee29c42013-01-17 14:44:34 -080070 public WindowOrientationListener(Context context, Handler handler) {
71 this(context, handler, SensorManager.SENSOR_DELAY_UI);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 }
Michael Wright814de9b2015-08-17 23:31:55 +010073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 /**
75 * Creates a new WindowOrientationListener.
76 *
77 * @param context for the WindowOrientationListener.
Craig Mautnereee29c42013-01-17 14:44:34 -080078 * @param handler Provides the Looper for receiving sensor updates.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 * @param rate at which sensor events are processed (see also
80 * {@link android.hardware.SensorManager SensorManager}). Use the default
81 * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
82 * SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
Steve Howard1ba101f2010-02-23 14:30:13 -080083 *
Jeff Brown4519f072011-01-23 13:16:01 -080084 * This constructor is private since no one uses it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 */
Craig Mautnereee29c42013-01-17 14:44:34 -080086 private WindowOrientationListener(Context context, Handler handler, int rate) {
87 mHandler = handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
89 mRate = rate;
Trevor Bunker8e4591c2016-01-15 09:00:55 -080090 mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DEVICE_ORIENTATION);
Michael Wright814de9b2015-08-17 23:31:55 +010091
Trevor Bunker8e4591c2016-01-15 09:00:55 -080092 if (mSensor != null) {
93 mOrientationJudge = new OrientationSensorJudge();
Michael Wright814de9b2015-08-17 23:31:55 +010094 }
95
96 if (mOrientationJudge == null) {
97 mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
98 ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
99 if (mSensor != null) {
100 // Create listener only if sensors do exist
101 mOrientationJudge = new AccelSensorJudge(context);
102 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 }
104 }
105
106 /**
107 * Enables the WindowOrientationListener so it will monitor the sensor and call
Craig Mautnereee29c42013-01-17 14:44:34 -0800108 * {@link #onProposedRotationChanged(int)} when the device orientation changes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 */
110 public void enable() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800111 synchronized (mLock) {
112 if (mSensor == null) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700113 Slog.w(TAG, "Cannot detect sensors. Not enabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800114 return;
Jeff Browndaf5d892012-05-07 18:30:18 -0700115 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800116 if (mEnabled == false) {
117 if (LOG) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700118 Slog.d(TAG, "WindowOrientationListener enabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800119 }
Michael Wright814de9b2015-08-17 23:31:55 +0100120 mOrientationJudge.resetLocked();
121 mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
Craig Mautnereee29c42013-01-17 14:44:34 -0800122 mEnabled = true;
123 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 }
125 }
126
127 /**
128 * Disables the WindowOrientationListener.
129 */
130 public void disable() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800131 synchronized (mLock) {
132 if (mSensor == null) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700133 Slog.w(TAG, "Cannot detect sensors. Invalid disable");
Craig Mautnereee29c42013-01-17 14:44:34 -0800134 return;
Jeff Browndaf5d892012-05-07 18:30:18 -0700135 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800136 if (mEnabled == true) {
137 if (LOG) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700138 Slog.d(TAG, "WindowOrientationListener disabled");
Craig Mautnereee29c42013-01-17 14:44:34 -0800139 }
Michael Wright814de9b2015-08-17 23:31:55 +0100140 mSensorManager.unregisterListener(mOrientationJudge);
Craig Mautnereee29c42013-01-17 14:44:34 -0800141 mEnabled = false;
142 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 }
144 }
145
Adrian Roos3595be42015-03-05 16:31:15 +0100146 public void onTouchStart() {
147 synchronized (mLock) {
Michael Wright814de9b2015-08-17 23:31:55 +0100148 if (mOrientationJudge != null) {
149 mOrientationJudge.onTouchStartLocked();
Adrian Roos12741962015-03-31 11:20:30 -0700150 }
Adrian Roos3595be42015-03-05 16:31:15 +0100151 }
152 }
153
154 public void onTouchEnd() {
155 long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
156
157 synchronized (mLock) {
Michael Wright814de9b2015-08-17 23:31:55 +0100158 if (mOrientationJudge != null) {
159 mOrientationJudge.onTouchEndLocked(whenElapsedNanos);
Adrian Roos12741962015-03-31 11:20:30 -0700160 }
Adrian Roos3595be42015-03-05 16:31:15 +0100161 }
162 }
163
Jeff Brown4519f072011-01-23 13:16:01 -0800164 /**
Jeff Brownc0347aa2011-09-23 17:26:09 -0700165 * Sets the current rotation.
166 *
167 * @param rotation The current rotation.
Jeff Brown4519f072011-01-23 13:16:01 -0800168 */
Jeff Brownc0347aa2011-09-23 17:26:09 -0700169 public void setCurrentRotation(int rotation) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800170 synchronized (mLock) {
171 mCurrentRotation = rotation;
172 }
Jeff Brownc0347aa2011-09-23 17:26:09 -0700173 }
174
175 /**
176 * Gets the proposed rotation.
177 *
178 * This method only returns a rotation if the orientation listener is certain
179 * of its proposal. If the rotation is indeterminate, returns -1.
180 *
181 * @return The proposed rotation, or -1 if unknown.
182 */
183 public int getProposedRotation() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800184 synchronized (mLock) {
185 if (mEnabled) {
Michael Wright814de9b2015-08-17 23:31:55 +0100186 return mOrientationJudge.getProposedRotationLocked();
Craig Mautnereee29c42013-01-17 14:44:34 -0800187 }
188 return -1;
Suchi Amalapurapu63104ed2009-12-15 14:06:08 -0800189 }
Dianne Hackborne4fbd622009-03-27 18:09:16 -0700190 }
Steve Howard5f531ae2010-08-05 17:14:53 -0700191
192 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 * Returns true if sensor is enabled and false otherwise
194 */
195 public boolean canDetectOrientation() {
Craig Mautnereee29c42013-01-17 14:44:34 -0800196 synchronized (mLock) {
197 return mSensor != null;
198 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 }
Steve Howard1ba101f2010-02-23 14:30:13 -0800200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700202 * Called when the rotation view of the device has changed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 *
Jeff Brownc0347aa2011-09-23 17:26:09 -0700204 * This method is called whenever the orientation becomes certain of an orientation.
205 * It is called each time the orientation determination transitions from being
206 * uncertain to being certain again, even if it is the same orientation as before.
207 *
Michael Wright74f6c792015-08-25 21:15:12 +0100208 * This should only be called on the Handler thread.
209 *
Steve Howard1ba101f2010-02-23 14:30:13 -0800210 * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
Craig Mautnereee29c42013-01-17 14:44:34 -0800211 * @see android.view.Surface
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 */
Jeff Brownc0347aa2011-09-23 17:26:09 -0700213 public abstract void onProposedRotationChanged(int rotation);
Jeff Brown4519f072011-01-23 13:16:01 -0800214
Jeff Brown600f0032014-05-22 17:06:00 -0700215 public void dump(PrintWriter pw, String prefix) {
216 synchronized (mLock) {
217 pw.println(prefix + TAG);
218 prefix += " ";
219 pw.println(prefix + "mEnabled=" + mEnabled);
220 pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
Michael Wright814de9b2015-08-17 23:31:55 +0100221 pw.println(prefix + "mSensorType=" + mSensorType);
Jeff Brown600f0032014-05-22 17:06:00 -0700222 pw.println(prefix + "mSensor=" + mSensor);
223 pw.println(prefix + "mRate=" + mRate);
224
Michael Wright814de9b2015-08-17 23:31:55 +0100225 if (mOrientationJudge != null) {
226 mOrientationJudge.dumpLocked(pw, prefix);
Jaewan Kim0abc28e2014-06-26 09:13:11 +0900227 }
Jeff Brown600f0032014-05-22 17:06:00 -0700228 }
229 }
230
Michael Wright814de9b2015-08-17 23:31:55 +0100231 abstract class OrientationJudge implements SensorEventListener {
232 // Number of nanoseconds per millisecond.
233 protected static final long NANOS_PER_MS = 1000000;
234
235 // Number of milliseconds per nano second.
236 protected static final float MILLIS_PER_NANO = 0.000001f;
237
238 // The minimum amount of time that must have elapsed since the screen was last touched
239 // before the proposed rotation can change.
240 protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
241 500 * NANOS_PER_MS;
242
243 /**
244 * Gets the proposed rotation.
245 *
246 * This method only returns a rotation if the orientation listener is certain
247 * of its proposal. If the rotation is indeterminate, returns -1.
248 *
249 * Should only be called when holding WindowOrientationListener lock.
250 *
251 * @return The proposed rotation, or -1 if unknown.
252 */
253 public abstract int getProposedRotationLocked();
254
255 /**
256 * Notifies the orientation judge that the screen is being touched.
257 *
258 * Should only be called when holding WindowOrientationListener lock.
259 */
260 public abstract void onTouchStartLocked();
261
262 /**
263 * Notifies the orientation judge that the screen is no longer being touched.
264 *
265 * Should only be called when holding WindowOrientationListener lock.
266 *
267 * @param whenElapsedNanos Given in the elapsed realtime nanos time base.
268 */
269 public abstract void onTouchEndLocked(long whenElapsedNanos);
270
271 /**
272 * Resets the state of the judge.
273 *
274 * Should only be called when holding WindowOrientationListener lock.
275 */
276 public abstract void resetLocked();
277
278 /**
279 * Dumps internal state of the orientation judge.
280 *
281 * Should only be called when holding WindowOrientationListener lock.
282 */
283 public abstract void dumpLocked(PrintWriter pw, String prefix);
284
285 @Override
286 public abstract void onAccuracyChanged(Sensor sensor, int accuracy);
287
288 @Override
289 public abstract void onSensorChanged(SensorEvent event);
290 }
291
Jeff Brown4519f072011-01-23 13:16:01 -0800292 /**
Jeff Brown4519f072011-01-23 13:16:01 -0800293 * This class filters the raw accelerometer data and tries to detect actual changes in
294 * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
295 * but here's the outline:
296 *
297 * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in
298 * cartesian space because the orientation calculations are sensitive to the
299 * absolute magnitude of the acceleration. In particular, there are singularities
300 * in the calculation as the magnitude approaches 0. By performing the low-pass
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800301 * filtering early, we can eliminate most spurious high-frequency impulses due to noise.
Jeff Brown4519f072011-01-23 13:16:01 -0800302 *
303 * - Convert the acceleromter vector from cartesian to spherical coordinates.
304 * Since we're dealing with rotation of the device, this is the sensible coordinate
305 * system to work in. The zenith direction is the Z-axis, the direction the screen
306 * is facing. The radial distance is referred to as the magnitude below.
307 * The elevation angle is referred to as the "tilt" below.
308 * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is
309 * the Y-axis).
310 * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference.
311 *
312 * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing.
313 * The orientation angle is not meaningful when the device is nearly horizontal.
314 * The tilt angle thresholds are set differently for each orientation and different
315 * limits are applied when the device is facing down as opposed to when it is facing
316 * forward or facing up.
317 *
318 * - When the orientation angle reaches a certain threshold, consider transitioning
319 * to the corresponding orientation. These thresholds have some hysteresis built-in
320 * to avoid oscillations between adjacent orientations.
321 *
Jeff Brownc0347aa2011-09-23 17:26:09 -0700322 * - Wait for the device to settle for a little bit. Once that happens, issue the
323 * new orientation proposal.
Jeff Brown4519f072011-01-23 13:16:01 -0800324 *
325 * Details are explained inline.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800326 *
327 * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
328 * signal processing background.
Jeff Brown4519f072011-01-23 13:16:01 -0800329 */
Michael Wright814de9b2015-08-17 23:31:55 +0100330 final class AccelSensorJudge extends OrientationJudge {
Jeff Brown4519f072011-01-23 13:16:01 -0800331 // We work with all angles in degrees in this class.
332 private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
333
334 // Indices into SensorEvent.values for the accelerometer sensor.
335 private static final int ACCELEROMETER_DATA_X = 0;
336 private static final int ACCELEROMETER_DATA_Y = 1;
337 private static final int ACCELEROMETER_DATA_Z = 2;
338
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800339 // The minimum amount of time that a predicted rotation must be stable before it
340 // is accepted as a valid rotation proposal. This value can be quite small because
341 // the low-pass filter already suppresses most of the noise so we're really just
342 // looking for quick confirmation that the last few samples are in agreement as to
343 // the desired orientation.
344 private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800345
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800346 // The minimum amount of time that must have elapsed since the device last exited
347 // the flat state (time since it was picked up) before the proposed rotation
348 // can change.
349 private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800350
Jeff Browndaf5d892012-05-07 18:30:18 -0700351 // The minimum amount of time that must have elapsed since the device stopped
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800352 // swinging (time since device appeared to be in the process of being put down
353 // or put away into a pocket) before the proposed rotation can change.
354 private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700355
Jeff Browndaf5d892012-05-07 18:30:18 -0700356 // The minimum amount of time that must have elapsed since the device stopped
357 // undergoing external acceleration before the proposed rotation can change.
358 private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
359 500 * NANOS_PER_MS;
360
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800361 // If the tilt angle remains greater than the specified angle for a minimum of
362 // the specified time, then the device is deemed to be lying flat
363 // (just chillin' on a table).
Jeff Browne97c9a22015-06-10 22:12:54 -0700364 private static final float FLAT_ANGLE = 80;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800365 private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS;
366
367 // If the tilt angle has increased by at least delta degrees within the specified amount
368 // of time, then the device is deemed to be swinging away from the user
369 // down towards flat (tilt = 90).
370 private static final float SWING_AWAY_ANGLE_DELTA = 20;
371 private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700372
Jeff Brown4519f072011-01-23 13:16:01 -0800373 // The maximum sample inter-arrival time in milliseconds.
374 // If the acceleration samples are further apart than this amount in time, we reset the
375 // state of the low-pass filter and orientation properties. This helps to handle
376 // boundary conditions when the device is turned on, wakes from suspend or there is
377 // a significant gap in samples.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800378 private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS;
Jeff Brown4519f072011-01-23 13:16:01 -0800379
Jeff Brownc0347aa2011-09-23 17:26:09 -0700380 // The acceleration filter time constant.
381 //
382 // This time constant is used to tune the acceleration filter such that
383 // impulses and vibrational noise (think car dock) is suppressed before we
384 // try to calculate the tilt and orientation angles.
385 //
386 // The filter time constant is related to the filter cutoff frequency, which is the
387 // frequency at which signals are attenuated by 3dB (half the passband power).
Jeff Brown4519f072011-01-23 13:16:01 -0800388 // Each successive octave beyond this frequency is attenuated by an additional 6dB.
389 //
Jeff Brownc0347aa2011-09-23 17:26:09 -0700390 // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
391 // is given by Fc = 1 / (2pi * t).
392 //
393 // The higher the time constant, the lower the cutoff frequency, so more noise
394 // will be suppressed.
395 //
396 // Filtering adds latency proportional the time constant (inversely proportional
397 // to the cutoff frequency) so we don't want to make the time constant too
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800398 // large or we can lose responsiveness. Likewise we don't want to make it too
399 // small or we do a poor job suppressing acceleration spikes.
400 // Empirically, 100ms seems to be too small and 500ms is too large.
401 private static final float FILTER_TIME_CONSTANT_MS = 200.0f;
Jeff Brown4519f072011-01-23 13:16:01 -0800402
403 /* State for orientation detection. */
404
405 // Thresholds for minimum and maximum allowable deviation from gravity.
406 //
407 // If the device is undergoing external acceleration (being bumped, in a car
408 // that is turning around a corner or a plane taking off) then the magnitude
409 // may be substantially more or less than gravity. This can skew our orientation
410 // detection by making us think that up is pointed in a different direction.
411 //
412 // Conversely, if the device is in freefall, then there will be no gravity to
413 // measure at all. This is problematic because we cannot detect the orientation
414 // without gravity to tell us which way is up. A magnitude near 0 produces
415 // singularities in the tilt and orientation calculations.
416 //
417 // In both cases, we postpone choosing an orientation.
Jeff Browndaf5d892012-05-07 18:30:18 -0700418 //
419 // However, we need to tolerate some acceleration because the angular momentum
420 // of turning the device can skew the observed acceleration for a short period of time.
421 private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2
422 private static final float ACCELERATION_TOLERANCE = 4; // m/s^2
Jeff Brown4519f072011-01-23 13:16:01 -0800423 private static final float MIN_ACCELERATION_MAGNITUDE =
Jeff Browndaf5d892012-05-07 18:30:18 -0700424 SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
Jeff Brown4519f072011-01-23 13:16:01 -0800425 private static final float MAX_ACCELERATION_MAGNITUDE =
Jeff Browndaf5d892012-05-07 18:30:18 -0700426 SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
Jeff Brown4519f072011-01-23 13:16:01 -0800427
428 // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
Jeff Browne97c9a22015-06-10 22:12:54 -0700429 // when screen is facing the sky or ground), we completely ignore orientation data
430 // because it's too unstable.
431 private static final int MAX_TILT = 80;
432
433 // The tilt angle below which we conclude that the user is holding the device
434 // overhead reading in bed and lock into that state.
435 private static final int TILT_OVERHEAD_ENTER = -40;
436
437 // The tilt angle above which we conclude that the user would like a rotation
438 // change to occur and unlock from the overhead state.
439 private static final int TILT_OVERHEAD_EXIT = -15;
440
441 // The gap angle in degrees between adjacent orientation angles for hysteresis.
442 // This creates a "dead zone" between the current orientation and a proposed
443 // adjacent orientation. No orientation proposal is made when the orientation
444 // angle is within the gap between the current orientation and the adjacent
445 // orientation.
446 private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
Jeff Brown4519f072011-01-23 13:16:01 -0800447
448 // The tilt angle range in degrees for each orientation.
449 // Beyond these tilt angles, we don't even consider transitioning into the
450 // specified orientation. We place more stringent requirements on unnatural
451 // orientations than natural ones to make it less likely to accidentally transition
452 // into those states.
453 // The first value of each pair is negative so it applies a limit when the device is
454 // facing down (overhead reading in bed).
455 // The second value of each pair is positive so it applies a limit when the device is
456 // facing up (resting on a table).
457 // The ideal tilt angle is 0 (when the device is vertical) so the limits establish
458 // how close to vertical the device must be in order to change orientation.
Jeff Browne97c9a22015-06-10 22:12:54 -0700459 private final int[][] mTiltToleranceConfig = new int[][] {
460 /* ROTATION_0 */ { -25, 70 }, // note: these are overridden by config.xml
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800461 /* ROTATION_90 */ { -25, 65 },
462 /* ROTATION_180 */ { -25, 60 },
463 /* ROTATION_270 */ { -25, 65 }
Jeff Brown4519f072011-01-23 13:16:01 -0800464 };
465
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800466 // Timestamp and value of the last accelerometer sample.
467 private long mLastFilteredTimestampNanos;
468 private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
Jeff Brown4519f072011-01-23 13:16:01 -0800469
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800470 // The last proposed rotation, -1 if unknown.
471 private int mProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800472
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800473 // Value of the current predicted rotation, -1 if unknown.
474 private int mPredictedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800475
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800476 // Timestamp of when the predicted rotation most recently changed.
477 private long mPredictedRotationTimestampNanos;
478
479 // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
480 private long mFlatTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700481 private boolean mFlat;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800482
483 // Timestamp when the device last appeared to be swinging.
484 private long mSwingTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700485 private boolean mSwinging;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800486
Jeff Browndaf5d892012-05-07 18:30:18 -0700487 // Timestamp when the device last appeared to be undergoing external acceleration.
488 private long mAccelerationTimestampNanos;
Jeff Brown600f0032014-05-22 17:06:00 -0700489 private boolean mAccelerating;
490
Adrian Roos3595be42015-03-05 16:31:15 +0100491 // Timestamp when the last touch to the touch screen ended
492 private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
493 private boolean mTouched;
494
Jeff Brown600f0032014-05-22 17:06:00 -0700495 // Whether we are locked into an overhead usage mode.
496 private boolean mOverhead;
Jeff Browndaf5d892012-05-07 18:30:18 -0700497
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800498 // History of observed tilt angles.
Jeff Browne97c9a22015-06-10 22:12:54 -0700499 private static final int TILT_HISTORY_SIZE = 200;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800500 private float[] mTiltHistory = new float[TILT_HISTORY_SIZE];
501 private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
502 private int mTiltHistoryIndex;
Jeff Brown4519f072011-01-23 13:16:01 -0800503
Michael Wright814de9b2015-08-17 23:31:55 +0100504 public AccelSensorJudge(Context context) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700505 // Load tilt tolerance configuration.
506 int[] tiltTolerance = context.getResources().getIntArray(
507 com.android.internal.R.array.config_autoRotationTiltTolerance);
508 if (tiltTolerance.length == 8) {
509 for (int i = 0; i < 4; i++) {
510 int min = tiltTolerance[i * 2];
511 int max = tiltTolerance[i * 2 + 1];
512 if (min >= -90 && min <= max && max <= 90) {
513 mTiltToleranceConfig[i][0] = min;
514 mTiltToleranceConfig[i][1] = max;
515 } else {
516 Slog.wtf(TAG, "config_autoRotationTiltTolerance contains invalid range: "
517 + "min=" + min + ", max=" + max);
518 }
519 }
520 } else {
521 Slog.wtf(TAG, "config_autoRotationTiltTolerance should have exactly 8 elements");
522 }
523 }
524
Michael Wright814de9b2015-08-17 23:31:55 +0100525 @Override
Craig Mautnereee29c42013-01-17 14:44:34 -0800526 public int getProposedRotationLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800527 return mProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800528 }
529
Michael Wright814de9b2015-08-17 23:31:55 +0100530 @Override
Jeff Brown600f0032014-05-22 17:06:00 -0700531 public void dumpLocked(PrintWriter pw, String prefix) {
Michael Wright814de9b2015-08-17 23:31:55 +0100532 pw.println(prefix + "AccelSensorJudge");
533 prefix += " ";
Jeff Brown600f0032014-05-22 17:06:00 -0700534 pw.println(prefix + "mProposedRotation=" + mProposedRotation);
535 pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
536 pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
537 pw.println(prefix + "mLastFilteredY=" + mLastFilteredY);
538 pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ);
Michael Wright6eabf572015-08-12 15:35:31 +0100539 final long delta = SystemClock.elapsedRealtimeNanos() - mLastFilteredTimestampNanos;
540 pw.println(prefix + "mLastFilteredTimestampNanos=" + mLastFilteredTimestampNanos
541 + " (" + (delta * 0.000001f) + "ms ago)");
Jeff Brown600f0032014-05-22 17:06:00 -0700542 pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}");
543 pw.println(prefix + "mFlat=" + mFlat);
544 pw.println(prefix + "mSwinging=" + mSwinging);
545 pw.println(prefix + "mAccelerating=" + mAccelerating);
546 pw.println(prefix + "mOverhead=" + mOverhead);
Adrian Roos3595be42015-03-05 16:31:15 +0100547 pw.println(prefix + "mTouched=" + mTouched);
Jeff Browne97c9a22015-06-10 22:12:54 -0700548 pw.print(prefix + "mTiltToleranceConfig=[");
549 for (int i = 0; i < 4; i++) {
550 if (i != 0) {
551 pw.print(", ");
552 }
553 pw.print("[");
554 pw.print(mTiltToleranceConfig[i][0]);
555 pw.print(", ");
556 pw.print(mTiltToleranceConfig[i][1]);
557 pw.print("]");
558 }
559 pw.println("]");
Jeff Brown600f0032014-05-22 17:06:00 -0700560 }
561
Jeff Brown4519f072011-01-23 13:16:01 -0800562 @Override
563 public void onAccuracyChanged(Sensor sensor, int accuracy) {
564 }
565
566 @Override
567 public void onSensorChanged(SensorEvent event) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800568 int proposedRotation;
569 int oldProposedRotation;
Jeff Brown4519f072011-01-23 13:16:01 -0800570
Craig Mautnereee29c42013-01-17 14:44:34 -0800571 synchronized (mLock) {
572 // The vector given in the SensorEvent points straight up (towards the sky) under
573 // ideal conditions (the phone is not accelerating). I'll call this up vector
574 // elsewhere.
575 float x = event.values[ACCELEROMETER_DATA_X];
576 float y = event.values[ACCELEROMETER_DATA_Y];
577 float z = event.values[ACCELEROMETER_DATA_Z];
Jeff Brown4519f072011-01-23 13:16:01 -0800578
Jeff Browndaf5d892012-05-07 18:30:18 -0700579 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800580 Slog.v(TAG, "Raw acceleration vector: "
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800581 + "x=" + x + ", y=" + y + ", z=" + z
Neil Fuller33253a42014-10-01 11:55:10 +0100582 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z));
Jeff Brown4519f072011-01-23 13:16:01 -0800583 }
Jeff Brown4519f072011-01-23 13:16:01 -0800584
Craig Mautnereee29c42013-01-17 14:44:34 -0800585 // Apply a low-pass filter to the acceleration up vector in cartesian space.
586 // Reset the orientation listener state if the samples are too far apart in time
587 // or when we see values of (0, 0, 0) which indicates that we polled the
588 // accelerometer too soon after turning it on and we don't have any data yet.
589 final long now = event.timestamp;
590 final long then = mLastFilteredTimestampNanos;
591 final float timeDeltaMS = (now - then) * 0.000001f;
592 final boolean skipSample;
593 if (now < then
594 || now > then + MAX_FILTER_DELTA_TIME_NANOS
595 || (x == 0 && y == 0 && z == 0)) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700596 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800597 Slog.v(TAG, "Resetting orientation listener.");
Jeff Brown4519f072011-01-23 13:16:01 -0800598 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800599 resetLocked();
600 skipSample = true;
Jeff Brown4519f072011-01-23 13:16:01 -0800601 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800602 final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
603 x = alpha * (x - mLastFilteredX) + mLastFilteredX;
604 y = alpha * (y - mLastFilteredY) + mLastFilteredY;
605 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
606 if (LOG) {
607 Slog.v(TAG, "Filtered acceleration vector: "
608 + "x=" + x + ", y=" + y + ", z=" + z
Neil Fuller33253a42014-10-01 11:55:10 +0100609 + ", magnitude=" + Math.sqrt(x * x + y * y + z * z));
Jeff Browndaf5d892012-05-07 18:30:18 -0700610 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800611 skipSample = false;
612 }
613 mLastFilteredTimestampNanos = now;
614 mLastFilteredX = x;
615 mLastFilteredY = y;
616 mLastFilteredZ = z;
Jeff Browndaf5d892012-05-07 18:30:18 -0700617
Craig Mautnereee29c42013-01-17 14:44:34 -0800618 boolean isAccelerating = false;
619 boolean isFlat = false;
620 boolean isSwinging = false;
621 if (!skipSample) {
622 // Calculate the magnitude of the acceleration vector.
Neil Fuller33253a42014-10-01 11:55:10 +0100623 final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
Craig Mautnereee29c42013-01-17 14:44:34 -0800624 if (magnitude < NEAR_ZERO_MAGNITUDE) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700625 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800626 Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero.");
Jeff Brown4519f072011-01-23 13:16:01 -0800627 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800628 clearPredictedRotationLocked();
Jeff Brown4519f072011-01-23 13:16:01 -0800629 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800630 // Determine whether the device appears to be undergoing external
631 // acceleration.
632 if (isAcceleratingLocked(magnitude)) {
633 isAccelerating = true;
634 mAccelerationTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700635 }
636
Craig Mautnereee29c42013-01-17 14:44:34 -0800637 // Calculate the tilt angle.
638 // This is the angle between the up vector and the x-y plane (the plane of
639 // the screen) in a range of [-90, 90] degrees.
640 // -90 degrees: screen horizontal and facing the ground (overhead)
641 // 0 degrees: screen vertical
642 // 90 degrees: screen horizontal and facing the sky (on table)
643 final int tiltAngle = (int) Math.round(
644 Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
645 addTiltHistoryEntryLocked(now, tiltAngle);
646
647 // Determine whether the device appears to be flat or swinging.
648 if (isFlatLocked(now)) {
649 isFlat = true;
650 mFlatTimestampNanos = now;
651 }
652 if (isSwingingLocked(now, tiltAngle)) {
653 isSwinging = true;
654 mSwingTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700655 }
656
Craig Mautnereee29c42013-01-17 14:44:34 -0800657 // If the tilt angle is too close to horizontal then we cannot determine
658 // the orientation angle of the screen.
Jeff Brown600f0032014-05-22 17:06:00 -0700659 if (tiltAngle <= TILT_OVERHEAD_ENTER) {
660 mOverhead = true;
661 } else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
662 mOverhead = false;
663 }
664 if (mOverhead) {
665 if (LOG) {
666 Slog.v(TAG, "Ignoring sensor data, device is overhead: "
667 + "tiltAngle=" + tiltAngle);
668 }
669 clearPredictedRotationLocked();
670 } else if (Math.abs(tiltAngle) > MAX_TILT) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700671 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800672 Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
673 + "tiltAngle=" + tiltAngle);
Jeff Brownc0347aa2011-09-23 17:26:09 -0700674 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800675 clearPredictedRotationLocked();
Jeff Brownc0347aa2011-09-23 17:26:09 -0700676 } else {
Craig Mautnereee29c42013-01-17 14:44:34 -0800677 // Calculate the orientation angle.
678 // This is the angle between the x-y projection of the up vector onto
679 // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
680 int orientationAngle = (int) Math.round(
681 -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
682 if (orientationAngle < 0) {
683 // atan2 returns [-180, 180]; normalize to [0, 360]
684 orientationAngle += 360;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700685 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800686
687 // Find the nearest rotation.
688 int nearestRotation = (orientationAngle + 45) / 90;
689 if (nearestRotation == 4) {
690 nearestRotation = 0;
691 }
692
693 // Determine the predicted orientation.
694 if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle)
695 && isOrientationAngleAcceptableLocked(nearestRotation,
696 orientationAngle)) {
697 updatePredictedRotationLocked(now, nearestRotation);
698 if (LOG) {
699 Slog.v(TAG, "Predicted: "
700 + "tiltAngle=" + tiltAngle
701 + ", orientationAngle=" + orientationAngle
702 + ", predictedRotation=" + mPredictedRotation
703 + ", predictedRotationAgeMS="
704 + ((now - mPredictedRotationTimestampNanos)
705 * 0.000001f));
706 }
707 } else {
708 if (LOG) {
709 Slog.v(TAG, "Ignoring sensor data, no predicted rotation: "
710 + "tiltAngle=" + tiltAngle
711 + ", orientationAngle=" + orientationAngle);
712 }
713 clearPredictedRotationLocked();
714 }
Jeff Brown4519f072011-01-23 13:16:01 -0800715 }
716 }
717 }
Jeff Brown600f0032014-05-22 17:06:00 -0700718 mFlat = isFlat;
719 mSwinging = isSwinging;
720 mAccelerating = isAccelerating;
Jeff Brown4519f072011-01-23 13:16:01 -0800721
Craig Mautnereee29c42013-01-17 14:44:34 -0800722 // Determine new proposed rotation.
723 oldProposedRotation = mProposedRotation;
724 if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) {
725 mProposedRotation = mPredictedRotation;
726 }
727 proposedRotation = mProposedRotation;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800728
Craig Mautnereee29c42013-01-17 14:44:34 -0800729 // Write final statistics about where we are in the orientation detection process.
730 if (LOG) {
731 Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation
732 + ", proposedRotation=" + proposedRotation
733 + ", predictedRotation=" + mPredictedRotation
734 + ", timeDeltaMS=" + timeDeltaMS
735 + ", isAccelerating=" + isAccelerating
736 + ", isFlat=" + isFlat
737 + ", isSwinging=" + isSwinging
Jeff Brown600f0032014-05-22 17:06:00 -0700738 + ", isOverhead=" + mOverhead
Adrian Roos3595be42015-03-05 16:31:15 +0100739 + ", isTouched=" + mTouched
Craig Mautnereee29c42013-01-17 14:44:34 -0800740 + ", timeUntilSettledMS=" + remainingMS(now,
741 mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
742 + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
743 mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS)
744 + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
745 mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
746 + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
Adrian Roos3595be42015-03-05 16:31:15 +0100747 mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)
748 + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now,
749 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS));
Craig Mautnereee29c42013-01-17 14:44:34 -0800750 }
Jeff Brown4519f072011-01-23 13:16:01 -0800751 }
752
753 // Tell the listener.
Craig Mautnereee29c42013-01-17 14:44:34 -0800754 if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700755 if (LOG) {
Craig Mautnereee29c42013-01-17 14:44:34 -0800756 Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation
Jeff Brownc0347aa2011-09-23 17:26:09 -0700757 + ", oldProposedRotation=" + oldProposedRotation);
758 }
Craig Mautnereee29c42013-01-17 14:44:34 -0800759 onProposedRotationChanged(proposedRotation);
Jeff Brown4519f072011-01-23 13:16:01 -0800760 }
761 }
762
Michael Wright814de9b2015-08-17 23:31:55 +0100763 @Override
764 public void onTouchStartLocked() {
765 mTouched = true;
766 }
767
768 @Override
769 public void onTouchEndLocked(long whenElapsedNanos) {
770 mTouched = false;
771 mTouchEndedTimestampNanos = whenElapsedNanos;
772 }
773
774 @Override
775 public void resetLocked() {
776 mLastFilteredTimestampNanos = Long.MIN_VALUE;
777 mProposedRotation = -1;
778 mFlatTimestampNanos = Long.MIN_VALUE;
779 mFlat = false;
780 mSwingTimestampNanos = Long.MIN_VALUE;
781 mSwinging = false;
782 mAccelerationTimestampNanos = Long.MIN_VALUE;
783 mAccelerating = false;
784 mOverhead = false;
785 clearPredictedRotationLocked();
786 clearTiltHistoryLocked();
787 }
788
789
Jeff Brown4519f072011-01-23 13:16:01 -0800790 /**
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800791 * Returns true if the tilt angle is acceptable for a given predicted rotation.
Jeff Brown4519f072011-01-23 13:16:01 -0800792 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800793 private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) {
Jeff Browne97c9a22015-06-10 22:12:54 -0700794 return tiltAngle >= mTiltToleranceConfig[rotation][0]
795 && tiltAngle <= mTiltToleranceConfig[rotation][1];
Jeff Brown4519f072011-01-23 13:16:01 -0800796 }
797
798 /**
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800799 * Returns true if the orientation angle is acceptable for a given predicted rotation.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700800 *
Jeff Brown4519f072011-01-23 13:16:01 -0800801 * This function takes into account the gap between adjacent orientations
802 * for hysteresis.
803 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800804 private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) {
Jeff Brown4519f072011-01-23 13:16:01 -0800805 // If there is no current rotation, then there is no gap.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700806 // The gap is used only to introduce hysteresis among advertised orientation
807 // changes to avoid flapping.
Craig Mautnereee29c42013-01-17 14:44:34 -0800808 final int currentRotation = mCurrentRotation;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700809 if (currentRotation >= 0) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800810 // If the specified rotation is the same or is counter-clockwise adjacent
811 // to the current rotation, then we set a lower bound on the orientation angle.
Jeff Brownc0347aa2011-09-23 17:26:09 -0700812 // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90,
Jeff Brown4519f072011-01-23 13:16:01 -0800813 // then we want to check orientationAngle > 45 + GAP / 2.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800814 if (rotation == currentRotation
815 || rotation == (currentRotation + 1) % 4) {
816 int lowerBound = rotation * 90 - 45
Jeff Brown4519f072011-01-23 13:16:01 -0800817 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800818 if (rotation == 0) {
Jeff Brown4519f072011-01-23 13:16:01 -0800819 if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
820 return false;
821 }
822 } else {
823 if (orientationAngle < lowerBound) {
824 return false;
825 }
826 }
827 }
828
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800829 // If the specified rotation is the same or is clockwise adjacent,
Jeff Brown4519f072011-01-23 13:16:01 -0800830 // then we set an upper bound on the orientation angle.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800831 // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270,
Jeff Brown4519f072011-01-23 13:16:01 -0800832 // then we want to check orientationAngle < 315 - GAP / 2.
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800833 if (rotation == currentRotation
834 || rotation == (currentRotation + 3) % 4) {
835 int upperBound = rotation * 90 + 45
Jeff Brown4519f072011-01-23 13:16:01 -0800836 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800837 if (rotation == 0) {
Jeff Brown4519f072011-01-23 13:16:01 -0800838 if (orientationAngle <= 45 && orientationAngle > upperBound) {
839 return false;
840 }
841 } else {
842 if (orientationAngle > upperBound) {
843 return false;
844 }
845 }
846 }
847 }
848 return true;
849 }
850
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800851 /**
852 * Returns true if the predicted rotation is ready to be advertised as a
853 * proposed rotation.
854 */
Craig Mautnereee29c42013-01-17 14:44:34 -0800855 private boolean isPredictedRotationAcceptableLocked(long now) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800856 // The predicted rotation must have settled long enough.
857 if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
858 return false;
859 }
860
861 // The last flat state (time since picked up) must have been sufficiently long ago.
862 if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
863 return false;
864 }
865
866 // The last swing state (time since last movement to put down) must have been
867 // sufficiently long ago.
868 if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
869 return false;
870 }
871
Jeff Browndaf5d892012-05-07 18:30:18 -0700872 // The last acceleration state must have been sufficiently long ago.
873 if (now < mAccelerationTimestampNanos
874 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
875 return false;
876 }
877
Adrian Roos3595be42015-03-05 16:31:15 +0100878 // The last touch must have ended sufficiently long ago.
879 if (mTouched || now < mTouchEndedTimestampNanos
880 + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
881 return false;
882 }
883
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800884 // Looks good!
885 return true;
Jeff Brown4519f072011-01-23 13:16:01 -0800886 }
887
Craig Mautnereee29c42013-01-17 14:44:34 -0800888 private void clearPredictedRotationLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800889 mPredictedRotation = -1;
890 mPredictedRotationTimestampNanos = Long.MIN_VALUE;
891 }
892
Craig Mautnereee29c42013-01-17 14:44:34 -0800893 private void updatePredictedRotationLocked(long now, int rotation) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800894 if (mPredictedRotation != rotation) {
895 mPredictedRotation = rotation;
896 mPredictedRotationTimestampNanos = now;
Jeff Brownc0347aa2011-09-23 17:26:09 -0700897 }
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800898 }
899
Craig Mautnereee29c42013-01-17 14:44:34 -0800900 private boolean isAcceleratingLocked(float magnitude) {
Jeff Browndaf5d892012-05-07 18:30:18 -0700901 return magnitude < MIN_ACCELERATION_MAGNITUDE
902 || magnitude > MAX_ACCELERATION_MAGNITUDE;
903 }
904
Craig Mautnereee29c42013-01-17 14:44:34 -0800905 private void clearTiltHistoryLocked() {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800906 mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE;
907 mTiltHistoryIndex = 1;
908 }
909
Craig Mautnereee29c42013-01-17 14:44:34 -0800910 private void addTiltHistoryEntryLocked(long now, float tilt) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800911 mTiltHistory[mTiltHistoryIndex] = tilt;
912 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now;
913 mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE;
914 mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE;
915 }
916
Craig Mautnereee29c42013-01-17 14:44:34 -0800917 private boolean isFlatLocked(long now) {
918 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800919 if (mTiltHistory[i] < FLAT_ANGLE) {
920 break;
921 }
922 if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) {
923 // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
924 return true;
925 }
926 }
927 return false;
928 }
929
Craig Mautnereee29c42013-01-17 14:44:34 -0800930 private boolean isSwingingLocked(long now, float tilt) {
931 for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800932 if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) {
933 break;
934 }
935 if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) {
936 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
937 return true;
938 }
939 }
940 return false;
941 }
942
Craig Mautnereee29c42013-01-17 14:44:34 -0800943 private int nextTiltHistoryIndexLocked(int index) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800944 index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
945 return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
946 }
947
Jeff Brown600f0032014-05-22 17:06:00 -0700948 private float getLastTiltLocked() {
949 int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex);
950 return index >= 0 ? mTiltHistory[index] : Float.NaN;
951 }
952
Craig Mautnereee29c42013-01-17 14:44:34 -0800953 private float remainingMS(long now, long until) {
Jeff Brown5aa73ae2012-01-13 15:29:21 -0800954 return now >= until ? 0 : (until - now) * 0.000001f;
Jeff Brown4519f072011-01-23 13:16:01 -0800955 }
Michael Wright814de9b2015-08-17 23:31:55 +0100956 }
Adrian Roos3595be42015-03-05 16:31:15 +0100957
Michael Wright814de9b2015-08-17 23:31:55 +0100958 final class OrientationSensorJudge extends OrientationJudge {
959 private boolean mTouching;
960 private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
961 private int mProposedRotation = -1;
962 private int mDesiredRotation = -1;
963 private boolean mRotationEvaluationScheduled;
964
965 @Override
966 public int getProposedRotationLocked() {
967 return mProposedRotation;
Adrian Roos3595be42015-03-05 16:31:15 +0100968 }
969
Michael Wright814de9b2015-08-17 23:31:55 +0100970 @Override
971 public void onTouchStartLocked() {
972 mTouching = true;
973 }
974
975 @Override
976 public void onTouchEndLocked(long whenElapsedNanos) {
977 mTouching = false;
Adrian Roos3595be42015-03-05 16:31:15 +0100978 mTouchEndedTimestampNanos = whenElapsedNanos;
Michael Wright814de9b2015-08-17 23:31:55 +0100979 if (mDesiredRotation != mProposedRotation) {
980 final long now = SystemClock.elapsedRealtimeNanos();
981 scheduleRotationEvaluationIfNecessaryLocked(now);
982 }
Adrian Roos3595be42015-03-05 16:31:15 +0100983 }
Michael Wright814de9b2015-08-17 23:31:55 +0100984
985
986 @Override
987 public void onSensorChanged(SensorEvent event) {
Michael Wright74f6c792015-08-25 21:15:12 +0100988 int newRotation;
Michael Wright814de9b2015-08-17 23:31:55 +0100989 synchronized (mLock) {
990 mDesiredRotation = (int) event.values[0];
Michael Wright74f6c792015-08-25 21:15:12 +0100991 newRotation = evaluateRotationChangeLocked();
992 }
993 if (newRotation >=0) {
994 onProposedRotationChanged(newRotation);
Michael Wright814de9b2015-08-17 23:31:55 +0100995 }
996 }
997
998 @Override
999 public void onAccuracyChanged(Sensor sensor, int accuracy) { }
1000
1001 @Override
1002 public void dumpLocked(PrintWriter pw, String prefix) {
1003 pw.println(prefix + "OrientationSensorJudge");
1004 prefix += " ";
1005 pw.println(prefix + "mDesiredRotation=" + mDesiredRotation);
1006 pw.println(prefix + "mProposedRotation=" + mProposedRotation);
1007 pw.println(prefix + "mTouching=" + mTouching);
1008 pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos);
1009 }
1010
1011 @Override
1012 public void resetLocked() {
1013 mProposedRotation = -1;
1014 mDesiredRotation = -1;
1015 mTouching = false;
1016 mTouchEndedTimestampNanos = Long.MIN_VALUE;
1017 unscheduleRotationEvaluationLocked();
1018 }
1019
Michael Wright74f6c792015-08-25 21:15:12 +01001020 public int evaluateRotationChangeLocked() {
Michael Wright814de9b2015-08-17 23:31:55 +01001021 unscheduleRotationEvaluationLocked();
1022 if (mDesiredRotation == mProposedRotation) {
Michael Wright74f6c792015-08-25 21:15:12 +01001023 return -1;
Michael Wright814de9b2015-08-17 23:31:55 +01001024 }
1025 final long now = SystemClock.elapsedRealtimeNanos();
1026 if (isDesiredRotationAcceptableLocked(now)) {
1027 mProposedRotation = mDesiredRotation;
Michael Wright74f6c792015-08-25 21:15:12 +01001028 return mProposedRotation;
Michael Wright814de9b2015-08-17 23:31:55 +01001029 } else {
1030 scheduleRotationEvaluationIfNecessaryLocked(now);
1031 }
Michael Wright74f6c792015-08-25 21:15:12 +01001032 return -1;
Michael Wright814de9b2015-08-17 23:31:55 +01001033 }
1034
1035 private boolean isDesiredRotationAcceptableLocked(long now) {
1036 if (mTouching) {
1037 return false;
1038 }
1039 if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
1040 return false;
1041 }
1042 return true;
1043 }
1044
1045 private void scheduleRotationEvaluationIfNecessaryLocked(long now) {
1046 if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) {
1047 if (LOG) {
1048 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1049 "ignoring, an evaluation is already scheduled or is unnecessary.");
1050 }
1051 return;
1052 }
1053 if (mTouching) {
1054 if (LOG) {
1055 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1056 "ignoring, user is still touching the screen.");
1057 }
1058 return;
1059 }
1060 long timeOfNextPossibleRotationNanos =
1061 mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS;
1062 if (now >= timeOfNextPossibleRotationNanos) {
1063 if (LOG) {
1064 Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
1065 "ignoring, already past the next possible time of rotation.");
1066 }
1067 return;
1068 }
1069 // Use a delay instead of an absolute time since handlers are in uptime millis and we
1070 // use elapsed realtime.
1071 final long delayMs =
1072 (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO);
1073 mHandler.postDelayed(mRotationEvaluator, delayMs);
1074 mRotationEvaluationScheduled = true;
1075 }
1076
1077 private void unscheduleRotationEvaluationLocked() {
1078 if (!mRotationEvaluationScheduled) {
1079 return;
1080 }
1081 mHandler.removeCallbacks(mRotationEvaluator);
1082 mRotationEvaluationScheduled = false;
1083 }
1084
1085 private Runnable mRotationEvaluator = new Runnable() {
1086 @Override
1087 public void run() {
Michael Wright74f6c792015-08-25 21:15:12 +01001088 int newRotation;
Michael Wright814de9b2015-08-17 23:31:55 +01001089 synchronized (mLock) {
1090 mRotationEvaluationScheduled = false;
Michael Wright74f6c792015-08-25 21:15:12 +01001091 newRotation = evaluateRotationChangeLocked();
1092 }
1093 if (newRotation >= 0) {
1094 onProposedRotationChanged(newRotation);
Michael Wright814de9b2015-08-17 23:31:55 +01001095 }
1096 }
1097 };
Jeff Brown4519f072011-01-23 13:16:01 -08001098 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099}