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
19namespace chromeos {
20
21namespace {
22
23// Stream operator for logging vector<uint8>.
24std::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
35BluetoothRemoteGattCharacteristicChromeOS::
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
60BluetoothRemoteGattCharacteristicChromeOS::
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
81std::string BluetoothRemoteGattCharacteristicChromeOS::GetIdentifier() const {
82  return object_path_.value();
83}
84
85device::BluetoothUUID
86BluetoothRemoteGattCharacteristicChromeOS::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
94bool BluetoothRemoteGattCharacteristicChromeOS::IsLocal() const {
95  return false;
96}
97
98const std::vector<uint8>&
99BluetoothRemoteGattCharacteristicChromeOS::GetValue() const {
100  return cached_value_;
101}
102
103device::BluetoothGattService*
104BluetoothRemoteGattCharacteristicChromeOS::GetService() const {
105  return service_;
106}
107
108device::BluetoothGattCharacteristic::Properties
109BluetoothRemoteGattCharacteristicChromeOS::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
145device::BluetoothGattCharacteristic::Permissions
146BluetoothRemoteGattCharacteristicChromeOS::GetPermissions() const {
147  // TODO(armansito): Once BlueZ defines the permissions, return the correct
148  // values here.
149  return kPermissionNone;
150}
151
152bool 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
162std::vector<device::BluetoothGattDescriptor*>
163BluetoothRemoteGattCharacteristicChromeOS::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
171device::BluetoothGattDescriptor*
172BluetoothRemoteGattCharacteristicChromeOS::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
181bool BluetoothRemoteGattCharacteristicChromeOS::AddDescriptor(
182    device::BluetoothGattDescriptor* descriptor) {
183  VLOG(1) << "Descriptors cannot be added to a remote GATT characteristic.";
184  return false;
185}
186
187bool 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
193void 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
210void 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
227void 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
281void 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
318void 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
332void 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
362void 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
383void 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
395void 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
404void 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
429void 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
445void 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
457void 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
469void 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