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#include "content/browser/device_sensors/sensor_manager_android.h"
6
7#include <string.h>
8
9#include "base/android/jni_android.h"
10#include "base/memory/singleton.h"
11#include "base/metrics/histogram.h"
12#include "content/browser/device_sensors/inertial_sensor_consts.h"
13#include "jni/DeviceSensors_jni.h"
14
15using base::android::AttachCurrentThread;
16
17namespace {
18
19static void updateRotationVectorHistogram(bool value) {
20  UMA_HISTOGRAM_BOOLEAN("InertialSensor.RotationVectorAndroidAvailable", value);
21}
22
23}
24
25namespace content {
26
27SensorManagerAndroid::SensorManagerAndroid()
28    : number_active_device_motion_sensors_(0),
29      device_light_buffer_(NULL),
30      device_motion_buffer_(NULL),
31      device_orientation_buffer_(NULL),
32      is_light_buffer_ready_(false),
33      is_motion_buffer_ready_(false),
34      is_orientation_buffer_ready_(false) {
35  memset(received_motion_data_, 0, sizeof(received_motion_data_));
36  device_sensors_.Reset(Java_DeviceSensors_getInstance(
37      AttachCurrentThread(), base::android::GetApplicationContext()));
38}
39
40SensorManagerAndroid::~SensorManagerAndroid() {
41}
42
43bool SensorManagerAndroid::Register(JNIEnv* env) {
44  return RegisterNativesImpl(env);
45}
46
47SensorManagerAndroid* SensorManagerAndroid::GetInstance() {
48  return Singleton<SensorManagerAndroid,
49                   LeakySingletonTraits<SensorManagerAndroid> >::get();
50}
51
52void SensorManagerAndroid::GotOrientation(
53    JNIEnv*, jobject, double alpha, double beta, double gamma) {
54  base::AutoLock autolock(orientation_buffer_lock_);
55
56  if (!device_orientation_buffer_)
57    return;
58
59  device_orientation_buffer_->seqlock.WriteBegin();
60  device_orientation_buffer_->data.alpha = alpha;
61  device_orientation_buffer_->data.hasAlpha = true;
62  device_orientation_buffer_->data.beta = beta;
63  device_orientation_buffer_->data.hasBeta = true;
64  device_orientation_buffer_->data.gamma = gamma;
65  device_orientation_buffer_->data.hasGamma = true;
66  device_orientation_buffer_->seqlock.WriteEnd();
67
68  if (!is_orientation_buffer_ready_) {
69    SetOrientationBufferReadyStatus(true);
70    updateRotationVectorHistogram(true);
71  }
72}
73
74void SensorManagerAndroid::GotAcceleration(
75    JNIEnv*, jobject, double x, double y, double z) {
76  base::AutoLock autolock(motion_buffer_lock_);
77
78  if (!device_motion_buffer_)
79    return;
80
81  device_motion_buffer_->seqlock.WriteBegin();
82  device_motion_buffer_->data.accelerationX = x;
83  device_motion_buffer_->data.hasAccelerationX = true;
84  device_motion_buffer_->data.accelerationY = y;
85  device_motion_buffer_->data.hasAccelerationY = true;
86  device_motion_buffer_->data.accelerationZ = z;
87  device_motion_buffer_->data.hasAccelerationZ = true;
88  device_motion_buffer_->seqlock.WriteEnd();
89
90  if (!is_motion_buffer_ready_) {
91    received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] = 1;
92    CheckMotionBufferReadyToRead();
93  }
94}
95
96void SensorManagerAndroid::GotAccelerationIncludingGravity(
97    JNIEnv*, jobject, double x, double y, double z) {
98  base::AutoLock autolock(motion_buffer_lock_);
99
100  if (!device_motion_buffer_)
101    return;
102
103  device_motion_buffer_->seqlock.WriteBegin();
104  device_motion_buffer_->data.accelerationIncludingGravityX = x;
105  device_motion_buffer_->data.hasAccelerationIncludingGravityX = true;
106  device_motion_buffer_->data.accelerationIncludingGravityY = y;
107  device_motion_buffer_->data.hasAccelerationIncludingGravityY = true;
108  device_motion_buffer_->data.accelerationIncludingGravityZ = z;
109  device_motion_buffer_->data.hasAccelerationIncludingGravityZ = true;
110  device_motion_buffer_->seqlock.WriteEnd();
111
112  if (!is_motion_buffer_ready_) {
113    received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY] = 1;
114    CheckMotionBufferReadyToRead();
115  }
116}
117
118void SensorManagerAndroid::GotRotationRate(
119    JNIEnv*, jobject, double alpha, double beta, double gamma) {
120  base::AutoLock autolock(motion_buffer_lock_);
121
122  if (!device_motion_buffer_)
123    return;
124
125  device_motion_buffer_->seqlock.WriteBegin();
126  device_motion_buffer_->data.rotationRateAlpha = alpha;
127  device_motion_buffer_->data.hasRotationRateAlpha = true;
128  device_motion_buffer_->data.rotationRateBeta = beta;
129  device_motion_buffer_->data.hasRotationRateBeta = true;
130  device_motion_buffer_->data.rotationRateGamma = gamma;
131  device_motion_buffer_->data.hasRotationRateGamma = true;
132  device_motion_buffer_->seqlock.WriteEnd();
133
134  if (!is_motion_buffer_ready_) {
135    received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] = 1;
136    CheckMotionBufferReadyToRead();
137  }
138}
139
140void SensorManagerAndroid::GotLight(JNIEnv*, jobject, double value) {
141  base::AutoLock autolock(light_buffer_lock_);
142
143  if (!device_light_buffer_)
144    return;
145
146  device_light_buffer_->seqlock.WriteBegin();
147  device_light_buffer_->data.value = value;
148  device_light_buffer_->seqlock.WriteEnd();
149}
150
151bool SensorManagerAndroid::Start(EventType event_type) {
152  DCHECK(!device_sensors_.is_null());
153  int rate_in_milliseconds = (event_type == kTypeLight)
154                                 ? kLightSensorIntervalMillis
155                                 : kInertialSensorIntervalMillis;
156  return Java_DeviceSensors_start(AttachCurrentThread(),
157                                  device_sensors_.obj(),
158                                  reinterpret_cast<intptr_t>(this),
159                                  static_cast<jint>(event_type),
160                                  rate_in_milliseconds);
161}
162
163void SensorManagerAndroid::Stop(EventType event_type) {
164  DCHECK(!device_sensors_.is_null());
165  Java_DeviceSensors_stop(AttachCurrentThread(),
166                          device_sensors_.obj(),
167                          static_cast<jint>(event_type));
168}
169
170int SensorManagerAndroid::GetNumberActiveDeviceMotionSensors() {
171  DCHECK(!device_sensors_.is_null());
172  return Java_DeviceSensors_getNumberActiveDeviceMotionSensors(
173      AttachCurrentThread(), device_sensors_.obj());
174}
175
176
177// ----- Shared memory API methods
178
179// --- Device Light
180
181bool SensorManagerAndroid::StartFetchingDeviceLightData(
182    DeviceLightHardwareBuffer* buffer) {
183  DCHECK(buffer);
184  {
185    base::AutoLock autolock(light_buffer_lock_);
186    device_light_buffer_ = buffer;
187    SetLightBufferValue(-1);
188  }
189  bool success = Start(kTypeLight);
190  if (!success) {
191    base::AutoLock autolock(light_buffer_lock_);
192    SetLightBufferValue(std::numeric_limits<double>::infinity());
193  }
194  return success;
195}
196
197void SensorManagerAndroid::StopFetchingDeviceLightData() {
198  Stop(kTypeLight);
199  {
200    base::AutoLock autolock(light_buffer_lock_);
201    if (device_light_buffer_) {
202      SetLightBufferValue(-1);
203      device_light_buffer_ = NULL;
204    }
205  }
206}
207
208void SensorManagerAndroid::SetLightBufferValue(double lux) {
209  device_light_buffer_->seqlock.WriteBegin();
210  device_light_buffer_->data.value = lux;
211  device_light_buffer_->seqlock.WriteEnd();
212}
213// --- Device Motion
214
215bool SensorManagerAndroid::StartFetchingDeviceMotionData(
216    DeviceMotionHardwareBuffer* buffer) {
217  DCHECK(buffer);
218  {
219    base::AutoLock autolock(motion_buffer_lock_);
220    device_motion_buffer_ = buffer;
221    ClearInternalMotionBuffers();
222  }
223  bool success = Start(kTypeMotion);
224
225  // If no motion data can ever be provided, the number of active device motion
226  // sensors will be zero. In that case flag the shared memory buffer
227  // as ready to read, as it will not change anyway.
228  number_active_device_motion_sensors_ = GetNumberActiveDeviceMotionSensors();
229  {
230    base::AutoLock autolock(motion_buffer_lock_);
231    CheckMotionBufferReadyToRead();
232  }
233  return success;
234}
235
236void SensorManagerAndroid::StopFetchingDeviceMotionData() {
237  Stop(kTypeMotion);
238  {
239    base::AutoLock autolock(motion_buffer_lock_);
240    if (device_motion_buffer_) {
241      ClearInternalMotionBuffers();
242      device_motion_buffer_ = NULL;
243    }
244  }
245}
246
247void SensorManagerAndroid::CheckMotionBufferReadyToRead() {
248  if (received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] +
249      received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY] +
250      received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] ==
251      number_active_device_motion_sensors_) {
252    device_motion_buffer_->seqlock.WriteBegin();
253    device_motion_buffer_->data.interval = kInertialSensorIntervalMillis;
254    device_motion_buffer_->seqlock.WriteEnd();
255    SetMotionBufferReadyStatus(true);
256
257    UMA_HISTOGRAM_BOOLEAN("InertialSensor.AccelerometerAndroidAvailable",
258        received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] > 0);
259    UMA_HISTOGRAM_BOOLEAN(
260        "InertialSensor.AccelerometerIncGravityAndroidAvailable",
261        received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY]
262        > 0);
263    UMA_HISTOGRAM_BOOLEAN("InertialSensor.GyroscopeAndroidAvailable",
264        received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] > 0);
265  }
266}
267
268void SensorManagerAndroid::SetMotionBufferReadyStatus(bool ready) {
269  device_motion_buffer_->seqlock.WriteBegin();
270  device_motion_buffer_->data.allAvailableSensorsAreActive = ready;
271  device_motion_buffer_->seqlock.WriteEnd();
272  is_motion_buffer_ready_ = ready;
273}
274
275void SensorManagerAndroid::ClearInternalMotionBuffers() {
276  memset(received_motion_data_, 0, sizeof(received_motion_data_));
277  number_active_device_motion_sensors_ = 0;
278  SetMotionBufferReadyStatus(false);
279}
280
281// --- Device Orientation
282
283void SensorManagerAndroid::SetOrientationBufferReadyStatus(bool ready) {
284  device_orientation_buffer_->seqlock.WriteBegin();
285  device_orientation_buffer_->data.absolute = ready;
286  device_orientation_buffer_->data.hasAbsolute = ready;
287  device_orientation_buffer_->data.allAvailableSensorsAreActive = ready;
288  device_orientation_buffer_->seqlock.WriteEnd();
289  is_orientation_buffer_ready_ = ready;
290}
291
292bool SensorManagerAndroid::StartFetchingDeviceOrientationData(
293    DeviceOrientationHardwareBuffer* buffer) {
294  DCHECK(buffer);
295  {
296    base::AutoLock autolock(orientation_buffer_lock_);
297    device_orientation_buffer_ = buffer;
298  }
299  bool success = Start(kTypeOrientation);
300
301  {
302    base::AutoLock autolock(orientation_buffer_lock_);
303    // If Start() was unsuccessful then set the buffer ready flag to true
304    // to start firing all-null events.
305    SetOrientationBufferReadyStatus(!success);
306  }
307
308  if (!success)
309    updateRotationVectorHistogram(false);
310
311  return success;
312}
313
314void SensorManagerAndroid::StopFetchingDeviceOrientationData() {
315  Stop(kTypeOrientation);
316  {
317    base::AutoLock autolock(orientation_buffer_lock_);
318    if (device_orientation_buffer_) {
319      SetOrientationBufferReadyStatus(false);
320      device_orientation_buffer_ = NULL;
321    }
322  }
323}
324
325}  // namespace content
326