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 |