diff options
Diffstat (limited to 'device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc')
-rw-r--r-- | device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc new file mode 100644 index 0000000..7374f16 --- /dev/null +++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc @@ -0,0 +1,487 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h" + +#include <limits> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "device/bluetooth/bluetooth_adapter_chromeos.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_gatt_notify_session_chromeos.h" +#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h" +#include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h" +#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h" +#include "device/bluetooth/dbus/bluez_dbus_manager.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +// Stream operator for logging vector<uint8>. +std::ostream& operator<<(std::ostream& out, const std::vector<uint8> bytes) { + out << "["; + for (std::vector<uint8>::const_iterator iter = bytes.begin(); + iter != bytes.end(); ++iter) { + out << base::StringPrintf("%02X", *iter); + } + return out << "]"; +} + +} // namespace + +BluetoothRemoteGattCharacteristicChromeOS:: + BluetoothRemoteGattCharacteristicChromeOS( + BluetoothRemoteGattServiceChromeOS* service, + const dbus::ObjectPath& object_path) + : object_path_(object_path), + service_(service), + num_notify_sessions_(0), + notify_call_pending_(false), + weak_ptr_factory_(this) { + VLOG(1) << "Creating remote GATT characteristic with identifier: " + << GetIdentifier() << ", UUID: " << GetUUID().canonical_value(); + bluez::BluezDBusManager::Get() + ->GetBluetoothGattDescriptorClient() + ->AddObserver(this); + + // Add all known GATT characteristic descriptors. + const std::vector<dbus::ObjectPath>& gatt_descs = + bluez::BluezDBusManager::Get() + ->GetBluetoothGattDescriptorClient() + ->GetDescriptors(); + for (std::vector<dbus::ObjectPath>::const_iterator iter = gatt_descs.begin(); + iter != gatt_descs.end(); ++iter) + GattDescriptorAdded(*iter); +} + +BluetoothRemoteGattCharacteristicChromeOS:: + ~BluetoothRemoteGattCharacteristicChromeOS() { + bluez::BluezDBusManager::Get() + ->GetBluetoothGattDescriptorClient() + ->RemoveObserver(this); + + // Clean up all the descriptors. There isn't much point in notifying service + // observers for each descriptor that gets removed, so just delete them. + for (DescriptorMap::iterator iter = descriptors_.begin(); + iter != descriptors_.end(); ++iter) + delete iter->second; + + // Report an error for all pending calls to StartNotifySession. + while (!pending_start_notify_calls_.empty()) { + PendingStartNotifyCall callbacks = pending_start_notify_calls_.front(); + pending_start_notify_calls_.pop(); + callbacks.second.Run(device::BluetoothGattService::GATT_ERROR_FAILED); + } +} + +std::string BluetoothRemoteGattCharacteristicChromeOS::GetIdentifier() const { + return object_path_.value(); +} + +device::BluetoothUUID +BluetoothRemoteGattCharacteristicChromeOS::GetUUID() const { + bluez::BluetoothGattCharacteristicClient::Properties* properties = + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->GetProperties(object_path_); + DCHECK(properties); + return device::BluetoothUUID(properties->uuid.value()); +} + +bool BluetoothRemoteGattCharacteristicChromeOS::IsLocal() const { + return false; +} + +const std::vector<uint8>& +BluetoothRemoteGattCharacteristicChromeOS::GetValue() const { + bluez::BluetoothGattCharacteristicClient::Properties* properties = + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->GetProperties(object_path_); + + DCHECK(properties); + + return properties->value.value(); +} + +device::BluetoothGattService* +BluetoothRemoteGattCharacteristicChromeOS::GetService() const { + return service_; +} + +device::BluetoothGattCharacteristic::Properties +BluetoothRemoteGattCharacteristicChromeOS::GetProperties() const { + bluez::BluetoothGattCharacteristicClient::Properties* properties = + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->GetProperties(object_path_); + DCHECK(properties); + + Properties props = PROPERTY_NONE; + const std::vector<std::string>& flags = properties->flags.value(); + for (std::vector<std::string>::const_iterator iter = flags.begin(); + iter != flags.end(); + ++iter) { + if (*iter == bluetooth_gatt_characteristic::kFlagBroadcast) + props |= PROPERTY_BROADCAST; + if (*iter == bluetooth_gatt_characteristic::kFlagRead) + props |= PROPERTY_READ; + if (*iter == bluetooth_gatt_characteristic::kFlagWriteWithoutResponse) + props |= PROPERTY_WRITE_WITHOUT_RESPONSE; + if (*iter == bluetooth_gatt_characteristic::kFlagWrite) + props |= PROPERTY_WRITE; + if (*iter == bluetooth_gatt_characteristic::kFlagNotify) + props |= PROPERTY_NOTIFY; + if (*iter == bluetooth_gatt_characteristic::kFlagIndicate) + props |= PROPERTY_INDICATE; + if (*iter == bluetooth_gatt_characteristic::kFlagAuthenticatedSignedWrites) + props |= PROPERTY_AUTHENTICATED_SIGNED_WRITES; + if (*iter == bluetooth_gatt_characteristic::kFlagExtendedProperties) + props |= PROPERTY_EXTENDED_PROPERTIES; + if (*iter == bluetooth_gatt_characteristic::kFlagReliableWrite) + props |= PROPERTY_RELIABLE_WRITE; + if (*iter == bluetooth_gatt_characteristic::kFlagWritableAuxiliaries) + props |= PROPERTY_WRITABLE_AUXILIARIES; + } + + return props; +} + +device::BluetoothGattCharacteristic::Permissions +BluetoothRemoteGattCharacteristicChromeOS::GetPermissions() const { + // TODO(armansito): Once BlueZ defines the permissions, return the correct + // values here. + return PERMISSION_NONE; +} + +bool BluetoothRemoteGattCharacteristicChromeOS::IsNotifying() const { + bluez::BluetoothGattCharacteristicClient::Properties* properties = + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->GetProperties(object_path_); + DCHECK(properties); + + return properties->notifying.value(); +} + +std::vector<device::BluetoothGattDescriptor*> +BluetoothRemoteGattCharacteristicChromeOS::GetDescriptors() const { + std::vector<device::BluetoothGattDescriptor*> descriptors; + for (DescriptorMap::const_iterator iter = descriptors_.begin(); + iter != descriptors_.end(); ++iter) + descriptors.push_back(iter->second); + return descriptors; +} + +device::BluetoothGattDescriptor* +BluetoothRemoteGattCharacteristicChromeOS::GetDescriptor( + const std::string& identifier) const { + DescriptorMap::const_iterator iter = + descriptors_.find(dbus::ObjectPath(identifier)); + if (iter == descriptors_.end()) + return NULL; + return iter->second; +} + +bool BluetoothRemoteGattCharacteristicChromeOS::AddDescriptor( + device::BluetoothGattDescriptor* descriptor) { + VLOG(1) << "Descriptors cannot be added to a remote GATT characteristic."; + return false; +} + +bool BluetoothRemoteGattCharacteristicChromeOS::UpdateValue( + const std::vector<uint8>& value) { + VLOG(1) << "Cannot update the value of a remote GATT characteristic."; + return false; +} + +void BluetoothRemoteGattCharacteristicChromeOS::ReadRemoteCharacteristic( + const ValueCallback& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Sending GATT characteristic read request to characteristic: " + << GetIdentifier() << ", UUID: " << GetUUID().canonical_value() + << "."; + + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->ReadValue( + object_path_, callback, + base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); +} + +void BluetoothRemoteGattCharacteristicChromeOS::WriteRemoteCharacteristic( + const std::vector<uint8>& new_value, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Sending GATT characteristic write request to characteristic: " + << GetIdentifier() << ", UUID: " << GetUUID().canonical_value() + << ", with value: " << new_value << "."; + + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->WriteValue( + object_path_, new_value, callback, + base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); +} + +void BluetoothRemoteGattCharacteristicChromeOS::StartNotifySession( + const NotifySessionCallback& callback, + const ErrorCallback& error_callback) { + VLOG(1) << __func__; + + if (num_notify_sessions_ > 0) { + // The characteristic might have stopped notifying even though the session + // count is nonzero. This means that notifications stopped outside of our + // control and we should reset the count. If the characteristic is still + // notifying, then return success. Otherwise, reset the count and treat + // this call as if the count were 0. + if (IsNotifying()) { + // Check for overflows, though unlikely. + if (num_notify_sessions_ == std::numeric_limits<size_t>::max()) { + error_callback.Run(device::BluetoothGattService::GATT_ERROR_FAILED); + return; + } + + ++num_notify_sessions_; + DCHECK(service_); + DCHECK(service_->GetAdapter()); + DCHECK(service_->GetDevice()); + scoped_ptr<device::BluetoothGattNotifySession> session( + new BluetoothGattNotifySessionChromeOS( + service_->GetAdapter(), + service_->GetDevice()->GetAddress(), + service_->GetIdentifier(), + GetIdentifier(), + object_path_)); + callback.Run(session.Pass()); + return; + } + + num_notify_sessions_ = 0; + } + + // Queue the callbacks if there is a pending call to bluetoothd. + if (notify_call_pending_) { + pending_start_notify_calls_.push(std::make_pair(callback, error_callback)); + return; + } + + notify_call_pending_ = true; + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->StartNotify( + object_path_, + base::Bind( + &BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind( + &BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); +} + +void BluetoothRemoteGattCharacteristicChromeOS::RemoveNotifySession( + const base::Closure& callback) { + VLOG(1) << __func__; + + if (num_notify_sessions_ > 1) { + DCHECK(!notify_call_pending_); + --num_notify_sessions_; + callback.Run(); + return; + } + + // Notifications may have stopped outside our control. If the characteristic + // is no longer notifying, return success. + if (!IsNotifying()) { + num_notify_sessions_ = 0; + callback.Run(); + return; + } + + if (notify_call_pending_ || num_notify_sessions_ == 0) { + callback.Run(); + return; + } + + DCHECK(num_notify_sessions_ == 1); + notify_call_pending_ = true; + bluez::BluezDBusManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->StopNotify( + object_path_, + base::Bind( + &BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind( + &BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError, + weak_ptr_factory_.GetWeakPtr(), callback)); +} + +void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorAdded( + const dbus::ObjectPath& object_path) { + if (descriptors_.find(object_path) != descriptors_.end()) { + VLOG(1) << "Remote GATT characteristic descriptor already exists: " + << object_path.value(); + return; + } + + bluez::BluetoothGattDescriptorClient::Properties* properties = + bluez::BluezDBusManager::Get() + ->GetBluetoothGattDescriptorClient() + ->GetProperties(object_path); + DCHECK(properties); + if (properties->characteristic.value() != object_path_) { + VLOG(3) << "Remote GATT descriptor does not belong to this characteristic."; + return; + } + + VLOG(1) << "Adding new remote GATT descriptor for GATT characteristic: " + << GetIdentifier() << ", UUID: " << GetUUID().canonical_value(); + + BluetoothRemoteGattDescriptorChromeOS* descriptor = + new BluetoothRemoteGattDescriptorChromeOS(this, object_path); + descriptors_[object_path] = descriptor; + DCHECK(descriptor->GetIdentifier() == object_path.value()); + DCHECK(descriptor->GetUUID().IsValid()); + DCHECK(service_); + + service_->NotifyDescriptorAddedOrRemoved(this, descriptor, true /* added */); +} + +void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorRemoved( + const dbus::ObjectPath& object_path) { + DescriptorMap::iterator iter = descriptors_.find(object_path); + if (iter == descriptors_.end()) { + VLOG(2) << "Unknown descriptor removed: " << object_path.value(); + return; + } + + VLOG(1) << "Removing remote GATT descriptor from characteristic: " + << GetIdentifier() << ", UUID: " << GetUUID().canonical_value(); + + BluetoothRemoteGattDescriptorChromeOS* descriptor = iter->second; + DCHECK(descriptor->object_path() == object_path); + descriptors_.erase(iter); + + DCHECK(service_); + service_->NotifyDescriptorAddedOrRemoved(this, descriptor, false /* added */); + + delete descriptor; +} + +void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + DescriptorMap::iterator iter = descriptors_.find(object_path); + if (iter == descriptors_.end()) { + VLOG(2) << "Unknown descriptor removed: " << object_path.value(); + return; + } + + bluez::BluetoothGattDescriptorClient::Properties* properties = + bluez::BluezDBusManager::Get() + ->GetBluetoothGattDescriptorClient() + ->GetProperties(object_path); + + DCHECK(properties); + + if (property_name != properties->value.name()) + return; + + DCHECK(service_); + service_->NotifyDescriptorValueChanged(this, iter->second, + properties->value.value()); +} + +void BluetoothRemoteGattCharacteristicChromeOS::OnError( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + VLOG(1) << "Operation failed: " << error_name << ", message: " + << error_message; + error_callback.Run( + BluetoothRemoteGattServiceChromeOS::DBusErrorToServiceError(error_name)); +} + +void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess( + const NotifySessionCallback& callback) { + VLOG(1) << "Started notifications from characteristic: " + << object_path_.value(); + DCHECK(num_notify_sessions_ == 0); + DCHECK(notify_call_pending_); + + ++num_notify_sessions_; + notify_call_pending_ = false; + + // Invoke the queued callbacks for this operation. + DCHECK(service_); + DCHECK(service_->GetDevice()); + scoped_ptr<device::BluetoothGattNotifySession> session( + new BluetoothGattNotifySessionChromeOS( + service_->GetAdapter(), + service_->GetDevice()->GetAddress(), + service_->GetIdentifier(), + GetIdentifier(), + object_path_)); + callback.Run(session.Pass()); + + ProcessStartNotifyQueue(); +} + +void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + VLOG(1) << "Failed to start notifications from characteristic: " + << object_path_.value() << ": " << error_name << ", " + << error_message; + DCHECK(num_notify_sessions_ == 0); + DCHECK(notify_call_pending_); + + notify_call_pending_ = false; + + error_callback.Run( + BluetoothRemoteGattServiceChromeOS::DBusErrorToServiceError(error_name)); + + ProcessStartNotifyQueue(); +} + +void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess( + const base::Closure& callback) { + DCHECK(notify_call_pending_); + DCHECK(num_notify_sessions_ == 1); + + notify_call_pending_ = false; + --num_notify_sessions_; + callback.Run(); + + ProcessStartNotifyQueue(); +} + +void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError( + const base::Closure& callback, + const std::string& error_name, + const std::string& error_message) { + VLOG(1) << "Call to stop notifications failed for characteristic: " + << object_path_.value() << ": " << error_name << ", " + << error_message; + + // Since this is a best effort operation, treat this as success. + OnStopNotifySuccess(callback); +} + +void BluetoothRemoteGattCharacteristicChromeOS::ProcessStartNotifyQueue() { + while (!pending_start_notify_calls_.empty()) { + PendingStartNotifyCall callbacks = pending_start_notify_calls_.front(); + pending_start_notify_calls_.pop(); + StartNotifySession(callbacks.first, callbacks.second); + } +} + +} // namespace chromeos |