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.
5 #include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
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_characteristic_chromeos.h"
16 #include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
17 #include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
18 #include "third_party/cros_system_api/dbus/service_constants.h"
24 // Stream operator for logging vector<uint8>.
25 std::ostream
& operator<<(std::ostream
& out
, const std::vector
<uint8
> bytes
) {
27 for (std::vector
<uint8
>::const_iterator iter
= bytes
.begin();
28 iter
!= bytes
.end(); ++iter
) {
29 out
<< base::StringPrintf("%02X", *iter
);
36 BluetoothRemoteGattCharacteristicChromeOS::
37 BluetoothRemoteGattCharacteristicChromeOS(
38 BluetoothRemoteGattServiceChromeOS
* service
,
39 const dbus::ObjectPath
& object_path
)
40 : object_path_(object_path
),
42 num_notify_sessions_(0),
43 notify_call_pending_(false),
44 weak_ptr_factory_(this) {
45 VLOG(1) << "Creating remote GATT characteristic with identifier: "
46 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
47 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
50 // Add all known GATT characteristic descriptors.
51 const std::vector
<dbus::ObjectPath
>& gatt_descs
=
52 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
54 for (std::vector
<dbus::ObjectPath
>::const_iterator iter
= gatt_descs
.begin();
55 iter
!= gatt_descs
.end(); ++iter
)
56 GattDescriptorAdded(*iter
);
59 BluetoothRemoteGattCharacteristicChromeOS::
60 ~BluetoothRemoteGattCharacteristicChromeOS() {
61 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
64 // Clean up all the descriptors. There isn't much point in notifying service
65 // observers for each descriptor that gets removed, so just delete them.
66 for (DescriptorMap::iterator iter
= descriptors_
.begin();
67 iter
!= descriptors_
.end(); ++iter
)
70 // Report an error for all pending calls to StartNotifySession.
71 while (!pending_start_notify_calls_
.empty()) {
72 PendingStartNotifyCall callbacks
= pending_start_notify_calls_
.front();
73 pending_start_notify_calls_
.pop();
74 callbacks
.second
.Run(device::BluetoothGattService::GATT_ERROR_FAILED
);
78 std::string
BluetoothRemoteGattCharacteristicChromeOS::GetIdentifier() const {
79 return object_path_
.value();
83 BluetoothRemoteGattCharacteristicChromeOS::GetUUID() const {
84 BluetoothGattCharacteristicClient::Properties
* properties
=
85 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
86 GetProperties(object_path_
);
88 return device::BluetoothUUID(properties
->uuid
.value());
91 bool BluetoothRemoteGattCharacteristicChromeOS::IsLocal() const {
95 const std::vector
<uint8
>&
96 BluetoothRemoteGattCharacteristicChromeOS::GetValue() const {
97 BluetoothGattCharacteristicClient::Properties
* properties
=
98 DBusThreadManager::Get()
99 ->GetBluetoothGattCharacteristicClient()
100 ->GetProperties(object_path_
);
104 return properties
->value
.value();
107 device::BluetoothGattService
*
108 BluetoothRemoteGattCharacteristicChromeOS::GetService() const {
112 device::BluetoothGattCharacteristic::Properties
113 BluetoothRemoteGattCharacteristicChromeOS::GetProperties() const {
114 BluetoothGattCharacteristicClient::Properties
* properties
=
115 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
116 GetProperties(object_path_
);
119 Properties props
= PROPERTY_NONE
;
120 const std::vector
<std::string
>& flags
= properties
->flags
.value();
121 for (std::vector
<std::string
>::const_iterator iter
= flags
.begin();
124 if (*iter
== bluetooth_gatt_characteristic::kFlagBroadcast
)
125 props
|= PROPERTY_BROADCAST
;
126 if (*iter
== bluetooth_gatt_characteristic::kFlagRead
)
127 props
|= PROPERTY_READ
;
128 if (*iter
== bluetooth_gatt_characteristic::kFlagWriteWithoutResponse
)
129 props
|= PROPERTY_WRITE_WITHOUT_RESPONSE
;
130 if (*iter
== bluetooth_gatt_characteristic::kFlagWrite
)
131 props
|= PROPERTY_WRITE
;
132 if (*iter
== bluetooth_gatt_characteristic::kFlagNotify
)
133 props
|= PROPERTY_NOTIFY
;
134 if (*iter
== bluetooth_gatt_characteristic::kFlagIndicate
)
135 props
|= PROPERTY_INDICATE
;
136 if (*iter
== bluetooth_gatt_characteristic::kFlagAuthenticatedSignedWrites
)
137 props
|= PROPERTY_AUTHENTICATED_SIGNED_WRITES
;
138 if (*iter
== bluetooth_gatt_characteristic::kFlagExtendedProperties
)
139 props
|= PROPERTY_EXTENDED_PROPERTIES
;
140 if (*iter
== bluetooth_gatt_characteristic::kFlagReliableWrite
)
141 props
|= PROPERTY_RELIABLE_WRITE
;
142 if (*iter
== bluetooth_gatt_characteristic::kFlagWritableAuxiliaries
)
143 props
|= PROPERTY_WRITABLE_AUXILIARIES
;
149 device::BluetoothGattCharacteristic::Permissions
150 BluetoothRemoteGattCharacteristicChromeOS::GetPermissions() const {
151 // TODO(armansito): Once BlueZ defines the permissions, return the correct
153 return PERMISSION_NONE
;
156 bool BluetoothRemoteGattCharacteristicChromeOS::IsNotifying() const {
157 BluetoothGattCharacteristicClient::Properties
* properties
=
158 DBusThreadManager::Get()
159 ->GetBluetoothGattCharacteristicClient()
160 ->GetProperties(object_path_
);
163 return properties
->notifying
.value();
166 std::vector
<device::BluetoothGattDescriptor
*>
167 BluetoothRemoteGattCharacteristicChromeOS::GetDescriptors() const {
168 std::vector
<device::BluetoothGattDescriptor
*> descriptors
;
169 for (DescriptorMap::const_iterator iter
= descriptors_
.begin();
170 iter
!= descriptors_
.end(); ++iter
)
171 descriptors
.push_back(iter
->second
);
175 device::BluetoothGattDescriptor
*
176 BluetoothRemoteGattCharacteristicChromeOS::GetDescriptor(
177 const std::string
& identifier
) const {
178 DescriptorMap::const_iterator iter
=
179 descriptors_
.find(dbus::ObjectPath(identifier
));
180 if (iter
== descriptors_
.end())
185 bool BluetoothRemoteGattCharacteristicChromeOS::AddDescriptor(
186 device::BluetoothGattDescriptor
* descriptor
) {
187 VLOG(1) << "Descriptors cannot be added to a remote GATT characteristic.";
191 bool BluetoothRemoteGattCharacteristicChromeOS::UpdateValue(
192 const std::vector
<uint8
>& value
) {
193 VLOG(1) << "Cannot update the value of a remote GATT characteristic.";
197 void BluetoothRemoteGattCharacteristicChromeOS::ReadRemoteCharacteristic(
198 const ValueCallback
& callback
,
199 const ErrorCallback
& error_callback
) {
200 VLOG(1) << "Sending GATT characteristic read request to characteristic: "
201 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value()
204 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->ReadValue(
205 object_path_
, callback
,
206 base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError
,
207 weak_ptr_factory_
.GetWeakPtr(), 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
<< ".";
218 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->WriteValue(
222 base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError
,
223 weak_ptr_factory_
.GetWeakPtr(),
227 void BluetoothRemoteGattCharacteristicChromeOS::StartNotifySession(
228 const NotifySessionCallback
& callback
,
229 const ErrorCallback
& error_callback
) {
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.
239 // Check for overflows, though unlikely.
240 if (num_notify_sessions_
== std::numeric_limits
<size_t>::max()) {
241 error_callback
.Run(device::BluetoothGattService::GATT_ERROR_FAILED
);
245 ++num_notify_sessions_
;
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(),
256 callback
.Run(session
.Pass());
260 num_notify_sessions_
= 0;
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
));
269 notify_call_pending_
= true;
270 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StartNotify(
273 &BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess
,
274 weak_ptr_factory_
.GetWeakPtr(),
276 base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError
,
277 weak_ptr_factory_
.GetWeakPtr(),
281 void BluetoothRemoteGattCharacteristicChromeOS::RemoveNotifySession(
282 const base::Closure
& callback
) {
285 if (num_notify_sessions_
> 1) {
286 DCHECK(!notify_call_pending_
);
287 --num_notify_sessions_
;
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;
300 if (notify_call_pending_
|| num_notify_sessions_
== 0) {
305 DCHECK(num_notify_sessions_
== 1);
306 notify_call_pending_
= true;
307 DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StopNotify(
310 &BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess
,
311 weak_ptr_factory_
.GetWeakPtr(),
313 base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError
,
314 weak_ptr_factory_
.GetWeakPtr(),
318 void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorAdded(
319 const dbus::ObjectPath
& object_path
) {
320 if (descriptors_
.find(object_path
) != descriptors_
.end()) {
321 VLOG(1) << "Remote GATT characteristic descriptor already exists: "
322 << object_path
.value();
326 BluetoothGattDescriptorClient::Properties
* properties
=
327 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
328 GetProperties(object_path
);
330 if (properties
->characteristic
.value() != object_path_
) {
331 VLOG(3) << "Remote GATT descriptor does not belong to this characteristic.";
335 VLOG(1) << "Adding new remote GATT descriptor for GATT characteristic: "
336 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
338 BluetoothRemoteGattDescriptorChromeOS
* descriptor
=
339 new BluetoothRemoteGattDescriptorChromeOS(this, object_path
);
340 descriptors_
[object_path
] = descriptor
;
341 DCHECK(descriptor
->GetIdentifier() == object_path
.value());
342 DCHECK(descriptor
->GetUUID().IsValid());
345 service_
->NotifyDescriptorAddedOrRemoved(this, descriptor
, true /* added */);
348 void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorRemoved(
349 const dbus::ObjectPath
& object_path
) {
350 DescriptorMap::iterator iter
= descriptors_
.find(object_path
);
351 if (iter
== descriptors_
.end()) {
352 VLOG(2) << "Unknown descriptor removed: " << object_path
.value();
356 VLOG(1) << "Removing remote GATT descriptor from characteristic: "
357 << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
359 BluetoothRemoteGattDescriptorChromeOS
* descriptor
= iter
->second
;
360 DCHECK(descriptor
->object_path() == object_path
);
361 descriptors_
.erase(iter
);
364 service_
->NotifyDescriptorAddedOrRemoved(this, descriptor
, false /* added */);
369 void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorPropertyChanged(
370 const dbus::ObjectPath
& object_path
,
371 const std::string
& property_name
) {
372 DescriptorMap::iterator iter
= descriptors_
.find(object_path
);
373 if (iter
== descriptors_
.end()) {
374 VLOG(2) << "Unknown descriptor removed: " << object_path
.value();
378 BluetoothGattDescriptorClient::Properties
* properties
=
379 DBusThreadManager::Get()
380 ->GetBluetoothGattDescriptorClient()
381 ->GetProperties(object_path
);
385 if (property_name
!= properties
->value
.name())
389 service_
->NotifyDescriptorValueChanged(this, iter
->second
,
390 properties
->value
.value());
393 void BluetoothRemoteGattCharacteristicChromeOS::OnError(
394 const ErrorCallback
& error_callback
,
395 const std::string
& error_name
,
396 const std::string
& error_message
) {
397 VLOG(1) << "Operation failed: " << error_name
<< ", message: "
400 BluetoothRemoteGattServiceChromeOS::DBusErrorToServiceError(error_name
));
403 void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess(
404 const NotifySessionCallback
& callback
) {
405 VLOG(1) << "Started notifications from characteristic: "
406 << object_path_
.value();
407 DCHECK(num_notify_sessions_
== 0);
408 DCHECK(notify_call_pending_
);
410 ++num_notify_sessions_
;
411 notify_call_pending_
= false;
413 // Invoke the queued callbacks for this operation.
415 DCHECK(service_
->GetDevice());
416 scoped_ptr
<device::BluetoothGattNotifySession
> session(
417 new BluetoothGattNotifySessionChromeOS(
418 service_
->GetAdapter(),
419 service_
->GetDevice()->GetAddress(),
420 service_
->GetIdentifier(),
423 callback
.Run(session
.Pass());
425 ProcessStartNotifyQueue();
428 void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError(
429 const ErrorCallback
& error_callback
,
430 const std::string
& error_name
,
431 const std::string
& error_message
) {
432 VLOG(1) << "Failed to start notifications from characteristic: "
433 << object_path_
.value() << ": " << error_name
<< ", "
435 DCHECK(num_notify_sessions_
== 0);
436 DCHECK(notify_call_pending_
);
438 notify_call_pending_
= false;
441 BluetoothRemoteGattServiceChromeOS::DBusErrorToServiceError(error_name
));
443 ProcessStartNotifyQueue();
446 void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess(
447 const base::Closure
& callback
) {
448 DCHECK(notify_call_pending_
);
449 DCHECK(num_notify_sessions_
== 1);
451 notify_call_pending_
= false;
452 --num_notify_sessions_
;
455 ProcessStartNotifyQueue();
458 void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError(
459 const base::Closure
& callback
,
460 const std::string
& error_name
,
461 const std::string
& error_message
) {
462 VLOG(1) << "Call to stop notifications failed for characteristic: "
463 << object_path_
.value() << ": " << error_name
<< ", "
466 // Since this is a best effort operation, treat this as success.
467 OnStopNotifySuccess(callback
);
470 void BluetoothRemoteGattCharacteristicChromeOS::ProcessStartNotifyQueue() {
471 while (!pending_start_notify_calls_
.empty()) {
472 PendingStartNotifyCall callbacks
= pending_start_notify_calls_
.front();
473 pending_start_notify_calls_
.pop();
474 StartNotifySession(callbacks
.first
, callbacks
.second
);
478 } // namespace chromeos