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 kStartNotifyResponseIntervalMs = 200;
20 const int kHeartRateMeasurementNotificationIntervalMs = 2000;
21
22 } // namespace
23
24 // static
25 const char FakeBluetoothGattCharacteristicClient::
26 kHeartRateMeasurementPathComponent[] = "char0000";
27 const char FakeBluetoothGattCharacteristicClient::
28 kBodySensorLocationPathComponent[] = "char0001";
29 const char FakeBluetoothGattCharacteristicClient::
30 kHeartRateControlPointPathComponent[] = "char0002";
31
32 // static
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";
39
Properties(const PropertyChangedCallback & callback)40 FakeBluetoothGattCharacteristicClient::Properties::Properties(
41 const PropertyChangedCallback& callback)
42 : BluetoothGattCharacteristicClient::Properties(
43 NULL,
44 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
45 callback) {
46 }
47
~Properties()48 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
49 }
50
Get(dbus::PropertyBase * property,dbus::PropertySet::GetCallback callback)51 void FakeBluetoothGattCharacteristicClient::Properties::Get(
52 dbus::PropertyBase* property,
53 dbus::PropertySet::GetCallback callback) {
54 VLOG(1) << "Get " << property->name();
55 callback.Run(true);
56 }
57
GetAll()58 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
59 VLOG(1) << "GetAll";
60 }
61
Set(dbus::PropertyBase * property,dbus::PropertySet::SetCallback callback)62 void FakeBluetoothGattCharacteristicClient::Properties::Set(
63 dbus::PropertyBase* property,
64 dbus::PropertySet::SetCallback callback) {
65 VLOG(1) << "Set " << property->name();
66 callback.Run(false);
67 }
68
FakeBluetoothGattCharacteristicClient()69 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
70 : heart_rate_visible_(false),
71 calories_burned_(0),
72 weak_ptr_factory_(this) {
73 }
74
75 FakeBluetoothGattCharacteristicClient::
~FakeBluetoothGattCharacteristicClient()76 ~FakeBluetoothGattCharacteristicClient() {
77 }
78
Init(dbus::Bus * bus)79 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
80 }
81
AddObserver(Observer * observer)82 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
83 observers_.AddObserver(observer);
84 }
85
RemoveObserver(Observer * observer)86 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
87 observers_.RemoveObserver(observer);
88 }
89
90 std::vector<dbus::ObjectPath>
GetCharacteristics()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_));
97 }
98 return paths;
99 }
100
101 FakeBluetoothGattCharacteristicClient::Properties*
GetProperties(const dbus::ObjectPath & object_path)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();
107 }
108 if (object_path.value() == body_sensor_location_path_) {
109 DCHECK(body_sensor_location_properties_.get());
110 return body_sensor_location_properties_.get();
111 }
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();
115 }
116 return NULL;
117 }
118
ReadValue(const dbus::ObjectPath & object_path,const ValueCallback & callback,const ErrorCallback & error_callback)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, "");
125 return;
126 }
127
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");
132 return;
133 }
134
135 if (object_path.value() != body_sensor_location_path_) {
136 error_callback.Run(kUnknownCharacteristicError, "");
137 return;
138 }
139
140 std::vector<uint8> value;
141 value.push_back(0x06); // Location is "foot".
142 callback.Run(value);
143 }
144
WriteValue(const dbus::ObjectPath & object_path,const std::vector<uint8> & value,const base::Closure & callback,const ErrorCallback & error_callback)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, "");
152 return;
153 }
154
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");
158 return;
159 }
160
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");
165 return;
166 }
167
168 if (value[0] == 1)
169 calories_burned_ = 0;
170
171 callback.Run();
172 }
173
StartNotify(const dbus::ObjectPath & object_path,const base::Closure & callback,const ErrorCallback & error_callback)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, "");
180 return;
181 }
182
183 if (object_path.value() != heart_rate_measurement_path_) {
184 error_callback.Run("org.bluez.Error.NotSupported",
185 "This characteristic does not support notifications");
186 return;
187 }
188
189 if (heart_rate_measurement_properties_->notifying.value()) {
190 error_callback.Run("org.bluez.Error.Busy",
191 "Characteristic already notifying");
192 return;
193 }
194
195 heart_rate_measurement_properties_->notifying.ReplaceValue(true);
196 ScheduleHeartRateMeasurementValueChange();
197
198 // Respond asynchronously.
199 base::MessageLoop::current()->PostDelayedTask(
200 FROM_HERE,
201 callback,
202 base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs));
203 }
204
StopNotify(const dbus::ObjectPath & object_path,const base::Closure & callback,const ErrorCallback & error_callback)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, "");
211 return;
212 }
213
214 if (object_path.value() != heart_rate_measurement_path_) {
215 error_callback.Run("org.bluez.Error.NotSupported",
216 "This characteristic does not support notifications");
217 return;
218 }
219
220 if (!heart_rate_measurement_properties_->notifying.value()) {
221 error_callback.Run("org.bluez.Error.Failed", "Not notifying");
222 return;
223 }
224
225 heart_rate_measurement_properties_->notifying.ReplaceValue(false);
226
227 callback.Run();
228 }
229
ExposeHeartRateCharacteristics(const dbus::ObjectPath & service_path)230 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
231 const dbus::ObjectPath& service_path) {
232 if (IsHeartRateVisible()) {
233 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
234 return;
235 }
236
237 VLOG(2) << "Exposing fake Heart Rate characteristics.";
238
239 std::vector<std::string> flags;
240
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);
253
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);
263 flags.clear();
264 flags.push_back(bluetooth_gatt_characteristic::kFlagRead);
265 body_sensor_location_properties_->flags.ReplaceValue(flags);
266
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);
277 flags.clear();
278 flags.push_back(bluetooth_gatt_characteristic::kFlagWrite);
279 heart_rate_control_point_properties_->flags.ReplaceValue(flags);
280
281 heart_rate_visible_ = true;
282
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_));
286
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();
297 }
298
HideHeartRateCharacteristics()299 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
300 VLOG(2) << "Hiding fake Heart Rate characteristics.";
301
302 // Hide the descriptors.
303 FakeBluetoothGattDescriptorClient* descriptor_client =
304 static_cast<FakeBluetoothGattDescriptorClient*>(
305 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
306 descriptor_client->HideDescriptor(
307 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_));
308
309 // Notify the observers before deleting the properties structures so that they
310 // can be accessed from the observer method.
311 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_));
312 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_));
313 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_));
314
315 heart_rate_measurement_properties_.reset();
316 body_sensor_location_properties_.reset();
317 heart_rate_control_point_properties_.reset();
318
319 heart_rate_measurement_path_.clear();
320 body_sensor_location_path_.clear();
321 heart_rate_control_point_path_.clear();
322 heart_rate_visible_ = false;
323 }
324
325 dbus::ObjectPath
GetHeartRateMeasurementPath() const326 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
327 return dbus::ObjectPath(heart_rate_measurement_path_);
328 }
329
330 dbus::ObjectPath
GetBodySensorLocationPath() const331 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
332 return dbus::ObjectPath(body_sensor_location_path_);
333 }
334
335 dbus::ObjectPath
GetHeartRateControlPointPath() const336 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
337 return dbus::ObjectPath(heart_rate_control_point_path_);
338 }
339
OnPropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)340 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
341 const dbus::ObjectPath& object_path,
342 const std::string& property_name) {
343 VLOG(2) << "Characteristic property changed: " << object_path.value()
344 << ": " << property_name;
345
346 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
347 GattCharacteristicPropertyChanged(
348 object_path, property_name));
349 }
350
NotifyCharacteristicAdded(const dbus::ObjectPath & object_path)351 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
352 const dbus::ObjectPath& object_path) {
353 VLOG(2) << "GATT characteristic added: " << object_path.value();
354 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
355 GattCharacteristicAdded(object_path));
356 }
357
NotifyCharacteristicRemoved(const dbus::ObjectPath & object_path)358 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
359 const dbus::ObjectPath& object_path) {
360 VLOG(2) << "GATT characteristic removed: " << object_path.value();
361 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
362 GattCharacteristicRemoved(object_path));
363 }
364
365 void FakeBluetoothGattCharacteristicClient::
ScheduleHeartRateMeasurementValueChange()366 ScheduleHeartRateMeasurementValueChange() {
367 if (!IsHeartRateVisible())
368 return;
369
370 // Don't send updates if the characteristic is not notifying.
371 if (!heart_rate_measurement_properties_->notifying.value())
372 return;
373
374 VLOG(2) << "Updating heart rate value.";
375 std::vector<uint8> measurement = GetHeartRateMeasurementValue();
376
377 FOR_EACH_OBSERVER(
378 BluetoothGattCharacteristicClient::Observer,
379 observers_,
380 GattCharacteristicValueUpdated(
381 dbus::ObjectPath(heart_rate_measurement_path_), measurement));
382
383 base::MessageLoop::current()->PostDelayedTask(
384 FROM_HERE,
385 base::Bind(&FakeBluetoothGattCharacteristicClient::
386 ScheduleHeartRateMeasurementValueChange,
387 weak_ptr_factory_.GetWeakPtr()),
388 base::TimeDelta::FromMilliseconds(
389 kHeartRateMeasurementNotificationIntervalMs));
390 }
391
392 std::vector<uint8>
GetHeartRateMeasurementValue()393 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
394 // TODO(armansito): We should make sure to properly pack this struct to ensure
395 // correct byte alignment and endianness. It doesn't matter too much right now
396 // as this is a fake and GCC on Linux seems to do the right thing.
397 struct {
398 uint8 flags;
399 uint8 bpm;
400 uint16 energy_expanded;
401 uint16 rr_interval;
402 } value;
403
404 // Flags in LSB: 0 11 1 1 000
405 // | | | | |
406 // 8-bit bpm format -- | | | |
407 // Sensor contact supported -- | | |
408 // Energy expanded field present -- | |
409 // RR-Interval values present ------- |
410 // Reserved for future use ------------
411 value.flags = 0x0;
412 value.flags |= (0x03 << 1);
413 value.flags |= (0x01 << 3);
414 value.flags |= (0x01 << 4);
415
416 // Pick a value between 117 bpm and 153 bpm for heart rate.
417 value.bpm = static_cast<uint8>(base::RandInt(117, 153));
418
419 // Total calories burned in kJoules since the last reset. Increment this by 1
420 // every time. It's fine if it overflows: it becomes 0 when the user resets
421 // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
422 value.energy_expanded = calories_burned_++;
423
424 // Include one RR-Interval value, in seconds.
425 value.rr_interval = 60/value.bpm;
426
427 // Return the bytes in an array.
428 uint8* bytes = reinterpret_cast<uint8*>(&value);
429 std::vector<uint8> return_value;
430 return_value.assign(bytes, bytes + sizeof(value));
431 return return_value;
432 }
433
IsHeartRateVisible() const434 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
435 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty());
436 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty());
437 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty());
438 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get());
439 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get());
440 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get());
441 return heart_rate_visible_;
442 }
443
444 } // namespace chromeos
445