Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chromeos / dbus / fake_bluetooth_gatt_characteristic_client.cc
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 "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h"
6
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/rand_util.h"
10 #include "base/time/time.h"
11 #include "chromeos/dbus/dbus_thread_manager.h"
12 #include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h"
13 #include "third_party/cros_system_api/dbus/service_constants.h"
14
15 namespace chromeos {
16
17 namespace {
18
19 const int kHeartRateMeasurementNotificationIntervalMs = 2000;
20
21 }  // namespace
22
23 // static
24 const char FakeBluetoothGattCharacteristicClient::
25     kHeartRateMeasurementPathComponent[] = "char0000";
26 const char FakeBluetoothGattCharacteristicClient::
27     kBodySensorLocationPathComponent[] = "char0001";
28 const char FakeBluetoothGattCharacteristicClient::
29     kHeartRateControlPointPathComponent[] = "char0002";
30
31 // static
32 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] =
33     "00002a37-0000-1000-8000-00805f9b34fb";
34 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] =
35     "00002a38-0000-1000-8000-00805f9b34fb";
36 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] =
37     "00002a39-0000-1000-8000-00805f9b34fb";
38
39 FakeBluetoothGattCharacteristicClient::Properties::Properties(
40     const PropertyChangedCallback& callback)
41     : BluetoothGattCharacteristicClient::Properties(
42           NULL,
43           bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
44           callback) {
45 }
46
47 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
48 }
49
50 void FakeBluetoothGattCharacteristicClient::Properties::Get(
51     dbus::PropertyBase* property,
52     dbus::PropertySet::GetCallback callback) {
53   VLOG(1) << "Get " << property->name();
54
55   // TODO(armansito): Return success or failure here based on characteristic
56   // read permission.
57   callback.Run(true);
58 }
59
60 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
61   VLOG(1) << "GetAll";
62 }
63
64 void FakeBluetoothGattCharacteristicClient::Properties::Set(
65     dbus::PropertyBase* property,
66     dbus::PropertySet::SetCallback callback) {
67   VLOG(1) << "Set " << property->name();
68   if (property->name() != value.name()) {
69     callback.Run(false);
70     return;
71   }
72   // Allow writing to only certain characteristics that are defined with the
73   // write permission.
74   // TODO(armansito): Actually check against the permissions property instead of
75   // UUID, once that property is fully defined in the API.
76   if (uuid.value() != kHeartRateControlPointUUID) {
77     callback.Run(false);
78     return;
79   }
80   callback.Run(true);
81   property->ReplaceValueWithSetValue();
82 }
83
84 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
85     : heart_rate_visible_(false),
86       calories_burned_(0),
87       weak_ptr_factory_(this) {
88 }
89
90 FakeBluetoothGattCharacteristicClient::
91     ~FakeBluetoothGattCharacteristicClient() {
92 }
93
94 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
95 }
96
97 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
98   observers_.AddObserver(observer);
99 }
100
101 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
102   observers_.RemoveObserver(observer);
103 }
104
105 std::vector<dbus::ObjectPath>
106 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
107   std::vector<dbus::ObjectPath> paths;
108   if (IsHeartRateVisible()) {
109     paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_));
110     paths.push_back(dbus::ObjectPath(body_sensor_location_path_));
111     paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_));
112   }
113   return paths;
114 }
115
116 FakeBluetoothGattCharacteristicClient::Properties*
117 FakeBluetoothGattCharacteristicClient::GetProperties(
118     const dbus::ObjectPath& object_path) {
119   if (object_path.value() == heart_rate_measurement_path_) {
120     DCHECK(heart_rate_measurement_properties_.get());
121     return heart_rate_measurement_properties_.get();
122   }
123   if (object_path.value() == body_sensor_location_path_) {
124     DCHECK(body_sensor_location_properties_.get());
125     return body_sensor_location_properties_.get();
126   }
127   if (object_path.value() == heart_rate_control_point_path_) {
128     DCHECK(heart_rate_control_point_properties_.get());
129     return heart_rate_control_point_properties_.get();
130   }
131   return NULL;
132 }
133
134 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
135     const dbus::ObjectPath& service_path) {
136   if (IsHeartRateVisible()) {
137     VLOG(2) << "Fake Heart Rate characteristics are already visible.";
138     return;
139   }
140
141   VLOG(2) << "Exposing fake Heart Rate characteristics.";
142
143   // ==== Heart Rate Measurement Characteristic ====
144   heart_rate_measurement_path_ =
145       service_path.value() + "/" + kHeartRateMeasurementPathComponent;
146   heart_rate_measurement_properties_.reset(new Properties(base::Bind(
147       &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
148       weak_ptr_factory_.GetWeakPtr(),
149       dbus::ObjectPath(heart_rate_measurement_path_))));
150   heart_rate_measurement_properties_->uuid.ReplaceValue(
151       kHeartRateMeasurementUUID);
152   heart_rate_measurement_properties_->service.ReplaceValue(service_path);
153
154   // TODO(armansito): Fill out the flags field once bindings for the values have
155   // been added. For now, leave it empty.
156
157   std::vector<uint8> measurement_value = GetHeartRateMeasurementValue();
158   heart_rate_measurement_properties_->value.ReplaceValue(measurement_value);
159
160   // ==== Body Sensor Location Characteristic ====
161   body_sensor_location_path_ =
162       service_path.value() + "/" + kBodySensorLocationPathComponent;
163   body_sensor_location_properties_.reset(new Properties(base::Bind(
164       &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
165       weak_ptr_factory_.GetWeakPtr(),
166       dbus::ObjectPath(body_sensor_location_path_))));
167   body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID);
168   body_sensor_location_properties_->service.ReplaceValue(service_path);
169
170   // TODO(armansito): Fill out the flags field once bindings for the values have
171   // been added. For now, leave it empty.
172
173   // The sensor is in the "Other" location.
174   std::vector<uint8> body_sensor_location_value;
175   body_sensor_location_value.push_back(0);
176   body_sensor_location_properties_->value.ReplaceValue(
177       body_sensor_location_value);
178
179   // ==== Heart Rate Control Point Characteristic ====
180   heart_rate_control_point_path_ =
181       service_path.value() + "/" + kHeartRateControlPointPathComponent;
182   heart_rate_control_point_properties_.reset(new Properties(base::Bind(
183       &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
184       weak_ptr_factory_.GetWeakPtr(),
185       dbus::ObjectPath(heart_rate_control_point_path_))));
186   heart_rate_control_point_properties_->uuid.ReplaceValue(
187       kHeartRateControlPointUUID);
188   heart_rate_control_point_properties_->service.ReplaceValue(service_path);
189
190   // TODO(armansito): Fill out the flags field once bindings for the values have
191   // been added. For now, leave it empty.
192
193   // Set the initial value to 0. Whenever this gets set to 1, we will reset the
194   // total calories burned and change the value back to 0.
195   std::vector<uint8> heart_rate_control_point_value;
196   heart_rate_control_point_value.push_back(0);
197   heart_rate_control_point_properties_->value.ReplaceValue(
198       heart_rate_control_point_value);
199
200   heart_rate_visible_ = true;
201
202   NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_));
203   NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
204   NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
205
206   // Set up notifications for heart rate measurement.
207   // TODO(armansito): Do this based on the value of the "client characteristic
208   // configuration" descriptor. Since it's still unclear how descriptors will
209   // be handled by BlueZ, automatically set up notifications for now.
210   ScheduleHeartRateMeasurementValueChange();
211
212   // Expose CCC descriptor for Heart Rate Measurement characteristic.
213   FakeBluetoothGattDescriptorClient* descriptor_client =
214       static_cast<FakeBluetoothGattDescriptorClient*>(
215           DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
216   dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor(
217       dbus::ObjectPath(heart_rate_measurement_path_),
218       FakeBluetoothGattDescriptorClient::
219           kClientCharacteristicConfigurationUUID));
220   DCHECK(ccc_path.IsValid());
221   heart_rate_measurement_ccc_desc_path_ = ccc_path.value();
222 }
223
224 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
225   VLOG(2) << "Hiding fake Heart Rate characteristics.";
226
227   // Hide the descriptors.
228   FakeBluetoothGattDescriptorClient* descriptor_client =
229       static_cast<FakeBluetoothGattDescriptorClient*>(
230           DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
231   descriptor_client->HideDescriptor(
232       dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_));
233
234   // Notify the observers before deleting the properties structures so that they
235   // can be accessed from the observer method.
236   NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_));
237   NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_));
238   NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_));
239
240   heart_rate_measurement_properties_.reset();
241   body_sensor_location_properties_.reset();
242   heart_rate_control_point_properties_.reset();
243
244   heart_rate_measurement_path_.clear();
245   body_sensor_location_path_.clear();
246   heart_rate_control_point_path_.clear();
247   heart_rate_visible_ = false;
248 }
249
250 dbus::ObjectPath
251 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
252   return dbus::ObjectPath(heart_rate_measurement_path_);
253 }
254
255 dbus::ObjectPath
256 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
257   return dbus::ObjectPath(body_sensor_location_path_);
258 }
259
260 dbus::ObjectPath
261 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
262   return dbus::ObjectPath(heart_rate_control_point_path_);
263 }
264
265 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
266     const dbus::ObjectPath& object_path,
267     const std::string& property_name) {
268   VLOG(2) << "Characteristic property changed: " << object_path.value()
269           << ": " << property_name;
270
271   FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
272                     GattCharacteristicPropertyChanged(
273                         object_path, property_name));
274
275   // If the heart rate control point was set, reset the calories burned.
276   if (object_path.value() != heart_rate_control_point_path_)
277     return;
278   DCHECK(heart_rate_control_point_properties_.get());
279   dbus::Property<std::vector<uint8> >* value_prop =
280       &heart_rate_control_point_properties_->value;
281   if (property_name != value_prop->name())
282     return;
283
284   std::vector<uint8> value = value_prop->value();
285   DCHECK(value.size() == 1);
286   if (value[0] == 0)
287     return;
288
289   DCHECK(value[0] == 1);
290   calories_burned_ = 0;
291   value[0] = 0;
292   value_prop->ReplaceValue(value);
293 }
294
295 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
296     const dbus::ObjectPath& object_path) {
297   VLOG(2) << "GATT characteristic added: " << object_path.value();
298   FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
299                     GattCharacteristicAdded(object_path));
300 }
301
302 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
303     const dbus::ObjectPath& object_path) {
304   VLOG(2) << "GATT characteristic removed: " << object_path.value();
305   FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
306                     GattCharacteristicRemoved(object_path));
307 }
308
309 void FakeBluetoothGattCharacteristicClient::
310     ScheduleHeartRateMeasurementValueChange() {
311   if (!IsHeartRateVisible())
312     return;
313   VLOG(2) << "Updating heart rate value.";
314   std::vector<uint8> measurement = GetHeartRateMeasurementValue();
315   heart_rate_measurement_properties_->value.ReplaceValue(measurement);
316
317   base::MessageLoop::current()->PostDelayedTask(
318       FROM_HERE,
319       base::Bind(&FakeBluetoothGattCharacteristicClient::
320                      ScheduleHeartRateMeasurementValueChange,
321                  weak_ptr_factory_.GetWeakPtr()),
322                  base::TimeDelta::FromMilliseconds(
323                      kHeartRateMeasurementNotificationIntervalMs));
324 }
325
326 std::vector<uint8>
327 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
328   // TODO(armansito): We should make sure to properly pack this struct to ensure
329   // correct byte alignment and endianness. It doesn't matter too much right now
330   // as this is a fake and GCC on Linux seems to do the right thing.
331   struct {
332     uint8 flags;
333     uint8 bpm;
334     uint16 energy_expanded;
335     uint16 rr_interval;
336   } value;
337
338   // Flags in LSB:     0       11   1 1 000
339   //                   |       |    | | |
340   // 8-bit bpm format --       |    | | |
341   // Sensor contact supported --    | | |
342   // Energy expanded field present -- | |
343   // RR-Interval values present ------- |
344   // Reserved for future use ------------
345   value.flags = 0x0;
346   value.flags |= (0x03 << 1);
347   value.flags |= (0x01 << 3);
348   value.flags |= (0x01 << 4);
349
350   // Pick a value between 117 bpm and 153 bpm for heart rate.
351   value.bpm = static_cast<uint8>(base::RandInt(117, 153));
352
353   // Total calories burned in kJoules since the last reset. Increment this by 1
354   // every time. It's fine if it overflows: it becomes 0 when the user resets
355   // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
356   value.energy_expanded = calories_burned_++;
357
358   // Include one RR-Interval value, in seconds.
359   value.rr_interval = 60/value.bpm;
360
361   // Return the bytes in an array.
362   uint8* bytes = reinterpret_cast<uint8*>(&value);
363   std::vector<uint8> return_value;
364   return_value.assign(bytes, bytes + sizeof(value));
365   return return_value;
366 }
367
368 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
369   DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty());
370   DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty());
371   DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty());
372   DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get());
373   DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get());
374   DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get());
375   return heart_rate_visible_;
376 }
377
378 }  // namespace chromeos