Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / chromeos / dbus / fake_bluetooth_gatt_characteristic_client.cc
blob1f3a9bf93345eb013626d88bb4b88480fc6298a9
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/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"
15 namespace chromeos {
17 namespace {
19 const int kStartNotifyResponseIntervalMs = 200;
20 const int kHeartRateMeasurementNotificationIntervalMs = 2000;
22 } // namespace
24 FakeBluetoothGattCharacteristicClient::DelayedCallback::DelayedCallback(
25 base::Closure callback,
26 size_t delay)
27 : callback_(callback), delay_(delay) {
30 FakeBluetoothGattCharacteristicClient::DelayedCallback::~DelayedCallback() {
33 // static
34 const char FakeBluetoothGattCharacteristicClient::
35 kHeartRateMeasurementPathComponent[] = "char0000";
36 const char FakeBluetoothGattCharacteristicClient::
37 kBodySensorLocationPathComponent[] = "char0001";
38 const char FakeBluetoothGattCharacteristicClient::
39 kHeartRateControlPointPathComponent[] = "char0002";
41 // static
42 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] =
43 "00002a37-0000-1000-8000-00805f9b34fb";
44 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] =
45 "00002a38-0000-1000-8000-00805f9b34fb";
46 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] =
47 "00002a39-0000-1000-8000-00805f9b34fb";
49 FakeBluetoothGattCharacteristicClient::Properties::Properties(
50 const PropertyChangedCallback& callback)
51 : BluetoothGattCharacteristicClient::Properties(
52 NULL,
53 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
54 callback) {
57 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
60 void FakeBluetoothGattCharacteristicClient::Properties::Get(
61 dbus::PropertyBase* property,
62 dbus::PropertySet::GetCallback callback) {
63 VLOG(1) << "Get " << property->name();
64 callback.Run(true);
67 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
68 VLOG(1) << "GetAll";
71 void FakeBluetoothGattCharacteristicClient::Properties::Set(
72 dbus::PropertyBase* property,
73 dbus::PropertySet::SetCallback callback) {
74 VLOG(1) << "Set " << property->name();
75 callback.Run(false);
78 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
79 : heart_rate_visible_(false),
80 authorized_(true),
81 authenticated_(true),
82 calories_burned_(0),
83 extra_requests_(0),
84 weak_ptr_factory_(this) {
87 FakeBluetoothGattCharacteristicClient::
88 ~FakeBluetoothGattCharacteristicClient() {
89 for (const auto& it : action_extra_requests_) {
90 delete it.second;
92 action_extra_requests_.clear();
95 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
98 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
99 observers_.AddObserver(observer);
102 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
103 observers_.RemoveObserver(observer);
106 std::vector<dbus::ObjectPath>
107 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
108 std::vector<dbus::ObjectPath> paths;
109 if (IsHeartRateVisible()) {
110 paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_));
111 paths.push_back(dbus::ObjectPath(body_sensor_location_path_));
112 paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_));
114 return paths;
117 FakeBluetoothGattCharacteristicClient::Properties*
118 FakeBluetoothGattCharacteristicClient::GetProperties(
119 const dbus::ObjectPath& object_path) {
120 if (object_path.value() == heart_rate_measurement_path_) {
121 DCHECK(heart_rate_measurement_properties_.get());
122 return heart_rate_measurement_properties_.get();
124 if (object_path.value() == body_sensor_location_path_) {
125 DCHECK(body_sensor_location_properties_.get());
126 return body_sensor_location_properties_.get();
128 if (object_path.value() == heart_rate_control_point_path_) {
129 DCHECK(heart_rate_control_point_properties_.get());
130 return heart_rate_control_point_properties_.get();
132 return NULL;
135 void FakeBluetoothGattCharacteristicClient::ReadValue(
136 const dbus::ObjectPath& object_path,
137 const ValueCallback& callback,
138 const ErrorCallback& error_callback) {
139 if (!authenticated_) {
140 error_callback.Run("org.bluez.Error.NotPaired", "Please login");
141 return;
144 if (!authorized_) {
145 error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first");
146 return;
149 if (object_path.value() == heart_rate_control_point_path_) {
150 error_callback.Run("org.bluez.Error.ReadNotPermitted",
151 "Reads of this value are not allowed");
152 return;
155 if (object_path.value() == heart_rate_measurement_path_) {
156 error_callback.Run("org.bluez.Error.NotSupported",
157 "Action not supported on this characteristic");
158 return;
161 if (object_path.value() != body_sensor_location_path_) {
162 error_callback.Run(kUnknownCharacteristicError, "");
163 return;
166 if (action_extra_requests_.find("ReadValue") !=
167 action_extra_requests_.end()) {
168 DelayedCallback* delayed = action_extra_requests_["ReadValue"];
169 delayed->delay_--;
170 error_callback.Run("org.bluez.Error.InProgress",
171 "Another read is currenty in progress");
172 if (delayed->delay_ == 0) {
173 delayed->callback_.Run();
174 action_extra_requests_.erase("ReadValue");
175 delete delayed;
177 return;
180 base::Closure completed_callback;
181 if (!IsHeartRateVisible()) {
182 completed_callback =
183 base::Bind(error_callback, kUnknownCharacteristicError, "");
184 } else {
185 std::vector<uint8> value = {0x06}; // Location is "foot".
186 completed_callback = base::Bind(
187 &FakeBluetoothGattCharacteristicClient::DelayedReadValueCallback,
188 weak_ptr_factory_.GetWeakPtr(), object_path, callback, value);
191 if (extra_requests_ > 0) {
192 action_extra_requests_["ReadValue"] =
193 new DelayedCallback(completed_callback, extra_requests_);
194 return;
197 completed_callback.Run();
200 void FakeBluetoothGattCharacteristicClient::WriteValue(
201 const dbus::ObjectPath& object_path,
202 const std::vector<uint8>& value,
203 const base::Closure& callback,
204 const ErrorCallback& error_callback) {
205 if (!authenticated_) {
206 error_callback.Run("org.bluez.Error.NotPaired", "Please login");
207 return;
210 if (!authorized_) {
211 error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first");
212 return;
215 if (!IsHeartRateVisible()) {
216 error_callback.Run(kUnknownCharacteristicError, "");
217 return;
220 if (object_path.value() == heart_rate_measurement_path_) {
221 error_callback.Run("org.bluez.Error.NotSupported",
222 "Action not supported on this characteristic");
223 return;
226 if (object_path.value() != heart_rate_control_point_path_) {
227 error_callback.Run("org.bluez.Error.WriteNotPermitted",
228 "Writes of this value are not allowed");
229 return;
232 DCHECK(heart_rate_control_point_properties_.get());
233 if (action_extra_requests_.find("WriteValue") !=
234 action_extra_requests_.end()) {
235 DelayedCallback* delayed = action_extra_requests_["WriteValue"];
236 delayed->delay_--;
237 error_callback.Run("org.bluez.Error.InProgress",
238 "Another write is in progress");
239 if (delayed->delay_ == 0) {
240 delayed->callback_.Run();
241 action_extra_requests_.erase("WriteValue");
242 delete delayed;
244 return;
246 base::Closure completed_callback;
247 if (value.size() != 1) {
248 completed_callback = base::Bind(error_callback,
249 "org.bluez.Error.InvalidValueLength",
250 "Invalid length for write");
251 } else if (value[0] > 1) {
252 completed_callback = base::Bind(error_callback,
253 "org.bluez.Error.Failed",
254 "Invalid value given for write");
255 } else if (value[0] == 1) {
256 // TODO(jamuraa): make this happen when the callback happens
257 calories_burned_ = 0;
258 completed_callback = callback;
261 if (extra_requests_ > 0) {
262 action_extra_requests_["WriteValue"] =
263 new DelayedCallback(completed_callback, extra_requests_);
264 return;
266 completed_callback.Run();
269 void FakeBluetoothGattCharacteristicClient::StartNotify(
270 const dbus::ObjectPath& object_path,
271 const base::Closure& callback,
272 const ErrorCallback& error_callback) {
273 if (!IsHeartRateVisible()) {
274 error_callback.Run(kUnknownCharacteristicError, "");
275 return;
278 if (object_path.value() != heart_rate_measurement_path_) {
279 error_callback.Run("org.bluez.Error.NotSupported",
280 "This characteristic does not support notifications");
281 return;
284 if (heart_rate_measurement_properties_->notifying.value()) {
285 error_callback.Run("org.bluez.Error.InProgress",
286 "Characteristic already notifying");
287 return;
290 heart_rate_measurement_properties_->notifying.ReplaceValue(true);
291 ScheduleHeartRateMeasurementValueChange();
293 // Respond asynchronously.
294 base::MessageLoop::current()->PostDelayedTask(
295 FROM_HERE,
296 callback,
297 base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs));
300 void FakeBluetoothGattCharacteristicClient::StopNotify(
301 const dbus::ObjectPath& object_path,
302 const base::Closure& callback,
303 const ErrorCallback& error_callback) {
304 if (!IsHeartRateVisible()) {
305 error_callback.Run(kUnknownCharacteristicError, "");
306 return;
309 if (object_path.value() != heart_rate_measurement_path_) {
310 error_callback.Run("org.bluez.Error.NotSupported",
311 "This characteristic does not support notifications");
312 return;
315 if (!heart_rate_measurement_properties_->notifying.value()) {
316 error_callback.Run("org.bluez.Error.Failed", "Not notifying");
317 return;
320 heart_rate_measurement_properties_->notifying.ReplaceValue(false);
322 callback.Run();
325 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
326 const dbus::ObjectPath& service_path) {
327 if (IsHeartRateVisible()) {
328 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
329 return;
332 VLOG(2) << "Exposing fake Heart Rate characteristics.";
334 std::vector<std::string> flags;
336 // ==== Heart Rate Measurement Characteristic ====
337 heart_rate_measurement_path_ =
338 service_path.value() + "/" + kHeartRateMeasurementPathComponent;
339 heart_rate_measurement_properties_.reset(new Properties(base::Bind(
340 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
341 weak_ptr_factory_.GetWeakPtr(),
342 dbus::ObjectPath(heart_rate_measurement_path_))));
343 heart_rate_measurement_properties_->uuid.ReplaceValue(
344 kHeartRateMeasurementUUID);
345 heart_rate_measurement_properties_->service.ReplaceValue(service_path);
346 flags.push_back(bluetooth_gatt_characteristic::kFlagNotify);
347 heart_rate_measurement_properties_->flags.ReplaceValue(flags);
349 // ==== Body Sensor Location Characteristic ====
350 body_sensor_location_path_ =
351 service_path.value() + "/" + kBodySensorLocationPathComponent;
352 body_sensor_location_properties_.reset(new Properties(base::Bind(
353 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
354 weak_ptr_factory_.GetWeakPtr(),
355 dbus::ObjectPath(body_sensor_location_path_))));
356 body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID);
357 body_sensor_location_properties_->service.ReplaceValue(service_path);
358 flags.clear();
359 flags.push_back(bluetooth_gatt_characteristic::kFlagRead);
360 body_sensor_location_properties_->flags.ReplaceValue(flags);
362 // ==== Heart Rate Control Point Characteristic ====
363 heart_rate_control_point_path_ =
364 service_path.value() + "/" + kHeartRateControlPointPathComponent;
365 heart_rate_control_point_properties_.reset(new Properties(base::Bind(
366 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
367 weak_ptr_factory_.GetWeakPtr(),
368 dbus::ObjectPath(heart_rate_control_point_path_))));
369 heart_rate_control_point_properties_->uuid.ReplaceValue(
370 kHeartRateControlPointUUID);
371 heart_rate_control_point_properties_->service.ReplaceValue(service_path);
372 flags.clear();
373 flags.push_back(bluetooth_gatt_characteristic::kFlagWrite);
374 heart_rate_control_point_properties_->flags.ReplaceValue(flags);
376 heart_rate_visible_ = true;
378 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_));
379 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
380 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
382 // Expose CCC descriptor for Heart Rate Measurement characteristic.
383 FakeBluetoothGattDescriptorClient* descriptor_client =
384 static_cast<FakeBluetoothGattDescriptorClient*>(
385 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
386 dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor(
387 dbus::ObjectPath(heart_rate_measurement_path_),
388 FakeBluetoothGattDescriptorClient::
389 kClientCharacteristicConfigurationUUID));
390 DCHECK(ccc_path.IsValid());
391 heart_rate_measurement_ccc_desc_path_ = ccc_path.value();
393 std::vector<dbus::ObjectPath> desc_paths;
394 desc_paths.push_back(ccc_path);
396 heart_rate_measurement_properties_->descriptors.ReplaceValue(desc_paths);
399 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
400 VLOG(2) << "Hiding fake Heart Rate characteristics.";
402 // Hide the descriptors.
403 FakeBluetoothGattDescriptorClient* descriptor_client =
404 static_cast<FakeBluetoothGattDescriptorClient*>(
405 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
406 descriptor_client->HideDescriptor(
407 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_));
409 // Notify the observers before deleting the properties structures so that they
410 // can be accessed from the observer method.
411 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_));
412 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_));
413 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_));
415 heart_rate_measurement_properties_.reset();
416 body_sensor_location_properties_.reset();
417 heart_rate_control_point_properties_.reset();
419 heart_rate_measurement_path_.clear();
420 body_sensor_location_path_.clear();
421 heart_rate_control_point_path_.clear();
422 heart_rate_visible_ = false;
425 void FakeBluetoothGattCharacteristicClient::SetExtraProcessing(
426 size_t requests) {
427 extra_requests_ = requests;
428 if (extra_requests_ == 0) {
429 for (const auto& it : action_extra_requests_) {
430 it.second->callback_.Run();
431 delete it.second;
433 action_extra_requests_.clear();
434 return;
436 VLOG(2) << "Requests SLOW now, " << requests << " InProgress errors each.";
439 size_t FakeBluetoothGattCharacteristicClient::GetExtraProcessing() const {
440 return extra_requests_;
443 dbus::ObjectPath
444 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
445 return dbus::ObjectPath(heart_rate_measurement_path_);
448 dbus::ObjectPath
449 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
450 return dbus::ObjectPath(body_sensor_location_path_);
453 dbus::ObjectPath
454 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
455 return dbus::ObjectPath(heart_rate_control_point_path_);
458 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
459 const dbus::ObjectPath& object_path,
460 const std::string& property_name) {
461 VLOG(2) << "Characteristic property changed: " << object_path.value()
462 << ": " << property_name;
464 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
465 GattCharacteristicPropertyChanged(
466 object_path, property_name));
469 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
470 const dbus::ObjectPath& object_path) {
471 VLOG(2) << "GATT characteristic added: " << object_path.value();
472 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
473 GattCharacteristicAdded(object_path));
476 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
477 const dbus::ObjectPath& object_path) {
478 VLOG(2) << "GATT characteristic removed: " << object_path.value();
479 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
480 GattCharacteristicRemoved(object_path));
483 void FakeBluetoothGattCharacteristicClient::
484 ScheduleHeartRateMeasurementValueChange() {
485 if (!IsHeartRateVisible())
486 return;
488 // Don't send updates if the characteristic is not notifying.
489 if (!heart_rate_measurement_properties_->notifying.value())
490 return;
492 VLOG(2) << "Updating heart rate value.";
493 std::vector<uint8> measurement = GetHeartRateMeasurementValue();
494 heart_rate_measurement_properties_->value.ReplaceValue(measurement);
496 base::MessageLoop::current()->PostDelayedTask(
497 FROM_HERE,
498 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