| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h" | 5 #include "device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h" |
| 6 | 6 |
| 7 #include <iterator> | 7 #include <iterator> |
| 8 #include <limits> | 8 #include <limits> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 } | 36 } |
| 37 return out << "]"; | 37 return out << "]"; |
| 38 } | 38 } |
| 39 | 39 |
| 40 } // namespace | 40 } // namespace |
| 41 | 41 |
| 42 BluetoothRemoteGattCharacteristicBlueZ::BluetoothRemoteGattCharacteristicBlueZ( | 42 BluetoothRemoteGattCharacteristicBlueZ::BluetoothRemoteGattCharacteristicBlueZ( |
| 43 BluetoothRemoteGattServiceBlueZ* service, | 43 BluetoothRemoteGattServiceBlueZ* service, |
| 44 const dbus::ObjectPath& object_path) | 44 const dbus::ObjectPath& object_path) |
| 45 : BluetoothGattCharacteristicBlueZ(object_path), | 45 : BluetoothGattCharacteristicBlueZ(object_path), |
| 46 num_notify_sessions_(0), | 46 has_notify_session_(false), |
| 47 notify_call_pending_(false), | |
| 48 service_(service), | 47 service_(service), |
| 49 weak_ptr_factory_(this) { | 48 weak_ptr_factory_(this) { |
| 50 VLOG(1) << "Creating remote GATT characteristic with identifier: " | 49 VLOG(1) << "Creating remote GATT characteristic with identifier: " |
| 51 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value(); | 50 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value(); |
| 52 bluez::BluezDBusManager::Get() | 51 bluez::BluezDBusManager::Get() |
| 53 ->GetBluetoothGattDescriptorClient() | 52 ->GetBluetoothGattDescriptorClient() |
| 54 ->AddObserver(this); | 53 ->AddObserver(this); |
| 55 | 54 |
| 56 // Add all known GATT characteristic descriptors. | 55 // Add all known GATT characteristic descriptors. |
| 57 const std::vector<dbus::ObjectPath>& gatt_descs = | 56 const std::vector<dbus::ObjectPath>& gatt_descs = |
| 58 bluez::BluezDBusManager::Get() | 57 bluez::BluezDBusManager::Get() |
| 59 ->GetBluetoothGattDescriptorClient() | 58 ->GetBluetoothGattDescriptorClient() |
| 60 ->GetDescriptors(); | 59 ->GetDescriptors(); |
| 61 for (std::vector<dbus::ObjectPath>::const_iterator iter = gatt_descs.begin(); | 60 for (std::vector<dbus::ObjectPath>::const_iterator iter = gatt_descs.begin(); |
| 62 iter != gatt_descs.end(); ++iter) | 61 iter != gatt_descs.end(); ++iter) |
| 63 GattDescriptorAdded(*iter); | 62 GattDescriptorAdded(*iter); |
| 64 } | 63 } |
| 65 | 64 |
| 66 BluetoothRemoteGattCharacteristicBlueZ:: | 65 BluetoothRemoteGattCharacteristicBlueZ:: |
| 67 ~BluetoothRemoteGattCharacteristicBlueZ() { | 66 ~BluetoothRemoteGattCharacteristicBlueZ() { |
| 68 bluez::BluezDBusManager::Get() | 67 bluez::BluezDBusManager::Get() |
| 69 ->GetBluetoothGattDescriptorClient() | 68 ->GetBluetoothGattDescriptorClient() |
| 70 ->RemoveObserver(this); | 69 ->RemoveObserver(this); |
| 71 | 70 |
| 72 // Clean up all the descriptors. There isn't much point in notifying service | 71 // Clean up all the descriptors. There isn't much point in notifying service |
| 73 // observers for each descriptor that gets removed, so just delete them. | 72 // observers for each descriptor that gets removed, so just delete them. |
| 74 for (DescriptorMap::iterator iter = descriptors_.begin(); | 73 for (DescriptorMap::iterator iter = descriptors_.begin(); |
| 75 iter != descriptors_.end(); ++iter) | 74 iter != descriptors_.end(); ++iter) |
| 76 delete iter->second; | 75 delete iter->second; |
| 77 | |
| 78 // Report an error for all pending calls to StartNotifySession. | |
| 79 while (!pending_start_notify_calls_.empty()) { | |
| 80 PendingStartNotifyCall callbacks = pending_start_notify_calls_.front(); | |
| 81 pending_start_notify_calls_.pop(); | |
| 82 callbacks.second.Run(device::BluetoothRemoteGattService::GATT_ERROR_FAILED); | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 void BluetoothRemoteGattCharacteristicBlueZ::StopNotifySession( | |
| 87 device::BluetoothGattNotifySession* session, | |
| 88 const base::Closure& callback) { | |
| 89 VLOG(1) << __func__; | |
| 90 | |
| 91 if (num_notify_sessions_ > 1) { | |
| 92 DCHECK(!notify_call_pending_); | |
| 93 --num_notify_sessions_; | |
| 94 callback.Run(); | |
| 95 return; | |
| 96 } | |
| 97 | |
| 98 // Notifications may have stopped outside our control. If the characteristic | |
| 99 // is no longer notifying, return success. | |
| 100 if (!IsNotifying()) { | |
| 101 num_notify_sessions_ = 0; | |
| 102 callback.Run(); | |
| 103 return; | |
| 104 } | |
| 105 | |
| 106 if (notify_call_pending_ || num_notify_sessions_ == 0) { | |
| 107 callback.Run(); | |
| 108 return; | |
| 109 } | |
| 110 | |
| 111 DCHECK(num_notify_sessions_ == 1); | |
| 112 notify_call_pending_ = true; | |
| 113 bluez::BluezDBusManager::Get() | |
| 114 ->GetBluetoothGattCharacteristicClient() | |
| 115 ->StopNotify( | |
| 116 object_path(), | |
| 117 base::Bind( | |
| 118 &BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifySuccess, | |
| 119 weak_ptr_factory_.GetWeakPtr(), callback), | |
| 120 base::Bind(&BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifyError, | |
| 121 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 122 } | 76 } |
| 123 | 77 |
| 124 device::BluetoothUUID BluetoothRemoteGattCharacteristicBlueZ::GetUUID() const { | 78 device::BluetoothUUID BluetoothRemoteGattCharacteristicBlueZ::GetUUID() const { |
| 125 bluez::BluetoothGattCharacteristicClient::Properties* properties = | 79 bluez::BluetoothGattCharacteristicClient::Properties* properties = |
| 126 bluez::BluezDBusManager::Get() | 80 bluez::BluezDBusManager::Get() |
| 127 ->GetBluetoothGattCharacteristicClient() | 81 ->GetBluetoothGattCharacteristicClient() |
| 128 ->GetProperties(object_path()); | 82 ->GetProperties(object_path()); |
| 129 DCHECK(properties); | 83 DCHECK(properties); |
| 130 return device::BluetoothUUID(properties->uuid.value()); | 84 return device::BluetoothUUID(properties->uuid.value()); |
| 131 } | 85 } |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 return service_; | 145 return service_; |
| 192 } | 146 } |
| 193 | 147 |
| 194 bool BluetoothRemoteGattCharacteristicBlueZ::IsNotifying() const { | 148 bool BluetoothRemoteGattCharacteristicBlueZ::IsNotifying() const { |
| 195 bluez::BluetoothGattCharacteristicClient::Properties* properties = | 149 bluez::BluetoothGattCharacteristicClient::Properties* properties = |
| 196 bluez::BluezDBusManager::Get() | 150 bluez::BluezDBusManager::Get() |
| 197 ->GetBluetoothGattCharacteristicClient() | 151 ->GetBluetoothGattCharacteristicClient() |
| 198 ->GetProperties(object_path()); | 152 ->GetProperties(object_path()); |
| 199 DCHECK(properties); | 153 DCHECK(properties); |
| 200 | 154 |
| 201 return properties->notifying.value(); | 155 // It is not enough to only check notifying.value(). Bluez also |
| 156 // needs a notify client/session in order to deliver the |
| 157 // notifications. |
| 158 return has_notify_session_ && properties->notifying.value(); |
| 202 } | 159 } |
| 203 | 160 |
| 204 std::vector<device::BluetoothRemoteGattDescriptor*> | 161 std::vector<device::BluetoothRemoteGattDescriptor*> |
| 205 BluetoothRemoteGattCharacteristicBlueZ::GetDescriptors() const { | 162 BluetoothRemoteGattCharacteristicBlueZ::GetDescriptors() const { |
| 206 std::vector<device::BluetoothRemoteGattDescriptor*> descriptors; | 163 std::vector<device::BluetoothRemoteGattDescriptor*> descriptors; |
| 207 for (DescriptorMap::const_iterator iter = descriptors_.begin(); | 164 for (DescriptorMap::const_iterator iter = descriptors_.begin(); |
| 208 iter != descriptors_.end(); ++iter) | 165 iter != descriptors_.end(); ++iter) |
| 209 descriptors.push_back(iter->second); | 166 descriptors.push_back(iter->second); |
| 210 return descriptors; | 167 return descriptors; |
| 211 } | 168 } |
| 212 | 169 |
| 213 device::BluetoothRemoteGattDescriptor* | 170 device::BluetoothRemoteGattDescriptor* |
| 214 BluetoothRemoteGattCharacteristicBlueZ::GetDescriptor( | 171 BluetoothRemoteGattCharacteristicBlueZ::GetDescriptor( |
| 215 const std::string& identifier) const { | 172 const std::string& identifier) const { |
| 216 DescriptorMap::const_iterator iter = | 173 DescriptorMap::const_iterator iter = |
| 217 descriptors_.find(dbus::ObjectPath(identifier)); | 174 descriptors_.find(dbus::ObjectPath(identifier)); |
| 218 if (iter == descriptors_.end()) | 175 if (iter == descriptors_.end()) |
| 219 return nullptr; | 176 return nullptr; |
| 220 return iter->second; | 177 return iter->second; |
| 221 } | 178 } |
| 222 | 179 |
| 223 void BluetoothRemoteGattCharacteristicBlueZ::StartNotifySession( | |
| 224 const NotifySessionCallback& callback, | |
| 225 const ErrorCallback& error_callback) { | |
| 226 VLOG(1) << __func__; | |
| 227 | |
| 228 if (num_notify_sessions_ > 0) { | |
| 229 // The characteristic might have stopped notifying even though the session | |
| 230 // count is nonzero. This means that notifications stopped outside of our | |
| 231 // control and we should reset the count. If the characteristic is still | |
| 232 // notifying, then return success. Otherwise, reset the count and treat | |
| 233 // this call as if the count were 0. | |
| 234 if (IsNotifying()) { | |
| 235 // Check for overflows, though unlikely. | |
| 236 if (num_notify_sessions_ == std::numeric_limits<size_t>::max()) { | |
| 237 error_callback.Run( | |
| 238 device::BluetoothRemoteGattService::GATT_ERROR_FAILED); | |
| 239 return; | |
| 240 } | |
| 241 | |
| 242 ++num_notify_sessions_; | |
| 243 DCHECK(service_); | |
| 244 DCHECK(service_->GetAdapter()); | |
| 245 DCHECK(service_->GetDevice()); | |
| 246 std::unique_ptr<device::BluetoothGattNotifySession> session( | |
| 247 new device::BluetoothGattNotifySession( | |
| 248 weak_ptr_factory_.GetWeakPtr())); | |
| 249 callback.Run(std::move(session)); | |
| 250 return; | |
| 251 } | |
| 252 | |
| 253 num_notify_sessions_ = 0; | |
| 254 } | |
| 255 | |
| 256 // Queue the callbacks if there is a pending call to bluetoothd. | |
| 257 if (notify_call_pending_) { | |
| 258 pending_start_notify_calls_.push(std::make_pair(callback, error_callback)); | |
| 259 return; | |
| 260 } | |
| 261 | |
| 262 notify_call_pending_ = true; | |
| 263 bluez::BluezDBusManager::Get() | |
| 264 ->GetBluetoothGattCharacteristicClient() | |
| 265 ->StartNotify( | |
| 266 object_path(), | |
| 267 base::Bind( | |
| 268 &BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifySuccess, | |
| 269 weak_ptr_factory_.GetWeakPtr(), callback), | |
| 270 base::Bind( | |
| 271 &BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifyError, | |
| 272 weak_ptr_factory_.GetWeakPtr(), error_callback)); | |
| 273 } | |
| 274 | |
| 275 void BluetoothRemoteGattCharacteristicBlueZ::ReadRemoteCharacteristic( | 180 void BluetoothRemoteGattCharacteristicBlueZ::ReadRemoteCharacteristic( |
| 276 const ValueCallback& callback, | 181 const ValueCallback& callback, |
| 277 const ErrorCallback& error_callback) { | 182 const ErrorCallback& error_callback) { |
| 278 VLOG(1) << "Sending GATT characteristic read request to characteristic: " | 183 VLOG(1) << "Sending GATT characteristic read request to characteristic: " |
| 279 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value() | 184 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value() |
| 280 << "."; | 185 << "."; |
| 281 | 186 |
| 282 bluez::BluezDBusManager::Get() | 187 bluez::BluezDBusManager::Get() |
| 283 ->GetBluetoothGattCharacteristicClient() | 188 ->GetBluetoothGattCharacteristicClient() |
| 284 ->ReadValue(object_path(), callback, | 189 ->ReadValue(object_path(), callback, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 298 ->GetBluetoothGattCharacteristicClient() | 203 ->GetBluetoothGattCharacteristicClient() |
| 299 ->WriteValue(object_path(), value, callback, | 204 ->WriteValue(object_path(), value, callback, |
| 300 base::Bind(&BluetoothRemoteGattCharacteristicBlueZ::OnError, | 205 base::Bind(&BluetoothRemoteGattCharacteristicBlueZ::OnError, |
| 301 weak_ptr_factory_.GetWeakPtr(), error_callback)); | 206 weak_ptr_factory_.GetWeakPtr(), error_callback)); |
| 302 } | 207 } |
| 303 | 208 |
| 304 void BluetoothRemoteGattCharacteristicBlueZ::SubscribeToNotifications( | 209 void BluetoothRemoteGattCharacteristicBlueZ::SubscribeToNotifications( |
| 305 device::BluetoothRemoteGattDescriptor* ccc_descriptor, | 210 device::BluetoothRemoteGattDescriptor* ccc_descriptor, |
| 306 const base::Closure& callback, | 211 const base::Closure& callback, |
| 307 const ErrorCallback& error_callback) { | 212 const ErrorCallback& error_callback) { |
| 308 // TODO(http://crbug.com/636275): Implement this method | 213 bluez::BluezDBusManager::Get() |
| 309 NOTIMPLEMENTED(); | 214 ->GetBluetoothGattCharacteristicClient() |
| 215 ->StartNotify( |
| 216 object_path(), |
| 217 base::Bind( |
| 218 &BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifySuccess, |
| 219 weak_ptr_factory_.GetWeakPtr(), callback), |
| 220 base::Bind( |
| 221 &BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifyError, |
| 222 weak_ptr_factory_.GetWeakPtr(), error_callback)); |
| 310 } | 223 } |
| 311 | 224 |
| 312 void BluetoothRemoteGattCharacteristicBlueZ::UnsubscribeFromNotifications( | 225 void BluetoothRemoteGattCharacteristicBlueZ::UnsubscribeFromNotifications( |
| 313 device::BluetoothRemoteGattDescriptor* ccc_descriptor, | 226 device::BluetoothRemoteGattDescriptor* ccc_descriptor, |
| 314 const base::Closure& callback, | 227 const base::Closure& callback, |
| 315 const ErrorCallback& error_callback) { | 228 const ErrorCallback& error_callback) { |
| 316 // TODO(http://crbug.com/636275): Implement this method | 229 bluez::BluezDBusManager::Get() |
| 317 NOTIMPLEMENTED(); | 230 ->GetBluetoothGattCharacteristicClient() |
| 231 ->StopNotify( |
| 232 object_path(), |
| 233 base::Bind( |
| 234 &BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifySuccess, |
| 235 weak_ptr_factory_.GetWeakPtr(), callback), |
| 236 base::Bind(&BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifyError, |
| 237 weak_ptr_factory_.GetWeakPtr(), callback)); |
| 318 } | 238 } |
| 319 | 239 |
| 320 void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorAdded( | 240 void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorAdded( |
| 321 const dbus::ObjectPath& object_path) { | 241 const dbus::ObjectPath& object_path) { |
| 322 if (descriptors_.find(object_path) != descriptors_.end()) { | 242 if (descriptors_.find(object_path) != descriptors_.end()) { |
| 323 VLOG(1) << "Remote GATT characteristic descriptor already exists: " | 243 VLOG(1) << "Remote GATT characteristic descriptor already exists: " |
| 324 << object_path.value(); | 244 << object_path.value(); |
| 325 return; | 245 return; |
| 326 } | 246 } |
| 327 | 247 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 if (property_name != properties->value.name()) | 310 if (property_name != properties->value.name()) |
| 391 return; | 311 return; |
| 392 | 312 |
| 393 DCHECK(service_); | 313 DCHECK(service_); |
| 394 static_cast<BluetoothRemoteGattServiceBlueZ*>(service_) | 314 static_cast<BluetoothRemoteGattServiceBlueZ*>(service_) |
| 395 ->NotifyDescriptorValueChanged(this, iter->second, | 315 ->NotifyDescriptorValueChanged(this, iter->second, |
| 396 properties->value.value()); | 316 properties->value.value()); |
| 397 } | 317 } |
| 398 | 318 |
| 399 void BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifySuccess( | 319 void BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifySuccess( |
| 400 const NotifySessionCallback& callback) { | 320 const base::Closure& callback) { |
| 401 VLOG(1) << "Started notifications from characteristic: " | 321 VLOG(1) << "Started notifications from characteristic: " |
| 402 << object_path().value(); | 322 << object_path().value(); |
| 403 DCHECK(num_notify_sessions_ == 0); | 323 has_notify_session_ = true; |
| 404 DCHECK(notify_call_pending_); | 324 callback.Run(); |
| 405 | |
| 406 ++num_notify_sessions_; | |
| 407 notify_call_pending_ = false; | |
| 408 | |
| 409 // Invoke the queued callbacks for this operation. | |
| 410 DCHECK(service_); | |
| 411 DCHECK(service_->GetDevice()); | |
| 412 std::unique_ptr<device::BluetoothGattNotifySession> session( | |
| 413 new device::BluetoothGattNotifySession(weak_ptr_factory_.GetWeakPtr())); | |
| 414 callback.Run(std::move(session)); | |
| 415 | |
| 416 ProcessStartNotifyQueue(); | |
| 417 } | 325 } |
| 418 | 326 |
| 419 void BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifyError( | 327 void BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifyError( |
| 420 const ErrorCallback& error_callback, | 328 const ErrorCallback& error_callback, |
| 421 const std::string& error_name, | 329 const std::string& error_name, |
| 422 const std::string& error_message) { | 330 const std::string& error_message) { |
| 423 VLOG(1) << "Failed to start notifications from characteristic: " | 331 VLOG(1) << "Failed to start notifications from characteristic: " |
| 424 << object_path().value() << ": " << error_name << ", " | 332 << object_path().value() << ": " << error_name << ", " |
| 425 << error_message; | 333 << error_message; |
| 426 DCHECK(num_notify_sessions_ == 0); | |
| 427 DCHECK(notify_call_pending_); | |
| 428 | |
| 429 notify_call_pending_ = false; | |
| 430 | |
| 431 error_callback.Run( | 334 error_callback.Run( |
| 432 BluetoothRemoteGattServiceBlueZ::DBusErrorToServiceError(error_name)); | 335 BluetoothRemoteGattServiceBlueZ::DBusErrorToServiceError(error_name)); |
| 433 | |
| 434 ProcessStartNotifyQueue(); | |
| 435 } | 336 } |
| 436 | 337 |
| 437 void BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifySuccess( | 338 void BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifySuccess( |
| 438 const base::Closure& callback) { | 339 const base::Closure& callback) { |
| 439 DCHECK(notify_call_pending_); | 340 has_notify_session_ = false; |
| 440 DCHECK(num_notify_sessions_ == 1); | |
| 441 | |
| 442 notify_call_pending_ = false; | |
| 443 --num_notify_sessions_; | |
| 444 callback.Run(); | 341 callback.Run(); |
| 445 | |
| 446 ProcessStartNotifyQueue(); | |
| 447 } | 342 } |
| 448 | 343 |
| 449 void BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifyError( | 344 void BluetoothRemoteGattCharacteristicBlueZ::OnStopNotifyError( |
| 450 const base::Closure& callback, | 345 const base::Closure& callback, |
| 451 const std::string& error_name, | 346 const std::string& error_name, |
| 452 const std::string& error_message) { | 347 const std::string& error_message) { |
| 453 VLOG(1) << "Call to stop notifications failed for characteristic: " | 348 VLOG(1) << "Call to stop notifications failed for characteristic: " |
| 454 << object_path().value() << ": " << error_name << ", " | 349 << object_path().value() << ": " << error_name << ", " |
| 455 << error_message; | 350 << error_message; |
| 456 | 351 |
| 457 // Since this is a best effort operation, treat this as success. | 352 // Since this is a best effort operation, treat this as success. |
| 458 OnStopNotifySuccess(callback); | 353 OnStopNotifySuccess(callback); |
| 459 } | 354 } |
| 460 | 355 |
| 461 void BluetoothRemoteGattCharacteristicBlueZ::ProcessStartNotifyQueue() { | |
| 462 while (!pending_start_notify_calls_.empty()) { | |
| 463 PendingStartNotifyCall callbacks = pending_start_notify_calls_.front(); | |
| 464 pending_start_notify_calls_.pop(); | |
| 465 StartNotifySession(callbacks.first, callbacks.second); | |
| 466 } | |
| 467 } | |
| 468 | |
| 469 void BluetoothRemoteGattCharacteristicBlueZ::OnError( | 356 void BluetoothRemoteGattCharacteristicBlueZ::OnError( |
| 470 const ErrorCallback& error_callback, | 357 const ErrorCallback& error_callback, |
| 471 const std::string& error_name, | 358 const std::string& error_name, |
| 472 const std::string& error_message) { | 359 const std::string& error_message) { |
| 473 VLOG(1) << "Operation failed: " << error_name | 360 VLOG(1) << "Operation failed: " << error_name |
| 474 << ", message: " << error_message; | 361 << ", message: " << error_message; |
| 475 error_callback.Run( | 362 error_callback.Run( |
| 476 BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name)); | 363 BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name)); |
| 477 } | 364 } |
| 478 | 365 |
| 479 } // namespace bluez | 366 } // namespace bluez |
| OLD | NEW |