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>.
operator <<(std::ostream & out,const std::vector<uint8> bytes)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::
BluetoothRemoteGattCharacteristicChromeOS(BluetoothRemoteGattServiceChromeOS * service,const dbus::ObjectPath & object_path)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::
~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
GetIdentifier() const81 std::string BluetoothRemoteGattCharacteristicChromeOS::GetIdentifier() const {
82 return object_path_.value();
83 }
84
85 device::BluetoothUUID
GetUUID() const86 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
IsLocal() const94 bool BluetoothRemoteGattCharacteristicChromeOS::IsLocal() const {
95 return false;
96 }
97
98 const std::vector<uint8>&
GetValue() const99 BluetoothRemoteGattCharacteristicChromeOS::GetValue() const {
100 return cached_value_;
101 }
102
103 device::BluetoothGattService*
GetService() const104 BluetoothRemoteGattCharacteristicChromeOS::GetService() const {
105 return service_;
106 }
107
108 device::BluetoothGattCharacteristic::Properties
GetProperties() const109 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
GetPermissions() const146 BluetoothRemoteGattCharacteristicChromeOS::GetPermissions() const {
147 // TODO(armansito): Once BlueZ defines the permissions, return the correct
148 // values here.
149 return kPermissionNone;
150 }
151
IsNotifying() const152 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*>
GetDescriptors() const163 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*
GetDescriptor(const std::string & identifier) const172 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
AddDescriptor(device::BluetoothGattDescriptor * descriptor)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
UpdateValue(const std::vector<uint8> & value)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
ReadRemoteCharacteristic(const ValueCallback & callback,const ErrorCallback & error_callback)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
WriteRemoteCharacteristic(const std::vector<uint8> & new_value,const base::Closure & callback,const ErrorCallback & error_callback)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
StartNotifySession(const NotifySessionCallback & callback,const ErrorCallback & error_callback)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
RemoveNotifySession(const base::Closure & callback)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
GattCharacteristicValueUpdated(const dbus::ObjectPath & object_path,const std::vector<uint8> & value)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
GattDescriptorAdded(const dbus::ObjectPath & object_path)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
GattDescriptorRemoved(const dbus::ObjectPath & object_path)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
OnValueSuccess(const ValueCallback & callback,const std::vector<uint8> & value)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
OnError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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
OnStartNotifySuccess(const NotifySessionCallback & callback)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
OnStartNotifyError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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
OnStopNotifySuccess(const base::Closure & callback)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
OnStopNotifyError(const base::Closure & callback,const std::string & error_name,const std::string & error_message)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
ProcessStartNotifyQueue()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