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
5#include "content/browser/device_orientation/data_fetcher_impl_win.h"
6
7#include <InitGuid.h>
8#include <PortableDeviceTypes.h>
9#include <Sensors.h>
10
11#include "base/logging.h"
12#include "base/win/iunknown_impl.h"
13#include "base/win/windows_version.h"
14#include "content/browser/device_orientation/orientation.h"
15
16namespace {
17
18// This should match ProviderImpl::kDesiredSamplingIntervalMs.
19const int kPeriodInMilliseconds = 100;
20
21}  // namespace
22
23namespace content {
24
25class DataFetcherImplWin::SensorEventSink : public ISensorEvents,
26                                            public base::win::IUnknownImpl {
27 public:
28  explicit SensorEventSink(DataFetcherImplWin* const fetcher)
29      : fetcher_(fetcher) {}
30
31  virtual ~SensorEventSink() {}
32
33  // IUnknown interface
34  virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE {
35    return IUnknownImpl::AddRef();
36  }
37
38  virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE {
39    return IUnknownImpl::Release();
40  }
41
42  virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE {
43    if (riid == __uuidof(ISensorEvents)) {
44      *ppv = static_cast<ISensorEvents*>(this);
45      AddRef();
46      return S_OK;
47    }
48    return IUnknownImpl::QueryInterface(riid, ppv);
49  }
50
51  // ISensorEvents interface
52  STDMETHODIMP OnEvent(ISensor* sensor,
53                       REFGUID event_id,
54                       IPortableDeviceValues* event_data) OVERRIDE {
55    return S_OK;
56  }
57
58  STDMETHODIMP OnDataUpdated(ISensor* sensor,
59                             ISensorDataReport* new_data) OVERRIDE {
60    if (NULL == new_data || NULL == sensor)
61      return E_INVALIDARG;
62
63    PROPVARIANT value = {};
64    scoped_refptr<Orientation> orientation = new Orientation();
65
66    if (SUCCEEDED(new_data->GetSensorValue(
67            SENSOR_DATA_TYPE_TILT_X_DEGREES, &value))) {
68      orientation->set_beta(value.fltVal);
69    }
70    PropVariantClear(&value);
71
72    if (SUCCEEDED(new_data->GetSensorValue(
73            SENSOR_DATA_TYPE_TILT_Y_DEGREES, &value))) {
74      orientation->set_gamma(value.fltVal);
75    }
76    PropVariantClear(&value);
77
78    if (SUCCEEDED(new_data->GetSensorValue(
79            SENSOR_DATA_TYPE_TILT_Z_DEGREES, &value))) {
80      orientation->set_alpha(value.fltVal);
81    }
82    PropVariantClear(&value);
83
84    orientation->set_absolute(true);
85    fetcher_->OnOrientationData(orientation.get());
86
87    return S_OK;
88  }
89
90  STDMETHODIMP OnLeave(REFSENSOR_ID sensor_id) OVERRIDE {
91    return S_OK;
92  }
93
94  STDMETHODIMP OnStateChanged(ISensor* sensor, SensorState state) OVERRIDE {
95    return S_OK;
96  }
97
98 private:
99  DataFetcherImplWin* const fetcher_;
100
101  DISALLOW_COPY_AND_ASSIGN(SensorEventSink);
102};
103
104// Create a DataFetcherImplWin object and return NULL if no valid sensor found.
105// static
106DataFetcher* DataFetcherImplWin::Create() {
107  scoped_ptr<DataFetcherImplWin> fetcher(new DataFetcherImplWin);
108  if (fetcher->Initialize())
109    return fetcher.release();
110
111  LOG(ERROR) << "DataFetcherImplWin::Initialize failed!";
112  return NULL;
113}
114
115DataFetcherImplWin::~DataFetcherImplWin() {
116  if (sensor_)
117    sensor_->SetEventSink(NULL);
118}
119
120DataFetcherImplWin::DataFetcherImplWin() {
121}
122
123void DataFetcherImplWin::OnOrientationData(Orientation* orientation) {
124  // This method is called on Windows sensor thread.
125  base::AutoLock autolock(next_orientation_lock_);
126  next_orientation_ = orientation;
127}
128
129const DeviceData* DataFetcherImplWin::GetDeviceData(DeviceData::Type type) {
130  if (type != DeviceData::kTypeOrientation)
131    return NULL;
132  return GetOrientation();
133}
134
135const Orientation* DataFetcherImplWin::GetOrientation() {
136  if (next_orientation_.get()) {
137    base::AutoLock autolock(next_orientation_lock_);
138    next_orientation_.swap(current_orientation_);
139  }
140  if (!current_orientation_.get())
141    return new Orientation();
142  return current_orientation_.get();
143}
144
145bool DataFetcherImplWin::Initialize() {
146  if (base::win::GetVersion() < base::win::VERSION_WIN7)
147    return false;
148
149  base::win::ScopedComPtr<ISensorManager> sensor_manager;
150  HRESULT hr = sensor_manager.CreateInstance(CLSID_SensorManager);
151  if (FAILED(hr) || !sensor_manager)
152    return false;
153
154  base::win::ScopedComPtr<ISensorCollection> sensor_collection;
155  hr = sensor_manager->GetSensorsByType(
156      SENSOR_TYPE_INCLINOMETER_3D, sensor_collection.Receive());
157
158  if (FAILED(hr) || !sensor_collection)
159    return false;
160
161  ULONG count = 0;
162  hr = sensor_collection->GetCount(&count);
163  if (FAILED(hr) || !count)
164    return false;
165
166  hr = sensor_collection->GetAt(0, sensor_.Receive());
167  if (FAILED(hr) || !sensor_)
168    return false;
169
170  base::win::ScopedComPtr<IPortableDeviceValues> device_values;
171  if (SUCCEEDED(device_values.CreateInstance(CLSID_PortableDeviceValues))) {
172    if (SUCCEEDED(device_values->SetUnsignedIntegerValue(
173            SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, kPeriodInMilliseconds))) {
174      base::win::ScopedComPtr<IPortableDeviceValues> return_values;
175      sensor_->SetProperties(device_values.get(), return_values.Receive());
176    }
177  }
178
179  scoped_refptr<SensorEventSink> sensor_event_impl(new SensorEventSink(this));
180  base::win::ScopedComPtr<ISensorEvents> sensor_events;
181  hr = sensor_event_impl->QueryInterface(
182      __uuidof(ISensorEvents), sensor_events.ReceiveVoid());
183  if (FAILED(hr) || !sensor_events)
184    return false;
185
186  hr = sensor_->SetEventSink(sensor_events);
187  if (FAILED(hr))
188    return false;
189
190  return true;
191}
192
193}  // namespace content
194