Home | History | Annotate | Download | only in bluetooth
      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 "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
      6 
      7 #include <limits>
      8 
      9 #include "base/logging.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "chromeos/dbus/dbus_thread_manager.h"
     12 #include "device/bluetooth/bluetooth_adapter.h"
     13 #include "device/bluetooth/bluetooth_device.h"
     14 #include "device/bluetooth/bluetooth_gatt_notify_session_chromeos.h"
     15 #include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
     16 #include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
     17 #include "third_party/cros_system_api/dbus/service_constants.h"
     18 
     19 namespace chromeos {
     20 
     21 namespace {
     22 
     23 // Stream operator for logging vector<uint8>.
     24 std::ostream& operator<<(std::ostream& out, const std::vector<uint8> bytes) {
     25   out << "[";
     26   for (std::vector<uint8>::const_iterator iter = bytes.begin();
     27        iter != bytes.end(); ++iter) {
     28     out << base::StringPrintf("%02X", *iter);
     29   }
     30   return out << "]";
     31 }
     32 
     33 }  // namespace
     34 
     35 BluetoothRemoteGattCharacteristicChromeOS::
     36     BluetoothRemoteGattCharacteristicChromeOS(
     37         BluetoothRemoteGattServiceChromeOS* service,
     38         const dbus::ObjectPath& object_path)
     39     : object_path_(object_path),
     40       service_(service),
     41       num_notify_sessions_(0),
     42       notify_call_pending_(false),
     43       weak_ptr_factory_(this) {
     44   VLOG(1) << "Creating remote GATT characteristic with identifier: "
     45           << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
     46   DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
     47       AddObserver(this);
     48   DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
     49       AddObserver(this);
     50 
     51   // Add all known GATT characteristic descriptors.
     52   const std::vector<dbus::ObjectPath>& gatt_descs =
     53       DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
     54           GetDescriptors();
     55   for (std::vector<dbus::ObjectPath>::const_iterator iter = gatt_descs.begin();
     56        iter != gatt_descs.end(); ++iter)
     57     GattDescriptorAdded(*iter);
     58 }
     59 
     60 BluetoothRemoteGattCharacteristicChromeOS::
     61     ~BluetoothRemoteGattCharacteristicChromeOS() {
     62   DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
     63       RemoveObserver(this);
     64   DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
     65       RemoveObserver(this);
     66 
     67   // Clean up all the descriptors. There isn't much point in notifying service
     68   // observers for each descriptor that gets removed, so just delete them.
     69   for (DescriptorMap::iterator iter = descriptors_.begin();
     70        iter != descriptors_.end(); ++iter)
     71     delete iter->second;
     72 
     73   // Report an error for all pending calls to StartNotifySession.
     74   while (!pending_start_notify_calls_.empty()) {
     75     PendingStartNotifyCall callbacks = pending_start_notify_calls_.front();
     76     pending_start_notify_calls_.pop();
     77     callbacks.second.Run();
     78   }
     79 }
     80 
     81 std::string BluetoothRemoteGattCharacteristicChromeOS::GetIdentifier() const {
     82   return object_path_.value();
     83 }
     84 
     85 device::BluetoothUUID
     86 BluetoothRemoteGattCharacteristicChromeOS::GetUUID() const {
     87   BluetoothGattCharacteristicClient::Properties* properties =
     88       DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
     89           GetProperties(object_path_);
     90   DCHECK(properties);
     91   return device::BluetoothUUID(properties->uuid.value());
     92 }
     93 
     94 bool BluetoothRemoteGattCharacteristicChromeOS::IsLocal() const {
     95   return false;
     96 }
     97 
     98 const std::vector<uint8>&
     99 BluetoothRemoteGattCharacteristicChromeOS::GetValue() const {
    100   return cached_value_;
    101 }
    102 
    103 device::BluetoothGattService*
    104 BluetoothRemoteGattCharacteristicChromeOS::GetService() const {
    105   return service_;
    106 }
    107 
    108 device::BluetoothGattCharacteristic::Properties
    109 BluetoothRemoteGattCharacteristicChromeOS::GetProperties() const {
    110   BluetoothGattCharacteristicClient::Properties* properties =
    111       DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
    112           GetProperties(object_path_);
    113   DCHECK(properties);
    114 
    115   Properties props = kPropertyNone;
    116   const std::vector<std::string>& flags = properties->flags.value();
    117   for (std::vector<std::string>::const_iterator iter = flags.begin();
    118        iter != flags.end();
    119        ++iter) {
    120     if (*iter == bluetooth_gatt_characteristic::kFlagBroadcast)
    121       props |= kPropertyBroadcast;
    122     if (*iter == bluetooth_gatt_characteristic::kFlagRead)
    123       props |= kPropertyRead;
    124     if (*iter == bluetooth_gatt_characteristic::kFlagWriteWithoutResponse)
    125       props |= kPropertyWriteWithoutResponse;
    126     if (*iter == bluetooth_gatt_characteristic::kFlagWrite)
    127       props |= kPropertyWrite;
    128     if (*iter == bluetooth_gatt_characteristic::kFlagNotify)
    129       props |= kPropertyNotify;
    130     if (*iter == bluetooth_gatt_characteristic::kFlagIndicate)
    131       props |= kPropertyIndicate;
    132     if (*iter == bluetooth_gatt_characteristic::kFlagAuthenticatedSignedWrites)
    133       props |= kPropertyAuthenticatedSignedWrites;
    134     if (*iter == bluetooth_gatt_characteristic::kFlagExtendedProperties)
    135       props |= kPropertyExtendedProperties;
    136     if (*iter == bluetooth_gatt_characteristic::kFlagReliableWrite)
    137       props |= kPropertyReliableWrite;
    138     if (*iter == bluetooth_gatt_characteristic::kFlagWritableAuxiliaries)
    139       props |= kPropertyWritableAuxiliaries;
    140   }
    141 
    142   return props;
    143 }
    144 
    145 device::BluetoothGattCharacteristic::Permissions
    146 BluetoothRemoteGattCharacteristicChromeOS::GetPermissions() const {
    147   // TODO(armansito): Once BlueZ defines the permissions, return the correct
    148   // values here.
    149   return kPermissionNone;
    150 }
    151 
    152 bool BluetoothRemoteGattCharacteristicChromeOS::IsNotifying() const {
    153   BluetoothGattCharacteristicClient::Properties* properties =
    154       DBusThreadManager::Get()
    155           ->GetBluetoothGattCharacteristicClient()
    156           ->GetProperties(object_path_);
    157   DCHECK(properties);
    158 
    159   return properties->notifying.value();
    160 }
    161 
    162 std::vector<device::BluetoothGattDescriptor*>
    163 BluetoothRemoteGattCharacteristicChromeOS::GetDescriptors() const {
    164   std::vector<device::BluetoothGattDescriptor*> descriptors;
    165   for (DescriptorMap::const_iterator iter = descriptors_.begin();
    166        iter != descriptors_.end(); ++iter)
    167     descriptors.push_back(iter->second);
    168   return descriptors;
    169 }
    170 
    171 device::BluetoothGattDescriptor*
    172 BluetoothRemoteGattCharacteristicChromeOS::GetDescriptor(
    173     const std::string& identifier) const {
    174   DescriptorMap::const_iterator iter =
    175       descriptors_.find(dbus::ObjectPath(identifier));
    176   if (iter == descriptors_.end())
    177     return NULL;
    178   return iter->second;
    179 }
    180 
    181 bool BluetoothRemoteGattCharacteristicChromeOS::AddDescriptor(
    182     device::BluetoothGattDescriptor* descriptor) {
    183   VLOG(1) << "Descriptors cannot be added to a remote GATT characteristic.";
    184   return false;
    185 }
    186 
    187 bool BluetoothRemoteGattCharacteristicChromeOS::UpdateValue(
    188     const std::vector<uint8>& value) {
    189   VLOG(1) << "Cannot update the value of a remote GATT characteristic.";
    190   return false;
    191 }
    192 
    193 void BluetoothRemoteGattCharacteristicChromeOS::ReadRemoteCharacteristic(
    194     const ValueCallback& callback,
    195     const ErrorCallback& error_callback) {
    196   VLOG(1) << "Sending GATT characteristic read request to characteristic: "
    197           << GetIdentifier() << ", UUID: " << GetUUID().canonical_value()
    198           << ".";
    199 
    200   DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->ReadValue(
    201       object_path_,
    202       base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnValueSuccess,
    203                  weak_ptr_factory_.GetWeakPtr(),
    204                  callback),
    205       base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError,
    206                  weak_ptr_factory_.GetWeakPtr(),
    207                  error_callback));
    208 }
    209 
    210 void BluetoothRemoteGattCharacteristicChromeOS::WriteRemoteCharacteristic(
    211     const std::vector<uint8>& new_value,
    212     const base::Closure& callback,
    213     const ErrorCallback& error_callback) {
    214   VLOG(1) << "Sending GATT characteristic write request to characteristic: "
    215           << GetIdentifier() << ", UUID: " << GetUUID().canonical_value()
    216           << ", with value: " << new_value << ".";
    217 
    218   DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->WriteValue(
    219       object_path_,
    220       new_value,
    221       callback,
    222       base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError,
    223                  weak_ptr_factory_.GetWeakPtr(),
    224                  error_callback));
    225 }
    226 
    227 void BluetoothRemoteGattCharacteristicChromeOS::StartNotifySession(
    228     const NotifySessionCallback& callback,
    229     const ErrorCallback& error_callback) {
    230   VLOG(1) << __func__;
    231 
    232   if (num_notify_sessions_ > 0) {
    233     // The characteristic might have stopped notifying even though the session
    234     // count is nonzero. This means that notifications stopped outside of our
    235     // control and we should reset the count. If the characteristic is still
    236     // notifying, then return success. Otherwise, reset the count and treat
    237     // this call as if the count were 0.
    238     if (IsNotifying()) {
    239       // Check for overflows, though unlikely.
    240       if (num_notify_sessions_ == std::numeric_limits<size_t>::max()) {
    241         error_callback.Run();
    242         return;
    243       }
    244 
    245       ++num_notify_sessions_;
    246       DCHECK(service_);
    247       DCHECK(service_->GetDevice());
    248       scoped_ptr<device::BluetoothGattNotifySession> session(
    249           new BluetoothGattNotifySessionChromeOS(
    250               service_->GetAdapter(),
    251               service_->GetDevice()->GetAddress(),
    252               service_->GetIdentifier(),
    253               GetIdentifier(),
    254               object_path_));
    255       callback.Run(session.Pass());
    256       return;
    257     }
    258 
    259     num_notify_sessions_ = 0;
    260   }
    261 
    262   // Queue the callbacks if there is a pending call to bluetoothd.
    263   if (notify_call_pending_) {
    264     pending_start_notify_calls_.push(std::make_pair(callback, error_callback));
    265     return;
    266   }
    267 
    268   notify_call_pending_ = true;
    269   DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StartNotify(
    270       object_path_,
    271       base::Bind(
    272           &BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess,
    273           weak_ptr_factory_.GetWeakPtr(),
    274           callback),
    275       base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError,
    276                  weak_ptr_factory_.GetWeakPtr(),
    277                  error_callback));
    278 }
    279 
    280 void BluetoothRemoteGattCharacteristicChromeOS::RemoveNotifySession(
    281     const base::Closure& callback) {
    282   VLOG(1) << __func__;
    283 
    284   if (num_notify_sessions_ > 1) {
    285     DCHECK(!notify_call_pending_);
    286     --num_notify_sessions_;
    287     callback.Run();
    288     return;
    289   }
    290 
    291   // Notifications may have stopped outside our control. If the characteristic
    292   // is no longer notifying, return success.
    293   if (!IsNotifying()) {
    294     num_notify_sessions_ = 0;
    295     callback.Run();
    296     return;
    297   }
    298 
    299   if (notify_call_pending_ || num_notify_sessions_ == 0) {
    300     callback.Run();
    301     return;
    302   }
    303 
    304   DCHECK(num_notify_sessions_ == 1);
    305   notify_call_pending_ = true;
    306   DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StopNotify(
    307       object_path_,
    308       base::Bind(
    309           &BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess,
    310           weak_ptr_factory_.GetWeakPtr(),
    311           callback),
    312       base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError,
    313                  weak_ptr_factory_.GetWeakPtr(),
    314                  callback));
    315 }
    316 
    317 void BluetoothRemoteGattCharacteristicChromeOS::GattCharacteristicValueUpdated(
    318     const dbus::ObjectPath& object_path,
    319     const std::vector<uint8>& value) {
    320   if (object_path != object_path_)
    321     return;
    322 
    323   cached_value_ = value;
    324 
    325   VLOG(1) << "GATT characteristic value has changed: " << object_path.value()
    326           << ": " << value;
    327   DCHECK(service_);
    328   service_->NotifyCharacteristicValueChanged(this, value);
    329 }
    330 
    331 void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorAdded(
    332     const dbus::ObjectPath& object_path) {
    333   if (descriptors_.find(object_path) != descriptors_.end()) {
    334     VLOG(1) << "Remote GATT characteristic descriptor already exists: "
    335             << object_path.value();
    336     return;
    337   }
    338 
    339   BluetoothGattDescriptorClient::Properties* properties =
    340       DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
    341           GetProperties(object_path);
    342   DCHECK(properties);
    343   if (properties->characteristic.value() != object_path_) {
    344     VLOG(2) << "Remote GATT descriptor does not belong to this characteristic.";
    345     return;
    346   }
    347 
    348   VLOG(1) << "Adding new remote GATT descriptor for GATT characteristic: "
    349           << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
    350 
    351   BluetoothRemoteGattDescriptorChromeOS* descriptor =
    352       new BluetoothRemoteGattDescriptorChromeOS(this, object_path);
    353   descriptors_[object_path] = descriptor;
    354   DCHECK(descriptor->GetIdentifier() == object_path.value());
    355   DCHECK(descriptor->GetUUID().IsValid());
    356   DCHECK(service_);
    357 
    358   service_->NotifyDescriptorAddedOrRemoved(this, descriptor, true /* added */);
    359   service_->NotifyServiceChanged();
    360 }
    361 
    362 void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorRemoved(
    363     const dbus::ObjectPath& object_path) {
    364   DescriptorMap::iterator iter = descriptors_.find(object_path);
    365   if (iter == descriptors_.end()) {
    366     VLOG(2) << "Unknown descriptor removed: " << object_path.value();
    367     return;
    368   }
    369 
    370   VLOG(1) << "Removing remote GATT descriptor from characteristic: "
    371           << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
    372 
    373   BluetoothRemoteGattDescriptorChromeOS* descriptor = iter->second;
    374   DCHECK(descriptor->object_path() == object_path);
    375   descriptors_.erase(iter);
    376 
    377   service_->NotifyDescriptorAddedOrRemoved(this, descriptor, false /* added */);
    378   delete descriptor;
    379 
    380   DCHECK(service_);
    381 
    382   service_->NotifyServiceChanged();
    383 }
    384 
    385 void BluetoothRemoteGattCharacteristicChromeOS::OnValueSuccess(
    386     const ValueCallback& callback,
    387     const std::vector<uint8>& value) {
    388   VLOG(1) << "Characteristic value read: " << value;
    389   cached_value_ = value;
    390 
    391   DCHECK(service_);
    392   service_->NotifyCharacteristicValueChanged(this, cached_value_);
    393 
    394   callback.Run(value);
    395 }
    396 
    397 void BluetoothRemoteGattCharacteristicChromeOS::OnError(
    398     const ErrorCallback& error_callback,
    399     const std::string& error_name,
    400     const std::string& error_message) {
    401   VLOG(1) << "Operation failed: " << error_name << ", message: "
    402           << error_message;
    403   error_callback.Run();
    404 }
    405 
    406 void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess(
    407     const NotifySessionCallback& callback) {
    408   VLOG(1) << "Started notifications from characteristic: "
    409           << object_path_.value();
    410   DCHECK(num_notify_sessions_ == 0);
    411   DCHECK(notify_call_pending_);
    412 
    413   ++num_notify_sessions_;
    414   notify_call_pending_ = false;
    415 
    416   // Invoke the queued callbacks for this operation.
    417   DCHECK(service_);
    418   DCHECK(service_->GetDevice());
    419   scoped_ptr<device::BluetoothGattNotifySession> session(
    420       new BluetoothGattNotifySessionChromeOS(
    421           service_->GetAdapter(),
    422           service_->GetDevice()->GetAddress(),
    423           service_->GetIdentifier(),
    424           GetIdentifier(),
    425           object_path_));
    426   callback.Run(session.Pass());
    427 
    428   ProcessStartNotifyQueue();
    429 }
    430 
    431 void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError(
    432     const ErrorCallback& error_callback,
    433     const std::string& error_name,
    434     const std::string& error_message) {
    435   VLOG(1) << "Failed to start notifications from characteristic: "
    436           << object_path_.value() << ": " << error_name << ", "
    437           << error_message;
    438   DCHECK(num_notify_sessions_ == 0);
    439   DCHECK(notify_call_pending_);
    440 
    441   notify_call_pending_ = false;
    442   error_callback.Run();
    443 
    444   ProcessStartNotifyQueue();
    445 }
    446 
    447 void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess(
    448     const base::Closure& callback) {
    449   DCHECK(notify_call_pending_);
    450   DCHECK(num_notify_sessions_ == 1);
    451 
    452   notify_call_pending_ = false;
    453   --num_notify_sessions_;
    454   callback.Run();
    455 
    456   ProcessStartNotifyQueue();
    457 }
    458 
    459 void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError(
    460     const base::Closure& callback,
    461     const std::string& error_name,
    462     const std::string& error_message) {
    463   VLOG(1) << "Call to stop notifications failed for characteristic: "
    464           << object_path_.value() << ": " << error_name << ", "
    465           << error_message;
    466 
    467   // Since this is a best effort operation, treat this as success.
    468   OnStopNotifySuccess(callback);
    469 }
    470 
    471 void BluetoothRemoteGattCharacteristicChromeOS::ProcessStartNotifyQueue() {
    472   while (!pending_start_notify_calls_.empty()) {
    473     PendingStartNotifyCall callbacks = pending_start_notify_calls_.front();
    474     pending_start_notify_calls_.pop();
    475     StartNotifySession(callbacks.first, callbacks.second);
    476   }
    477 }
    478 
    479 }  // namespace chromeos
    480