// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/device/bluetooth/le/remote_device_impl.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/not_fatal_until.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "chromecast/base/bind_to_task_runner.h"
#include "chromecast/device/bluetooth/bluetooth_util.h"
#include "chromecast/device/bluetooth/le/gatt_client_manager_impl.h"
#include "chromecast/device/bluetooth/le/remote_characteristic_impl.h"
#include "chromecast/device/bluetooth/le/remote_descriptor_impl.h"
#include "chromecast/device/bluetooth/le/remote_service_impl.h"
#include "chromecast/public/bluetooth/gatt.h"
namespace chromecast {
namespace bluetooth {
#define RUN_ON_IO_THREAD(method, ...) \
io_task_runner_->PostTask( \
FROM_HERE, \
base::BindOnce(&RemoteDeviceImpl::method, this, ##__VA_ARGS__));
#define MAKE_SURE_IO_THREAD(method, ...) \
DCHECK(io_task_runner_); \
if (!io_task_runner_->BelongsToCurrentThread()) { \
RUN_ON_IO_THREAD(method, ##__VA_ARGS__) \
return; \
}
#define EXEC_CB_AND_RET(cb, ret, ...) \
do { \
if (cb) { \
std::move(cb).Run(ret, ##__VA_ARGS__); \
} \
return; \
} while (0)
#define CHECK_CONNECTED(cb) \
do { \
if (!connected_) { \
LOG(ERROR) << __func__ << "failed: Not connected"; \
EXEC_CB_AND_RET(cb, false); \
} \
} while (0)
#define LOG_EXEC_CB_AND_RET(cb, ret) \
do { \
if (!ret) { \
LOG(ERROR) << __func__ << "failed"; \
} \
EXEC_CB_AND_RET(cb, ret); \
} while (0)
// static
constexpr base::TimeDelta RemoteDeviceImpl::kCommandTimeout;
RemoteDeviceImpl::RemoteDeviceImpl(
const bluetooth_v2_shlib::Addr& addr,
base::WeakPtr<GattClientManagerImpl> gatt_client_manager,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: gatt_client_manager_(gatt_client_manager),
addr_(addr),
io_task_runner_(io_task_runner) {
DCHECK(gatt_client_manager);
DCHECK(io_task_runner_->BelongsToCurrentThread());
}
RemoteDeviceImpl::~RemoteDeviceImpl() = default;
void RemoteDeviceImpl::Connect(ConnectCallback cb,
bluetooth_v2_shlib::Gatt::Client::Transport transport) {
MAKE_SURE_IO_THREAD(Connect, BindToCurrentSequence(std::move(cb)), transport);
LOG(INFO) << "Connect(" << util::AddrLastByteString(addr_) << ")";
if (!gatt_client_manager_) {
LOG(ERROR) << __func__ << " failed: Destroyed";
EXEC_CB_AND_RET(cb, ConnectStatus::kGattClientManagerDestroyed);
}
if (connect_cb_) {
LOG(ERROR) << __func__ << " failed: Connection pending";
EXEC_CB_AND_RET(cb, ConnectStatus::kConnectPending);
}
gatt_client_manager_->NotifyConnect(addr_);
connect_cb_ = std::move(cb);
gatt_client_manager_->EnqueueConnectRequest(addr_, true, transport);
}
void RemoteDeviceImpl::Disconnect(StatusCallback cb) {
MAKE_SURE_IO_THREAD(Disconnect, BindToCurrentSequence(std::move(cb)));
LOG(INFO) << "Disconnect(" << util::AddrLastByteString(addr_) << ")";
if (!gatt_client_manager_) {
LOG(ERROR) << __func__ << " failed: Destroyed";
EXEC_CB_AND_RET(cb, false);
}
disconnect_pending_ = true;
disconnect_cb_ = std::move(cb);
gatt_client_manager_->EnqueueConnectRequest(addr_, false);
}
void RemoteDeviceImpl::CreateBond(StatusCallback cb) {
MAKE_SURE_IO_THREAD(CreateBond, BindToCurrentSequence(std::move(cb)));
LOG(INFO) << "CreateBond(" << util::AddrLastByteString(addr_) << ")";
if (!gatt_client_manager_) {
LOG(ERROR) << __func__ << " failed: Destroyed";
EXEC_CB_AND_RET(cb, false);
}
if (!connected_) {
LOG(ERROR) << "Not connected";
EXEC_CB_AND_RET(cb, false);
}
if (create_bond_pending_ || remove_bond_pending_) {
// TODO(tiansong): b/120489954 Implement queuing and timeout logic.
LOG(ERROR) << __func__ << " failed: waiting for pending bond command";
EXEC_CB_AND_RET(cb, false);
}
if (bonded_) {
LOG(ERROR) << "Already bonded";
EXEC_CB_AND_RET(cb, false);
}
if (!gatt_client_manager_->gatt_client()->CreateBond(addr_)) {
LOG(ERROR) << __func__ << " failed";
EXEC_CB_AND_RET(cb, false);
}
create_bond_pending_ = true;
create_bond_cb_ = std::move(cb);
}
void RemoteDeviceImpl::RemoveBond(StatusCallback cb) {
MAKE_SURE_IO_THREAD(RemoveBond, BindToCurrentSequence(std::move(cb)));
LOG(INFO) << "RemoveBond(" << util::AddrLastByteString(addr_) << ")";
if (!gatt_client_manager_) {
LOG(ERROR) << __func__ << " failed: Destroyed";
EXEC_CB_AND_RET(cb, false);
}
if (create_bond_pending_ || remove_bond_pending_) {
// TODO(tiansong): b/120489954 Implement queuing and timeout logic.
LOG(ERROR) << __func__ << " failed: waiting for pending bond command";
EXEC_CB_AND_RET(cb, false);
}
if (!bonded_) {
LOG(WARNING) << "Not bonded";
}
if (!gatt_client_manager_->gatt_client()->RemoveBond(addr_)) {
LOG(ERROR) << __func__ << " failed";
EXEC_CB_AND_RET(cb, false);
}
remove_bond_pending_ = true;
remove_bond_cb_ = std::move(cb);
}
void RemoteDeviceImpl::ReadRemoteRssi(RssiCallback cb) {
MAKE_SURE_IO_THREAD(ReadRemoteRssi, BindToCurrentSequence(std::move(cb)));
if (!gatt_client_manager_) {
LOG(ERROR) << __func__ << " failed: Destroyed";
EXEC_CB_AND_RET(cb, false, 0);
}
if (rssi_pending_) {
LOG(ERROR) << "Read remote RSSI already pending";
EXEC_CB_AND_RET(cb, false, 0);
}
rssi_pending_ = true;
rssi_cb_ = std::move(cb);
gatt_client_manager_->EnqueueReadRemoteRssiRequest(addr_);
}
void RemoteDeviceImpl::RequestMtu(int mtu, StatusCallback cb) {
MAKE_SURE_IO_THREAD(RequestMtu, mtu, BindToCurrentSequence(std::move(cb)));
LOG(INFO) << "RequestMtu(" << util::AddrLastByteString(addr_) << ", " << mtu
<< ")";
DCHECK(cb);
if (!gatt_client_manager_) {
LOG(ERROR) << __func__ << " failed: Destroyed";
EXEC_CB_AND_RET(cb, false);
}
CHECK_CONNECTED(cb);
mtu_callbacks_.push(std::move(cb));
EnqueueOperation(
__func__, base::BindOnce(&RemoteDeviceImpl::RequestMtuImpl, this, mtu));
}
void RemoteDeviceImpl::ConnectionParameterUpdate(int min_interval,
int max_interval,
int latency,
int timeout,
StatusCallback cb) {
MAKE_SURE_IO_THREAD(ConnectionParameterUpdate, min_interval, max_interval,
latency, timeout, BindToCurrentSequence(std::move(cb)));
LOG(INFO) << "ConnectionParameterUpdate(" << util::AddrLastByteString(addr_)
<< ", " << min_interval << ", " << max_interval << ", " << latency
<< ", " << timeout << ")";
if (!gatt_client_manager_) {
LOG(ERROR) << __func__ << " failed: Destroyed";
EXEC_CB_AND_RET(cb, false);
}
CHECK_CONNECTED(cb);
bool ret = gatt_client_manager_->gatt_client()->ConnectionParameterUpdate(
addr_, min_interval, max_interval, latency, timeout);
LOG_EXEC_CB_AND_RET(cb, ret);
}
bool RemoteDeviceImpl::IsConnected() {
return connected_ && !disconnect_pending_;
}
bool RemoteDeviceImpl::IsBonded() {
return bonded_;
}
int RemoteDeviceImpl::GetMtu() {
return mtu_;
}
void RemoteDeviceImpl::GetServices(
base::OnceCallback<void(std::vector<scoped_refptr<RemoteService>>)> cb) {
MAKE_SURE_IO_THREAD(GetServices, BindToCurrentSequence(std::move(cb)));
auto ret = GetServicesSync();
EXEC_CB_AND_RET(cb, std::move(ret));
}
std::vector<scoped_refptr<RemoteService>> RemoteDeviceImpl::GetServicesSync() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
std::vector<scoped_refptr<RemoteService>> services;
services.reserve(uuid_to_service_.size());
for (const auto& pair : uuid_to_service_)
services.push_back(pair.second);
return services;
}
void RemoteDeviceImpl::GetServiceByUuid(
const bluetooth_v2_shlib::Uuid& uuid,
base::OnceCallback<void(scoped_refptr<RemoteService>)> cb) {
MAKE_SURE_IO_THREAD(GetServiceByUuid, uuid,
BindToCurrentSequence(std::move(cb)));
auto ret = GetServiceByUuidSync(uuid);
EXEC_CB_AND_RET(cb, std::move(ret));
}
const bluetooth_v2_shlib::Addr& RemoteDeviceImpl::addr() const {
return addr_;
}
void RemoteDeviceImpl::ReadCharacteristic(
scoped_refptr<RemoteCharacteristicImpl> characteristic,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req,
RemoteCharacteristic::ReadCallback cb) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
handle_to_characteristic_read_cbs_[characteristic->handle()].push(
std::move(cb));
EnqueueOperation(
__func__, base::BindOnce(&RemoteDeviceImpl::ReadCharacteristicImpl, this,
std::move(characteristic), auth_req));
}
void RemoteDeviceImpl::WriteCharacteristic(
scoped_refptr<RemoteCharacteristicImpl> characteristic,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req,
bluetooth_v2_shlib::Gatt::WriteType write_type,
std::vector<uint8_t> value,
RemoteCharacteristic::StatusCallback cb) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
handle_to_characteristic_write_cbs_[characteristic->handle()].push(
std::move(cb));
EnqueueOperation(
__func__, base::BindOnce(&RemoteDeviceImpl::WriteCharacteristicImpl, this,
std::move(characteristic), auth_req, write_type,
std::move(value)));
}
void RemoteDeviceImpl::ReadDescriptor(
scoped_refptr<RemoteDescriptorImpl> descriptor,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req,
RemoteDescriptor::ReadCallback cb) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
handle_to_descriptor_read_cbs_[descriptor->handle()].push(std::move(cb));
EnqueueOperation(__func__,
base::BindOnce(&RemoteDeviceImpl::ReadDescriptorImpl, this,
std::move(descriptor), auth_req));
}
void RemoteDeviceImpl::WriteDescriptor(
scoped_refptr<RemoteDescriptorImpl> descriptor,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req,
std::vector<uint8_t> value,
RemoteDescriptor::StatusCallback cb) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
handle_to_descriptor_write_cbs_[descriptor->handle()].push(std::move(cb));
EnqueueOperation(
__func__,
base::BindOnce(&RemoteDeviceImpl::WriteDescriptorImpl, this,
std::move(descriptor), auth_req, std::move(value)));
}
scoped_refptr<RemoteService> RemoteDeviceImpl::GetServiceByUuidSync(
const bluetooth_v2_shlib::Uuid& uuid) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
auto it = uuid_to_service_.find(uuid);
if (it == uuid_to_service_.end())
return nullptr;
return it->second;
}
void RemoteDeviceImpl::SetConnected(bool connected) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
// We only set connected = true and call the callback after services are
// discovered.
if (!connected) {
connected_ = false;
ConnectComplete(false);
}
if (disconnect_pending_) {
disconnect_pending_ = false;
if (disconnect_cb_) {
std::move(disconnect_cb_).Run(!connected);
}
}
if (!connected && rssi_pending_) {
LOG(ERROR) << "Read remote RSSI failed: disconnected";
if (rssi_cb_) {
std::move(rssi_cb_).Run(false, 0);
}
rssi_pending_ = false;
}
if (connected) {
if (!gatt_client_manager_) {
LOG(ERROR) << "Couldn't discover services: Destroyed";
return;
}
if (!gatt_client_manager_->gatt_client()->GetServices(addr_)) {
LOG(ERROR) << "Couldn't discover services, disconnecting";
Disconnect({});
ConnectComplete(false);
}
} else {
// Reset state after disconnection
mtu_ = kDefaultMtu;
ClearServices();
}
}
void RemoteDeviceImpl::SetBonded(bool bonded) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
bonded_ = bonded;
if (create_bond_pending_) {
create_bond_pending_ = false;
if (create_bond_cb_) {
std::move(create_bond_cb_).Run(bonded);
}
}
if (remove_bond_pending_) {
remove_bond_pending_ = false;
if (remove_bond_cb_) {
std::move(remove_bond_cb_).Run(!bonded);
}
}
}
void RemoteDeviceImpl::SetServicesDiscovered(bool discovered) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
services_discovered_ = discovered;
if (!discovered) {
return;
}
connected_ = true;
ConnectComplete(true);
}
bool RemoteDeviceImpl::GetServicesDiscovered() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
return services_discovered_;
}
void RemoteDeviceImpl::SetMtu(int mtu) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
mtu_ = mtu;
if (!mtu_callbacks_.empty()) {
std::move(mtu_callbacks_.front()).Run(true);
mtu_callbacks_.pop();
NotifyQueueOperationComplete();
}
}
scoped_refptr<RemoteCharacteristic> RemoteDeviceImpl::CharacteristicFromHandle(
uint16_t handle) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
auto it = handle_to_characteristic_.find(handle);
if (it == handle_to_characteristic_.end())
return nullptr;
return it->second;
}
void RemoteDeviceImpl::OnCharacteristicRead(bool status,
uint16_t handle,
const std::vector<uint8_t>& value) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
auto it = handle_to_characteristic_read_cbs_.find(handle);
if (it == handle_to_characteristic_read_cbs_.end() || it->second.empty()) {
LOG(ERROR) << "No such characteristic read";
} else {
std::move(it->second.front()).Run(status, value);
it->second.pop();
}
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::OnCharacteristicWrite(bool status, uint16_t handle) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
auto it = handle_to_characteristic_write_cbs_.find(handle);
if (it == handle_to_characteristic_write_cbs_.end() || it->second.empty()) {
LOG(ERROR) << "No such characteristic write";
} else {
std::move(it->second.front()).Run(status);
it->second.pop();
}
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::OnDescriptorRead(bool status,
uint16_t handle,
const std::vector<uint8_t>& value) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
auto it = handle_to_descriptor_read_cbs_.find(handle);
if (it == handle_to_descriptor_read_cbs_.end() || it->second.empty()) {
LOG(ERROR) << "No such descriptor read";
} else {
std::move(it->second.front()).Run(status, value);
it->second.pop();
}
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::OnDescriptorWrite(bool status, uint16_t handle) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
auto it = handle_to_descriptor_write_cbs_.find(handle);
if (it == handle_to_descriptor_write_cbs_.end() || it->second.empty()) {
LOG(ERROR) << "No such descriptor write";
} else {
std::move(it->second.front()).Run(status);
it->second.pop();
}
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::OnGetServices(
const std::vector<bluetooth_v2_shlib::Gatt::Service>& services) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ClearServices();
OnServicesAdded(services);
}
void RemoteDeviceImpl::OnServicesRemoved(uint16_t start_handle,
uint16_t end_handle) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
for (auto it = uuid_to_service_.begin(); it != uuid_to_service_.end();) {
if (it->second->handle() >= start_handle &&
it->second->handle() <= end_handle) {
for (auto& characteristic : it->second->GetCharacteristics()) {
handle_to_characteristic_.erase(characteristic->handle());
}
it = uuid_to_service_.erase(it);
} else {
++it;
}
}
}
void RemoteDeviceImpl::OnServicesAdded(
const std::vector<bluetooth_v2_shlib::Gatt::Service>& services) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
for (const auto& service : services) {
uuid_to_service_[service.uuid] = new RemoteServiceImpl(
this, gatt_client_manager_, service, io_task_runner_);
}
for (const auto& pair : uuid_to_service_) {
for (auto& characteristic : pair.second->GetCharacteristics()) {
handle_to_characteristic_.emplace(
characteristic->handle(),
static_cast<RemoteCharacteristicImpl*>(characteristic.get()));
}
}
}
void RemoteDeviceImpl::OnReadRemoteRssiComplete(bool status, int rssi) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
rssi_pending_ = false;
if (rssi_cb_) {
std::move(rssi_cb_).Run(status, rssi);
}
}
void RemoteDeviceImpl::ConnectComplete(bool success) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (connect_cb_) {
std::move(connect_cb_)
.Run(success ? ConnectStatus::kSuccess : ConnectStatus::kFailure);
}
}
void RemoteDeviceImpl::EnqueueOperation(const std::string& name,
base::OnceClosure op) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
command_queue_.emplace_back(name, std::move(op));
// Run the operation if this is the only operation in the queue. Otherwise, it
// will be executed when the current operation completes.
if (command_queue_.size() == 1) {
RunNextOperation();
}
}
void RemoteDeviceImpl::NotifyQueueOperationComplete() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (command_queue_.empty()) {
LOG(ERROR) << "Command queue is empty, device might be disconnected";
return;
}
command_queue_.pop_front();
command_timeout_timer_.Stop();
// Run the next operation if there is one in the queue.
if (!command_queue_.empty()) {
RunNextOperation();
}
}
void RemoteDeviceImpl::RunNextOperation() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (command_queue_.empty()) {
LOG(ERROR) << "Command queue is empty, device might be disconnected";
return;
}
auto& front = command_queue_.front();
command_timeout_timer_.Start(
FROM_HERE, kCommandTimeout,
base::BindRepeating(&RemoteDeviceImpl::OnCommandTimeout, this,
front.first));
std::move(front.second).Run();
}
void RemoteDeviceImpl::RequestMtuImpl(int mtu) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (gatt_client_manager_->gatt_client()->RequestMtu(addr_, mtu)) {
return;
}
LOG(ERROR) << __func__ << " failed";
DCHECK(!mtu_callbacks_.empty());
std::move(mtu_callbacks_.front()).Run(false);
mtu_callbacks_.pop();
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::ReadCharacteristicImpl(
scoped_refptr<RemoteCharacteristicImpl> characteristic,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (gatt_client_manager_->gatt_client()->ReadCharacteristic(
addr(), characteristic->characteristic(), auth_req)) {
return;
}
LOG(ERROR) << __func__ << " failed";
auto it = handle_to_characteristic_read_cbs_.find(characteristic->handle());
CHECK(it != handle_to_characteristic_read_cbs_.end(),
base::NotFatalUntil::M130);
DCHECK(!it->second.empty());
std::move(it->second.front()).Run(false, {});
it->second.pop();
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::WriteCharacteristicImpl(
scoped_refptr<RemoteCharacteristicImpl> characteristic,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req,
bluetooth_v2_shlib::Gatt::WriteType write_type,
std::vector<uint8_t> value) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (gatt_client_manager_->gatt_client()->WriteCharacteristic(
addr(), characteristic->characteristic(), auth_req, write_type,
value)) {
return;
}
LOG(ERROR) << __func__ << " failed";
auto it = handle_to_characteristic_write_cbs_.find(characteristic->handle());
CHECK(it != handle_to_characteristic_write_cbs_.end(),
base::NotFatalUntil::M130);
DCHECK(!it->second.empty());
std::move(it->second.front()).Run(false);
it->second.pop();
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::ReadDescriptorImpl(
scoped_refptr<RemoteDescriptorImpl> descriptor,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (gatt_client_manager_->gatt_client()->ReadDescriptor(
addr(), descriptor->descriptor(), auth_req)) {
return;
}
LOG(ERROR) << __func__ << " failed";
auto it = handle_to_descriptor_read_cbs_.find(descriptor->handle());
CHECK(it != handle_to_descriptor_read_cbs_.end(), base::NotFatalUntil::M130);
DCHECK(!it->second.empty());
std::move(it->second.front()).Run(false, {});
it->second.pop();
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::WriteDescriptorImpl(
scoped_refptr<RemoteDescriptorImpl> descriptor,
bluetooth_v2_shlib::Gatt::Client::AuthReq auth_req,
std::vector<uint8_t> value) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (gatt_client_manager_->gatt_client()->WriteDescriptor(
addr(), descriptor->descriptor(), auth_req, value)) {
return;
}
LOG(ERROR) << __func__ << " failed";
auto it = handle_to_descriptor_write_cbs_.find(descriptor->handle());
CHECK(it != handle_to_descriptor_write_cbs_.end(), base::NotFatalUntil::M130);
DCHECK(!it->second.empty());
std::move(it->second.front()).Run(false);
it->second.pop();
NotifyQueueOperationComplete();
}
void RemoteDeviceImpl::ClearServices() {
for (auto& item : handle_to_characteristic_) {
item.second->Invalidate();
}
uuid_to_service_.clear();
handle_to_characteristic_.clear();
command_queue_.clear();
command_timeout_timer_.Stop();
while (!mtu_callbacks_.empty()) {
LOG(ERROR) << "RequestMtu failed: disconnected";
std::move(mtu_callbacks_.front()).Run(false);
mtu_callbacks_.pop();
}
for (auto& item : handle_to_characteristic_read_cbs_) {
auto& queue = item.second;
while (!queue.empty()) {
LOG(ERROR) << "Characteristic read failed: disconnected";
std::move(queue.front()).Run(false, {});
queue.pop();
}
}
handle_to_characteristic_read_cbs_.clear();
for (auto& item : handle_to_characteristic_write_cbs_) {
auto& queue = item.second;
while (!queue.empty()) {
LOG(ERROR) << "Characteristic write failed: disconnected";
std::move(queue.front()).Run(false);
queue.pop();
}
}
handle_to_characteristic_write_cbs_.clear();
for (auto& item : handle_to_descriptor_read_cbs_) {
auto& queue = item.second;
while (!queue.empty()) {
LOG(ERROR) << "Descriptor read failed: disconnected";
std::move(queue.front()).Run(false, {});
queue.pop();
}
}
handle_to_descriptor_read_cbs_.clear();
for (auto& item : handle_to_descriptor_write_cbs_) {
auto& queue = item.second;
while (!queue.empty()) {
LOG(ERROR) << "Descriptor write failed: disconnected";
std::move(queue.front()).Run(false);
queue.pop();
}
}
handle_to_descriptor_write_cbs_.clear();
}
void RemoteDeviceImpl::OnCommandTimeout(const std::string& name) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
LOG(ERROR) << name << "(" << util::AddrLastByteString(addr_) << ")"
<< " timed out. Disconnecting";
Disconnect(base::DoNothing());
}
} // namespace bluetooth
} // namespace chromecast