• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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