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 kHeartRateMeasurementNotificationIntervalMs
= 2000;
24 const char FakeBluetoothGattCharacteristicClient::
25 kHeartRateMeasurementPathComponent
[] = "char0000";
26 const char FakeBluetoothGattCharacteristicClient::
27 kBodySensorLocationPathComponent
[] = "char0001";
28 const char FakeBluetoothGattCharacteristicClient::
29 kHeartRateControlPointPathComponent
[] = "char0002";
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";
39 FakeBluetoothGattCharacteristicClient::Properties::Properties(
40 const PropertyChangedCallback
& callback
)
41 : BluetoothGattCharacteristicClient::Properties(
43 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface
,
47 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
50 void FakeBluetoothGattCharacteristicClient::Properties::Get(
51 dbus::PropertyBase
* property
,
52 dbus::PropertySet::GetCallback callback
) {
53 VLOG(1) << "Get " << property
->name();
57 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
61 void FakeBluetoothGattCharacteristicClient::Properties::Set(
62 dbus::PropertyBase
* property
,
63 dbus::PropertySet::SetCallback callback
) {
64 VLOG(1) << "Set " << property
->name();
68 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
69 : heart_rate_visible_(false),
71 weak_ptr_factory_(this) {
74 FakeBluetoothGattCharacteristicClient::
75 ~FakeBluetoothGattCharacteristicClient() {
78 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus
* bus
) {
81 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer
* observer
) {
82 observers_
.AddObserver(observer
);
85 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer
* observer
) {
86 observers_
.RemoveObserver(observer
);
89 std::vector
<dbus::ObjectPath
>
90 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
91 std::vector
<dbus::ObjectPath
> paths
;
92 if (IsHeartRateVisible()) {
93 paths
.push_back(dbus::ObjectPath(heart_rate_measurement_path_
));
94 paths
.push_back(dbus::ObjectPath(body_sensor_location_path_
));
95 paths
.push_back(dbus::ObjectPath(heart_rate_control_point_path_
));
100 FakeBluetoothGattCharacteristicClient::Properties
*
101 FakeBluetoothGattCharacteristicClient::GetProperties(
102 const dbus::ObjectPath
& object_path
) {
103 if (object_path
.value() == heart_rate_measurement_path_
) {
104 DCHECK(heart_rate_measurement_properties_
.get());
105 return heart_rate_measurement_properties_
.get();
107 if (object_path
.value() == body_sensor_location_path_
) {
108 DCHECK(body_sensor_location_properties_
.get());
109 return body_sensor_location_properties_
.get();
111 if (object_path
.value() == heart_rate_control_point_path_
) {
112 DCHECK(heart_rate_control_point_properties_
.get());
113 return heart_rate_control_point_properties_
.get();
118 void FakeBluetoothGattCharacteristicClient::ReadValue(
119 const dbus::ObjectPath
& object_path
,
120 const ValueCallback
& callback
,
121 const ErrorCallback
& error_callback
) {
122 if (!IsHeartRateVisible()) {
123 error_callback
.Run(kUnknownCharacteristicError
, "");
127 if (object_path
.value() == heart_rate_measurement_path_
||
128 object_path
.value() == heart_rate_control_point_path_
) {
129 error_callback
.Run("org.bluez.Error.ReadNotPermitted",
130 "Reads of this value are not allowed");
134 if (object_path
.value() != body_sensor_location_path_
) {
135 error_callback
.Run(kUnknownCharacteristicError
, "");
139 std::vector
<uint8
> value
;
140 value
.push_back(0x06); // Location is "foot".
144 void FakeBluetoothGattCharacteristicClient::WriteValue(
145 const dbus::ObjectPath
& object_path
,
146 const std::vector
<uint8
>& value
,
147 const base::Closure
& callback
,
148 const ErrorCallback
& error_callback
) {
149 if (!IsHeartRateVisible()) {
150 error_callback
.Run(kUnknownCharacteristicError
, "");
154 if (object_path
.value() != heart_rate_control_point_path_
) {
155 error_callback
.Run("org.bluez.Error.WriteNotPermitted",
156 "Writes of this value are not allowed");
160 DCHECK(heart_rate_control_point_properties_
.get());
161 if (value
.size() != 1 || value
[0] > 1) {
162 error_callback
.Run("org.bluez.Error.Failed",
163 "Invalid value given for write");
168 calories_burned_
= 0;
173 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
174 const dbus::ObjectPath
& service_path
) {
175 if (IsHeartRateVisible()) {
176 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
180 VLOG(2) << "Exposing fake Heart Rate characteristics.";
182 std::vector
<std::string
> flags
;
184 // ==== Heart Rate Measurement Characteristic ====
185 heart_rate_measurement_path_
=
186 service_path
.value() + "/" + kHeartRateMeasurementPathComponent
;
187 heart_rate_measurement_properties_
.reset(new Properties(base::Bind(
188 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged
,
189 weak_ptr_factory_
.GetWeakPtr(),
190 dbus::ObjectPath(heart_rate_measurement_path_
))));
191 heart_rate_measurement_properties_
->uuid
.ReplaceValue(
192 kHeartRateMeasurementUUID
);
193 heart_rate_measurement_properties_
->service
.ReplaceValue(service_path
);
194 flags
.push_back(bluetooth_gatt_characteristic::kFlagNotify
);
195 heart_rate_measurement_properties_
->flags
.ReplaceValue(flags
);
197 // ==== Body Sensor Location Characteristic ====
198 body_sensor_location_path_
=
199 service_path
.value() + "/" + kBodySensorLocationPathComponent
;
200 body_sensor_location_properties_
.reset(new Properties(base::Bind(
201 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged
,
202 weak_ptr_factory_
.GetWeakPtr(),
203 dbus::ObjectPath(body_sensor_location_path_
))));
204 body_sensor_location_properties_
->uuid
.ReplaceValue(kBodySensorLocationUUID
);
205 body_sensor_location_properties_
->service
.ReplaceValue(service_path
);
207 flags
.push_back(bluetooth_gatt_characteristic::kFlagRead
);
208 body_sensor_location_properties_
->flags
.ReplaceValue(flags
);
210 // ==== Heart Rate Control Point Characteristic ====
211 heart_rate_control_point_path_
=
212 service_path
.value() + "/" + kHeartRateControlPointPathComponent
;
213 heart_rate_control_point_properties_
.reset(new Properties(base::Bind(
214 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged
,
215 weak_ptr_factory_
.GetWeakPtr(),
216 dbus::ObjectPath(heart_rate_control_point_path_
))));
217 heart_rate_control_point_properties_
->uuid
.ReplaceValue(
218 kHeartRateControlPointUUID
);
219 heart_rate_control_point_properties_
->service
.ReplaceValue(service_path
);
221 flags
.push_back(bluetooth_gatt_characteristic::kFlagWrite
);
222 heart_rate_control_point_properties_
->flags
.ReplaceValue(flags
);
224 heart_rate_visible_
= true;
226 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_
));
227 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_
));
228 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_
));
230 // Set up notifications for heart rate measurement.
231 // TODO(armansito): Do this based on the value of the "client characteristic
232 // configuration" descriptor. Since it's still unclear how descriptors will
233 // be handled by BlueZ, automatically set up notifications for now.
234 ScheduleHeartRateMeasurementValueChange();
236 // Expose CCC descriptor for Heart Rate Measurement characteristic.
237 FakeBluetoothGattDescriptorClient
* descriptor_client
=
238 static_cast<FakeBluetoothGattDescriptorClient
*>(
239 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
240 dbus::ObjectPath
ccc_path(descriptor_client
->ExposeDescriptor(
241 dbus::ObjectPath(heart_rate_measurement_path_
),
242 FakeBluetoothGattDescriptorClient::
243 kClientCharacteristicConfigurationUUID
));
244 DCHECK(ccc_path
.IsValid());
245 heart_rate_measurement_ccc_desc_path_
= ccc_path
.value();
248 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
249 VLOG(2) << "Hiding fake Heart Rate characteristics.";
251 // Hide the descriptors.
252 FakeBluetoothGattDescriptorClient
* descriptor_client
=
253 static_cast<FakeBluetoothGattDescriptorClient
*>(
254 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
255 descriptor_client
->HideDescriptor(
256 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_
));
258 // Notify the observers before deleting the properties structures so that they
259 // can be accessed from the observer method.
260 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_
));
261 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_
));
262 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_
));
264 heart_rate_measurement_properties_
.reset();
265 body_sensor_location_properties_
.reset();
266 heart_rate_control_point_properties_
.reset();
268 heart_rate_measurement_path_
.clear();
269 body_sensor_location_path_
.clear();
270 heart_rate_control_point_path_
.clear();
271 heart_rate_visible_
= false;
275 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
276 return dbus::ObjectPath(heart_rate_measurement_path_
);
280 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
281 return dbus::ObjectPath(body_sensor_location_path_
);
285 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
286 return dbus::ObjectPath(heart_rate_control_point_path_
);
289 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
290 const dbus::ObjectPath
& object_path
,
291 const std::string
& property_name
) {
292 VLOG(2) << "Characteristic property changed: " << object_path
.value()
293 << ": " << property_name
;
295 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer
, observers_
,
296 GattCharacteristicPropertyChanged(
297 object_path
, property_name
));
300 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
301 const dbus::ObjectPath
& object_path
) {
302 VLOG(2) << "GATT characteristic added: " << object_path
.value();
303 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer
, observers_
,
304 GattCharacteristicAdded(object_path
));
307 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
308 const dbus::ObjectPath
& object_path
) {
309 VLOG(2) << "GATT characteristic removed: " << object_path
.value();
310 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer
, observers_
,
311 GattCharacteristicRemoved(object_path
));
314 void FakeBluetoothGattCharacteristicClient::
315 ScheduleHeartRateMeasurementValueChange() {
316 if (!IsHeartRateVisible())
318 VLOG(2) << "Updating heart rate value.";
319 std::vector
<uint8
> measurement
= GetHeartRateMeasurementValue();
322 BluetoothGattCharacteristicClient::Observer
,
324 GattCharacteristicValueUpdated(
325 dbus::ObjectPath(heart_rate_measurement_path_
), measurement
));
327 base::MessageLoop::current()->PostDelayedTask(
329 base::Bind(&FakeBluetoothGattCharacteristicClient::
330 ScheduleHeartRateMeasurementValueChange
,
331 weak_ptr_factory_
.GetWeakPtr()),
332 base::TimeDelta::FromMilliseconds(
333 kHeartRateMeasurementNotificationIntervalMs
));
337 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
338 // TODO(armansito): We should make sure to properly pack this struct to ensure
339 // correct byte alignment and endianness. It doesn't matter too much right now
340 // as this is a fake and GCC on Linux seems to do the right thing.
344 uint16 energy_expanded
;
348 // Flags in LSB: 0 11 1 1 000
350 // 8-bit bpm format -- | | | |
351 // Sensor contact supported -- | | |
352 // Energy expanded field present -- | |
353 // RR-Interval values present ------- |
354 // Reserved for future use ------------
356 value
.flags
|= (0x03 << 1);
357 value
.flags
|= (0x01 << 3);
358 value
.flags
|= (0x01 << 4);
360 // Pick a value between 117 bpm and 153 bpm for heart rate.
361 value
.bpm
= static_cast<uint8
>(base::RandInt(117, 153));
363 // Total calories burned in kJoules since the last reset. Increment this by 1
364 // every time. It's fine if it overflows: it becomes 0 when the user resets
365 // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
366 value
.energy_expanded
= calories_burned_
++;
368 // Include one RR-Interval value, in seconds.
369 value
.rr_interval
= 60/value
.bpm
;
371 // Return the bytes in an array.
372 uint8
* bytes
= reinterpret_cast<uint8
*>(&value
);
373 std::vector
<uint8
> return_value
;
374 return_value
.assign(bytes
, bytes
+ sizeof(value
));
378 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
379 DCHECK(heart_rate_visible_
!= heart_rate_measurement_path_
.empty());
380 DCHECK(heart_rate_visible_
!= body_sensor_location_path_
.empty());
381 DCHECK(heart_rate_visible_
!= heart_rate_control_point_path_
.empty());
382 DCHECK(heart_rate_visible_
== !!heart_rate_measurement_properties_
.get());
383 DCHECK(heart_rate_visible_
== !!body_sensor_location_properties_
.get());
384 DCHECK(heart_rate_visible_
== !!heart_rate_control_point_properties_
.get());
385 return heart_rate_visible_
;
388 } // namespace chromeos