blob: 4757c8f78c70a996ffcb71567bca766e614878d0 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "profile_server.h"
#include <zircon/status.h>
#include "helpers.h"
#include "lib/fpromise/result.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/uuid.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/types.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h"
namespace fidlbredr = fuchsia::bluetooth::bredr;
using fidlbredr::DataElement;
using fidlbredr::Profile;
namespace bthost {
namespace {
bt::l2cap::ChannelParameters FidlToChannelParameters(const fidlbredr::ChannelParameters& fidl) {
bt::l2cap::ChannelParameters params;
if (fidl.has_channel_mode()) {
switch (fidl.channel_mode()) {
case fidlbredr::ChannelMode::BASIC:
params.mode = bt::l2cap::ChannelMode::kBasic;
break;
case fidlbredr::ChannelMode::ENHANCED_RETRANSMISSION:
params.mode = bt::l2cap::ChannelMode::kEnhancedRetransmission;
break;
default:
ZX_PANIC("FIDL channel parameter contains invalid mode");
}
}
if (fidl.has_max_rx_sdu_size()) {
params.max_rx_sdu_size = fidl.max_rx_sdu_size();
}
if (fidl.has_flush_timeout()) {
params.flush_timeout = zx::duration(fidl.flush_timeout());
}
return params;
}
fidlbredr::ChannelMode ChannelModeToFidl(bt::l2cap::ChannelMode mode) {
switch (mode) {
case bt::l2cap::ChannelMode::kBasic:
return fidlbredr::ChannelMode::BASIC;
break;
case bt::l2cap::ChannelMode::kEnhancedRetransmission:
return fidlbredr::ChannelMode::ENHANCED_RETRANSMISSION;
break;
default:
ZX_PANIC("Could not convert channel parameter mode to unsupported FIDL mode");
}
}
fidlbredr::ChannelParameters ChannelInfoToFidlChannelParameters(
const bt::l2cap::ChannelInfo& info) {
fidlbredr::ChannelParameters params;
params.set_channel_mode(ChannelModeToFidl(info.mode));
params.set_max_rx_sdu_size(info.max_rx_sdu_size);
if (info.flush_timeout) {
params.set_flush_timeout(info.flush_timeout.value().get());
}
return params;
}
// NOLINTNEXTLINE(misc-no-recursion)
fidlbredr::DataElementPtr DataElementToFidl(const bt::sdp::DataElement* in) {
auto elem = std::make_unique<fidlbredr::DataElement>();
bt_log(TRACE, "fidl", "DataElementToFidl: %s", in->ToString().c_str());
ZX_DEBUG_ASSERT(in);
switch (in->type()) {
case bt::sdp::DataElement::Type::kUnsignedInt: {
switch (in->size()) {
case bt::sdp::DataElement::Size::kOneByte:
elem->set_uint8(*in->Get<uint8_t>());
break;
case bt::sdp::DataElement::Size::kTwoBytes:
elem->set_uint16(*in->Get<uint16_t>());
break;
case bt::sdp::DataElement::Size::kFourBytes:
elem->set_uint32(*in->Get<uint32_t>());
break;
case bt::sdp::DataElement::Size::kEightBytes:
elem->set_uint64(*in->Get<uint64_t>());
break;
default:
bt_log(INFO, "fidl", "no 128-bit integer support in FIDL yet");
return nullptr;
}
return elem;
}
case bt::sdp::DataElement::Type::kSignedInt: {
switch (in->size()) {
case bt::sdp::DataElement::Size::kOneByte:
elem->set_int8(*in->Get<int8_t>());
break;
case bt::sdp::DataElement::Size::kTwoBytes:
elem->set_int16(*in->Get<int16_t>());
break;
case bt::sdp::DataElement::Size::kFourBytes:
elem->set_int32(*in->Get<int32_t>());
break;
case bt::sdp::DataElement::Size::kEightBytes:
elem->set_int64(*in->Get<int64_t>());
break;
default:
bt_log(INFO, "fidl", "no 128-bit integer support in FIDL yet");
return nullptr;
}
return elem;
}
case bt::sdp::DataElement::Type::kUuid: {
auto uuid = in->Get<bt::UUID>();
ZX_DEBUG_ASSERT(uuid);
elem->set_uuid(fidl_helpers::UuidToFidl(*uuid));
return elem;
}
case bt::sdp::DataElement::Type::kString: {
elem->set_str(*in->Get<std::string>());
return elem;
}
case bt::sdp::DataElement::Type::kBoolean: {
elem->set_b(*in->Get<bool>());
return elem;
}
case bt::sdp::DataElement::Type::kSequence: {
std::vector<fidlbredr::DataElementPtr> elems;
const bt::sdp::DataElement* it;
for (size_t idx = 0; (it = in->At(idx)); ++idx) {
elems.emplace_back(DataElementToFidl(it));
}
elem->set_sequence(std::move(elems));
return elem;
}
case bt::sdp::DataElement::Type::kAlternative: {
std::vector<fidlbredr::DataElementPtr> elems;
const bt::sdp::DataElement* it;
for (size_t idx = 0; (it = in->At(idx)); ++idx) {
elems.emplace_back(DataElementToFidl(it));
}
elem->set_alternatives(std::move(elems));
return elem;
}
case bt::sdp::DataElement::Type::kUrl: {
elem->set_url(*in->GetUrl());
return elem;
}
case bt::sdp::DataElement::Type::kNull: {
bt_log(INFO, "fidl", "no support for null DataElement types in FIDL");
return nullptr;
}
}
}
fidlbredr::ProtocolDescriptorPtr DataElementToProtocolDescriptor(const bt::sdp::DataElement* in) {
auto desc = std::make_unique<fidlbredr::ProtocolDescriptor>();
if (in->type() != bt::sdp::DataElement::Type::kSequence) {
bt_log(DEBUG, "fidl", "DataElement type is not kSequence (in: %s)", bt_str(*in));
return nullptr;
}
const auto protocol_uuid = in->At(0)->Get<bt::UUID>();
if (!protocol_uuid) {
bt_log(DEBUG, "fidl", "first DataElement in sequence is not type kUUID (in: %s)", bt_str(*in));
return nullptr;
}
desc->protocol = static_cast<fidlbredr::ProtocolIdentifier>(*protocol_uuid->As16Bit());
const bt::sdp::DataElement* it;
for (size_t idx = 1; (it = in->At(idx)); ++idx) {
desc->params.push_back(std::move(*DataElementToFidl(it)));
}
return desc;
}
bt::hci::AclPriority FidlToAclPriority(fidlbredr::A2dpDirectionPriority in) {
switch (in) {
case fidlbredr::A2dpDirectionPriority::SOURCE:
return bt::hci::AclPriority::kSource;
case fidlbredr::A2dpDirectionPriority::SINK:
return bt::hci::AclPriority::kSink;
default:
return bt::hci::AclPriority::kNormal;
}
}
} // namespace
ProfileServer::ProfileServer(fxl::WeakPtr<bt::gap::Adapter> adapter,
fidl::InterfaceRequest<Profile> request)
: ServerBase(this, std::move(request)),
advertised_total_(0),
searches_total_(0),
adapter_(std::move(adapter)),
weak_ptr_factory_(this) {}
ProfileServer::~ProfileServer() {
if (adapter()) {
// Unregister anything that we have registered.
for (const auto& it : current_advertised_) {
adapter()->bredr()->UnregisterService(it.second.registration_handle);
it.second.disconnection_cb(fpromise::ok());
}
for (const auto& it : searches_) {
adapter()->bredr()->RemoveServiceSearch(it.second.search_id);
}
}
}
ProfileServer::L2capParametersExt::L2capParametersExt(
fidl::InterfaceRequest<fuchsia::bluetooth::bredr::L2capParametersExt> request,
fbl::RefPtr<bt::l2cap::Channel> channel)
: ServerBase(this, std::move(request)), channel_(std::move(channel)) {}
void ProfileServer::L2capParametersExt::RequestParameters(
fuchsia::bluetooth::bredr::ChannelParameters requested, RequestParametersCallback callback) {
if (requested.has_flush_timeout()) {
channel_->SetBrEdrAutomaticFlushTimeout(
zx::duration(requested.flush_timeout()),
[chan = channel_, cb = std::move(callback)](auto result) {
if (result.is_ok()) {
bt_log(DEBUG, "fidl",
"L2capParametersExt::RequestParameters: setting flush timeout succeeded");
} else {
bt_log(INFO, "fidl",
"L2capParametersExt::RequestParameters: setting flush timeout failed");
}
// Return the current parameters even if the request failed.
// TODO(fxb/73039): set current security requirements in returned channel parameters
cb(ChannelInfoToFidlChannelParameters(chan->info()));
});
return;
}
// No other channel parameters are supported, so just return the current parameters.
// TODO(fxb/73039): set current security requirements in returned channel parameters
callback(ChannelInfoToFidlChannelParameters(channel_->info()));
}
ProfileServer::ScoConnectionServer::ScoConnectionServer(
fidl::InterfaceRequest<fuchsia::bluetooth::bredr::ScoConnection> request,
fbl::RefPtr<bt::sco::ScoConnection> connection)
: ServerBase(this, std::move(request)), connection_(std::move(connection)) {
binding()->set_error_handler([this](zx_status_t) { Close(ZX_ERR_CANCELED); });
}
ProfileServer::ScoConnectionServer::~ScoConnectionServer() {
if (connection_) {
connection_->Deactivate();
}
}
void ProfileServer::ScoConnectionServer::Activate(fit::callback<void()> on_closed) {
on_closed_ = std::move(on_closed);
auto rx_callback = [this] { TryRead(); };
auto closed_cb = [this] { Close(ZX_ERR_PEER_CLOSED); };
connection_->Activate(std::move(rx_callback), std::move(closed_cb));
}
void ProfileServer::ScoConnectionServer::Read(ReadCallback callback) {
if (connection_->parameters().input_data_path != bt::hci_spec::ScoDataPath::kHci) {
bt_log(WARN, "fidl", "%s called for an offloaded SCO connection", __func__);
Close(ZX_ERR_IO_NOT_PRESENT);
return;
}
if (read_cb_) {
bt_log(WARN, "fidl", "%s called when a read callback was already present", __func__);
Close(ZX_ERR_BAD_STATE);
return;
}
read_cb_ = std::move(callback);
TryRead();
}
void ProfileServer::ScoConnectionServer::Write(std::vector<uint8_t> data, WriteCallback callback) {
if (connection_->parameters().output_data_path != bt::hci_spec::ScoDataPath::kHci) {
bt_log(WARN, "fidl", "%s called for a non-HCI SCO connection", __func__);
Close(ZX_ERR_IO_NOT_PRESENT);
return;
}
auto buffer = std::make_unique<bt::DynamicByteBuffer>(data.size());
buffer->Write(data.data(), data.size());
if (!connection_->Send(std::move(buffer))) {
bt_log(WARN, "fidl", "%s: failed to send SCO packet", __func__);
Close(ZX_ERR_IO);
return;
}
callback();
}
void ProfileServer::ScoConnectionServer::TryRead() {
if (!read_cb_) {
return;
}
std::unique_ptr<bt::hci::ScoDataPacket> packet = connection_->Read();
if (!packet) {
return;
}
std::vector<uint8_t> payload;
fuchsia::bluetooth::bredr::RxPacketStatus status =
fidl_helpers::ScoPacketStatusToFidl(packet->packet_status_flag());
if (packet->packet_status_flag() !=
bt::hci_spec::SynchronousDataPacketStatusFlag::kNoDataReceived) {
payload = packet->view().payload_data().ToVector();
}
read_cb_(status, std::move(payload));
}
void ProfileServer::ScoConnectionServer::Close(zx_status_t epitaph) {
connection_->Deactivate();
connection_.reset();
binding()->Close(epitaph);
if (on_closed_) {
on_closed_();
}
}
void ProfileServer::Advertise(
std::vector<fidlbredr::ServiceDefinition> definitions, fidlbredr::ChannelParameters parameters,
fidl::InterfaceHandle<fuchsia::bluetooth::bredr::ConnectionReceiver> receiver,
AdvertiseCallback callback) {
std::vector<bt::sdp::ServiceRecord> registering;
for (auto& definition : definitions) {
auto rec = fidl_helpers::ServiceDefinitionToServiceRecord(definition);
// Drop the receiver on error.
if (rec.is_error()) {
bt_log(WARN, "fidl", "%s: Failed to create service record from service defintion",
__FUNCTION__);
callback(fpromise::error(fuchsia::bluetooth::ErrorCode::INVALID_ARGUMENTS));
return;
}
registering.emplace_back(std::move(rec.value()));
}
ZX_ASSERT(adapter());
ZX_ASSERT(adapter()->bredr());
uint64_t next = advertised_total_ + 1;
auto registration_handle = adapter()->bredr()->RegisterService(
std::move(registering), FidlToChannelParameters(parameters),
[this, next](auto channel, const auto& protocol_list) {
OnChannelConnected(next, std::move(channel), std::move(protocol_list));
});
if (!registration_handle) {
bt_log(WARN, "fidl", "%s: Failed to register service", __FUNCTION__);
callback(fpromise::error(fuchsia::bluetooth::ErrorCode::INVALID_ARGUMENTS));
return;
};
auto receiverptr = receiver.Bind();
receiverptr.set_error_handler(
[this, next](zx_status_t /*status*/) { OnConnectionReceiverError(next); });
current_advertised_.try_emplace(next, std::move(receiverptr), registration_handle,
std::move(callback));
advertised_total_ = next;
}
void ProfileServer::Search(
fidlbredr::ServiceClassProfileIdentifier service_uuid, std::vector<uint16_t> attr_ids,
fidl::InterfaceHandle<fuchsia::bluetooth::bredr::SearchResults> results) {
bt::UUID search_uuid(static_cast<uint32_t>(service_uuid));
std::unordered_set<bt::sdp::AttributeId> attributes(attr_ids.begin(), attr_ids.end());
if (!attr_ids.empty()) {
// Always request the ProfileDescriptor for the event
attributes.insert(bt::sdp::kBluetoothProfileDescriptorList);
}
ZX_DEBUG_ASSERT(adapter());
auto next = searches_total_ + 1;
auto search_id = adapter()->bredr()->AddServiceSearch(
search_uuid, std::move(attributes),
[this, next](auto id, const auto& attrs) { OnServiceFound(next, id, attrs); });
if (!search_id) {
return;
}
auto results_ptr = results.Bind();
results_ptr.set_error_handler(
[this, next](zx_status_t status) { OnSearchResultError(next, status); });
searches_.try_emplace(next, std::move(results_ptr), search_id);
searches_total_ = next;
}
void ProfileServer::Connect(fuchsia::bluetooth::PeerId peer_id,
fidlbredr::ConnectParameters connection, ConnectCallback callback) {
bt::PeerId id{peer_id.value};
// Anything other than L2CAP is not supported by this server.
if (!connection.is_l2cap()) {
bt_log(WARN, "fidl", "%s: non-l2cap connections are not supported (is_rfcomm: %d, peer: %s)",
__FUNCTION__, connection.is_rfcomm(), bt_str(id));
callback(fpromise::error(fuchsia::bluetooth::ErrorCode::INVALID_ARGUMENTS));
return;
}
// The L2CAP parameters must include a PSM. ChannelParameters are optional.
auto l2cap_params = std::move(connection.l2cap());
if (!l2cap_params.has_psm()) {
bt_log(WARN, "fidl", "%s: missing l2cap psm (peer: %s)", __FUNCTION__, bt_str(id));
callback(fpromise::error(fuchsia::bluetooth::ErrorCode::INVALID_ARGUMENTS));
return;
}
uint16_t psm = l2cap_params.psm();
fidlbredr::ChannelParameters parameters = std::move(*l2cap_params.mutable_parameters());
auto connected_cb = [self = weak_ptr_factory_.GetWeakPtr(), cb = callback.share(),
id](fbl::RefPtr<bt::l2cap::Channel> chan) {
if (!chan) {
bt_log(INFO, "fidl", "Connect: Channel socket is empty, returning failed. (peer: %s)",
bt_str(id));
cb(fpromise::error(fuchsia::bluetooth::ErrorCode::FAILED));
return;
}
if (!self) {
cb(fpromise::error(fuchsia::bluetooth::ErrorCode::FAILED));
return;
}
auto fidl_chan = self->ChannelToFidl(std::move(chan));
cb(fpromise::ok(std::move(fidl_chan)));
};
ZX_DEBUG_ASSERT(adapter());
adapter()->bredr()->OpenL2capChannel(
id, psm, fidl_helpers::FidlToBrEdrSecurityRequirements(parameters),
FidlToChannelParameters(parameters), std::move(connected_cb));
}
void ProfileServer::ConnectSco(fuchsia::bluetooth::PeerId fidl_peer_id, bool initiator,
std::vector<fidlbredr::ScoConnectionParameters> fidl_params,
fidl::InterfaceHandle<fidlbredr::ScoConnectionReceiver> receiver) {
bt::PeerId peer_id(fidl_peer_id.value);
auto client = receiver.Bind();
if (fidl_params.empty()) {
bt_log(WARN, "fidl", "%s: empty parameters (peer: %s)", __FUNCTION__, bt_str(peer_id));
client->Error(fidlbredr::ScoErrorCode::INVALID_ARGUMENTS);
return;
}
if (initiator && fidl_params.size() != 1u) {
bt_log(WARN, "fidl", "%s: too many parameters in initiator request (peer: %s)", __FUNCTION__,
bt_str(peer_id));
client->Error(fidlbredr::ScoErrorCode::INVALID_ARGUMENTS);
return;
}
auto params_result = fidl_helpers::FidlToScoParametersVector(fidl_params);
if (params_result.is_error()) {
bt_log(WARN, "fidl", "%s: invalid parameters (peer: %s)", __FUNCTION__, bt_str(peer_id));
client->Error(fidlbredr::ScoErrorCode::INVALID_ARGUMENTS);
return;
}
auto params = params_result.value();
auto request = fbl::MakeRefCounted<ScoRequest>();
client.set_error_handler([request](zx_status_t status) { request->request_handle.reset(); });
request->receiver = std::move(client);
request->parameters = std::move(fidl_params);
if (initiator) {
auto callback = [self = weak_ptr_factory_.GetWeakPtr(),
request](bt::sco::ScoConnectionManager::OpenConnectionResult result) mutable {
// The connection may complete after this server is destroyed.
if (!self) {
// Prevent leaking connections.
if (result.is_ok()) {
result.value()->Deactivate();
}
return;
}
// Convert result type.
if (result.is_error()) {
self->OnScoConnectionResult(std::move(request), result.take_error());
return;
}
self->OnScoConnectionResult(
std::move(request),
fitx::ok(std::make_pair(std::move(result.value()), /*parameter index=*/0u)));
};
request->request_handle =
adapter()->bredr()->OpenScoConnection(peer_id, params.front(), std::move(callback));
return;
}
auto callback = [self = weak_ptr_factory_.GetWeakPtr(),
request](bt::sco::ScoConnectionManager::AcceptConnectionResult result) mutable {
// The connection may complete after this server is destroyed.
if (!self) {
// Prevent leaking connections.
if (result.is_ok()) {
result.value().first->Deactivate();
}
return;
}
self->OnScoConnectionResult(std::move(request), std::move(result));
};
request->request_handle =
adapter()->bredr()->AcceptScoConnection(peer_id, params, std::move(callback));
}
void ProfileServer::OnChannelConnected(uint64_t ad_id, fbl::RefPtr<bt::l2cap::Channel> channel,
const bt::sdp::DataElement& protocol_list) {
auto it = current_advertised_.find(ad_id);
if (it == current_advertised_.end()) {
// The receiver has disappeared, do nothing.
return;
}
ZX_DEBUG_ASSERT(adapter());
auto handle = channel->link_handle();
auto id = adapter()->bredr()->GetPeerId(handle);
// The protocol that is connected should be L2CAP, because that is the only thing that
// we can connect. We can't say anything about what the higher level protocols will be.
auto prot_seq = protocol_list.At(0);
ZX_ASSERT(prot_seq);
fidlbredr::ProtocolDescriptorPtr desc = DataElementToProtocolDescriptor(prot_seq);
ZX_ASSERT(desc);
fuchsia::bluetooth::PeerId peer_id{id.value()};
std::vector<fidlbredr::ProtocolDescriptor> list;
list.emplace_back(std::move(*desc));
auto fidl_chan = ChannelToFidl(std::move(channel));
it->second.receiver->Connected(peer_id, std::move(fidl_chan), std::move(list));
}
void ProfileServer::OnConnectionReceiverError(uint64_t ad_id) {
bt_log(DEBUG, "fidl", "Connection receiver closed, ending advertisement %lu", ad_id);
auto it = current_advertised_.find(ad_id);
if (it == current_advertised_.end() || !adapter()) {
return;
}
adapter()->bredr()->UnregisterService(it->second.registration_handle);
it->second.disconnection_cb(fpromise::ok());
current_advertised_.erase(it);
}
void ProfileServer::OnSearchResultError(uint64_t search_id, zx_status_t status) {
bt_log(DEBUG, "fidl", "Search result closed, ending search %lu reason %s", search_id,
zx_status_get_string(status));
auto it = searches_.find(search_id);
if (it == searches_.end() || !adapter()) {
return;
}
adapter()->bredr()->RemoveServiceSearch(it->second.search_id);
searches_.erase(it);
}
void ProfileServer::OnServiceFound(
uint64_t search_id, bt::PeerId peer_id,
const std::map<bt::sdp::AttributeId, bt::sdp::DataElement>& attributes) {
auto search_it = searches_.find(search_id);
if (search_it == searches_.end()) {
// Search was de-registered.
return;
}
// Convert ProfileDescriptor Attribute
auto it = attributes.find(bt::sdp::kProtocolDescriptorList);
fidl::VectorPtr<fidlbredr::ProtocolDescriptor> descriptor_list;
if (it != attributes.end()) {
std::vector<fidlbredr::ProtocolDescriptor> list;
size_t idx = 0;
auto* sdp_list_element = it->second.At(idx);
while (sdp_list_element != nullptr) {
fidlbredr::ProtocolDescriptorPtr desc = DataElementToProtocolDescriptor(sdp_list_element);
if (!desc) {
break;
}
list.push_back(std::move(*desc));
sdp_list_element = it->second.At(++idx);
}
descriptor_list = std::move(list);
}
// Add the rest of the attributes
std::vector<fidlbredr::Attribute> fidl_attrs;
for (const auto& it : attributes) {
auto attr = std::make_unique<fidlbredr::Attribute>();
attr->id = it.first;
attr->element = std::move(*DataElementToFidl(&it.second));
fidl_attrs.emplace_back(std::move(*attr));
}
fuchsia::bluetooth::PeerId fidl_peer_id{peer_id.value()};
search_it->second.results->ServiceFound(fidl_peer_id, std::move(descriptor_list),
std::move(fidl_attrs), []() {});
}
void ProfileServer::OnScoConnectionResult(
fbl::RefPtr<ScoRequest> request, // NOLINT(performance-unnecessary-value-param)
bt::sco::ScoConnectionManager::AcceptConnectionResult result) {
auto receiver = std::move(request->receiver);
if (result.is_error()) {
if (!receiver.is_bound()) {
return;
}
bt_log(INFO, "fidl", "%s: SCO connection failed (status: %s)", __FUNCTION__,
bt::HostErrorToString(result.error_value()).c_str());
fidlbredr::ScoErrorCode fidl_error = fidlbredr::ScoErrorCode::FAILURE;
if (result.error_value() == bt::HostError::kCanceled) {
fidl_error = fidlbredr::ScoErrorCode::CANCELLED;
}
if (result.error_value() == bt::HostError::kParametersRejected) {
fidl_error = fidlbredr::ScoErrorCode::PARAMETERS_REJECTED;
}
receiver->Error(fidl_error);
return;
}
fbl::RefPtr<bt::sco::ScoConnection> connection = std::move(result.value().first);
const uint16_t max_tx_data_size = connection->max_tx_sdu_size();
fidl::InterfaceHandle<fidlbredr::ScoConnection> sco_handle;
std::unique_ptr<ScoConnectionServer> sco_server =
std::make_unique<ScoConnectionServer>(sco_handle.NewRequest(), connection);
auto [server_iter, _] = sco_connection_servers_.emplace(connection.get(), std::move(sco_server));
// Activate after adding the connection to the map in case on_closed is called synchronously.
auto on_closed = [this, conn = connection.get()] {
auto iter = sco_connection_servers_.find(conn);
ZX_ASSERT(iter != sco_connection_servers_.end());
sco_connection_servers_.erase(iter);
};
server_iter->second->Activate(std::move(on_closed));
if (!receiver.is_bound()) {
return;
}
size_t parameter_index = result.value().second;
ZX_ASSERT_MSG(parameter_index < request->parameters.size(),
"parameter_index (%zu) >= request->parameters.size() (%zu)", parameter_index,
request->parameters.size());
fidlbredr::ScoConnectionParameters parameters = std::move(request->parameters[parameter_index]);
parameters.set_max_tx_data_size(max_tx_data_size);
receiver->Connected(std::move(sco_handle), std::move(parameters));
}
void ProfileServer::OnAudioDirectionExtError(AudioDirectionExt* ext_server, zx_status_t status) {
bt_log(DEBUG, "fidl", "audio direction ext server closed (reason: %s)",
zx_status_get_string(status));
auto it = audio_direction_ext_servers_.find(ext_server);
if (it == audio_direction_ext_servers_.end()) {
bt_log(WARN, "fidl", "could not find ext server in audio direction ext error callback");
return;
}
audio_direction_ext_servers_.erase(it);
}
fidl::InterfaceHandle<fidlbredr::AudioDirectionExt> ProfileServer::BindAudioDirectionExtServer(
fbl::RefPtr<bt::l2cap::Channel> channel) {
fidl::InterfaceHandle<fidlbredr::AudioDirectionExt> client;
auto audio_direction_ext_server =
std::make_unique<AudioDirectionExt>(client.NewRequest(), std::move(channel));
AudioDirectionExt* server_ptr = audio_direction_ext_server.get();
audio_direction_ext_server->set_error_handler(
[this, server_ptr](zx_status_t status) { OnAudioDirectionExtError(server_ptr, status); });
audio_direction_ext_servers_[server_ptr] = std::move(audio_direction_ext_server);
return client;
}
void ProfileServer::OnL2capParametersExtError(L2capParametersExt* ext_server, zx_status_t status) {
bt_log(DEBUG, "fidl", "fidl parameters ext server closed (reason: %s)",
zx_status_get_string(status));
auto handle = l2cap_parameters_ext_servers_.extract(ext_server);
ZX_ASSERT(handle);
}
fidl::InterfaceHandle<fidlbredr::L2capParametersExt> ProfileServer::BindL2capParametersExtServer(
fbl::RefPtr<bt::l2cap::Channel> channel) {
fidl::InterfaceHandle<fidlbredr::L2capParametersExt> client;
auto l2cap_parameters_ext_server =
std::make_unique<L2capParametersExt>(client.NewRequest(), std::move(channel));
L2capParametersExt* server_ptr = l2cap_parameters_ext_server.get();
l2cap_parameters_ext_server->set_error_handler(
[this, server_ptr](zx_status_t status) { OnL2capParametersExtError(server_ptr, status); });
l2cap_parameters_ext_servers_[server_ptr] = std::move(l2cap_parameters_ext_server);
return client;
}
fuchsia::bluetooth::bredr::Channel ProfileServer::ChannelToFidl(
fbl::RefPtr<bt::l2cap::Channel> channel) {
ZX_ASSERT(channel);
fidlbredr::Channel fidl_chan;
fidl_chan.set_channel_mode(ChannelModeToFidl(channel->mode()));
fidl_chan.set_max_tx_sdu_size(channel->max_tx_sdu_size());
if (channel->info().flush_timeout) {
fidl_chan.set_flush_timeout(channel->info().flush_timeout->get());
}
auto sock = l2cap_socket_factory_.MakeSocketForChannel(channel);
fidl_chan.set_socket(std::move(sock));
if (adapter()->state().IsVendorFeatureSupported(
bt::hci::VendorFeaturesBits::kSetAclPriorityCommand)) {
fidl_chan.set_ext_direction(BindAudioDirectionExtServer(channel));
}
fidl_chan.set_ext_l2cap(BindL2capParametersExtServer(std::move(channel)));
return fidl_chan;
}
ProfileServer::AudioDirectionExt::AudioDirectionExt(
fidl::InterfaceRequest<fidlbredr::AudioDirectionExt> request,
fbl::RefPtr<bt::l2cap::Channel> channel)
: ServerBase(this, std::move(request)), channel_(std::move(channel)) {}
void ProfileServer::AudioDirectionExt::SetPriority(
fuchsia::bluetooth::bredr::A2dpDirectionPriority priority, SetPriorityCallback callback) {
channel_->RequestAclPriority(FidlToAclPriority(priority),
[cb = std::move(callback)](auto result) {
if (result.is_ok()) {
cb(fpromise::ok());
return;
}
bt_log(DEBUG, "fidl", "ACL priority request failed");
cb(fpromise::error(fuchsia::bluetooth::ErrorCode::FAILED));
});
}
} // namespace bthost