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_chromeos.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_->GetAdapter());
248 DCHECK(service_->GetDevice());
249 scoped_ptr<device::BluetoothGattNotifySession> session(
250 new BluetoothGattNotifySessionChromeOS(
251 service_->GetAdapter(),
252 service_->GetDevice()->GetAddress(),
253 service_->GetIdentifier(),
254 GetIdentifier(),
255 object_path_));
256 callback.Run(session.Pass());
257 return;
258 }
259
260 num_notify_sessions_ = 0;
261 }
262
263 // Queue the callbacks if there is a pending call to bluetoothd.
264 if (notify_call_pending_) {
265 pending_start_notify_calls_.push(std::make_pair(callback, error_callback));
266 return;
267 }
268
269 notify_call_pending_ = true;
270 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StartNotify(
271 object_path_,
272 base::Bind(
273 &BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess,
274 weak_ptr_factory_.GetWeakPtr(),
275 callback),
276 base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError,
277 weak_ptr_factory_.GetWeakPtr(),
278 error_callback));
279 }
280
RemoveNotifySession(const base::Closure & callback)281 void BluetoothRemoteGattCharacteristicChromeOS::RemoveNotifySession(
282 const base::Closure& callback) {
283 VLOG(1) << __func__;
284
285 if (num_notify_sessions_ > 1) {
286 DCHECK(!notify_call_pending_);
287 --num_notify_sessions_;
288 callback.Run();
289 return;
290 }
291
292 // Notifications may have stopped outside our control. If the characteristic
293 // is no longer notifying, return success.
294 if (!IsNotifying()) {
295 num_notify_sessions_ = 0;
296 callback.Run();
297 return;
298 }
299
300 if (notify_call_pending_ || num_notify_sessions_ == 0) {
301 callback.Run();
302 return;
303 }
304
305 DCHECK(num_notify_sessions_ == 1);
306 notify_call_pending_ = true;
307 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StopNotify(
308 object_path_,
309 base::Bind(
310 &BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess,
311 weak_ptr_factory_.GetWeakPtr(),
312 callback),
313 base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError,
314 weak_ptr_factory_.GetWeakPtr(),
315 callback));
316 }
317
GattCharacteristicValueUpdated(const dbus::ObjectPath & object_path,const std::vector<uint8> & value)318 void BluetoothRemoteGattCharacteristicChromeOS::GattCharacteristicValueUpdated(
319 const dbus::ObjectPath& object_path,
320 const std::vector<uint8>& value) {
321 if (object_path != object_path_)
322 return;
323
324 cached_value_ = value;
325
326 VLOG(1) << "GATT characteristic value has changed: " << object_path.value()
327 << ": " << value;
328 DCHECK(service_);
329 service_->NotifyCharacteristicValueChanged(this, value);
330 }
331
GattDescriptorAdded(const dbus::ObjectPath & object_path)332 void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorAdded(
333 const dbus::ObjectPath& object_path) {
334 if (descriptors_.find(object_path) != descriptors_.end()) {
335 VLOG(1) << "Remote GATT characteristic descriptor already exists: "
336 << object_path.value();
337 return;
338 }
339
340 BluetoothGattDescriptorClient::Properties* properties =
341 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
342 GetProperties(object_path);
343 DCHECK(properties);
344 if (properties->characteristic.value() != object_path_) {
345 VLOG(3) << "Remote GATT descriptor does not belong to this characteristic.";
346 return;
347 }
348
349 VLOG(1) << "Adding new remote GATT descriptor for GATT characteristic: "
350 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
351
352 BluetoothRemoteGattDescriptorChromeOS* descriptor =
353 new BluetoothRemoteGattDescriptorChromeOS(this, object_path);
354 descriptors_[object_path] = descriptor;
355 DCHECK(descriptor->GetIdentifier() == object_path.value());
356 DCHECK(descriptor->GetUUID().IsValid());
357 DCHECK(service_);
358
359 service_->NotifyDescriptorAddedOrRemoved(this, descriptor, true /* added */);
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 DCHECK(service_);
378 service_->NotifyDescriptorAddedOrRemoved(this, descriptor, false /* added */);
379
380 delete descriptor;
381 }
382
OnValueSuccess(const ValueCallback & callback,const std::vector<uint8> & value)383 void BluetoothRemoteGattCharacteristicChromeOS::OnValueSuccess(
384 const ValueCallback& callback,
385 const std::vector<uint8>& value) {
386 VLOG(1) << "Characteristic value read: " << value;
387 cached_value_ = value;
388
389 DCHECK(service_);
390 service_->NotifyCharacteristicValueChanged(this, cached_value_);
391
392 callback.Run(value);
393 }
394
OnError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)395 void BluetoothRemoteGattCharacteristicChromeOS::OnError(
396 const ErrorCallback& error_callback,
397 const std::string& error_name,
398 const std::string& error_message) {
399 VLOG(1) << "Operation failed: " << error_name << ", message: "
400 << error_message;
401 error_callback.Run();
402 }
403
OnStartNotifySuccess(const NotifySessionCallback & callback)404 void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess(
405 const NotifySessionCallback& callback) {
406 VLOG(1) << "Started notifications from characteristic: "
407 << object_path_.value();
408 DCHECK(num_notify_sessions_ == 0);
409 DCHECK(notify_call_pending_);
410
411 ++num_notify_sessions_;
412 notify_call_pending_ = false;
413
414 // Invoke the queued callbacks for this operation.
415 DCHECK(service_);
416 DCHECK(service_->GetDevice());
417 scoped_ptr<device::BluetoothGattNotifySession> session(
418 new BluetoothGattNotifySessionChromeOS(
419 service_->GetAdapter(),
420 service_->GetDevice()->GetAddress(),
421 service_->GetIdentifier(),
422 GetIdentifier(),
423 object_path_));
424 callback.Run(session.Pass());
425
426 ProcessStartNotifyQueue();
427 }
428
OnStartNotifyError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)429 void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError(
430 const ErrorCallback& error_callback,
431 const std::string& error_name,
432 const std::string& error_message) {
433 VLOG(1) << "Failed to start notifications from characteristic: "
434 << object_path_.value() << ": " << error_name << ", "
435 << error_message;
436 DCHECK(num_notify_sessions_ == 0);
437 DCHECK(notify_call_pending_);
438
439 notify_call_pending_ = false;
440 error_callback.Run();
441
442 ProcessStartNotifyQueue();
443 }
444
OnStopNotifySuccess(const base::Closure & callback)445 void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess(
446 const base::Closure& callback) {
447 DCHECK(notify_call_pending_);
448 DCHECK(num_notify_sessions_ == 1);
449
450 notify_call_pending_ = false;
451 --num_notify_sessions_;
452 callback.Run();
453
454 ProcessStartNotifyQueue();
455 }
456
OnStopNotifyError(const base::Closure & callback,const std::string & error_name,const std::string & error_message)457 void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError(
458 const base::Closure& callback,
459 const std::string& error_name,
460 const std::string& error_message) {
461 VLOG(1) << "Call to stop notifications failed for characteristic: "
462 << object_path_.value() << ": " << error_name << ", "
463 << error_message;
464
465 // Since this is a best effort operation, treat this as success.
466 OnStopNotifySuccess(callback);
467 }
468
ProcessStartNotifyQueue()469 void BluetoothRemoteGattCharacteristicChromeOS::ProcessStartNotifyQueue() {
470 while (!pending_start_notify_calls_.empty()) {
471 PendingStartNotifyCall callbacks = pending_start_notify_calls_.front();
472 pending_start_notify_calls_.pop();
473 StartNotifySession(callbacks.first, callbacks.second);
474 }
475 }
476
477 } // namespace chromeos
478