blob: 21a6ec29e8451dfadff88f2b3af091845852db08 [file] [log] [blame]
// Copyright 2016 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.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
namespace device {
BluetoothRemoteGattCharacteristic::BluetoothRemoteGattCharacteristic()
: weak_ptr_factory_(this) {}
BluetoothRemoteGattCharacteristic::~BluetoothRemoteGattCharacteristic() {
while (!pending_notify_commands_.empty()) {
pending_notify_commands_.front()->Cancel();
}
}
std::vector<BluetoothRemoteGattDescriptor*>
BluetoothRemoteGattCharacteristic::GetDescriptors() const {
std::vector<BluetoothRemoteGattDescriptor*> descriptors;
descriptors.reserve(descriptors_.size());
for (const auto& pair : descriptors_)
descriptors.push_back(pair.second.get());
return descriptors;
}
BluetoothRemoteGattDescriptor* BluetoothRemoteGattCharacteristic::GetDescriptor(
const std::string& identifier) const {
auto iter = descriptors_.find(identifier);
return iter != descriptors_.end() ? iter->second.get() : nullptr;
}
std::vector<BluetoothRemoteGattDescriptor*>
BluetoothRemoteGattCharacteristic::GetDescriptorsByUUID(
const BluetoothUUID& uuid) const {
std::vector<BluetoothRemoteGattDescriptor*> descriptors;
for (const auto& pair : descriptors_) {
if (pair.second->GetUUID() == uuid)
descriptors.push_back(pair.second.get());
}
return descriptors;
}
base::WeakPtr<device::BluetoothRemoteGattCharacteristic>
BluetoothRemoteGattCharacteristic::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
bool BluetoothRemoteGattCharacteristic::IsNotifying() const {
return !notify_sessions_.empty();
}
BluetoothRemoteGattCharacteristic::NotifySessionCommand::NotifySessionCommand(
ExecuteCallback execute_callback,
base::OnceClosure cancel_callback)
: execute_callback_(std::move(execute_callback)),
cancel_callback_(std::move(cancel_callback)) {}
BluetoothRemoteGattCharacteristic::NotifySessionCommand::
~NotifySessionCommand() = default;
void BluetoothRemoteGattCharacteristic::NotifySessionCommand::Execute() {
std::move(execute_callback_)
.Run(COMMAND_NONE, RESULT_SUCCESS,
BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
}
void BluetoothRemoteGattCharacteristic::NotifySessionCommand::Execute(
Type previous_command_type,
Result previous_command_result,
BluetoothRemoteGattService::GattErrorCode previous_command_error_code) {
std::move(execute_callback_)
.Run(previous_command_type, previous_command_result,
previous_command_error_code);
}
void BluetoothRemoteGattCharacteristic::NotifySessionCommand::Cancel() {
std::move(cancel_callback_).Run();
}
void BluetoothRemoteGattCharacteristic::StartNotifySession(
NotifySessionCallback callback,
ErrorCallback error_callback) {
StartNotifySessionInternal(base::nullopt, std::move(callback),
std::move(error_callback));
}
#if defined(OS_CHROMEOS)
void BluetoothRemoteGattCharacteristic::StartNotifySession(
NotificationType notification_type,
NotifySessionCallback callback,
ErrorCallback error_callback) {
StartNotifySessionInternal(notification_type, std::move(callback),
std::move(error_callback));
}
#endif
bool BluetoothRemoteGattCharacteristic::WriteWithoutResponse(
base::span<const uint8_t> value) {
NOTIMPLEMENTED();
return false;
}
bool BluetoothRemoteGattCharacteristic::AddDescriptor(
std::unique_ptr<BluetoothRemoteGattDescriptor> descriptor) {
if (!descriptor)
return false;
auto* descriptor_raw = descriptor.get();
return descriptors_
.try_emplace(descriptor_raw->GetIdentifier(), std::move(descriptor))
.second;
}
void BluetoothRemoteGattCharacteristic::StartNotifySessionInternal(
const base::Optional<NotificationType>& notification_type,
NotifySessionCallback callback,
ErrorCallback error_callback) {
auto repeating_error_callback =
base::AdaptCallbackForRepeating(std::move(error_callback));
NotifySessionCommand* command = new NotifySessionCommand(
base::BindOnce(
&BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession,
GetWeakPtr(), notification_type, std::move(callback),
repeating_error_callback),
base::BindOnce(
&BluetoothRemoteGattCharacteristic::CancelStartNotifySession,
GetWeakPtr(),
base::BindOnce(repeating_error_callback,
BluetoothRemoteGattService::GATT_ERROR_FAILED)));
pending_notify_commands_.push(base::WrapUnique(command));
if (pending_notify_commands_.size() == 1) {
command->Execute();
}
}
void BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession(
const base::Optional<NotificationType>& notification_type,
NotifySessionCallback callback,
ErrorCallback error_callback,
NotifySessionCommand::Type previous_command_type,
NotifySessionCommand::Result previous_command_result,
BluetoothRemoteGattService::GattErrorCode previous_command_error_code) {
// If the command that was resolved immediately before this command was run,
// this command should be resolved with the same result.
if (previous_command_type == NotifySessionCommand::COMMAND_START) {
if (previous_command_result == NotifySessionCommand::RESULT_SUCCESS) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess,
GetWeakPtr(), std::move(callback)));
return;
} else {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionError,
GetWeakPtr(), std::move(error_callback),
previous_command_error_code));
return;
}
}
if (!IsNotificationTypeSupported(notification_type)) {
if (notification_type)
LOG(ERROR) << "Characteristic doesn't support specified "
<< "notification_type";
else
LOG(ERROR) << "Characteristic needs NOTIFY or INDICATE";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionError,
GetWeakPtr(), std::move(error_callback),
BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED));
return;
}
// If the characteristic is already notifying, then we don't need to
// subscribe again. All we need to do is call the success callback, which
// will create and return a session object to the caller.
if (IsNotifying()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess,
GetWeakPtr(), std::move(callback)));
return;
}
// Find the Client Characteristic Configuration descriptor.
std::vector<BluetoothRemoteGattDescriptor*> ccc_descriptor =
GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
ClientCharacteristicConfigurationUuid());
if (ccc_descriptor.size() != 1u) {
LOG(ERROR) << "Found " << ccc_descriptor.size()
<< " client characteristic configuration descriptors.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionError,
GetWeakPtr(), std::move(error_callback),
(ccc_descriptor.size() == 0)
? BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED
: BluetoothRemoteGattService::GATT_ERROR_FAILED));
return;
}
// Pass the Client Characteristic Configuration descriptor to
// SubscribetoNotifications, which will write the correct value to it, and
// do whatever else is needed to get the notifications flowing.
SubscribeToNotifications(
ccc_descriptor[0],
#if defined(OS_CHROMEOS)
notification_type.value_or((GetProperties() & PROPERTY_NOTIFY)
? NotificationType::kNotification
: NotificationType::kIndication),
#endif
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess,
GetWeakPtr(), std::move(callback)),
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionError,
GetWeakPtr(), std::move(error_callback)));
}
void BluetoothRemoteGattCharacteristic::CancelStartNotifySession(
base::OnceClosure callback) {
std::unique_ptr<NotifySessionCommand> command =
std::move(pending_notify_commands_.front());
pending_notify_commands_.pop();
std::move(callback).Run();
}
void BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess(
NotifySessionCallback callback) {
std::unique_ptr<NotifySessionCommand> command =
std::move(pending_notify_commands_.front());
std::unique_ptr<device::BluetoothGattNotifySession> notify_session(
new BluetoothGattNotifySession(weak_ptr_factory_.GetWeakPtr()));
notify_sessions_.insert(notify_session.get());
std::move(callback).Run(std::move(notify_session));
pending_notify_commands_.pop();
if (!pending_notify_commands_.empty()) {
pending_notify_commands_.front()->Execute(
NotifySessionCommand::COMMAND_START,
NotifySessionCommand::RESULT_SUCCESS,
BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
}
}
void BluetoothRemoteGattCharacteristic::OnStartNotifySessionError(
ErrorCallback error_callback,
BluetoothRemoteGattService::GattErrorCode error) {
std::unique_ptr<NotifySessionCommand> command =
std::move(pending_notify_commands_.front());
std::move(error_callback).Run(error);
pending_notify_commands_.pop();
if (!pending_notify_commands_.empty()) {
pending_notify_commands_.front()->Execute(
NotifySessionCommand::COMMAND_START, NotifySessionCommand::RESULT_ERROR,
error);
}
}
void BluetoothRemoteGattCharacteristic::StopNotifySession(
BluetoothGattNotifySession* session,
base::OnceClosure callback) {
auto repeating_callback =
base::AdaptCallbackForRepeating(std::move(callback));
NotifySessionCommand* command = new NotifySessionCommand(
base::Bind(&BluetoothRemoteGattCharacteristic::ExecuteStopNotifySession,
GetWeakPtr(), session, repeating_callback),
base::Bind(&BluetoothRemoteGattCharacteristic::CancelStopNotifySession,
GetWeakPtr(), repeating_callback));
pending_notify_commands_.push(std::unique_ptr<NotifySessionCommand>(command));
if (pending_notify_commands_.size() == 1) {
command->Execute();
}
}
void BluetoothRemoteGattCharacteristic::ExecuteStopNotifySession(
BluetoothGattNotifySession* session,
base::OnceClosure callback,
NotifySessionCommand::Type previous_command_type,
NotifySessionCommand::Result previous_command_result,
BluetoothRemoteGattService::GattErrorCode previous_command_error_code) {
auto session_iterator = notify_sessions_.find(session);
// If the session does not even belong to this characteristic, we return an
// error right away.
if (session_iterator == notify_sessions_.end()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStopNotifySessionError,
GetWeakPtr(), session, std::move(callback),
BluetoothRemoteGattService::GATT_ERROR_FAILED));
return;
}
// If there are more active sessions, then we return right away.
if (notify_sessions_.size() > 1) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStopNotifySessionSuccess,
GetWeakPtr(), session, std::move(callback)));
return;
}
// Find the Client Characteristic Configuration descriptor.
std::vector<BluetoothRemoteGattDescriptor*> ccc_descriptor =
GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
ClientCharacteristicConfigurationUuid());
if (ccc_descriptor.size() != 1u) {
LOG(ERROR) << "Found " << ccc_descriptor.size()
<< " client characteristic configuration descriptors.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStopNotifySessionError,
GetWeakPtr(), session, std::move(callback),
BluetoothRemoteGattService::GATT_ERROR_FAILED));
return;
}
auto repeating_callback =
base::AdaptCallbackForRepeating(std::move(callback));
UnsubscribeFromNotifications(
ccc_descriptor[0],
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStopNotifySessionSuccess,
GetWeakPtr(), session, repeating_callback),
base::BindOnce(
&BluetoothRemoteGattCharacteristic::OnStopNotifySessionError,
GetWeakPtr(), session, repeating_callback));
}
void BluetoothRemoteGattCharacteristic::CancelStopNotifySession(
base::OnceClosure callback) {
std::unique_ptr<NotifySessionCommand> command =
std::move(pending_notify_commands_.front());
pending_notify_commands_.pop();
std::move(callback).Run();
}
void BluetoothRemoteGattCharacteristic::OnStopNotifySessionSuccess(
BluetoothGattNotifySession* session,
base::OnceClosure callback) {
std::unique_ptr<NotifySessionCommand> command =
std::move(pending_notify_commands_.front());
notify_sessions_.erase(session);
std::move(callback).Run();
pending_notify_commands_.pop();
if (!pending_notify_commands_.empty()) {
pending_notify_commands_.front()->Execute(
NotifySessionCommand::COMMAND_STOP,
NotifySessionCommand::RESULT_SUCCESS,
BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
}
}
void BluetoothRemoteGattCharacteristic::OnStopNotifySessionError(
BluetoothGattNotifySession* session,
base::OnceClosure callback,
BluetoothRemoteGattService::GattErrorCode error) {
std::unique_ptr<NotifySessionCommand> command =
std::move(pending_notify_commands_.front());
notify_sessions_.erase(session);
std::move(callback).Run();
pending_notify_commands_.pop();
if (!pending_notify_commands_.empty()) {
pending_notify_commands_.front()->Execute(
NotifySessionCommand::COMMAND_STOP, NotifySessionCommand::RESULT_ERROR,
error);
}
}
bool BluetoothRemoteGattCharacteristic::IsNotificationTypeSupported(
const base::Optional<NotificationType>& notification_type) {
Properties properties = GetProperties();
bool hasNotify = (properties & PROPERTY_NOTIFY) != 0;
bool hasIndicate = (properties & PROPERTY_INDICATE) != 0;
if (!notification_type)
return hasNotify || hasIndicate;
switch (notification_type.value()) {
case NotificationType::kNotification:
return hasNotify;
case NotificationType::kIndication:
return hasIndicate;
}
}
} // namespace device