EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.content.browser]

COVERAGE SUMMARY FOR SOURCE FILE [DeviceMotionAndOrientation.java]

nameclass, %method, %block, %line, %
DeviceMotionAndOrientation.java50%  (1/2)58%  (14/24)64%  (464/723)64%  (104.2/164)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DeviceMotionAndOrientation$SensorManagerProxyImpl0%   (0/1)0%   (0/3)0%   (0/44)0%   (0/11)
DeviceMotionAndOrientation$SensorManagerProxyImpl (SensorManager): void 0%   (0/1)0%   (0/6)0%   (0/3)
registerListener (SensorEventListener, int, int, Handler): boolean 0%   (0/1)0%   (0/21)0%   (0/4)
unregisterListener (SensorEventListener, int): void 0%   (0/1)0%   (0/17)0%   (0/4)
     
class DeviceMotionAndOrientation100% (1/1)67%  (14/21)68%  (464/679)68%  (104.2/153)
getInstance (): DeviceMotionAndOrientation 0%   (0/1)0%   (0/19)0%   (0/5)
gotAcceleration (double, double, double): void 0%   (0/1)0%   (0/24)0%   (0/5)
gotAccelerationIncludingGravity (double, double, double): void 0%   (0/1)0%   (0/24)0%   (0/5)
gotOrientation (double, double, double): void 0%   (0/1)0%   (0/24)0%   (0/5)
gotRotationRate (double, double, double): void 0%   (0/1)0%   (0/24)0%   (0/5)
onAccuracyChanged (Sensor, int): void 0%   (0/1)0%   (0/1)0%   (0/1)
onSensorChanged (SensorEvent): void 0%   (0/1)0%   (0/8)0%   (0/2)
getSensorManagerProxy (): DeviceMotionAndOrientation$SensorManagerProxy 100% (1/1)29%  (6/21)33%  (2/6)
start (int, int, int): boolean 100% (1/1)64%  (36/56)79%  (11/14)
stop (int): void 100% (1/1)68%  (50/74)79%  (13.4/17)
getOrientationUsingGetRotationMatrix (): void 100% (1/1)80%  (63/79)78%  (14/18)
getHandler (): Handler 100% (1/1)84%  (27/32)86%  (6/7)
registerSensors (Set, int, boolean): boolean 100% (1/1)86%  (42/49)83%  (10.8/13)
registerForSensorType (int, int): boolean 100% (1/1)89%  (17/19)80%  (4/5)
setEventTypeActive (int, boolean): void 100% (1/1)91%  (10/11)83%  (5/6)
sensorChanged (int, float []): void 100% (1/1)99%  (103/104)95%  (18/19)
<static initializer> 100% (1/1)100% (38/38)100% (3/3)
DeviceMotionAndOrientation (): void 100% (1/1)100% (24/24)100% (7/7)
getNumberActiveDeviceMotionSensors (): int 100% (1/1)100% (16/16)100% (3/3)
setSensorManagerProxy (DeviceMotionAndOrientation$SensorManagerProxy): void 100% (1/1)100% (4/4)100% (2/2)
unregisterSensors (Iterable): void 100% (1/1)100% (28/28)100% (5/5)

