Home | History | Annotate | Download | only in dbus
      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 
     40 FakeBluetoothGattCharacteristicClient::Properties::Properties(
     41     const PropertyChangedCallback& callback)
     42     : BluetoothGattCharacteristicClient::Properties(
     43           NULL,
     44           bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
     45           callback) {
     46 }
     47 
     48 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
     49 }
     50 
     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 
     58 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
     59   VLOG(1) << "GetAll";
     60 }
     61 
     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 
     69 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
     70     : heart_rate_visible_(false),
     71       calories_burned_(0),
     72       weak_ptr_factory_(this) {
     73 }
     74 
     75 FakeBluetoothGattCharacteristicClient::
     76     ~FakeBluetoothGattCharacteristicClient() {
     77 }
     78 
     79 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
     80 }
     81 
     82 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
     83   observers_.AddObserver(observer);
     84 }
     85 
     86 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
     87   observers_.RemoveObserver(observer);
     88 }
     89 
     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_));
     97   }
     98   return paths;
     99 }
    100 
    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();
    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 
    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 
    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 
    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 
    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 
    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   std::vector<dbus::ObjectPath> desc_paths;
    299   desc_paths.push_back(ccc_path);
    300 
    301   heart_rate_measurement_properties_->descriptors.ReplaceValue(desc_paths);
    302 }
    303 
    304 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
    305   VLOG(2) << "Hiding fake Heart Rate characteristics.";
    306 
    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_));
    313 
    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_));
    319 
    320   heart_rate_measurement_properties_.reset();
    321   body_sensor_location_properties_.reset();
    322   heart_rate_control_point_properties_.reset();
    323 
    324   heart_rate_measurement_path_.clear();
    325   body_sensor_location_path_.clear();
    326   heart_rate_control_point_path_.clear();
    327   heart_rate_visible_ = false;
    328 }
    329 
    330 dbus::ObjectPath
    331 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
    332   return dbus::ObjectPath(heart_rate_measurement_path_);
    333 }
    334 
    335 dbus::ObjectPath
    336 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
    337   return dbus::ObjectPath(body_sensor_location_path_);
    338 }
    339 
    340 dbus::ObjectPath
    341 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
    342   return dbus::ObjectPath(heart_rate_control_point_path_);
    343 }
    344 
    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;
    350 
    351   FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
    352                     GattCharacteristicPropertyChanged(
    353                         object_path, property_name));
    354 }
    355 
    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));
    361 }
    362 
    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));
    368 }
    369 
    370 void FakeBluetoothGattCharacteristicClient::
    371     ScheduleHeartRateMeasurementValueChange() {
    372   if (!IsHeartRateVisible())
    373     return;
    374 
    375   // Don't send updates if the characteristic is not notifying.
    376   if (!heart_rate_measurement_properties_->notifying.value())
    377     return;
    378 
    379   VLOG(2) << "Updating heart rate value.";
    380   std::vector<uint8> measurement = GetHeartRateMeasurementValue();
    381 
    382   FOR_EACH_OBSERVER(
    383       BluetoothGattCharacteristicClient::Observer,
    384       observers_,
    385       GattCharacteristicValueUpdated(
    386           dbus::ObjectPath(heart_rate_measurement_path_), measurement));
    387 
    388   base::MessageLoop::current()->PostDelayedTask(
    389       FROM_HERE,
    390       base::Bind(&FakeBluetoothGattCharacteristicClient::
    391                      ScheduleHeartRateMeasurementValueChange,
    392                  weak_ptr_factory_.GetWeakPtr()),
    393                  base::TimeDelta::FromMilliseconds(
    394                      kHeartRateMeasurementNotificationIntervalMs));
    395 }
    396 
    397 std::vector<uint8>
    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.
    402   struct {
    403     uint8 flags;
    404     uint8 bpm;
    405     uint16 energy_expanded;
    406     uint16 rr_interval;
    407   } value;
    408 
    409   // Flags in LSB:     0       11   1 1 000
    410   //                   |       |    | | |
    411   // 8-bit bpm format --       |    | | |
    412   // Sensor contact supported --    | | |
    413   // Energy expanded field present -- | |
    414   // RR-Interval values present ------- |
    415   // Reserved for future use ------------
    416   value.flags = 0x0;
    417   value.flags |= (0x03 << 1);
    418   value.flags |= (0x01 << 3);
    419   value.flags |= (0x01 << 4);
    420 
    421   // Pick a value between 117 bpm and 153 bpm for heart rate.
    422   value.bpm = static_cast<uint8>(base::RandInt(117, 153));
    423 
    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_++;
    428 
    429   // Include one RR-Interval value, in seconds.
    430   value.rr_interval = 60/value.bpm;
    431 
    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));
    436   return return_value;
    437 }
    438 
    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_;
    447 }
    448 
    449 }  // namespace chromeos
    450