OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.content.browser; | |
6 | |
7 import android.content.Context; | |
8 import android.hardware.Sensor; | |
9 import android.hardware.SensorEvent; | |
10 import android.hardware.SensorEventListener; | |
11 import android.hardware.SensorManager; | |
12 import android.os.Handler; | |
13 import android.os.HandlerThread; | |
14 | |
15 import org.chromium.base.CollectionUtil; | |
16 import org.chromium.base.Log; | |
17 import org.chromium.base.ThreadUtils; | |
18 import org.chromium.base.VisibleForTesting; | |
19 import org.chromium.base.annotations.CalledByNative; | |
20 import org.chromium.base.annotations.JNINamespace; | |
21 | |
22 import java.util.HashSet; | |
23 import java.util.List; | |
24 import java.util.Set; | |
25 | |
26 /** | |
27 * Android implementation of the device {motion|orientation|light} APIs. | |
28 */ | |
29 @JNINamespace("content") | |
30 class DeviceSensors implements SensorEventListener { | |
31 | |
32 private static final String TAG = "cr.DeviceSensors"; | |
33 | |
34 // Matches kEnableExperimentalWebPlatformFeatures. | |
35 private static final String EXPERIMENTAL_WEB_PLAFTORM_FEATURES = | |
36 "enable-experimental-web-platform-features"; | |
37 | |
38 // These fields are lazily initialized by getHandler(). | |
39 private Thread mThread; | |
40 private Handler mHandler; | |
41 | |
42 // A reference to the application context in order to acquire the SensorServ
ice. | |
43 private final Context mAppContext; | |
44 | |
45 // The lock to access the mHandler. | |
46 private final Object mHandlerLock = new Object(); | |
47 | |
48 // Non-zero if and only if we're listening for events. | |
49 // To avoid race conditions on the C++ side, access must be synchronized. | |
50 private long mNativePtr; | |
51 | |
52 // The lock to access the mNativePtr. | |
53 private final Object mNativePtrLock = new Object(); | |
54 | |
55 // The geomagnetic vector expressed in the body frame. | |
56 private float[] mMagneticFieldVector; | |
57 | |
58 // Holds a shortened version of the rotation vector for compatibility purpos
es. | |
59 private float[] mTruncatedRotationVector; | |
60 | |
61 // Holds current rotation matrix for the device. | |
62 private float[] mDeviceRotationMatrix; | |
63 | |
64 // Holds Euler angles corresponding to the rotation matrix. | |
65 private double[] mRotationAngles; | |
66 | |
67 // Lazily initialized when registering for notifications. | |
68 private SensorManagerProxy mSensorManagerProxy; | |
69 | |
70 // The only instance of that class and its associated lock. | |
71 private static DeviceSensors sSingleton; | |
72 private static Object sSingletonLock = new Object(); | |
73 | |
74 static final Set<Integer> DEVICE_ORIENTATION_SENSORS_A = CollectionUtil.newH
ashSet( | |
75 Sensor.TYPE_GAME_ROTATION_VECTOR); | |
76 static final Set<Integer> DEVICE_ORIENTATION_SENSORS_B = CollectionUtil.newH
ashSet( | |
77 Sensor.TYPE_ROTATION_VECTOR); | |
78 // Option C backup sensors are used when options A and B are not available. | |
79 static final Set<Integer> DEVICE_ORIENTATION_SENSORS_C = CollectionUtil.newH
ashSet( | |
80 Sensor.TYPE_ACCELEROMETER, | |
81 Sensor.TYPE_MAGNETIC_FIELD); | |
82 static final Set<Integer> DEVICE_ORIENTATION_ABSOLUTE_SENSORS = CollectionUt
il.newHashSet( | |
83 Sensor.TYPE_ROTATION_VECTOR); | |
84 static final Set<Integer> DEVICE_MOTION_SENSORS = CollectionUtil.newHashSet( | |
85 Sensor.TYPE_ACCELEROMETER, | |
86 Sensor.TYPE_LINEAR_ACCELERATION, | |
87 Sensor.TYPE_GYROSCOPE); | |
88 static final Set<Integer> DEVICE_LIGHT_SENSORS = CollectionUtil.newHashSet( | |
89 Sensor.TYPE_LIGHT); | |
90 | |
91 @VisibleForTesting | |
92 final Set<Integer> mActiveSensors = new HashSet<Integer>(); | |
93 final List<Set<Integer>> mOrientationSensorSets; | |
94 Set<Integer> mDeviceOrientationSensors; | |
95 boolean mDeviceLightIsActive; | |
96 boolean mDeviceMotionIsActive; | |
97 boolean mDeviceOrientationIsActive; | |
98 boolean mDeviceOrientationIsActiveWithBackupSensors; | |
99 boolean mDeviceOrientationAbsoluteIsActive; | |
100 boolean mOrientationNotAvailable; | |
101 | |
102 protected DeviceSensors(Context context) { | |
103 mAppContext = context.getApplicationContext(); | |
104 | |
105 mOrientationSensorSets = CollectionUtil.newArrayList(DEVICE_ORIENTATION_
SENSORS_A, | |
106 DEVICE_ORIENTATION_
SENSORS_B, | |
107 DEVICE_ORIENTATION_
SENSORS_C); | |
108 } | |
109 | |
110 // For orientation we use a 3-way fallback approach where up to 3 different
sets of sensors | |
111 // are attempted if necessary. The sensors to be used for orientation are de
termined in the | |
112 // following order: | |
113 // A: GAME_ROTATION_VECTOR (relative) | |
114 // B: ROTATION_VECTOR (absolute) | |
115 // C: combination of ACCELEROMETER and MAGNETIC_FIELD (absolute) | |
116 // Some of the sensors may not be available depending on the device and Andr
oid version, so | |
117 // the 3-way fallback ensures selection of the best possible option. | |
118 // Examples: | |
119 // * Nexus 9, Android 5.0.2 --> option A | |
120 // * Nexus 10, Android 5.1 --> option B | |
121 // * Moto G, Android 4.4.4 --> option C | |
122 @VisibleForTesting | |
123 protected boolean registerOrientationSensorsWithFallback(int rateInMicroseco
nds) { | |
124 if (mOrientationNotAvailable) return false; | |
125 if (mDeviceOrientationSensors != null) { | |
126 return registerSensors(mDeviceOrientationSensors, rateInMicroseconds
, true); | |
127 } | |
128 ensureRotationStructuresAllocated(); | |
129 | |
130 for (Set<Integer> sensors : mOrientationSensorSets) { | |
131 mDeviceOrientationSensors = sensors; | |
132 if (registerSensors(mDeviceOrientationSensors, rateInMicroseconds, t
rue)) return true; | |
133 } | |
134 | |
135 mOrientationNotAvailable = true; | |
136 mDeviceOrientationSensors = null; | |
137 mDeviceRotationMatrix = null; | |
138 mRotationAngles = null; | |
139 return false; | |
140 } | |
141 | |
142 /** | |
143 * Start listening for sensor events. If this object is already listening | |
144 * for events, the old callback is unregistered first. | |
145 * | |
146 * @param nativePtr Value to pass to nativeGotOrientation() for each event. | |
147 * @param rateInMicroseconds Requested callback rate in microseconds. The | |
148 * actual rate may be higher. Unwanted events should be ignored. | |
149 * @param eventType Type of event to listen to, can be either DEVICE_ORIENTA
TION, | |
150 * DEVICE_ORIENTATION_ABSOLUTE, DEVICE_MOTION or DEVICE_LIGHT. | |
151 * @return True on success. | |
152 */ | |
153 @CalledByNative | |
154 public boolean start(long nativePtr, int eventType, int rateInMicroseconds)
{ | |
155 boolean success = false; | |
156 synchronized (mNativePtrLock) { | |
157 switch (eventType) { | |
158 case ConsumerType.ORIENTATION: | |
159 success = registerOrientationSensorsWithFallback(rateInMicro
seconds); | |
160 break; | |
161 case ConsumerType.ORIENTATION_ABSOLUTE: | |
162 ensureRotationStructuresAllocated(); | |
163 success = registerSensors(DEVICE_ORIENTATION_ABSOLUTE_SENSOR
S, | |
164 rateInMicroseconds, true); | |
165 break; | |
166 case ConsumerType.MOTION: | |
167 // note: device motion spec does not require all sensors to
be available | |
168 success = registerSensors(DEVICE_MOTION_SENSORS, rateInMicro
seconds, false); | |
169 break; | |
170 case ConsumerType.LIGHT: | |
171 success = registerSensors(DEVICE_LIGHT_SENSORS, rateInMicros
econds, true); | |
172 break; | |
173 default: | |
174 Log.e(TAG, "Unknown event type: %d", eventType); | |
175 return false; | |
176 } | |
177 if (success) { | |
178 mNativePtr = nativePtr; | |
179 setEventTypeActive(eventType, true); | |
180 } | |
181 return success; | |
182 } | |
183 } | |
184 | |
185 @CalledByNative | |
186 public int getNumberActiveDeviceMotionSensors() { | |
187 Set<Integer> deviceMotionSensors = new HashSet<Integer>(DEVICE_MOTION_SE
NSORS); | |
188 deviceMotionSensors.removeAll(mActiveSensors); | |
189 return DEVICE_MOTION_SENSORS.size() - deviceMotionSensors.size(); | |
190 } | |
191 | |
192 @CalledByNative | |
193 public int getOrientationSensorTypeUsed() { | |
194 if (mOrientationNotAvailable) { | |
195 return OrientationSensorType.NOT_AVAILABLE; | |
196 } | |
197 if (mDeviceOrientationSensors == DEVICE_ORIENTATION_SENSORS_A) { | |
198 return OrientationSensorType.GAME_ROTATION_VECTOR; | |
199 } | |
200 if (mDeviceOrientationSensors == DEVICE_ORIENTATION_SENSORS_B) { | |
201 return OrientationSensorType.ROTATION_VECTOR; | |
202 } | |
203 if (mDeviceOrientationSensors == DEVICE_ORIENTATION_SENSORS_C) { | |
204 return OrientationSensorType.ACCELEROMETER_MAGNETIC; | |
205 } | |
206 | |
207 assert false; // should never happen | |
208 return OrientationSensorType.NOT_AVAILABLE; | |
209 } | |
210 | |
211 /** | |
212 * Stop listening to sensors for a given event type. Ensures that sensors ar
e not disabled | |
213 * if they are still in use by a different event type. | |
214 * | |
215 * @param eventType Type of event to listen to, can be either DEVICE_ORIENTA
TION or | |
216 * DEVICE_MOTION or DEVICE_LIGHT. | |
217 * We strictly guarantee that the corresponding native*() methods will not b
e called | |
218 * after this method returns. | |
219 */ | |
220 @CalledByNative | |
221 public void stop(int eventType) { | |
222 Set<Integer> sensorsToRemainActive = new HashSet<Integer>(); | |
223 | |
224 synchronized (mNativePtrLock) { | |
225 if (mDeviceOrientationIsActive && eventType != ConsumerType.ORIENTAT
ION) { | |
226 sensorsToRemainActive.addAll(mDeviceOrientationSensors); | |
227 } | |
228 | |
229 if (mDeviceOrientationAbsoluteIsActive | |
230 && eventType != ConsumerType.ORIENTATION_ABSOLUTE) { | |
231 sensorsToRemainActive.addAll(DEVICE_ORIENTATION_ABSOLUTE_SENSORS
); | |
232 } | |
233 | |
234 if (mDeviceMotionIsActive && eventType != ConsumerType.MOTION) { | |
235 sensorsToRemainActive.addAll(DEVICE_MOTION_SENSORS); | |
236 } | |
237 | |
238 if (mDeviceLightIsActive && eventType != ConsumerType.LIGHT) { | |
239 sensorsToRemainActive.addAll(DEVICE_LIGHT_SENSORS); | |
240 } | |
241 | |
242 Set<Integer> sensorsToDeactivate = new HashSet<Integer>(mActiveSenso
rs); | |
243 sensorsToDeactivate.removeAll(sensorsToRemainActive); | |
244 unregisterSensors(sensorsToDeactivate); | |
245 setEventTypeActive(eventType, false); | |
246 if (mActiveSensors.isEmpty()) { | |
247 mNativePtr = 0; | |
248 } | |
249 } | |
250 } | |
251 | |
252 @Override | |
253 public void onAccuracyChanged(Sensor sensor, int accuracy) { | |
254 // Nothing | |
255 } | |
256 | |
257 @Override | |
258 public void onSensorChanged(SensorEvent event) { | |
259 sensorChanged(event.sensor.getType(), event.values); | |
260 } | |
261 | |
262 @VisibleForTesting | |
263 void sensorChanged(int type, float[] values) { | |
264 switch (type) { | |
265 case Sensor.TYPE_ACCELEROMETER: | |
266 if (mDeviceMotionIsActive) { | |
267 gotAccelerationIncludingGravity(values[0], values[1], values
[2]); | |
268 } | |
269 if (mDeviceOrientationIsActiveWithBackupSensors) { | |
270 getOrientationFromGeomagneticVectors(values, mMagneticFieldV
ector); | |
271 } | |
272 break; | |
273 case Sensor.TYPE_LINEAR_ACCELERATION: | |
274 if (mDeviceMotionIsActive) { | |
275 gotAcceleration(values[0], values[1], values[2]); | |
276 } | |
277 break; | |
278 case Sensor.TYPE_GYROSCOPE: | |
279 if (mDeviceMotionIsActive) { | |
280 gotRotationRate(values[0], values[1], values[2]); | |
281 } | |
282 break; | |
283 case Sensor.TYPE_ROTATION_VECTOR: | |
284 if (mDeviceOrientationAbsoluteIsActive) { | |
285 convertRotationVectorToAngles(values, mRotationAngles); | |
286 gotOrientationAbsolute(mRotationAngles[0], mRotationAngles[1
], | |
287 mRotationAngles[2]); | |
288 } | |
289 if (mDeviceOrientationIsActive | |
290 && mDeviceOrientationSensors == DEVICE_ORIENTATION_SENSO
RS_B) { | |
291 if (!mDeviceOrientationAbsoluteIsActive) { | |
292 // only compute if not already computed for absolute ori
entation above. | |
293 convertRotationVectorToAngles(values, mRotationAngles); | |
294 } | |
295 gotOrientation(mRotationAngles[0], mRotationAngles[1], mRota
tionAngles[2]); | |
296 } | |
297 break; | |
298 case Sensor.TYPE_GAME_ROTATION_VECTOR: | |
299 if (mDeviceOrientationIsActive) { | |
300 convertRotationVectorToAngles(values, mRotationAngles); | |
301 gotOrientation(mRotationAngles[0], mRotationAngles[1], mRota
tionAngles[2]); | |
302 } | |
303 break; | |
304 case Sensor.TYPE_MAGNETIC_FIELD: | |
305 if (mDeviceOrientationIsActiveWithBackupSensors) { | |
306 if (mMagneticFieldVector == null) { | |
307 mMagneticFieldVector = new float[3]; | |
308 } | |
309 System.arraycopy(values, 0, mMagneticFieldVector, 0, | |
310 mMagneticFieldVector.length); | |
311 } | |
312 break; | |
313 case Sensor.TYPE_LIGHT: | |
314 if (mDeviceLightIsActive) { | |
315 gotLight(values[0]); | |
316 } | |
317 break; | |
318 default: | |
319 // Unexpected | |
320 return; | |
321 } | |
322 } | |
323 | |
324 /** | |
325 * Returns orientation angles from a rotation matrix, such that the angles a
re according | |
326 * to spec {@link http://dev.w3.org/geo/api/spec-source-orientation.html}. | |
327 * <p> | |
328 * It is assumed the rotation matrix transforms a 3D column vector from devi
ce coordinate system | |
329 * to the world's coordinate system, as e.g. computed by {@see SensorManager
.getRotationMatrix}. | |
330 * <p> | |
331 * In particular we compute the decomposition of a given rotation matrix R s
uch that <br> | |
332 * R = Rz(alpha) * Rx(beta) * Ry(gamma), <br> | |
333 * where Rz, Rx and Ry are rotation matrices around Z, X and Y axes in the w
orld coordinate | |
334 * reference frame respectively. The reference frame consists of three ortho
gonal axes X, Y, Z | |
335 * where X points East, Y points north and Z points upwards perpendicular to
the ground plane. | |
336 * The computed angles alpha, beta and gamma are in radians and clockwise-po
sitive when viewed | |
337 * along the positive direction of the corresponding axis. Except for the sp
ecial case when the | |
338 * beta angle is +-pi/2 these angles uniquely define the orientation of a mo
bile device in 3D | |
339 * space. The alpha-beta-gamma representation resembles the yaw-pitch-roll c
onvention used in | |
340 * vehicle dynamics, however it does not exactly match it. One of the differ
ences is that the | |
341 * 'pitch' angle beta is allowed to be within [-pi, pi). A mobile device wit
h pitch angle | |
342 * greater than pi/2 could correspond to a user lying down and looking upwar
d at the screen. | |
343 * | |
344 * <p> | |
345 * Upon return the array values is filled with the result, | |
346 * <ul> | |
347 * <li>values[0]: rotation around the Z axis, alpha in [0, 2*pi)</li> | |
348 * <li>values[1]: rotation around the X axis, beta in [-pi, pi)</li> | |
349 * <li>values[2]: rotation around the Y axis, gamma in [-pi/2, pi/2)</li> | |
350 * </ul> | |
351 * <p> | |
352 * | |
353 * @param matrixR | |
354 * a 3x3 rotation matrix {@see SensorManager.getRotationMatrix}. | |
355 * | |
356 * @param values | |
357 * an array of 3 doubles to hold the result. | |
358 * | |
359 * @return the array values passed as argument. | |
360 */ | |
361 @VisibleForTesting | |
362 public static double[] computeDeviceOrientationFromRotationMatrix( | |
363 float[] matrixR, double[] values) { | |
364 /* | |
365 * 3x3 (length=9) case: | |
366 * / R[ 0] R[ 1] R[ 2] \ | |
367 * | R[ 3] R[ 4] R[ 5] | | |
368 * \ R[ 6] R[ 7] R[ 8] / | |
369 * | |
370 */ | |
371 if (matrixR.length != 9) return values; | |
372 | |
373 if (matrixR[8] > 0) { // cos(beta) > 0 | |
374 values[0] = Math.atan2(-matrixR[1], matrixR[4]); | |
375 values[1] = Math.asin(matrixR[7]); // beta (-pi/2, p
i/2) | |
376 values[2] = Math.atan2(-matrixR[6], matrixR[8]); // gamma (-pi/2,
pi/2) | |
377 } else if (matrixR[8] < 0) { // cos(beta) < 0 | |
378 values[0] = Math.atan2(matrixR[1], -matrixR[4]); | |
379 values[1] = -Math.asin(matrixR[7]); | |
380 values[1] += (values[1] >= 0) ? -Math.PI : Math.PI; // beta [-pi,-pi
/2) U (pi/2,pi) | |
381 values[2] = Math.atan2(matrixR[6], -matrixR[8]); // gamma (-pi/2,
pi/2) | |
382 } else { // R[8] == 0 | |
383 if (matrixR[6] > 0) { // cos(gamma) == 0, cos(beta) > 0 | |
384 values[0] = Math.atan2(-matrixR[1], matrixR[4]); | |
385 values[1] = Math.asin(matrixR[7]); // beta [-pi/2, pi/2] | |
386 values[2] = -Math.PI / 2; // gamma = -pi/2 | |
387 } else if (matrixR[6] < 0) { // cos(gamma) == 0, cos(beta) < 0 | |
388 values[0] = Math.atan2(matrixR[1], -matrixR[4]); | |
389 values[1] = -Math.asin(matrixR[7]); | |
390 values[1] += (values[1] >= 0) ? -Math.PI : Math.PI; // beta [-pi
,-pi/2) U (pi/2,pi) | |
391 values[2] = -Math.PI / 2; // gamma = -
pi/2 | |
392 } else { // R[6] == 0, cos(beta) == 0 | |
393 // gimbal lock discontinuity | |
394 values[0] = Math.atan2(matrixR[3], matrixR[0]); | |
395 values[1] = (matrixR[7] > 0) ? Math.PI / 2 : -Math.PI / 2; // b
eta = +-pi/2 | |
396 values[2] = 0; // g
amma = 0 | |
397 } | |
398 } | |
399 | |
400 // alpha is in [-pi, pi], make sure it is in [0, 2*pi). | |
401 if (values[0] < 0) { | |
402 values[0] += 2 * Math.PI; // alpha [0, 2*pi) | |
403 } | |
404 | |
405 return values; | |
406 } | |
407 | |
408 /* | |
409 * Converts a given rotation vector to its Euler angles representation. The
angles | |
410 * are in degrees. | |
411 */ | |
412 public void convertRotationVectorToAngles(float[] rotationVector, double[] a
ngles) { | |
413 if (rotationVector.length > 4) { | |
414 // On some Samsung devices SensorManager.getRotationMatrixFromVector | |
415 // appears to throw an exception if rotation vector has length > 4. | |
416 // For the purposes of this class the first 4 values of the | |
417 // rotation vector are sufficient (see crbug.com/335298 for details)
. | |
418 System.arraycopy(rotationVector, 0, mTruncatedRotationVector, 0, 4); | |
419 SensorManager.getRotationMatrixFromVector(mDeviceRotationMatrix, | |
420 mTruncatedRotationVector); | |
421 } else { | |
422 SensorManager.getRotationMatrixFromVector(mDeviceRotationMatrix, rot
ationVector); | |
423 } | |
424 computeDeviceOrientationFromRotationMatrix(mDeviceRotationMatrix, angles
); | |
425 for (int i = 0; i < 3; i++) { | |
426 angles[i] = Math.toDegrees(angles[i]); | |
427 } | |
428 } | |
429 | |
430 private void getOrientationFromGeomagneticVectors(float[] acceleration, floa
t[] magnetic) { | |
431 if (acceleration == null || magnetic == null) { | |
432 return; | |
433 } | |
434 if (!SensorManager.getRotationMatrix(mDeviceRotationMatrix, null, accele
ration, magnetic)) { | |
435 return; | |
436 } | |
437 computeDeviceOrientationFromRotationMatrix(mDeviceRotationMatrix, mRotat
ionAngles); | |
438 | |
439 gotOrientation(Math.toDegrees(mRotationAngles[0]), | |
440 Math.toDegrees(mRotationAngles[1]), | |
441 Math.toDegrees(mRotationAngles[2])); | |
442 } | |
443 | |
444 private SensorManagerProxy getSensorManagerProxy() { | |
445 if (mSensorManagerProxy != null) { | |
446 return mSensorManagerProxy; | |
447 } | |
448 | |
449 ThreadUtils.assertOnUiThread(); | |
450 SensorManager sensorManager = | |
451 (SensorManager) mAppContext.getSystemService(Context.SENSOR_SERV
ICE); | |
452 | |
453 if (sensorManager != null) { | |
454 mSensorManagerProxy = new SensorManagerProxyImpl(sensorManager); | |
455 } | |
456 return mSensorManagerProxy; | |
457 } | |
458 | |
459 @VisibleForTesting | |
460 void setSensorManagerProxy(SensorManagerProxy sensorManagerProxy) { | |
461 mSensorManagerProxy = sensorManagerProxy; | |
462 } | |
463 | |
464 private void setEventTypeActive(int eventType, boolean active) { | |
465 switch (eventType) { | |
466 case ConsumerType.ORIENTATION: | |
467 mDeviceOrientationIsActive = active; | |
468 mDeviceOrientationIsActiveWithBackupSensors = active | |
469 && (mDeviceOrientationSensors == DEVICE_ORIENTATION_SENS
ORS_C); | |
470 return; | |
471 case ConsumerType.ORIENTATION_ABSOLUTE: | |
472 mDeviceOrientationAbsoluteIsActive = active; | |
473 return; | |
474 case ConsumerType.MOTION: | |
475 mDeviceMotionIsActive = active; | |
476 return; | |
477 case ConsumerType.LIGHT: | |
478 mDeviceLightIsActive = active; | |
479 return; | |
480 } | |
481 } | |
482 | |
483 private void ensureRotationStructuresAllocated() { | |
484 if (mDeviceRotationMatrix == null) { | |
485 mDeviceRotationMatrix = new float[9]; | |
486 } | |
487 if (mRotationAngles == null) { | |
488 mRotationAngles = new double[3]; | |
489 } | |
490 if (mTruncatedRotationVector == null) { | |
491 mTruncatedRotationVector = new float[4]; | |
492 } | |
493 } | |
494 | |
495 /** | |
496 * @param sensorTypes List of sensors to activate. | |
497 * @param rateInMicroseconds Intended delay (in microseconds) between sensor
readings. | |
498 * @param failOnMissingSensor If true the method returns true only if all se
nsors could be | |
499 * activated. When false the method return true i
f at least one | |
500 * sensor in sensorTypes could be activated. | |
501 */ | |
502 private boolean registerSensors(Set<Integer> sensorTypes, int rateInMicrosec
onds, | |
503 boolean failOnMissingSensor) { | |
504 Set<Integer> sensorsToActivate = new HashSet<Integer>(sensorTypes); | |
505 sensorsToActivate.removeAll(mActiveSensors); | |
506 if (sensorsToActivate.isEmpty()) return true; | |
507 | |
508 boolean success = false; | |
509 for (Integer sensorType : sensorsToActivate) { | |
510 boolean result = registerForSensorType(sensorType, rateInMicrosecond
s); | |
511 if (!result && failOnMissingSensor) { | |
512 // restore the previous state upon failure | |
513 unregisterSensors(sensorsToActivate); | |
514 return false; | |
515 } | |
516 if (result) { | |
517 mActiveSensors.add(sensorType); | |
518 success = true; | |
519 } | |
520 } | |
521 return success; | |
522 } | |
523 | |
524 private void unregisterSensors(Iterable<Integer> sensorTypes) { | |
525 for (Integer sensorType : sensorTypes) { | |
526 if (mActiveSensors.contains(sensorType)) { | |
527 getSensorManagerProxy().unregisterListener(this, sensorType); | |
528 mActiveSensors.remove(sensorType); | |
529 } | |
530 } | |
531 } | |
532 | |
533 private boolean registerForSensorType(int type, int rateInMicroseconds) { | |
534 SensorManagerProxy sensorManager = getSensorManagerProxy(); | |
535 if (sensorManager == null) { | |
536 return false; | |
537 } | |
538 return sensorManager.registerListener(this, type, rateInMicroseconds, ge
tHandler()); | |
539 } | |
540 | |
541 protected void gotOrientation(double alpha, double beta, double gamma) { | |
542 synchronized (mNativePtrLock) { | |
543 if (mNativePtr != 0) { | |
544 nativeGotOrientation(mNativePtr, alpha, beta, gamma); | |
545 } | |
546 } | |
547 } | |
548 | |
549 protected void gotOrientationAbsolute(double alpha, double beta, double gamm
a) { | |
550 synchronized (mNativePtrLock) { | |
551 if (mNativePtr != 0) { | |
552 nativeGotOrientationAbsolute(mNativePtr, alpha, beta, gamma); | |
553 } | |
554 } | |
555 } | |
556 | |
557 protected void gotAcceleration(double x, double y, double z) { | |
558 synchronized (mNativePtrLock) { | |
559 if (mNativePtr != 0) { | |
560 nativeGotAcceleration(mNativePtr, x, y, z); | |
561 } | |
562 } | |
563 } | |
564 | |
565 protected void gotAccelerationIncludingGravity(double x, double y, double z)
{ | |
566 synchronized (mNativePtrLock) { | |
567 if (mNativePtr != 0) { | |
568 nativeGotAccelerationIncludingGravity(mNativePtr, x, y, z); | |
569 } | |
570 } | |
571 } | |
572 | |
573 protected void gotRotationRate(double alpha, double beta, double gamma) { | |
574 synchronized (mNativePtrLock) { | |
575 if (mNativePtr != 0) { | |
576 nativeGotRotationRate(mNativePtr, alpha, beta, gamma); | |
577 } | |
578 } | |
579 } | |
580 | |
581 protected void gotLight(double value) { | |
582 synchronized (mNativePtrLock) { | |
583 if (mNativePtr != 0) { | |
584 nativeGotLight(mNativePtr, value); | |
585 } | |
586 } | |
587 } | |
588 | |
589 private Handler getHandler() { | |
590 // TODO(timvolodine): Remove the mHandlerLock when sure that getHandler
is not called | |
591 // from multiple threads. This will be the case when device motion and d
evice orientation | |
592 // use the same polling thread (also see crbug/234282). | |
593 synchronized (mHandlerLock) { | |
594 if (mHandler == null) { | |
595 HandlerThread thread = new HandlerThread("DeviceMotionAndOrienta
tion"); | |
596 thread.start(); | |
597 mHandler = new Handler(thread.getLooper()); // blocks on thread
start | |
598 } | |
599 return mHandler; | |
600 } | |
601 } | |
602 | |
603 @CalledByNative | |
604 static DeviceSensors getInstance(Context appContext) { | |
605 synchronized (sSingletonLock) { | |
606 if (sSingleton == null) { | |
607 sSingleton = new DeviceSensors(appContext); | |
608 } | |
609 return sSingleton; | |
610 } | |
611 } | |
612 | |
613 /** | |
614 * Native JNI calls, | |
615 * see content/browser/device_sensors/sensor_manager_android.cc | |
616 */ | |
617 | |
618 /** | |
619 * Orientation of the device with respect to its reference frame. | |
620 */ | |
621 private native void nativeGotOrientation( | |
622 long nativeSensorManagerAndroid, | |
623 double alpha, double beta, double gamma); | |
624 | |
625 /** | |
626 * Absolute orientation of the device with respect to its reference frame. | |
627 */ | |
628 private native void nativeGotOrientationAbsolute( | |
629 long nativeSensorManagerAndroid, | |
630 double alpha, double beta, double gamma); | |
631 | |
632 /** | |
633 * Linear acceleration without gravity of the device with respect to its bod
y frame. | |
634 */ | |
635 private native void nativeGotAcceleration( | |
636 long nativeSensorManagerAndroid, | |
637 double x, double y, double z); | |
638 | |
639 /** | |
640 * Acceleration including gravity of the device with respect to its body fra
me. | |
641 */ | |
642 private native void nativeGotAccelerationIncludingGravity( | |
643 long nativeSensorManagerAndroid, | |
644 double x, double y, double z); | |
645 | |
646 /** | |
647 * Rotation rate of the device with respect to its body frame. | |
648 */ | |
649 private native void nativeGotRotationRate( | |
650 long nativeSensorManagerAndroid, | |
651 double alpha, double beta, double gamma); | |
652 | |
653 /** | |
654 * Device Light value from Ambient Light sensors. | |
655 */ | |
656 private native void nativeGotLight( | |
657 long nativeSensorManagerAndroid, | |
658 double value); | |
659 | |
660 /** | |
661 * Need the an interface for SensorManager for testing. | |
662 */ | |
663 interface SensorManagerProxy { | |
664 public boolean registerListener(SensorEventListener listener, int sensor
Type, int rate, | |
665 Handler handler); | |
666 public void unregisterListener(SensorEventListener listener, int sensorT
ype); | |
667 } | |
668 | |
669 static class SensorManagerProxyImpl implements SensorManagerProxy { | |
670 private final SensorManager mSensorManager; | |
671 | |
672 SensorManagerProxyImpl(SensorManager sensorManager) { | |
673 mSensorManager = sensorManager; | |
674 } | |
675 | |
676 @Override | |
677 public boolean registerListener(SensorEventListener listener, int sensor
Type, int rate, | |
678 Handler handler) { | |
679 List<Sensor> sensors = mSensorManager.getSensorList(sensorType); | |
680 if (sensors.isEmpty()) { | |
681 return false; | |
682 } | |
683 return mSensorManager.registerListener(listener, sensors.get(0), rat
e, handler); | |
684 } | |
685 | |
686 @Override | |
687 public void unregisterListener(SensorEventListener listener, int sensorT
ype) { | |
688 List<Sensor> sensors = mSensorManager.getSensorList(sensorType); | |
689 if (sensors.isEmpty()) { | |
690 return; | |
691 } | |
692 try { | |
693 mSensorManager.unregisterListener(listener, sensors.get(0)); | |
694 } catch (IllegalArgumentException e) { | |
695 // Suppress occasional exception on Digma iDxD* devices: | |
696 // Receiver not registered: android.hardware.SystemSensorManager
$1 | |
697 // See crbug.com/596453. | |
698 Log.w(TAG, "Failed to unregister device sensor " + sensors.get(0
).getName()); | |
699 } | |
700 } | |
701 } | |
702 | |
703 } | |
OLD | NEW |