Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chromeos / dbus / fake_bluetooth_gatt_characteristic_client.cc
blob61bbb8dd92042fa88f0776c960b69cc999cc28ec
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.
5 #include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/rand_util.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/time/time.h"
13 #include "chromeos/dbus/dbus_thread_manager.h"
14 #include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h"
15 #include "third_party/cros_system_api/dbus/service_constants.h"
17 namespace chromeos {
19 namespace {
21 const int kStartNotifyResponseIntervalMs = 200;
22 const int kHeartRateMeasurementNotificationIntervalMs = 2000;
24 } // namespace
26 FakeBluetoothGattCharacteristicClient::DelayedCallback::DelayedCallback(
27 base::Closure callback,
28 size_t delay)
29 : callback_(callback), delay_(delay) {
32 FakeBluetoothGattCharacteristicClient::DelayedCallback::~DelayedCallback() {
35 // static
36 const char FakeBluetoothGattCharacteristicClient::
37 kHeartRateMeasurementPathComponent[] = "char0000";
38 const char FakeBluetoothGattCharacteristicClient::
39 kBodySensorLocationPathComponent[] = "char0001";
40 const char FakeBluetoothGattCharacteristicClient::
41 kHeartRateControlPointPathComponent[] = "char0002";
43 // static
44 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] =
45 "00002a37-0000-1000-8000-00805f9b34fb";
46 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] =
47 "00002a38-0000-1000-8000-00805f9b34fb";
48 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] =
49 "00002a39-0000-1000-8000-00805f9b34fb";
51 FakeBluetoothGattCharacteristicClient::Properties::Properties(
52 const PropertyChangedCallback& callback)
53 : BluetoothGattCharacteristicClient::Properties(
54 NULL,
55 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
56 callback) {
59 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
62 void FakeBluetoothGattCharacteristicClient::Properties::Get(
63 dbus::PropertyBase* property,
64 dbus::PropertySet::GetCallback callback) {
65 VLOG(1) << "Get " << property->name();
66 callback.Run(true);
69 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
70 VLOG(1) << "GetAll";
73 void FakeBluetoothGattCharacteristicClient::Properties::Set(
74 dbus::PropertyBase* property,
75 dbus::PropertySet::SetCallback callback) {
76 VLOG(1) << "Set " << property->name();
77 callback.Run(false);
80 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
81 : heart_rate_visible_(false),
82 authorized_(true),
83 authenticated_(true),
84 calories_burned_(0),
85 extra_requests_(0),
86 weak_ptr_factory_(this) {
89 FakeBluetoothGattCharacteristicClient::
90 ~FakeBluetoothGattCharacteristicClient() {
91 for (const auto& it : action_extra_requests_) {
92 delete it.second;
94 action_extra_requests_.clear();
97 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
100 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
101 observers_.AddObserver(observer);
104 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
105 observers_.RemoveObserver(observer);
108 std::vector<dbus::ObjectPath>
109 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
110 std::vector<dbus::ObjectPath> paths;
111 if (IsHeartRateVisible()) {
112 paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_));
113 paths.push_back(dbus::ObjectPath(body_sensor_location_path_));
114 paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_));
116 return paths;
119 FakeBluetoothGattCharacteristicClient::Properties*
120 FakeBluetoothGattCharacteristicClient::GetProperties(
121 const dbus::ObjectPath& object_path) {
122 if (object_path.value() == heart_rate_measurement_path_) {
123 DCHECK(heart_rate_measurement_properties_.get());
124 return heart_rate_measurement_properties_.get();
126 if (object_path.value() == body_sensor_location_path_) {
127 DCHECK(body_sensor_location_properties_.get());
128 return body_sensor_location_properties_.get();
130 if (object_path.value() == heart_rate_control_point_path_) {
131 DCHECK(heart_rate_control_point_properties_.get());
132 return heart_rate_control_point_properties_.get();
134 return NULL;
137 void FakeBluetoothGattCharacteristicClient::ReadValue(
138 const dbus::ObjectPath& object_path,
139 const ValueCallback& callback,
140 const ErrorCallback& error_callback) {
141 if (!authenticated_) {
142 error_callback.Run("org.bluez.Error.NotPaired", "Please login");
143 return;
146 if (!authorized_) {
147 error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first");
148 return;
151 if (object_path.value() == heart_rate_control_point_path_) {
152 error_callback.Run("org.bluez.Error.NotPermitted",
153 "Reads of this value are not allowed");
154 return;
157 if (object_path.value() == heart_rate_measurement_path_) {
158 error_callback.Run("org.bluez.Error.NotSupported",
159 "Action not supported on this characteristic");
160 return;
163 if (object_path.value() != body_sensor_location_path_) {
164 error_callback.Run(kUnknownCharacteristicError, "");
165 return;
168 if (action_extra_requests_.find("ReadValue") !=
169 action_extra_requests_.end()) {
170 DelayedCallback* delayed = action_extra_requests_["ReadValue"];
171 delayed->delay_--;
172 error_callback.Run("org.bluez.Error.InProgress",
173 "Another read is currenty in progress");
174 if (delayed->delay_ == 0) {
175 delayed->callback_.Run();
176 action_extra_requests_.erase("ReadValue");
177 delete delayed;
179 return;
182 base::Closure completed_callback;
183 if (!IsHeartRateVisible()) {
184 completed_callback =
185 base::Bind(error_callback, kUnknownCharacteristicError, "");
186 } else {
187 std::vector<uint8> value = {0x06}; // Location is "foot".
188 completed_callback = base::Bind(
189 &FakeBluetoothGattCharacteristicClient::DelayedReadValueCallback,
190 weak_ptr_factory_.GetWeakPtr(), object_path, callback, value);
193 if (extra_requests_ > 0) {
194 action_extra_requests_["ReadValue"] =
195 new DelayedCallback(completed_callback, extra_requests_);
196 return;
199 completed_callback.Run();
202 void FakeBluetoothGattCharacteristicClient::WriteValue(
203 const dbus::ObjectPath& object_path,
204 const std::vector<uint8>& value,
205 const base::Closure& callback,
206 const ErrorCallback& error_callback) {
207 if (!authenticated_) {
208 error_callback.Run("org.bluez.Error.NotPaired", "Please login");
209 return;
212 if (!authorized_) {
213 error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first");
214 return;
217 if (!IsHeartRateVisible()) {
218 error_callback.Run(kUnknownCharacteristicError, "");
219 return;
222 if (object_path.value() == heart_rate_measurement_path_) {
223 error_callback.Run("org.bluez.Error.NotSupported",
224 "Action not supported on this characteristic");
225 return;
228 if (object_path.value() != heart_rate_control_point_path_) {
229 error_callback.Run("org.bluez.Error.NotPermitted",
230 "Writes of this value are not allowed");
231 return;
234 DCHECK(heart_rate_control_point_properties_.get());
235 if (action_extra_requests_.find("WriteValue") !=
236 action_extra_requests_.end()) {
237 DelayedCallback* delayed = action_extra_requests_["WriteValue"];
238 delayed->delay_--;
239 error_callback.Run("org.bluez.Error.InProgress",
240 "Another write is in progress");
241 if (delayed->delay_ == 0) {
242 delayed->callback_.Run();
243 action_extra_requests_.erase("WriteValue");
244 delete delayed;
246 return;
248 base::Closure completed_callback;
249 if (value.size() != 1) {
250 completed_callback = base::Bind(error_callback,
251 "org.bluez.Error.InvalidValueLength",
252 "Invalid length for write");
253 } else if (value[0] > 1) {
254 completed_callback = base::Bind(error_callback,
255 "org.bluez.Error.Failed",
256 "Invalid value given for write");
257 } else if (value[0] == 1) {
258 // TODO(jamuraa): make this happen when the callback happens
259 calories_burned_ = 0;
260 completed_callback = callback;
263 if (extra_requests_ > 0) {
264 action_extra_requests_["WriteValue"] =
265 new DelayedCallback(completed_callback, extra_requests_);
266 return;
268 completed_callback.Run();
271 void FakeBluetoothGattCharacteristicClient::StartNotify(
272 const dbus::ObjectPath& object_path,
273 const base::Closure& callback,
274 const ErrorCallback& error_callback) {
275 if (!IsHeartRateVisible()) {
276 error_callback.Run(kUnknownCharacteristicError, "");
277 return;
280 if (object_path.value() != heart_rate_measurement_path_) {
281 error_callback.Run("org.bluez.Error.NotSupported",
282 "This characteristic does not support notifications");
283 return;
286 if (heart_rate_measurement_properties_->notifying.value()) {
287 error_callback.Run("org.bluez.Error.InProgress",
288 "Characteristic already notifying");
289 return;
292 heart_rate_measurement_properties_->notifying.ReplaceValue(true);
293 ScheduleHeartRateMeasurementValueChange();
295 // Respond asynchronously.
296 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
297 FROM_HERE, callback,
298 base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs));
301 void FakeBluetoothGattCharacteristicClient::StopNotify(
302 const dbus::ObjectPath& object_path,
303 const base::Closure& callback,
304 const ErrorCallback& error_callback) {
305 if (!IsHeartRateVisible()) {
306 error_callback.Run(kUnknownCharacteristicError, "");
307 return;
310 if (object_path.value() != heart_rate_measurement_path_) {
311 error_callback.Run("org.bluez.Error.NotSupported",
312 "This characteristic does not support notifications");
313 return;
316 if (!heart_rate_measurement_properties_->notifying.value()) {
317 error_callback.Run("org.bluez.Error.Failed", "Not notifying");
318 return;
321 heart_rate_measurement_properties_->notifying.ReplaceValue(false);
323 callback.Run();
326 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
327 const dbus::ObjectPath& service_path) {
328 if (IsHeartRateVisible()) {
329 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
330 return;
333 VLOG(2) << "Exposing fake Heart Rate characteristics.";
335 std::vector<std::string> flags;
337 // ==== Heart Rate Measurement Characteristic ====
338 heart_rate_measurement_path_ =
339 service_path.value() + "/" + kHeartRateMeasurementPathComponent;
340 heart_rate_measurement_properties_.reset(new Properties(base::Bind(
341 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
342 weak_ptr_factory_.GetWeakPtr(),
343 dbus::ObjectPath(heart_rate_measurement_path_))));
344 heart_rate_measurement_properties_->uuid.ReplaceValue(
345 kHeartRateMeasurementUUID);
346 heart_rate_measurement_properties_->service.ReplaceValue(service_path);
347 flags.push_back(bluetooth_gatt_characteristic::kFlagNotify);
348 heart_rate_measurement_properties_->flags.ReplaceValue(flags);
350 // ==== Body Sensor Location Characteristic ====
351 body_sensor_location_path_ =
352 service_path.value() + "/" + kBodySensorLocationPathComponent;
353 body_sensor_location_properties_.reset(new Properties(base::Bind(
354 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
355 weak_ptr_factory_.GetWeakPtr(),
356 dbus::ObjectPath(body_sensor_location_path_))));
357 body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID);
358 body_sensor_location_properties_->service.ReplaceValue(service_path);
359 flags.clear();
360 flags.push_back(bluetooth_gatt_characteristic::kFlagRead);
361 body_sensor_location_properties_->flags.ReplaceValue(flags);
363 // ==== Heart Rate Control Point Characteristic ====
364 heart_rate_control_point_path_ =
365 service_path.value() + "/" + kHeartRateControlPointPathComponent;
366 heart_rate_control_point_properties_.reset(new Properties(base::Bind(
367 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
368 weak_ptr_factory_.GetWeakPtr(),
369 dbus::ObjectPath(heart_rate_control_point_path_))));
370 heart_rate_control_point_properties_->uuid.ReplaceValue(
371 kHeartRateControlPointUUID);
372 heart_rate_control_point_properties_->service.ReplaceValue(service_path);
373 flags.clear();
374 flags.push_back(bluetooth_gatt_characteristic::kFlagWrite);
375 heart_rate_control_point_properties_->flags.ReplaceValue(flags);
377 heart_rate_visible_ = true;
379 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_));
380 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
381 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
383 // Expose CCC descriptor for Heart Rate Measurement characteristic.
384 FakeBluetoothGattDescriptorClient* descriptor_client =
385 static_cast<FakeBluetoothGattDescriptorClient*>(
386 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
387 dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor(
388 dbus::ObjectPath(heart_rate_measurement_path_),
389 FakeBluetoothGattDescriptorClient::
390 kClientCharacteristicConfigurationUUID));
391 DCHECK(ccc_path.IsValid());
392 heart_rate_measurement_ccc_desc_path_ = ccc_path.value();
394 std::vector<dbus::ObjectPath> desc_paths;
395 desc_paths.push_back(ccc_path);
397 heart_rate_measurement_properties_->descriptors.ReplaceValue(desc_paths);
400 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
401 VLOG(2) << "Hiding fake Heart Rate characteristics.";
403 // Hide the descriptors.
404 FakeBluetoothGattDescriptorClient* descriptor_client =
405 static_cast<FakeBluetoothGattDescriptorClient*>(
406 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
407 descriptor_client->HideDescriptor(
408 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_));
410 // Notify the observers before deleting the properties structures so that they
411 // can be accessed from the observer method.
412 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_));
413 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_));
414 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_));
416 heart_rate_measurement_properties_.reset();
417 body_sensor_location_properties_.reset();
418 heart_rate_control_point_properties_.reset();
420 heart_rate_measurement_path_.clear();
421 body_sensor_location_path_.clear();
422 heart_rate_control_point_path_.clear();
423 heart_rate_visible_ = false;
426 void FakeBluetoothGattCharacteristicClient::SetExtraProcessing(
427 size_t requests) {
428 extra_requests_ = requests;
429 if (extra_requests_ == 0) {
430 for (const auto& it : action_extra_requests_) {
431 it.second->callback_.Run();
432 delete it.second;
434 action_extra_requests_.clear();
435 return;
437 VLOG(2) << "Requests SLOW now, " << requests << " InProgress errors each.";
440 size_t FakeBluetoothGattCharacteristicClient::GetExtraProcessing() const {
441 return extra_requests_;
444 dbus::ObjectPath
445 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
446 return dbus::ObjectPath(heart_rate_measurement_path_);
449 dbus::ObjectPath
450 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
451 return dbus::ObjectPath(body_sensor_location_path_);
454 dbus::ObjectPath
455 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
456 return dbus::ObjectPath(heart_rate_control_point_path_);
459 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
460 const dbus::ObjectPath& object_path,
461 const std::string& property_name) {
462 VLOG(2) << "Characteristic property changed: " << object_path.value()
463 << ": " << property_name;
465 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
466 GattCharacteristicPropertyChanged(
467 object_path, property_name));
470 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
471 const dbus::ObjectPath& object_path) {
472 VLOG(2) << "GATT characteristic added: " << object_path.value();
473 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
474 GattCharacteristicAdded(object_path));
477 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
478 const dbus::ObjectPath& object_path) {
479 VLOG(2) << "GATT characteristic removed: " << object_path.value();
480 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
481 GattCharacteristicRemoved(object_path));
484 void FakeBluetoothGattCharacteristicClient::
485 ScheduleHeartRateMeasurementValueChange() {
486 if (!IsHeartRateVisible())
487 return;
489 // Don't send updates if the characteristic is not notifying.
490 if (!heart_rate_measurement_properties_->notifying.value())
491 return;
493 VLOG(2) << "Updating heart rate value.";
494 std::vector<uint8> measurement = GetHeartRateMeasurementValue();
495 heart_rate_measurement_properties_->value.ReplaceValue(measurement);
497 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
498 FROM_HERE, base::Bind(&FakeBluetoothGattCharacteristicClient::
499 ScheduleHeartRateMeasurementValueChange,
500 weak_ptr_factory_.GetWeakPtr()),
501 base::TimeDelta::FromMilliseconds(
502 kHeartRateMeasurementNotificationIntervalMs));
505 void FakeBluetoothGattCharacteristicClient::DelayedReadValueCallback(
506 const dbus::ObjectPath& object_path,
507 const ValueCallback& callback,
508 const std::vector<uint8_t>& value) {
509 Properties* properties = GetProperties(object_path);
510 DCHECK(properties);
512 properties->value.ReplaceValue(value);
513 callback.Run(value);
516 std::vector<uint8>
517 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
518 // TODO(armansito): We should make sure to properly pack this struct to ensure
519 // correct byte alignment and endianness. It doesn't matter too much right now
520 // as this is a fake and GCC on Linux seems to do the right thing.
521 struct {
522 uint8 flags;
523 uint8 bpm;
524 uint16 energy_expanded;
525 uint16 rr_interval;
526 } value;
528 // Flags in LSB: 0 11 1 1 000
529 // | | | | |
530 // 8-bit bpm format -- | | | |
531 // Sensor contact supported -- | | |
532 // Energy expanded field present -- | |
533 // RR-Interval values present ------- |
534 // Reserved for future use ------------
535 value.flags = 0x0;
536 value.flags |= (0x03 << 1);
537 value.flags |= (0x01 << 3);
538 value.flags |= (0x01 << 4);
540 // Pick a value between 117 bpm and 153 bpm for heart rate.
541 value.bpm = static_cast<uint8>(base::RandInt(117, 153));
543 // Total calories burned in kJoules since the last reset. Increment this by 1
544 // every time. It's fine if it overflows: it becomes 0 when the user resets
545 // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
546 value.energy_expanded = calories_burned_++;
548 // Include one RR-Interval value, in seconds.
549 value.rr_interval = 60/value.bpm;
551 // Return the bytes in an array.
552 uint8* bytes = reinterpret_cast<uint8*>(&value);
553 std::vector<uint8> return_value;
554 return_value.assign(bytes, bytes + sizeof(value));
555 return return_value;
558 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
559 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty());
560 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty());
561 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty());
562 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get());
563 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get());
564 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get());
565 return heart_rate_visible_;
568 } // namespace chromeos