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