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 "data_fetcher_shared_memory.h"
6
7#include <GuidDef.h>
8#include <InitGuid.h>
9#include <PortableDeviceTypes.h>
10#include <Sensors.h>
11
12#include "base/logging.h"
13#include "base/metrics/histogram.h"
14#include "base/win/iunknown_impl.h"
15#include "base/win/windows_version.h"
16
17namespace {
18
19const double kMeanGravity = 9.80665;
20
21}  // namespace
22
23
24namespace content {
25
26class DataFetcherSharedMemory::SensorEventSink
27    : public ISensorEvents, public base::win::IUnknownImpl {
28 public:
29  SensorEventSink() {}
30  virtual ~SensorEventSink() {}
31
32  // IUnknown interface
33  virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE {
34    return IUnknownImpl::AddRef();
35  }
36
37  virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE {
38    return IUnknownImpl::Release();
39  }
40
41  virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE {
42    if (riid == __uuidof(ISensorEvents)) {
43      *ppv = static_cast<ISensorEvents*>(this);
44      AddRef();
45      return S_OK;
46    }
47    return IUnknownImpl::QueryInterface(riid, ppv);
48  }
49
50  // ISensorEvents interface
51  STDMETHODIMP OnEvent(ISensor* sensor,
52                       REFGUID event_id,
53                       IPortableDeviceValues* event_data) OVERRIDE {
54    return S_OK;
55  }
56
57  STDMETHODIMP OnLeave(REFSENSOR_ID sensor_id) OVERRIDE {
58    return S_OK;
59  }
60
61  STDMETHODIMP OnStateChanged(ISensor* sensor, SensorState state) OVERRIDE {
62    return S_OK;
63  }
64
65  STDMETHODIMP OnDataUpdated(ISensor* sensor,
66                             ISensorDataReport* new_data) OVERRIDE {
67    if (NULL == new_data || NULL == sensor)
68      return E_INVALIDARG;
69    return UpdateSharedMemoryBuffer(sensor, new_data) ? S_OK : E_FAIL;
70  }
71
72protected:
73  virtual bool UpdateSharedMemoryBuffer(
74      ISensor* sensor, ISensorDataReport* new_data) = 0;
75
76  void GetSensorValue(REFPROPERTYKEY property, ISensorDataReport* new_data,
77      double* value, bool* has_value) {
78    PROPVARIANT variant_value = {};
79    if (SUCCEEDED(new_data->GetSensorValue(property, &variant_value))) {
80      if (variant_value.vt == VT_R8)
81        *value = variant_value.dblVal;
82      else if (variant_value.vt == VT_R4)
83        *value = variant_value.fltVal;
84      *has_value = true;
85    } else {
86      *value = 0;
87      *has_value = false;
88    }
89  }
90
91 private:
92
93  DISALLOW_COPY_AND_ASSIGN(SensorEventSink);
94};
95
96class DataFetcherSharedMemory::SensorEventSinkOrientation
97    : public DataFetcherSharedMemory::SensorEventSink {
98 public:
99  explicit SensorEventSinkOrientation(
100      DeviceOrientationHardwareBuffer* const buffer) : buffer_(buffer) {}
101  virtual ~SensorEventSinkOrientation() {}
102
103protected:
104  virtual bool UpdateSharedMemoryBuffer(
105      ISensor* sensor, ISensorDataReport* new_data) OVERRIDE {
106    double alpha, beta, gamma;
107    bool has_alpha, has_beta, has_gamma;
108
109    GetSensorValue(SENSOR_DATA_TYPE_TILT_X_DEGREES, new_data, &alpha,
110        &has_alpha);
111    GetSensorValue(SENSOR_DATA_TYPE_TILT_Y_DEGREES, new_data, &beta,
112        &has_beta);
113    GetSensorValue(SENSOR_DATA_TYPE_TILT_Z_DEGREES, new_data, &gamma,
114        &has_gamma);
115
116    if (buffer_) {
117      buffer_->seqlock.WriteBegin();
118      buffer_->data.alpha = alpha;
119      buffer_->data.hasAlpha = has_alpha;
120      buffer_->data.beta = beta;
121      buffer_->data.hasBeta = has_beta;
122      buffer_->data.gamma = gamma;
123      buffer_->data.hasGamma = has_gamma;
124      buffer_->data.absolute = true;
125      buffer_->data.hasAbsolute = has_alpha || has_beta || has_gamma;
126      buffer_->data.allAvailableSensorsAreActive = true;
127      buffer_->seqlock.WriteEnd();
128    }
129
130    return true;
131  }
132
133 private:
134  DeviceOrientationHardwareBuffer* const buffer_;
135
136  DISALLOW_COPY_AND_ASSIGN(SensorEventSinkOrientation);
137};
138
139class DataFetcherSharedMemory::SensorEventSinkMotion
140    : public DataFetcherSharedMemory::SensorEventSink {
141 public:
142  explicit SensorEventSinkMotion(DeviceMotionHardwareBuffer* const buffer)
143      : buffer_(buffer) {}
144  virtual ~SensorEventSinkMotion() {}
145
146 protected:
147  virtual bool UpdateSharedMemoryBuffer(
148      ISensor* sensor, ISensorDataReport* new_data) OVERRIDE {
149
150    SENSOR_TYPE_ID sensor_type = GUID_NULL;
151    if (!SUCCEEDED(sensor->GetType(&sensor_type)))
152      return false;
153
154    if (IsEqualIID(sensor_type, SENSOR_TYPE_ACCELEROMETER_3D)) {
155      double acceleration_including_gravity_x;
156      double acceleration_including_gravity_y;
157      double acceleration_including_gravity_z;
158      bool has_acceleration_including_gravity_x;
159      bool has_acceleration_including_gravity_y;
160      bool has_acceleration_including_gravity_z;
161
162      GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_X_G, new_data,
163          &acceleration_including_gravity_x,
164          &has_acceleration_including_gravity_x);
165      GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Y_G, new_data,
166          &acceleration_including_gravity_y,
167          &has_acceleration_including_gravity_y);
168      GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Z_G, new_data,
169          &acceleration_including_gravity_z,
170          &has_acceleration_including_gravity_z);
171
172      if (buffer_) {
173        buffer_->seqlock.WriteBegin();
174        buffer_->data.accelerationIncludingGravityX =
175            -acceleration_including_gravity_x * kMeanGravity;
176        buffer_->data.hasAccelerationIncludingGravityX =
177            has_acceleration_including_gravity_x;
178        buffer_->data.accelerationIncludingGravityY =
179            -acceleration_including_gravity_y * kMeanGravity;
180        buffer_->data.hasAccelerationIncludingGravityY =
181            has_acceleration_including_gravity_y;
182        buffer_->data.accelerationIncludingGravityZ =
183            -acceleration_including_gravity_z * kMeanGravity;
184        buffer_->data.hasAccelerationIncludingGravityZ =
185            has_acceleration_including_gravity_z;
186        // TODO(timvolodine): consider setting this after all
187        // sensors have fired.
188        buffer_->data.allAvailableSensorsAreActive = true;
189        buffer_->seqlock.WriteEnd();
190      }
191
192    } else if (IsEqualIID(sensor_type, SENSOR_TYPE_GYROMETER_3D)) {
193      double alpha, beta, gamma;
194      bool has_alpha, has_beta, has_gamma;
195
196      GetSensorValue(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND,
197          new_data, &alpha, &has_alpha);
198      GetSensorValue(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND,
199          new_data, &beta, &has_beta);
200      GetSensorValue(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND,
201          new_data, &gamma, &has_gamma);
202
203      if (buffer_) {
204        buffer_->seqlock.WriteBegin();
205        buffer_->data.rotationRateAlpha = alpha;
206        buffer_->data.hasRotationRateAlpha = has_alpha;
207        buffer_->data.rotationRateBeta = beta;
208        buffer_->data.hasRotationRateBeta = has_beta;
209        buffer_->data.rotationRateGamma = gamma;
210        buffer_->data.hasRotationRateGamma = has_gamma;
211        buffer_->data.allAvailableSensorsAreActive = true;
212        buffer_->seqlock.WriteEnd();
213      }
214    }
215
216    return true;
217  }
218
219  private:
220   DeviceMotionHardwareBuffer* const buffer_;
221
222   DISALLOW_COPY_AND_ASSIGN(SensorEventSinkMotion);
223 };
224
225
226DataFetcherSharedMemory::DataFetcherSharedMemory()
227    : motion_buffer_(NULL),
228      orientation_buffer_(NULL) {
229}
230
231DataFetcherSharedMemory::~DataFetcherSharedMemory() {
232}
233
234DataFetcherSharedMemory::FetcherType DataFetcherSharedMemory::GetType() const {
235  return FETCHER_TYPE_SEPARATE_THREAD;
236}
237
238bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) {
239  DCHECK(buffer);
240
241  switch (consumer_type) {
242    case CONSUMER_TYPE_ORIENTATION:
243      {
244        orientation_buffer_ =
245            static_cast<DeviceOrientationHardwareBuffer*>(buffer);
246        scoped_refptr<SensorEventSink> sink(
247            new SensorEventSinkOrientation(orientation_buffer_));
248        bool inclinometer_available = RegisterForSensor(
249            SENSOR_TYPE_INCLINOMETER_3D, sensor_inclinometer_.Receive(), sink);
250        UMA_HISTOGRAM_BOOLEAN("InertialSensor.InclinometerWindowsAvailable",
251            inclinometer_available);
252        if (inclinometer_available)
253          return true;
254        // if no sensors are available set buffer to ready, to fire null-events.
255        SetBufferAvailableState(consumer_type, true);
256      }
257      break;
258    case CONSUMER_TYPE_MOTION:
259      {
260        motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer);
261        scoped_refptr<SensorEventSink> sink(
262            new SensorEventSinkMotion(motion_buffer_));
263        bool accelerometer_available = RegisterForSensor(
264            SENSOR_TYPE_ACCELEROMETER_3D, sensor_accelerometer_.Receive(),
265            sink);
266        bool gyrometer_available = RegisterForSensor(
267            SENSOR_TYPE_GYROMETER_3D, sensor_gyrometer_.Receive(), sink);
268        UMA_HISTOGRAM_BOOLEAN("InertialSensor.AccelerometerWindowsAvailable",
269            accelerometer_available);
270        UMA_HISTOGRAM_BOOLEAN("InertialSensor.GyrometerWindowsAvailable",
271            gyrometer_available);
272        if (accelerometer_available || gyrometer_available) {
273          motion_buffer_->seqlock.WriteBegin();
274          motion_buffer_->data.interval = GetInterval().InMilliseconds();
275          motion_buffer_->seqlock.WriteEnd();
276          return true;
277        }
278        // if no sensors are available set buffer to ready, to fire null-events.
279        SetBufferAvailableState(consumer_type, true);
280      }
281      break;
282    default:
283      NOTREACHED();
284  }
285  return false;
286}
287
288bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) {
289  DisableSensors(consumer_type);
290  SetBufferAvailableState(consumer_type, false);
291  switch (consumer_type) {
292    case CONSUMER_TYPE_ORIENTATION:
293      orientation_buffer_ = NULL;
294      return true;
295    case CONSUMER_TYPE_MOTION:
296      motion_buffer_ = NULL;
297      return true;
298    default:
299      NOTREACHED();
300  }
301  return false;
302}
303
304bool DataFetcherSharedMemory::RegisterForSensor(
305    REFSENSOR_TYPE_ID sensor_type,
306    ISensor** sensor,
307    scoped_refptr<SensorEventSink> event_sink) {
308  if (base::win::GetVersion() < base::win::VERSION_WIN7)
309    return false;
310
311  base::win::ScopedComPtr<ISensorManager> sensor_manager;
312  HRESULT hr = sensor_manager.CreateInstance(CLSID_SensorManager);
313  if (FAILED(hr) || !sensor_manager)
314    return false;
315
316  base::win::ScopedComPtr<ISensorCollection> sensor_collection;
317  hr = sensor_manager->GetSensorsByType(
318      sensor_type, sensor_collection.Receive());
319
320  if (FAILED(hr) || !sensor_collection)
321    return false;
322
323  ULONG count = 0;
324  hr = sensor_collection->GetCount(&count);
325  if (FAILED(hr) || !count)
326    return false;
327
328  hr = sensor_collection->GetAt(0, sensor);
329  if (FAILED(hr) || !(*sensor))
330    return false;
331
332  base::win::ScopedComPtr<IPortableDeviceValues> device_values;
333  if (SUCCEEDED(device_values.CreateInstance(CLSID_PortableDeviceValues))) {
334    if (SUCCEEDED(device_values->SetUnsignedIntegerValue(
335        SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL,
336        GetInterval().InMilliseconds()))) {
337      base::win::ScopedComPtr<IPortableDeviceValues> return_values;
338      (*sensor)->SetProperties(device_values.get(), return_values.Receive());
339    }
340  }
341
342  base::win::ScopedComPtr<ISensorEvents> sensor_events;
343  hr = event_sink->QueryInterface(
344      __uuidof(ISensorEvents), sensor_events.ReceiveVoid());
345  if (FAILED(hr) || !sensor_events)
346    return false;
347
348  hr = (*sensor)->SetEventSink(sensor_events);
349  if (FAILED(hr))
350    return false;
351
352  return true;
353}
354
355void DataFetcherSharedMemory::DisableSensors(ConsumerType consumer_type) {
356  switch(consumer_type) {
357    case CONSUMER_TYPE_ORIENTATION:
358      if (sensor_inclinometer_) {
359        sensor_inclinometer_->SetEventSink(NULL);
360        sensor_inclinometer_.Release();
361      }
362      break;
363    case CONSUMER_TYPE_MOTION:
364      if (sensor_accelerometer_) {
365        sensor_accelerometer_->SetEventSink(NULL);
366        sensor_accelerometer_.Release();
367      }
368      if (sensor_gyrometer_) {
369        sensor_gyrometer_->SetEventSink(NULL);
370        sensor_gyrometer_.Release();
371      }
372      break;
373    default:
374      NOTREACHED();
375  }
376}
377
378void DataFetcherSharedMemory::SetBufferAvailableState(
379    ConsumerType consumer_type, bool enabled) {
380  switch(consumer_type) {
381    case CONSUMER_TYPE_ORIENTATION:
382      if (orientation_buffer_) {
383        orientation_buffer_->seqlock.WriteBegin();
384        orientation_buffer_->data.allAvailableSensorsAreActive = enabled;
385        orientation_buffer_->seqlock.WriteEnd();
386      }
387      break;
388    case CONSUMER_TYPE_MOTION:
389      if (motion_buffer_) {
390        motion_buffer_->seqlock.WriteBegin();
391        motion_buffer_->data.allAvailableSensorsAreActive = enabled;
392        motion_buffer_->seqlock.WriteEnd();
393      }
394      break;
395    default:
396      NOTREACHED();
397  }
398}
399
400}  // namespace content
401