blob: 0b4b55ecd25dba842bfeadf1fe980e7be27452f4 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h"
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/rfcomm.h>
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "chromeos/ash/experiences/arc/bluetooth/bluetooth_type_converters.h"
#include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/floss/bluetooth_device_floss.h"
#include "device/bluetooth/floss/floss_dbus_manager.h"
#include "device/bluetooth/floss/floss_sdp_types.h"
#include "device/bluetooth/floss/floss_socket_manager.h"
using device::BluetoothUUID;
using floss::BluetoothDeviceFloss;
namespace arc {
ArcFlossBridge::ArcFlossBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service)
: ArcBluetoothBridge(context, bridge_service) {}
ArcFlossBridge::~ArcFlossBridge() {
if (GetAdapter() && GetAdapter()->IsPowered() &&
floss::FlossDBusManager::Get()->GetAdapterClient()->HasObserver(this)) {
floss::FlossDBusManager::Get()->GetAdapterClient()->RemoveObserver(this);
}
}
floss::BluetoothAdapterFloss* ArcFlossBridge::GetAdapter() const {
return static_cast<floss::BluetoothAdapterFloss*>(bluetooth_adapter_.get());
}
void ArcFlossBridge::HandlePoweredOn() {
if (!floss::FlossDBusManager::Get()->GetAdapterClient()->HasObserver(this)) {
floss::FlossDBusManager::Get()->GetAdapterClient()->AddObserver(this);
}
}
void ArcFlossBridge::OnSdpSearchResult(mojom::BluetoothAddressPtr remote_addr,
const device::BluetoothUUID& target_uuid,
floss::DBusResult<bool> result) {
if (!result.has_value()) {
OnGetServiceRecordsError(
std::move(remote_addr), target_uuid,
bluez::BluetoothServiceRecordBlueZ::ErrorCode::UNKNOWN);
return;
}
if (!*result) {
OnGetServiceRecordsError(
std::move(remote_addr), target_uuid,
bluez::BluetoothServiceRecordBlueZ::ErrorCode::UNKNOWN);
return;
}
}
void ArcFlossBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr,
const BluetoothUUID& target_uuid) {
if (!AdapterReadyAndRegistered()) {
OnGetServiceRecordsError(
std::move(remote_addr), target_uuid,
bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY);
return;
}
const floss::FlossDeviceId remote_device = floss::FlossDeviceId(
{.address = remote_addr->To<std::string>(), .name = ""});
floss::ResponseCallback<bool> response_callback = base::BindOnce(
&ArcFlossBridge::OnSdpSearchResult, weak_factory_.GetWeakPtr(),
std::move(remote_addr), target_uuid);
floss::FlossDBusManager::Get()->GetAdapterClient()->SdpSearch(
std::move(response_callback), remote_device, target_uuid);
}
void ArcFlossBridge::CreateSdpRecordComplete(device::BluetoothUUID uuid,
floss::DBusResult<bool> result) {
if (!result.has_value()) {
arc::mojom::BluetoothCreateSdpRecordResultPtr callback_result =
arc::mojom::BluetoothCreateSdpRecordResult::New();
callback_result->status = mojom::BluetoothStatus::FAIL;
CompleteCreateSdpRecord(uuid, std::move(callback_result));
return;
}
if (!*result) {
arc::mojom::BluetoothCreateSdpRecordResultPtr callback_result =
arc::mojom::BluetoothCreateSdpRecordResult::New();
callback_result->status = mojom::BluetoothStatus::FAIL;
CompleteCreateSdpRecord(uuid, std::move(callback_result));
return;
}
}
void ArcFlossBridge::CreateSdpRecord(mojom::BluetoothSdpRecordPtr record_mojo,
CreateSdpRecordCallback callback) {
if (!AdapterReadyAndRegistered()) {
arc::mojom::BluetoothCreateSdpRecordResultPtr result =
arc::mojom::BluetoothCreateSdpRecordResult::New();
result->status = mojom::BluetoothStatus::NOT_READY;
std::move(callback).Run(std::move(result));
return;
}
const floss::BtSdpRecord sdp_record =
mojo::TypeConverter<floss::BtSdpRecord,
bluez::BluetoothServiceRecordBlueZ>::
Convert(mojo::TypeConverter<
bluez::BluetoothServiceRecordBlueZ,
mojom::BluetoothSdpRecordPtr>::Convert(record_mojo));
const std::optional<device::BluetoothUUID> uuid =
floss::GetUUIDFromSdpRecord(sdp_record);
if (!uuid.has_value()) {
arc::mojom::BluetoothCreateSdpRecordResultPtr result =
arc::mojom::BluetoothCreateSdpRecordResult::New();
result->status = mojom::BluetoothStatus::PARM_INVALID;
std::move(callback).Run(std::move(result));
return;
}
create_sdp_record_callbacks_.insert_or_assign(*uuid, std::move(callback));
floss::ResponseCallback<bool> response_callback =
base::BindOnce(&ArcFlossBridge::CreateSdpRecordComplete,
weak_factory_.GetWeakPtr(), *uuid);
floss::FlossDBusManager::Get()->GetAdapterClient()->CreateSdpRecord(
std::move(response_callback), sdp_record);
}
void ArcFlossBridge::RemoveSdpRecordComplete(RemoveSdpRecordCallback callback,
floss::DBusResult<bool> result) {
if (!result.has_value()) {
std::move(callback).Run(arc::mojom::BluetoothStatus::FAIL);
return;
}
if (!*result) {
std::move(callback).Run(arc::mojom::BluetoothStatus::FAIL);
return;
}
std::move(callback).Run(arc::mojom::BluetoothStatus::SUCCESS);
}
void ArcFlossBridge::RemoveSdpRecord(uint32_t service_handle,
RemoveSdpRecordCallback callback) {
if (!AdapterReadyAndRegistered()) {
std::move(callback).Run(arc::mojom::BluetoothStatus::NOT_READY);
return;
}
floss::ResponseCallback<bool> response_callback =
base::BindOnce(&ArcFlossBridge::RemoveSdpRecordComplete,
weak_factory_.GetWeakPtr(), std::move(callback));
floss::FlossDBusManager::Get()->GetAdapterClient()->RemoveSdpRecord(
std::move(response_callback), service_handle);
}
namespace {
void OnNoOpBtifResult(
floss::DBusResult<floss::FlossDBusClient::BtifStatus> result) {}
floss::FlossSocketManager::Security GetSecureFromFlags(
const mojom::BluetoothSocketFlagsPtr flags) {
// From Floss's masking logic secure = encrypt+auth
return (flags->encrypt && flags->auth)
? floss::FlossSocketManager::Security::kSecure
: floss::FlossSocketManager::Security::kInsecure;
}
} // namespace
void ArcFlossBridge::OnCloseBluetoothListeningSocketComplete(
BluetoothListeningSocket* socket,
floss::DBusResult<floss::FlossDBusClient::BtifStatus> result) {
// We are not going to keep the socket around regardless of whether Floss
// succeeded, but we give it a chance.
listening_sockets_.erase(socket);
}
void ArcFlossBridge::CloseBluetoothListeningSocket(
BluetoothListeningSocket* ptr) {
if (!listening_sockets_.contains(ptr)) {
return;
}
floss::ResponseCallback<floss::FlossAdapterClient::BtifStatus>
response_callback = base::BindOnce(
&ArcFlossBridge::OnCloseBluetoothListeningSocketComplete,
weak_factory_.GetWeakPtr(), ptr);
floss::FlossDBusManager::Get()->GetSocketManager()->Close(
listening_sockets_[ptr].second, std::move(response_callback));
}
void ArcFlossBridge::CloseBluetoothConnectingSocket(
BluetoothConnectingSocket* ptr) {
auto found = connecting_sockets_.find(ptr);
connecting_sockets_.erase(found);
}
void ArcFlossBridge::SdpSearchComplete(
const floss::FlossDeviceId device,
const device::BluetoothUUID uuid,
const std::vector<floss::BtSdpRecord>& records) {
mojom::BluetoothAddressPtr address =
mojom::BluetoothAddress::From(device.address);
std::vector<bluez::BluetoothServiceRecordBlueZ> records_bluez;
for (const auto& record : records) {
records_bluez.push_back(
mojo::TypeConverter<bluez::BluetoothServiceRecordBlueZ,
floss::BtSdpRecord>::Convert(record));
std::optional<floss::BtSdpHeaderOverlay> header =
GetHeaderOverlayFromSdpRecord(record);
if (!header.has_value()) {
continue;
}
// This record may be for an L2CAP service, but callers have to specify what
// protocol they want to use anyway.
uuid_lookups_.insert_or_assign(
std::make_pair(device.address, header->rfcomm_channel_number),
header->uuid);
}
OnGetServiceRecordsFinished(std::move(address), uuid, records_bluez);
}
void ArcFlossBridge::SdpRecordCreated(const floss::BtSdpRecord record,
const int32_t handle) {
const std::optional<device::BluetoothUUID> uuid =
floss::GetUUIDFromSdpRecord(record);
if (!uuid.has_value()) {
return;
}
arc::mojom::BluetoothCreateSdpRecordResultPtr callback_result =
arc::mojom::BluetoothCreateSdpRecordResult::New();
callback_result->status = mojom::BluetoothStatus::SUCCESS;
callback_result->service_handle = handle;
CompleteCreateSdpRecord(*uuid, std::move(callback_result));
}
void ArcFlossBridge::SendCachedDevices() const {
auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->bluetooth(), OnDevicePropertiesChanged);
if (!bluetooth_instance) {
return;
}
for (const auto* device : bluetooth_adapter_->GetDevices()) {
const BluetoothDeviceFloss* floss_device =
static_cast<const BluetoothDeviceFloss*>(device);
if (!floss_device->HasReadProperties()) {
VLOG(1) << "Skipping device that hasn't read properties: "
<< floss_device->GetAddress();
continue;
}
// Since a cached device may not be a currently available device, we use
// OnDevicePropertiesChanged() instead of OnDeviceFound() to avoid trigger
// the logic of device found in Android.
bluetooth_instance->OnDevicePropertiesChanged(
mojom::BluetoothAddress::From(device->GetAddress()),
GetDeviceProperties(mojom::BluetoothPropertyType::ALL, device));
}
}
void ArcFlossBridge::StartLEScanImpl() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!bluetooth_adapter_) {
LOG(DFATAL) << "Bluetooth adapter does not exist.";
return;
}
if (ble_scan_session_) {
LOG(ERROR) << "LE scan already running.";
StartLEScanOffTimer();
scanned_devices_.clear();
discovery_queue_.Pop();
return;
}
ble_scan_session_ = bluetooth_adapter_->StartLowEnergyScanSession(
nullptr, weak_factory_.GetWeakPtr());
}
void ArcFlossBridge::ResetLEScanSession() {
if (ble_scan_session_) {
ble_scan_session_.reset();
}
}
bool ArcFlossBridge::IsDiscoveringOrScanning() {
return discovery_session_ || ble_scan_session_;
}
void ArcFlossBridge::CreateBluetoothListenSocket(
mojom::BluetoothSocketType type,
mojom::BluetoothSocketFlagsPtr flags,
int port,
ArcFlossBridge::BluetoothSocketListenCallback callback) {
if (!AdapterReadyAndRegistered()) {
return;
}
if (type != mojom::BluetoothSocketType::TYPE_RFCOMM &&
type != mojom::BluetoothSocketType::TYPE_L2CAP_LE) {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL, /*port=*/0,
mojo::PendingReceiver<mojom::BluetoothListenSocketClient>());
return;
}
auto sock_wrapper = std::make_unique<BluetoothListeningSocket>();
sock_wrapper->sock_type = type;
auto connection_accepted_callback =
base::BindRepeating(&ArcFlossBridge::OnConnectionAccepted,
weak_factory_.GetWeakPtr(), sock_wrapper.get());
int socket_ready_callback_id = next_socket_ready_callback_id_++;
socket_ready_callbacks_.insert_or_assign(socket_ready_callback_id,
std::move(callback));
auto connection_state_changed_callback = base::BindRepeating(
&ArcFlossBridge::OnConnectionStateChanged, weak_factory_.GetWeakPtr(),
// TODO(crbug.com/40061562): Remove `UnsafeDanglingUntriaged`
base::UnsafeDanglingUntriaged(sock_wrapper.get()),
socket_ready_callback_id);
floss::ResponseCallback<floss::FlossDBusClient::BtifStatus>
response_callback =
base::BindOnce(&ArcFlossBridge::OnCreateListenSocketCallback,
weak_factory_.GetWeakPtr(), std::move(sock_wrapper),
socket_ready_callback_id);
switch (type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM: {
floss::FlossDBusManager::Get()->GetSocketManager()->ListenUsingRfcommAlt(
std::nullopt, std::nullopt, port,
floss::FlossSocketManager::GetRawFlossFlagsFromBluetoothFlags(
flags->encrypt, flags->auth, flags->auth_mitm,
flags->auth_16_digit, /*no_sdp=*/true),
std::move(response_callback),
std::move(connection_state_changed_callback),
std::move(connection_accepted_callback));
break;
}
case mojom::BluetoothSocketType::TYPE_L2CAP_LE: {
floss::FlossDBusManager::Get()->GetSocketManager()->ListenUsingL2capLe(
GetSecureFromFlags(std::move(flags)), std::move(response_callback),
std::move(connection_state_changed_callback),
std::move(connection_accepted_callback));
break;
}
default: {
return;
}
}
}
void ArcFlossBridge::CreateBluetoothConnectSocket(
mojom::BluetoothSocketType type,
mojom::BluetoothSocketFlagsPtr flags,
mojom::BluetoothAddressPtr addr,
int port,
ArcFlossBridge::BluetoothSocketConnectCallback callback) {
if (!AdapterReadyAndRegistered()) {
return;
}
const floss::FlossDeviceId remote_device =
floss::FlossDeviceId({.address = addr->To<std::string>(), .name = ""});
auto sock_wrapper = std::make_unique<BluetoothConnectingSocket>();
sock_wrapper->sock_type = type;
switch (type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM: {
const std::pair<std::string, int32_t> uuid_lookup_key =
std::make_pair(remote_device.address, port);
if (!uuid_lookups_.contains(uuid_lookup_key)) {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL,
mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
return;
}
floss::FlossDBusManager::Get()->GetSocketManager()->ConnectUsingRfcomm(
remote_device, uuid_lookups_.at(uuid_lookup_key),
GetSecureFromFlags(std::move(flags)),
base::BindOnce(&ArcFlossBridge::OnCreateConnectSocketCallback,
weak_factory_.GetWeakPtr(), std::move(sock_wrapper),
std::move(callback)));
break;
}
case mojom::BluetoothSocketType::TYPE_L2CAP_LE: {
floss::FlossDBusManager::Get()->GetSocketManager()->ConnectUsingL2capLe(
remote_device, port, GetSecureFromFlags(std::move(flags)),
base::BindOnce(&ArcFlossBridge::OnCreateConnectSocketCallback,
weak_factory_.GetWeakPtr(), std::move(sock_wrapper),
std::move(callback)));
break;
}
default: {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL,
mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
return;
}
}
}
void ArcFlossBridge::OnCreateListenSocketCallback(
std::unique_ptr<ArcBluetoothBridge::BluetoothListeningSocket> sock_wrapper,
int socket_ready_callback_id,
floss::DBusResult<floss::FlossDBusClient::BtifStatus> result) {
ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper_to_pass =
sock_wrapper.get();
listening_sockets_.insert_or_assign(
sock_wrapper.get(),
std::make_pair(std::move(sock_wrapper), empty_socket_id_));
if (!result.has_value()) {
CompleteListenSocketReady(socket_ready_callback_id,
mojom::BluetoothStatus::FAIL,
sock_wrapper_to_pass);
return;
}
if (*result != floss::FlossDBusClient::BtifStatus::kSuccess) {
CompleteListenSocketReady(socket_ready_callback_id,
mojom::BluetoothStatus::FAIL,
sock_wrapper_to_pass);
return;
}
}
void ArcFlossBridge::OnCreateConnectSocketCallback(
std::unique_ptr<ArcBluetoothBridge::BluetoothConnectingSocket> sock_wrapper,
ArcFlossBridge::BluetoothSocketConnectCallback callback,
floss::FlossDBusClient::BtifStatus status,
std::optional<floss::FlossSocketManager::FlossSocket>&& socket) {
if (status != floss::FlossDBusClient::BtifStatus::kSuccess) {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL,
mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
return;
}
if (!socket || !socket->fd) {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL,
mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
return;
}
sock_wrapper->file = std::move(socket->fd.value());
std::move(callback).Run(mojom::BluetoothStatus::SUCCESS,
sock_wrapper->remote.BindNewPipeAndPassReceiver());
auto connection = mojom::BluetoothSocketConnection::New();
mojo::ScopedHandle handle = mojo::WrapPlatformHandle(
mojo::PlatformHandle(std::move(sock_wrapper->file)));
connection->sock = std::move(handle);
switch (sock_wrapper->sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
connection->addr = mojom::BluetoothAddress::From<std::string>(
socket->remote_device.address);
connection->port = socket->port;
break;
default:
LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type;
return;
}
sock_wrapper->remote->OnConnected(std::move(connection));
connecting_sockets_.insert(std::move(sock_wrapper));
}
void ArcFlossBridge::OnConnectionStateChanged(
ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
int socket_ready_callback_id,
floss::FlossSocketManager::ServerSocketState state,
floss::FlossSocketManager::FlossListeningSocket socket,
floss::FlossSocketManager::BtifStatus status) {
if (!sock_wrapper) {
// sock_wrapper has been disposed (or worse), nothing to do, but resolve
// callback with failure if it is still around.
CompleteListenSocketReady(socket_ready_callback_id,
mojom::BluetoothStatus::FAIL, sock_wrapper,
socket);
return;
}
if (status != floss::FlossSocketManager::BtifStatus::kSuccess) {
LOG(ERROR) << "Received OnConnectionStateChanged callback with "
"non-Success status: "
<< static_cast<int>(status);
CompleteListenSocketReady(socket_ready_callback_id,
mojom::BluetoothStatus::FAIL, sock_wrapper,
socket);
return;
}
if (state != floss::FlossSocketManager::ServerSocketState::kReady) {
// Socket isn't ready for accepting connections, nothing to do
return;
}
// At this point we may assume that listening socket creation was a
// success. Let CompleteListenSocketReady know if it doesn't already.
CompleteListenSocketReady(socket_ready_callback_id,
mojom::BluetoothStatus::SUCCESS, sock_wrapper,
socket);
if (!AdapterReadyAndRegistered()) {
return;
}
floss::ResponseCallback<floss::FlossDBusClient::BtifStatus>
response_callback = base::BindOnce(&OnNoOpBtifResult);
// TODO: figure out the correct timeout here
floss::FlossDBusManager::Get()->GetSocketManager()->Accept(
socket.id, /*timeout_ms=*/std::nullopt, std::move(response_callback));
}
void ArcFlossBridge::OnConnectionAccepted(
const ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
floss::FlossSocketManager::FlossSocket&& socket) {
if (!sock_wrapper) {
// sock_wrapper has been disposed (or worse), nothing to do
return;
}
if (!socket.is_valid() || !socket.fd.has_value()) {
LOG(ERROR) << "New socket connection was accepted with invalid socket";
return;
}
mojo::ScopedHandle handle =
mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(*socket.fd)));
auto connection = mojom::BluetoothSocketConnection::New();
connection->sock = std::move(handle);
connection->addr =
mojom::BluetoothAddress::From(socket.remote_device.address);
connection->port = socket.port;
switch (socket.type) {
case floss::FlossSocketManager::SocketType::kRfcomm:
case floss::FlossSocketManager::SocketType::kL2cap:
sock_wrapper->remote->OnAccepted(std::move(connection));
break;
default:
return;
}
}
int ArcFlossBridge::GetPortOrChannel(
ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
floss::FlossSocketManager::FlossListeningSocket socket) {
switch (sock_wrapper->sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
if (!socket.channel) {
return 0;
}
return *socket.channel;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
if (!socket.psm) {
return 0;
}
return *socket.psm;
default:
return 0;
}
}
void ArcFlossBridge::CompleteListenSocketReady(
int socket_ready_callback_id,
mojom::BluetoothStatus status,
ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper) {
CompleteListenSocketReady(socket_ready_callback_id, status,
/*port_or_channel*/ 0, sock_wrapper);
}
void ArcFlossBridge::CompleteListenSocketReady(
int socket_ready_callback_id,
mojom::BluetoothStatus status,
int port_or_channel,
ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper) {
if (!socket_ready_callbacks_.contains(socket_ready_callback_id)) {
return;
}
BluetoothSocketListenCallback callback =
std::move(socket_ready_callbacks_[socket_ready_callback_id]);
socket_ready_callbacks_.erase(socket_ready_callback_id);
if (status != mojom::BluetoothStatus::SUCCESS) {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL, /*port=*/0,
mojo::PendingReceiver<mojom::BluetoothListenSocketClient>());
} else {
std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, port_or_channel,
sock_wrapper->remote.BindNewPipeAndPassReceiver());
sock_wrapper->remote.set_disconnect_handler(
base::BindOnce(&ArcFlossBridge::CloseBluetoothListeningSocket,
weak_factory_.GetWeakPtr(), sock_wrapper));
}
}
void ArcFlossBridge::CompleteListenSocketReady(
int socket_ready_callback_id,
mojom::BluetoothStatus status,
ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
floss::FlossSocketManager::FlossListeningSocket socket) {
if (!socket_ready_callbacks_.contains(socket_ready_callback_id)) {
return;
}
if (!listening_sockets_.contains(sock_wrapper)) {
return;
}
listening_sockets_[sock_wrapper].second = socket.id;
CompleteListenSocketReady(socket_ready_callback_id, status,
GetPortOrChannel(sock_wrapper, socket),
sock_wrapper);
}
void ArcFlossBridge::CompleteCreateSdpRecord(
device::BluetoothUUID uuid,
arc::mojom::BluetoothCreateSdpRecordResultPtr result) {
if (!base::Contains(create_sdp_record_callbacks_, uuid)) {
return;
}
CreateSdpRecordCallback callback =
std::move(create_sdp_record_callbacks_[uuid]);
create_sdp_record_callbacks_.erase(uuid);
std::move(callback).Run(std::move(result));
}
bool ArcFlossBridge::AdapterReadyAndRegistered() {
if (!GetAdapter() || !GetAdapter()->IsPresent()) {
return false;
}
if (!floss::FlossDBusManager::Get()->GetAdapterClient()->HasObserver(this)) {
floss::FlossDBusManager::Get()->GetAdapterClient()->AddObserver(this);
}
return true;
}
} // namespace arc