1// Copyright (c) 2013 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 
5package org.chromium.content.browser;
6 
7import android.content.Context;
8import android.hardware.Sensor;
9import android.hardware.SensorEvent;
10import android.hardware.SensorEventListener;
11import android.hardware.SensorManager;
12import android.os.Handler;
13import android.os.HandlerThread;
14import android.util.Log;
15 
16import com.google.common.annotations.VisibleForTesting;
17 
18import org.chromium.base.CalledByNative;
19import org.chromium.base.CollectionUtil;
20import org.chromium.base.JNINamespace;
21import org.chromium.base.WeakContext;
22 
23import java.util.HashSet;
24import java.util.List;
25import java.util.Set;
26 
27/**
28 * Android implementation of the device motion and orientation APIs.
29 */
30@JNINamespace("content")
31class DeviceMotionAndOrientation implements SensorEventListener {
32 
33    private static final String TAG = "DeviceMotionAndOrientation";
34 
35    // These fields are lazily initialized by getHandler().
36    private Thread mThread;
37    private Handler mHandler;
38 
39    // The lock to access the mHandler.
40    private final Object mHandlerLock = new Object();
41 
42    // Non-zero if and only if we're listening for events.
43    // To avoid race conditions on the C++ side, access must be synchronized.
44    private int mNativePtr;
45 
46    // The lock to access the mNativePtr.
47    private final Object mNativePtrLock = new Object();
48 
49    // The acceleration vector including gravity expressed in the body frame.
50    private float[] mAccelerationIncludingGravityVector;
51 
52    // The geomagnetic vector expressed in the body frame.
53    private float[] mMagneticFieldVector;
54 
55    // Lazily initialized when registering for notifications.
56    private SensorManagerProxy mSensorManagerProxy;
57 
58    // The only instance of that class and its associated lock.
59    private static DeviceMotionAndOrientation sSingleton;
60    private static Object sSingletonLock = new Object();
61 
62    /**
63     * constants for using in JNI calls, also see
64     * content/browser/device_orientation/data_fetcher_impl_android.cc
65     */
66    static final int DEVICE_ORIENTATION = 0;
67    static final int DEVICE_MOTION = 1;
68 
69    static final Set<Integer> DEVICE_ORIENTATION_SENSORS = CollectionUtil.newHashSet(
70            Sensor.TYPE_ACCELEROMETER,
71            Sensor.TYPE_MAGNETIC_FIELD);
72 
73    static final Set<Integer> DEVICE_MOTION_SENSORS = CollectionUtil.newHashSet(
74            Sensor.TYPE_ACCELEROMETER,
75            Sensor.TYPE_LINEAR_ACCELERATION,
76            Sensor.TYPE_GYROSCOPE);
77 
78    @VisibleForTesting
79    final Set<Integer> mActiveSensors = new HashSet<Integer>();
80    boolean mDeviceMotionIsActive = false;
81    boolean mDeviceOrientationIsActive = false;
82 
83    protected DeviceMotionAndOrientation() {
84    }
85 
86    /**
87     * Start listening for sensor events. If this object is already listening
88     * for events, the old callback is unregistered first.
89     *
90     * @param nativePtr Value to pass to nativeGotOrientation() for each event.
91     * @param rateInMilliseconds Requested callback rate in milliseconds. The
92     *            actual rate may be higher. Unwanted events should be ignored.
93     * @param eventType Type of event to listen to, can be either DEVICE_ORIENTATION or
94     *                  DEVICE_MOTION.
95     * @return True on success.
96     */
97    @CalledByNative
98    public boolean start(int nativePtr, int eventType, int rateInMilliseconds) {
99        boolean success = false;
100        synchronized (mNativePtrLock) {
101            switch (eventType) {
102                case DEVICE_ORIENTATION:
103                    success = registerSensors(DEVICE_ORIENTATION_SENSORS, rateInMilliseconds,
104                            true);
105                    break;
106                case DEVICE_MOTION:
107                    // note: device motion spec does not require all sensors to be available
108                    success = registerSensors(DEVICE_MOTION_SENSORS, rateInMilliseconds, false);
109                    break;
110                default:
111                    Log.e(TAG, "Unknown event type: " + eventType);
112                    return false;
113            }
114            if (success) {
115                mNativePtr = nativePtr;
116                setEventTypeActive(eventType, true);
117            }
118            return success;
119        }
120    }
121 
122    @CalledByNative
123    public int getNumberActiveDeviceMotionSensors() {
124        Set<Integer> deviceMotionSensors = new HashSet<Integer>(DEVICE_MOTION_SENSORS);
125        deviceMotionSensors.removeAll(mActiveSensors);
126        return DEVICE_MOTION_SENSORS.size() - deviceMotionSensors.size();
127    }
128 
129    /**
130     * Stop listening to sensors for a given event type. Ensures that sensors are not disabled
131     * if they are still in use by a different event type.
132     *
133     * @param eventType Type of event to listen to, can be either DEVICE_ORIENTATION or
134     *                  DEVICE_MOTION.
135     * We strictly guarantee that the corresponding native*() methods will not be called
136     * after this method returns.
137     */
138    @CalledByNative
139    public void stop(int eventType) {
140        Set<Integer> sensorsToRemainActive = new HashSet<Integer>();
141        synchronized (mNativePtrLock) {
142            switch (eventType) {
143                case DEVICE_ORIENTATION:
144                    if (mDeviceMotionIsActive) {
145                        sensorsToRemainActive.addAll(DEVICE_MOTION_SENSORS);
146                    }
147                    break;
148                case DEVICE_MOTION:
149                    if (mDeviceOrientationIsActive) {
150                        sensorsToRemainActive.addAll(DEVICE_ORIENTATION_SENSORS);
151                    }
152                    break;
153                default:
154                    Log.e(TAG, "Unknown event type: " + eventType);
155                    return;
156            }
157 
158            Set<Integer> sensorsToDeactivate = new HashSet<Integer>(mActiveSensors);
159            sensorsToDeactivate.removeAll(sensorsToRemainActive);
160            unregisterSensors(sensorsToDeactivate);
161            setEventTypeActive(eventType, false);
162            if (mActiveSensors.isEmpty()) {
163                mNativePtr = 0;
164            }
165        }
166    }
167 
168    @Override
169    public void onAccuracyChanged(Sensor sensor, int accuracy) {
170        // Nothing
171    }
172 
173    @Override
174    public void onSensorChanged(SensorEvent event) {
175        sensorChanged(event.sensor.getType(), event.values);
176    }
177 
178    @VisibleForTesting
179    void sensorChanged(int type, float[] values) {
180 
181        switch (type) {
182            case Sensor.TYPE_ACCELEROMETER:
183                if (mAccelerationIncludingGravityVector == null) {
184                    mAccelerationIncludingGravityVector = new float[3];
185                }
186                System.arraycopy(values, 0, mAccelerationIncludingGravityVector,
187                        0, mAccelerationIncludingGravityVector.length);
188                if (mDeviceMotionIsActive) {
189                    gotAccelerationIncludingGravity(
190                            mAccelerationIncludingGravityVector[0],
191                            mAccelerationIncludingGravityVector[1],
192                            mAccelerationIncludingGravityVector[2]);
193                }
194                if (mDeviceOrientationIsActive) {
195                    getOrientationUsingGetRotationMatrix();
196                }
197                break;
198            case Sensor.TYPE_LINEAR_ACCELERATION:
199                if (mDeviceMotionIsActive) {
200                    gotAcceleration(values[0], values[1], values[2]);
201                }
202                break;
203            case Sensor.TYPE_GYROSCOPE:
204                if (mDeviceMotionIsActive) {
205                    gotRotationRate(values[0], values[1], values[2]);
206                }
207                break;
208            case Sensor.TYPE_MAGNETIC_FIELD:
209                if (mMagneticFieldVector == null) {
210                    mMagneticFieldVector = new float[3];
211                }
212                System.arraycopy(values, 0, mMagneticFieldVector, 0,
213                        mMagneticFieldVector.length);
214                if (mDeviceOrientationIsActive) {
215                    getOrientationUsingGetRotationMatrix();
216                }
217                break;
218            default:
219                // Unexpected
220                return;
221        }
222    }
223 
224    private void getOrientationUsingGetRotationMatrix() {
225        if (mAccelerationIncludingGravityVector == null || mMagneticFieldVector == null) {
226            return;
227        }
228 
229        // Get the rotation matrix.
230        // The rotation matrix that transforms from the body frame to the earth
231        // frame.
232        float[] deviceRotationMatrix = new float[9];
233        if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null,
234                mAccelerationIncludingGravityVector, mMagneticFieldVector)) {
235            return;
236        }
237 
238        // Convert rotation matrix to rotation angles.
239        // Assuming that the rotations are appied in the order listed at
240        // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
241        // the rotations are applied about the same axes and in the same order as required by the
242        // API. The only conversions are sign changes as follows.  The angles are in radians
243 
244        float[] rotationAngles = new float[3];
245        SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
246 
247        double alpha = Math.toDegrees(-rotationAngles[0]);
248        while (alpha < 0.0) {
249            alpha += 360.0; // [0, 360)
250        }
251 
252        double beta = Math.toDegrees(-rotationAngles[1]);
253        while (beta < -180.0) {
254            beta += 360.0; // [-180, 180)
255        }
256 
257        double gamma = Math.toDegrees(rotationAngles[2]);
258        while (gamma < -90.0) {
259            gamma += 360.0; // [-90, 90)
260        }
261 
262        gotOrientation(alpha, beta, gamma);
263    }
264 
265    private SensorManagerProxy getSensorManagerProxy() {
266        if (mSensorManagerProxy != null) {
267            return mSensorManagerProxy;
268        }
269        SensorManager sensorManager = (SensorManager)WeakContext.getSystemService(
270                Context.SENSOR_SERVICE);
271        if (sensorManager != null) {
272            mSensorManagerProxy = new SensorManagerProxyImpl(sensorManager);
273        }
274        return mSensorManagerProxy;
275    }
276 
277    @VisibleForTesting
278    void setSensorManagerProxy(SensorManagerProxy sensorManagerProxy) {
279        mSensorManagerProxy = sensorManagerProxy;
280    }
281 
282    private void setEventTypeActive(int eventType, boolean value) {
283        switch (eventType) {
284            case DEVICE_ORIENTATION:
285                mDeviceOrientationIsActive = value;
286                return;
287            case DEVICE_MOTION:
288                mDeviceMotionIsActive = value;
289                return;
290        }
291    }
292 
293    /**
294     * @param sensorTypes List of sensors to activate.
295     * @param rateInMilliseconds Intended delay (in milliseconds) between sensor readings.
296     * @param failOnMissingSensor If true the method returns true only if all sensors could be
297     *                            activated. When false the method return true if at least one
298     *                            sensor in sensorTypes could be activated.
299     */
300    private boolean registerSensors(Set<Integer> sensorTypes, int rateInMilliseconds,
301            boolean failOnMissingSensor) {
302        Set<Integer> sensorsToActivate = new HashSet<Integer>(sensorTypes);
303        sensorsToActivate.removeAll(mActiveSensors);
304        boolean success = false;
305 
306        for (Integer sensorType : sensorsToActivate) {
307            boolean result = registerForSensorType(sensorType, rateInMilliseconds);
308            if (!result && failOnMissingSensor) {
309                // restore the previous state upon failure
310                unregisterSensors(sensorsToActivate);
311                return false;
312            }
313            if (result) {
314                mActiveSensors.add(sensorType);
315                success = true;
316            }
317        }
318        return success;
319    }
320 
321    private void unregisterSensors(Iterable<Integer> sensorTypes) {
322        for (Integer sensorType : sensorTypes) {
323            if (mActiveSensors.contains(sensorType)) {
324                getSensorManagerProxy().unregisterListener(this, sensorType);
325                mActiveSensors.remove(sensorType);
326            }
327        }
328    }
329 
330    private boolean registerForSensorType(int type, int rateInMilliseconds) {
331        SensorManagerProxy sensorManager = getSensorManagerProxy();
332        if (sensorManager == null) {
333            return false;
334        }
335        final int rateInMicroseconds = 1000 * rateInMilliseconds;
336        return sensorManager.registerListener(this, type, rateInMicroseconds, getHandler());
337    }
338 
339    protected void gotOrientation(double alpha, double beta, double gamma) {
340        synchronized (mNativePtrLock) {
341            if (mNativePtr != 0) {
342              nativeGotOrientation(mNativePtr, alpha, beta, gamma);
343            }
344        }
345    }
346 
347    protected void gotAcceleration(double x, double y, double z) {
348        synchronized (mNativePtrLock) {
349            if (mNativePtr != 0) {
350              nativeGotAcceleration(mNativePtr, x, y, z);
351            }
352        }
353    }
354 
355    protected void gotAccelerationIncludingGravity(double x, double y, double z) {
356        synchronized (mNativePtrLock) {
357            if (mNativePtr != 0) {
358              nativeGotAccelerationIncludingGravity(mNativePtr, x, y, z);
359            }
360        }
361    }
362 
363    protected void gotRotationRate(double alpha, double beta, double gamma) {
364        synchronized (mNativePtrLock) {
365            if (mNativePtr != 0) {
366              nativeGotRotationRate(mNativePtr, alpha, beta, gamma);
367            }
368        }
369    }
370 
371    private Handler getHandler() {
372        // TODO(timvolodine): Remove the mHandlerLock when sure that getHandler is not called
373        // from multiple threads. This will be the case when device motion and device orientation
374        // use the same polling thread (also see crbug/234282).
375        synchronized (mHandlerLock) {
376            if (mHandler == null) {
377                HandlerThread thread = new HandlerThread("DeviceMotionAndOrientation");
378                thread.start();
379                mHandler = new Handler(thread.getLooper());  // blocks on thread start
380            }
381            return mHandler;
382        }
383    }
384 
385    @CalledByNative
386    static DeviceMotionAndOrientation getInstance() {
387        synchronized (sSingletonLock) {
388            if (sSingleton == null) {
389                sSingleton = new DeviceMotionAndOrientation();
390            }
391            return sSingleton;
392        }
393    }
394 
395    /**
396     * Native JNI calls,
397     * see content/browser/device_orientation/data_fetcher_impl_android.cc
398     */
399 
400    /**
401     * Orientation of the device with respect to its reference frame.
402     */
403    private native void nativeGotOrientation(
404            int nativeDataFetcherImplAndroid,
405            double alpha, double beta, double gamma);
406 
407    /**
408     * Linear acceleration without gravity of the device with respect to its body frame.
409     */
410    private native void nativeGotAcceleration(
411            int nativeDataFetcherImplAndroid,
412            double x, double y, double z);
413 
414    /**
415     * Acceleration including gravity of the device with respect to its body frame.
416     */
417    private native void nativeGotAccelerationIncludingGravity(
418            int nativeDataFetcherImplAndroid,
419            double x, double y, double z);
420 
421    /**
422     * Rotation rate of the device with respect to its body frame.
423     */
424    private native void nativeGotRotationRate(
425            int nativeDataFetcherImplAndroid,
426            double alpha, double beta, double gamma);
427 
428    /**
429     * Need the an interface for SensorManager for testing.
430     */
431    interface SensorManagerProxy {
432        public boolean registerListener(SensorEventListener listener, int sensorType, int rate,
433                Handler handler);
434        public void unregisterListener(SensorEventListener listener, int sensorType);
435    }
436 
437    static class SensorManagerProxyImpl implements SensorManagerProxy {
438        private final SensorManager mSensorManager;
439 
440        SensorManagerProxyImpl(SensorManager sensorManager) {
441            mSensorManager = sensorManager;
442        }
443 
444        @Override
445        public boolean registerListener(SensorEventListener listener, int sensorType, int rate,
446                Handler handler) {
447            List<Sensor> sensors = mSensorManager.getSensorList(sensorType);
448            if (sensors.isEmpty()) {
449                return false;
450            }
451            return mSensorManager.registerListener(listener, sensors.get(0), rate, handler);
452        }
453 
454        @Override
455        public void unregisterListener(SensorEventListener listener, int sensorType) {
456            List<Sensor> sensors = mSensorManager.getSensorList(sensorType);
457            if (!sensors.isEmpty()) {
458                mSensorManager.unregisterListener(listener, sensors.get(0));
459            }
460        }
461    }
462 
463}

[all classes][org.chromium.content.browser]
EMMA 2.0.5312 (C) Vladimir Roubtsov