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"
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"
19 const int kStartNotifyResponseIntervalMs
= 200;
20 const int kHeartRateMeasurementNotificationIntervalMs
= 2000;
25 const char FakeBluetoothGattCharacteristicClient::
26 kHeartRateMeasurementPathComponent
[] = "char0000";
27 const char FakeBluetoothGattCharacteristicClient::
28 kBodySensorLocationPathComponent
[] = "char0001";
29 const char FakeBluetoothGattCharacteristicClient::
30 kHeartRateControlPointPathComponent
[] = "char0002";
33 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID
[] =
34 "00002a37-0000-1000-8000-00805f9b34fb";
35 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID
[] =
36 "00002a38-0000-1000-8000-00805f9b34fb";
37 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID
[] =
38 "00002a39-0000-1000-8000-00805f9b34fb";
40 FakeBluetoothGattCharacteristicClient::Properties::Properties(
41 const PropertyChangedCallback
& callback
)
42 : BluetoothGattCharacteristicClient::Properties(
44 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface
,
48 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
51 void FakeBluetoothGattCharacteristicClient::Properties::Get(
52 dbus::PropertyBase
* property
,
53 dbus::PropertySet::GetCallback callback
) {
54 VLOG(1) << "Get " << property
->name();
58 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
62 void FakeBluetoothGattCharacteristicClient::Properties::Set(
63 dbus::PropertyBase
* property
,
64 dbus::PropertySet::SetCallback callback
) {
65 VLOG(1) << "Set " << property
->name();
69 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
70 : heart_rate_visible_(false),
72 weak_ptr_factory_(this) {
75 FakeBluetoothGattCharacteristicClient::
76 ~FakeBluetoothGattCharacteristicClient() {
79 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus
* bus
) {
82 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer
* observer
) {
83 observers_
.AddObserver(observer
);
86 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer
* observer
) {
87 observers_
.RemoveObserver(observer
);
90 std::vector
<dbus::ObjectPath
>
91 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
92 std::vector
<dbus::ObjectPath
> paths
;
93 if (IsHeartRateVisible()) {
94 paths
.push_back(dbus::ObjectPath(heart_rate_measurement_path_
));
95 paths
.push_back(dbus::ObjectPath(body_sensor_location_path_
));
96 paths
.push_back(dbus::ObjectPath(heart_rate_control_point_path_
));
101 FakeBluetoothGattCharacteristicClient::Properties
*
102 FakeBluetoothGattCharacteristicClient::GetProperties(
103 const dbus::ObjectPath
& object_path
) {
104 if (object_path
.value() == heart_rate_measurement_path_
) {
105 DCHECK(heart_rate_measurement_properties_
.get());
106 return heart_rate_measurement_properties_
.get();
108 if (object_path
.value() == body_sensor_location_path_
) {
109 DCHECK(body_sensor_location_properties_
.get());
110 return body_sensor_location_properties_
.get();
112 if (object_path
.value() == heart_rate_control_point_path_
) {
113 DCHECK(heart_rate_control_point_properties_
.get());
114 return heart_rate_control_point_properties_
.get();
119 void FakeBluetoothGattCharacteristicClient::ReadValue(
120 const dbus::ObjectPath
& object_path
,
121 const ValueCallback
& callback
,
122 const ErrorCallback
& error_callback
) {
123 if (!IsHeartRateVisible()) {
124 error_callback
.Run(kUnknownCharacteristicError
, "");
128 if (object_path
.value() == heart_rate_measurement_path_
||
129 object_path
.value() == heart_rate_control_point_path_
) {
130 error_callback
.Run("org.bluez.Error.ReadNotPermitted",
131 "Reads of this value are not allowed");
135 if (object_path
.value() != body_sensor_location_path_
) {
136 error_callback
.Run(kUnknownCharacteristicError
, "");
140 std::vector
<uint8
> value
;
141 value
.push_back(0x06); // Location is "foot".
145 void FakeBluetoothGattCharacteristicClient::WriteValue(
146 const dbus::ObjectPath
& object_path
,
147 const std::vector
<uint8
>& value
,
148 const base::Closure
& callback
,
149 const ErrorCallback
& error_callback
) {
150 if (!IsHeartRateVisible()) {
151 error_callback
.Run(kUnknownCharacteristicError
, "");
155 if (object_path
.value() != heart_rate_control_point_path_
) {
156 error_callback
.Run("org.bluez.Error.WriteNotPermitted",
157 "Writes of this value are not allowed");
161 DCHECK(heart_rate_control_point_properties_
.get());
162 if (value
.size() != 1 || value
[0] > 1) {
163 error_callback
.Run("org.bluez.Error.Failed",
164 "Invalid value given for write");
169 calories_burned_
= 0;
174 void FakeBluetoothGattCharacteristicClient::StartNotify(
175 const dbus::ObjectPath
& object_path
,
176 const base::Closure
& callback
,
177 const ErrorCallback
& error_callback
) {
178 if (!IsHeartRateVisible()) {
179 error_callback
.Run(kUnknownCharacteristicError
, "");
183 if (object_path
.value() != heart_rate_measurement_path_
) {
184 error_callback
.Run("org.bluez.Error.NotSupported",
185 "This characteristic does not support notifications");
189 if (heart_rate_measurement_properties_
->notifying
.value()) {
190 error_callback
.Run("org.bluez.Error.Busy",
191 "Characteristic already notifying");
195 heart_rate_measurement_properties_
->notifying
.ReplaceValue(true);
196 ScheduleHeartRateMeasurementValueChange();
198 // Respond asynchronously.
199 base::MessageLoop::current()->PostDelayedTask(
202 base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs
));
205 void FakeBluetoothGattCharacteristicClient::StopNotify(
206 const dbus::ObjectPath
& object_path
,
207 const base::Closure
& callback
,
208 const ErrorCallback
& error_callback
) {
209 if (!IsHeartRateVisible()) {
210 error_callback
.Run(kUnknownCharacteristicError
, "");
214 if (object_path
.value() != heart_rate_measurement_path_
) {
215 error_callback
.Run("org.bluez.Error.NotSupported",
216 "This characteristic does not support notifications");
220 if (!heart_rate_measurement_properties_
->notifying
.value()) {
221 error_callback
.Run("org.bluez.Error.Failed", "Not notifying");
225 heart_rate_measurement_properties_
->notifying
.ReplaceValue(false);
230 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
231 const dbus::ObjectPath
& service_path
) {
232 if (IsHeartRateVisible()) {
233 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
237 VLOG(2) << "Exposing fake Heart Rate characteristics.";
239 std::vector
<std::string
> flags
;
241 // ==== Heart Rate Measurement Characteristic ====
242 heart_rate_measurement_path_
=
243 service_path
.value() + "/" + kHeartRateMeasurementPathComponent
;
244 heart_rate_measurement_properties_
.reset(new Properties(base::Bind(
245 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged
,
246 weak_ptr_factory_
.GetWeakPtr(),
247 dbus::ObjectPath(heart_rate_measurement_path_
))));
248 heart_rate_measurement_properties_
->uuid
.ReplaceValue(
249 kHeartRateMeasurementUUID
);
250 heart_rate_measurement_properties_
->service
.ReplaceValue(service_path
);
251 flags
.push_back(bluetooth_gatt_characteristic::kFlagNotify
);
252 heart_rate_measurement_properties_
->flags
.ReplaceValue(flags
);
254 // ==== Body Sensor Location Characteristic ====
255 body_sensor_location_path_
=
256 service_path
.value() + "/" + kBodySensorLocationPathComponent
;
257 body_sensor_location_properties_
.reset(new Properties(base::Bind(
258 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged
,
259 weak_ptr_factory_
.GetWeakPtr(),
260 dbus::ObjectPath(body_sensor_location_path_
))));
261 body_sensor_location_properties_
->uuid
.ReplaceValue(kBodySensorLocationUUID
);
262 body_sensor_location_properties_
->service
.ReplaceValue(service_path
);
264 flags
.push_back(bluetooth_gatt_characteristic::kFlagRead
);
265 body_sensor_location_properties_
->flags
.ReplaceValue(flags
);
267 // ==== Heart Rate Control Point Characteristic ====
268 heart_rate_control_point_path_
=
269 service_path
.value() + "/" + kHeartRateControlPointPathComponent
;
270 heart_rate_control_point_properties_
.reset(new Properties(base::Bind(
271 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged
,
272 weak_ptr_factory_
.GetWeakPtr(),
273 dbus::ObjectPath(heart_rate_control_point_path_
))));
274 heart_rate_control_point_properties_
->uuid
.ReplaceValue(
275 kHeartRateControlPointUUID
);
276 heart_rate_control_point_properties_
->service
.ReplaceValue(service_path
);
278 flags
.push_back(bluetooth_gatt_characteristic::kFlagWrite
);
279 heart_rate_control_point_properties_
->flags
.ReplaceValue(flags
);
281 heart_rate_visible_
= true;
283 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_
));
284 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_
));
285 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_
));
287 // Expose CCC descriptor for Heart Rate Measurement characteristic.
288 FakeBluetoothGattDescriptorClient
* descriptor_client
=
289 static_cast<FakeBluetoothGattDescriptorClient
*>(
290 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
291 dbus::ObjectPath
ccc_path(descriptor_client
->ExposeDescriptor(
292 dbus::ObjectPath(heart_rate_measurement_path_
),
293 FakeBluetoothGattDescriptorClient::
294 kClientCharacteristicConfigurationUUID
));
295 DCHECK(ccc_path
.IsValid());
296 heart_rate_measurement_ccc_desc_path_
= ccc_path
.value();
298 std::vector
<dbus::ObjectPath
> desc_paths
;
299 desc_paths
.push_back(ccc_path
);
301 heart_rate_measurement_properties_
->descriptors
.ReplaceValue(desc_paths
);
304 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
305 VLOG(2) << "Hiding fake Heart Rate characteristics.";
307 // Hide the descriptors.
308 FakeBluetoothGattDescriptorClient
* descriptor_client
=
309 static_cast<FakeBluetoothGattDescriptorClient
*>(
310 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
311 descriptor_client
->HideDescriptor(
312 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_
));
314 // Notify the observers before deleting the properties structures so that they
315 // can be accessed from the observer method.
316 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_
));
317 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_
));
318 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_
));
320 heart_rate_measurement_properties_
.reset();
321 body_sensor_location_properties_
.reset();
322 heart_rate_control_point_properties_
.reset();
324 heart_rate_measurement_path_
.clear();
325 body_sensor_location_path_
.clear();
326 heart_rate_control_point_path_
.clear();
327 heart_rate_visible_
= false;
331 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
332 return dbus::ObjectPath(heart_rate_measurement_path_
);
336 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
337 return dbus::ObjectPath(body_sensor_location_path_
);
341 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
342 return dbus::ObjectPath(heart_rate_control_point_path_
);
345 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
346 const dbus::ObjectPath
& object_path
,
347 const std::string
& property_name
) {
348 VLOG(2) << "Characteristic property changed: " << object_path
.value()
349 << ": " << property_name
;
351 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer
, observers_
,
352 GattCharacteristicPropertyChanged(
353 object_path
, property_name
));
356 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
357 const dbus::ObjectPath
& object_path
) {
358 VLOG(2) << "GATT characteristic added: " << object_path
.value();
359 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer
, observers_
,
360 GattCharacteristicAdded(object_path
));
363 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
364 const dbus::ObjectPath
& object_path
) {
365 VLOG(2) << "GATT characteristic removed: " << object_path
.value();
366 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer
, observers_
,
367 GattCharacteristicRemoved(object_path
));
370 void FakeBluetoothGattCharacteristicClient::
371 ScheduleHeartRateMeasurementValueChange() {
372 if (!IsHeartRateVisible())
375 // Don't send updates if the characteristic is not notifying.
376 if (!heart_rate_measurement_properties_
->notifying
.value())
379 VLOG(2) << "Updating heart rate value.";
380 std::vector
<uint8
> measurement
= GetHeartRateMeasurementValue();
383 BluetoothGattCharacteristicClient::Observer
,
385 GattCharacteristicValueUpdated(
386 dbus::ObjectPath(heart_rate_measurement_path_
), measurement
));
388 base::MessageLoop::current()->PostDelayedTask(
390 base::Bind(&FakeBluetoothGattCharacteristicClient::
391 ScheduleHeartRateMeasurementValueChange
,
392 weak_ptr_factory_
.GetWeakPtr()),
393 base::TimeDelta::FromMilliseconds(
394 kHeartRateMeasurementNotificationIntervalMs
));
398 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
399 // TODO(armansito): We should make sure to properly pack this struct to ensure
400 // correct byte alignment and endianness. It doesn't matter too much right now
401 // as this is a fake and GCC on Linux seems to do the right thing.
405 uint16 energy_expanded
;
409 // Flags in LSB: 0 11 1 1 000
411 // 8-bit bpm format -- | | | |
412 // Sensor contact supported -- | | |
413 // Energy expanded field present -- | |
414 // RR-Interval values present ------- |
415 // Reserved for future use ------------
417 value
.flags
|= (0x03 << 1);
418 value
.flags
|= (0x01 << 3);
419 value
.flags
|= (0x01 << 4);
421 // Pick a value between 117 bpm and 153 bpm for heart rate.
422 value
.bpm
= static_cast<uint8
>(base::RandInt(117, 153));
424 // Total calories burned in kJoules since the last reset. Increment this by 1
425 // every time. It's fine if it overflows: it becomes 0 when the user resets
426 // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
427 value
.energy_expanded
= calories_burned_
++;
429 // Include one RR-Interval value, in seconds.
430 value
.rr_interval
= 60/value
.bpm
;
432 // Return the bytes in an array.
433 uint8
* bytes
= reinterpret_cast<uint8
*>(&value
);
434 std::vector
<uint8
> return_value
;
435 return_value
.assign(bytes
, bytes
+ sizeof(value
));
439 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
440 DCHECK(heart_rate_visible_
!= heart_rate_measurement_path_
.empty());
441 DCHECK(heart_rate_visible_
!= body_sensor_location_path_
.empty());
442 DCHECK(heart_rate_visible_
!= heart_rate_control_point_path_
.empty());
443 DCHECK(heart_rate_visible_
== !!heart_rate_measurement_properties_
.get());
444 DCHECK(heart_rate_visible_
== !!body_sensor_location_properties_
.get());
445 DCHECK(heart_rate_visible_
== !!heart_rate_control_point_properties_
.get());
446 return heart_rate_visible_
;
449 } // namespace chromeos