• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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