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