• 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_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