blob: 7af8f7750820dc7ea34589e3832e7f3d698e48ee [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "bt_gd_shim"
#include <cstdint>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <unordered_map>
#include <vector>
#include "common/bind.h"
#include "hci/address.h"
#include "hci/hci_packets.h"
#include "l2cap/classic/dynamic_channel_manager.h"
#include "l2cap/classic/l2cap_classic_module.h"
#include "l2cap/psm.h"
#include "l2cap/security_policy.h"
#include "module.h"
#include "os/handler.h"
#include "os/log.h"
#include "packet/packet_view.h"
#include "packet/raw_builder.h"
#include "shim/l2cap.h"
namespace bluetooth {
namespace shim {
const ModuleFactory L2cap::Factory = ModuleFactory([]() { return new L2cap(); });
using ConnectionInterfaceDescriptor = uint16_t;
static const ConnectionInterfaceDescriptor kInvalidConnectionInterfaceDescriptor = 0;
static const ConnectionInterfaceDescriptor kStartConnectionInterfaceDescriptor = 64;
static const ConnectionInterfaceDescriptor kMaxConnections = UINT16_MAX - kStartConnectionInterfaceDescriptor - 1;
using ServiceInterfaceCallback =
std::function<void(l2cap::Psm psm, l2cap::classic::DynamicChannelManager::RegistrationResult result)>;
using ConnectionInterfaceCallback = std::function<void(l2cap::Psm psm, std::unique_ptr<l2cap::DynamicChannel>)>;
std::unique_ptr<packet::RawBuilder> MakeUniquePacket(const uint8_t* data, size_t len) {
packet::RawBuilder builder;
std::vector<uint8_t> bytes(data, data + len);
auto payload = std::make_unique<packet::RawBuilder>();
payload->AddOctets(bytes);
return payload;
}
class ConnectionInterface {
public:
ConnectionInterface(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::DynamicChannel> channel,
os::Handler* handler)
: cid_(cid), channel_(std::move(channel)), handler_(handler), on_data_ready_callback_(nullptr),
on_connection_closed_callback_(nullptr), address_(channel_->GetDevice()) {
channel_->RegisterOnCloseCallback(
handler_, common::BindOnce(&ConnectionInterface::OnConnectionClosed, common::Unretained(this)));
channel_->GetQueueUpEnd()->RegisterDequeue(
handler_, common::Bind(&ConnectionInterface::OnReadReady, common::Unretained(this)));
dequeue_registered_ = true;
}
~ConnectionInterface() {
ASSERT(!dequeue_registered_);
}
void OnReadReady() {
std::unique_ptr<packet::PacketView<packet::kLittleEndian>> packet = channel_->GetQueueUpEnd()->TryDequeue();
if (packet == nullptr) {
LOG_WARN("Got read ready from gd l2cap but no packet is ready");
return;
}
std::vector<const uint8_t> data(packet->begin(), packet->end());
ASSERT(on_data_ready_callback_ != nullptr);
on_data_ready_callback_(cid_, data);
}
void SetReadDataReadyCallback(ReadDataReadyCallback on_data_ready) {
ASSERT(on_data_ready != nullptr);
ASSERT(on_data_ready_callback_ == nullptr);
on_data_ready_callback_ = on_data_ready;
}
std::unique_ptr<packet::BasePacketBuilder> WriteReady() {
auto data = std::move(write_queue_.front());
write_queue_.pop();
if (write_queue_.empty()) {
channel_->GetQueueUpEnd()->UnregisterEnqueue();
enqueue_registered_ = false;
}
return data;
}
void Write(std::unique_ptr<packet::RawBuilder> packet) {
LOG_DEBUG("Writing packet cid:%hd size:%zd", cid_, packet->size());
write_queue_.push(std::move(packet));
if (!enqueue_registered_) {
enqueue_registered_ = true;
channel_->GetQueueUpEnd()->RegisterEnqueue(
handler_, common::Bind(&ConnectionInterface::WriteReady, common::Unretained(this)));
}
}
void Close() {
if (dequeue_registered_) {
channel_->GetQueueUpEnd()->UnregisterDequeue();
dequeue_registered_ = false;
}
ASSERT(write_queue_.empty());
channel_->Close();
}
void OnConnectionClosed(hci::ErrorCode error_code) {
LOG_DEBUG("Channel interface closed reason:%s cid:%hd device:%s", hci::ErrorCodeText(error_code).c_str(), cid_,
address_.ToString().c_str());
ASSERT(on_connection_closed_callback_ != nullptr);
on_connection_closed_callback_(cid_, static_cast<int>(error_code));
}
void SetConnectionClosedCallback(::bluetooth::shim::ConnectionClosedCallback on_connection_closed) {
ASSERT(on_connection_closed != nullptr);
ASSERT(on_connection_closed_callback_ == nullptr);
on_connection_closed_callback_ = std::move(on_connection_closed);
}
private:
const ConnectionInterfaceDescriptor cid_;
const std::unique_ptr<l2cap::DynamicChannel> channel_;
os::Handler* handler_;
ReadDataReadyCallback on_data_ready_callback_;
ConnectionClosedCallback on_connection_closed_callback_;
const hci::Address address_;
std::queue<std::unique_ptr<packet::PacketBuilder<hci::kLittleEndian>>> write_queue_;
bool enqueue_registered_{false};
bool dequeue_registered_{false};
};
struct ConnectionInterfaceManager {
public:
ConnectionInterfaceDescriptor AddChannel(std::unique_ptr<l2cap::DynamicChannel> channel);
void RemoveConnection(ConnectionInterfaceDescriptor cid);
void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready);
void SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed);
bool Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet);
size_t NumberOfActiveConnections() const {
return cid_to_interface_map_.size();
}
void GeneralCallback(ConnectionOpenCallback on_open, hci::Address address, l2cap::Psm psm,
ConnectionInterfaceDescriptor cid) {
on_open(address.ToString(), static_cast<uint16_t>(psm), static_cast<uint16_t>(cid));
}
void ConnectionOpened(ConnectionOpenCallback on_open, hci::Address address, l2cap::Psm psm,
ConnectionInterfaceDescriptor cid) {
LOG_DEBUG("%s address:%s psm:%hd cid:%hd", __func__, address.ToString().c_str(), psm, cid);
handler_->Post(common::BindOnce(&ConnectionInterfaceManager::GeneralCallback, common::Unretained(this), on_open,
address, psm, cid));
// TODO(cmanton) queue this pending connection address/psm tuple up for deletion
// There may be multiple, so only remove one
}
void ConnectionFailed(hci::Address address, l2cap::Psm psm) {
LOG_DEBUG("%s Connection Failed", __func__);
// TODO(cmanton) queue this pending connection address/psm tuple up for deletion
// There may be multiple, so only remove one
}
ConnectionInterfaceManager(os::Handler* handler);
private:
os::Handler* handler_;
ConnectionInterfaceDescriptor current_connection_interface_descriptor_;
bool HasResources() const;
bool Exists(ConnectionInterfaceDescriptor id) const;
std::unordered_map<ConnectionInterfaceDescriptor, std::unique_ptr<ConnectionInterface>> cid_to_interface_map_;
ConnectionInterfaceDescriptor AllocateConnectionInterfaceDescriptor();
ConnectionInterfaceManager() = delete;
};
ConnectionInterfaceManager::ConnectionInterfaceManager(os::Handler* handler)
: handler_(handler), current_connection_interface_descriptor_(kStartConnectionInterfaceDescriptor) {}
bool ConnectionInterfaceManager::Exists(ConnectionInterfaceDescriptor cid) const {
return cid_to_interface_map_.find(cid) != cid_to_interface_map_.end();
}
ConnectionInterfaceDescriptor ConnectionInterfaceManager::AllocateConnectionInterfaceDescriptor() {
ASSERT(HasResources());
while (Exists(current_connection_interface_descriptor_)) {
if (++current_connection_interface_descriptor_ == kInvalidConnectionInterfaceDescriptor) {
current_connection_interface_descriptor_ = kStartConnectionInterfaceDescriptor;
}
}
return current_connection_interface_descriptor_++;
}
ConnectionInterfaceDescriptor ConnectionInterfaceManager::AddChannel(std::unique_ptr<l2cap::DynamicChannel> channel) {
if (!HasResources()) {
return kInvalidConnectionInterfaceDescriptor;
}
ConnectionInterfaceDescriptor cid = AllocateConnectionInterfaceDescriptor();
auto channel_interface = std::make_unique<ConnectionInterface>(cid, std::move(channel), handler_);
cid_to_interface_map_[cid] = std::move(channel_interface);
return cid;
}
void ConnectionInterfaceManager::RemoveConnection(ConnectionInterfaceDescriptor cid) {
ASSERT(cid_to_interface_map_.count(cid) == 1);
cid_to_interface_map_.find(cid)->second->Close();
cid_to_interface_map_.erase(cid);
}
bool ConnectionInterfaceManager::HasResources() const {
return cid_to_interface_map_.size() < kMaxConnections;
}
void ConnectionInterfaceManager::SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid,
ReadDataReadyCallback on_data_ready) {
ASSERT(Exists(cid));
return cid_to_interface_map_[cid]->SetReadDataReadyCallback(on_data_ready);
}
void ConnectionInterfaceManager::SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid,
ConnectionClosedCallback on_closed) {
ASSERT(Exists(cid));
return cid_to_interface_map_[cid]->SetConnectionClosedCallback(on_closed);
}
bool ConnectionInterfaceManager::Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet) {
if (!Exists(cid)) {
return false;
}
cid_to_interface_map_[cid]->Write(std::move(packet));
return true;
}
class PendingConnection {
public:
PendingConnection(ConnectionInterfaceManager* connection_interface_manager, l2cap::Psm psm, hci::Address address,
ConnectionOpenCallback on_open, std::promise<uint16_t> completed)
: connection_interface_manager_(connection_interface_manager), psm_(psm), address_(address),
on_open_(std::move(on_open)), completed_(std::move(completed)) {}
void OnConnectionOpen(std::unique_ptr<l2cap::DynamicChannel> channel) {
LOG_DEBUG("Local initiated connection is open to device:%s for psm:%hd", address_.ToString().c_str(), psm_);
ConnectionInterfaceDescriptor cid = connection_interface_manager_->AddChannel(std::move(channel));
completed_.set_value(cid);
// Attempt to avoid async race condition with upper stack
std::this_thread::yield();
connection_interface_manager_->ConnectionOpened(std::move(on_open_), address_, psm_, cid);
}
void OnConnectionFailure(l2cap::classic::DynamicChannelManager::ConnectionResult result) {
LOG_DEBUG("Connection failed to device:%s for psm:%hd", address_.ToString().c_str(), psm_);
switch (result.connection_result_code) {
case l2cap::classic::DynamicChannelManager::ConnectionResultCode::SUCCESS:
LOG_WARN("Connection failed result:success hci:%s", hci::ErrorCodeText(result.hci_error).c_str());
break;
case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED:
LOG_DEBUG("Connection failed result:no service registered hci:%s",
hci::ErrorCodeText(result.hci_error).c_str());
break;
case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_HCI_ERROR:
LOG_DEBUG("Connection failed result:hci error hci:%s", hci::ErrorCodeText(result.hci_error).c_str());
break;
case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR:
LOG_DEBUG("Connection failed result:l2cap error hci:%s l2cap:%s", hci::ErrorCodeText(result.hci_error).c_str(),
l2cap::ConnectionResponseResultText(result.l2cap_connection_response_result).c_str());
break;
}
completed_.set_value(kInvalidConnectionInterfaceDescriptor);
connection_interface_manager_->ConnectionFailed(address_, psm_);
}
private:
ConnectionInterfaceManager* connection_interface_manager_;
const l2cap::Psm psm_;
const hci::Address address_;
ConnectionOpenCallback on_open_;
std::promise<uint16_t> completed_;
};
class ServiceInterface {
public:
ServiceInterface(ConnectionInterfaceManager* connection_interface_manager, l2cap::Psm psm,
ConnectionOpenCallback on_open, std::promise<void> completed)
: connection_interface_manager_(connection_interface_manager), psm_(psm), on_open_(on_open),
completed_(std::move(completed)) {}
void OnRegistrationComplete(l2cap::classic::DynamicChannelManager::RegistrationResult result,
std::unique_ptr<l2cap::classic::DynamicChannelService> service) {
ASSERT(service_ == nullptr);
ASSERT(psm_ == service->GetPsm());
LOG_DEBUG("Registration is complete for psm:%hd", psm_);
service_ = std::move(service);
completed_.set_value();
}
void OnConnectionOpen(std::unique_ptr<l2cap::DynamicChannel> channel) {
LOG_DEBUG("Remote initiated connection is open from device:%s for psm:%hd", channel->GetDevice().ToString().c_str(),
psm_);
hci::Address address = channel->GetDevice();
ConnectionInterfaceDescriptor cid = connection_interface_manager_->AddChannel(std::move(channel));
connection_interface_manager_->ConnectionOpened(on_open_, address, psm_, cid);
}
l2cap::SecurityPolicy GetSecurityPolicy() const {
return security_policy_;
}
void RegisterService(
std::function<void(l2cap::Psm, l2cap::SecurityPolicy security_policy,
l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open)>
func) {
func(psm_, security_policy_, common::BindOnce(&ServiceInterface::OnRegistrationComplete, common::Unretained(this)),
common::Bind(&ServiceInterface::OnConnectionOpen, common::Unretained(this)));
}
private:
ConnectionInterfaceManager* connection_interface_manager_;
const l2cap::Psm psm_;
ConnectionOpenCallback on_open_;
std::promise<void> completed_;
std::unique_ptr<l2cap::classic::DynamicChannelService> service_;
const l2cap::SecurityPolicy security_policy_;
};
struct L2cap::impl {
void RegisterService(l2cap::Psm psm, ConnectionOpenCallback on_open, std::promise<void> completed);
void UnregisterService(l2cap::Psm psm);
void CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionOpenCallback on_open,
std::promise<uint16_t> completed);
void CloseConnection(ConnectionInterfaceDescriptor cid);
void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready);
void SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed);
void Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet);
void SendLoopbackResponse(std::function<void()> function);
impl(L2cap& module, l2cap::classic::L2capClassicModule* l2cap_module);
private:
L2cap& module_;
l2cap::classic::L2capClassicModule* l2cap_module_;
os::Handler* handler_;
ConnectionInterfaceManager connection_interface_manager_;
std::unique_ptr<l2cap::classic::DynamicChannelManager> dynamic_channel_manager_;
std::unordered_map<l2cap::Psm, std::shared_ptr<ServiceInterface>> psm_to_service_interface_map_;
std::unordered_map<l2cap::Psm, std::shared_ptr<PendingConnection>> psm_to_pending_connection_map_;
};
L2cap::impl::impl(L2cap& module, l2cap::classic::L2capClassicModule* l2cap_module)
: module_(module), l2cap_module_(l2cap_module), handler_(module_.GetHandler()),
connection_interface_manager_(handler_) {
dynamic_channel_manager_ = l2cap_module_->GetDynamicChannelManager();
}
void L2cap::impl::RegisterService(l2cap::Psm psm, ConnectionOpenCallback on_open, std::promise<void> completed) {
ASSERT(psm_to_service_interface_map_.find(psm) == psm_to_service_interface_map_.end());
auto service_interface =
std::make_shared<ServiceInterface>(&connection_interface_manager_, psm, on_open, std::move(completed));
psm_to_service_interface_map_.emplace(psm, service_interface);
// TODO(cmanton): Use the configuration option from user
service_interface->RegisterService(
[this](l2cap::Psm psm, l2cap::SecurityPolicy security_policy,
l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open) {
bool rc = dynamic_channel_manager_->RegisterService(psm, l2cap::classic::DynamicChannelConfigurationOption(),
security_policy, std::move(on_registration_complete),
on_connection_open, handler_);
ASSERT_LOG(rc == true, "Failed to register classic service");
});
}
void L2cap::impl::UnregisterService(l2cap::Psm psm) {
psm_to_service_interface_map_.erase(psm);
}
void L2cap::impl::CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionOpenCallback on_open,
std::promise<uint16_t> completed) {
LOG_DEBUG("Initiating classic connection to psm:%hd device:%s", psm, address.ToString().c_str());
auto pending_connection = std::make_shared<PendingConnection>(&connection_interface_manager_, psm, address,
std::move(on_open), std::move(completed));
// TODO(cmanton) hash psm/address pair into unordered map for pending_connection
// This is ok for now
psm_to_pending_connection_map_[psm] = pending_connection;
// TODO(cmanton): Add ERTM mode support by changing configuratio_option in ConnectChannel()
bool rc = dynamic_channel_manager_->ConnectChannel(
address, l2cap::classic::DynamicChannelConfigurationOption(), psm,
common::Bind(&PendingConnection::OnConnectionOpen, common::Unretained(pending_connection.get())),
common::BindOnce(&PendingConnection::OnConnectionFailure, common::Unretained(pending_connection.get())),
handler_);
ASSERT_LOG(rc == true, "Failed to create classic connection");
}
void L2cap::impl::CloseConnection(ConnectionInterfaceDescriptor cid) {
connection_interface_manager_.RemoveConnection(cid);
}
void L2cap::impl::SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready) {
connection_interface_manager_.SetReadDataReadyCallback(cid, on_data_ready);
}
void L2cap::impl::SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed) {
connection_interface_manager_.SetConnectionClosedCallback(cid, std::move(on_closed));
}
void L2cap::impl::Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet) {
connection_interface_manager_.Write(cid, std::move(packet));
}
void L2cap::impl::SendLoopbackResponse(std::function<void()> function) {
function();
}
void L2cap::RegisterService(uint16_t raw_psm, ConnectionOpenCallback on_open, std::promise<void> completed) {
l2cap::Psm psm{raw_psm};
GetHandler()->Post(common::BindOnce(&L2cap::impl::RegisterService, common::Unretained(pimpl_.get()), psm, on_open,
std::move(completed)));
}
void L2cap::UnregisterService(uint16_t raw_psm) {
l2cap::Psm psm{raw_psm};
GetHandler()->Post(common::Bind(&L2cap::impl::UnregisterService, common::Unretained(pimpl_.get()), psm));
}
void L2cap::CreateConnection(uint16_t raw_psm, const std::string address_string, ConnectionOpenCallback on_open,
std::promise<uint16_t> completed) {
l2cap::Psm psm{raw_psm};
hci::Address address;
hci::Address::FromString(address_string, address);
GetHandler()->Post(common::BindOnce(&L2cap::impl::CreateConnection, common::Unretained(pimpl_.get()), psm, address,
on_open, std::move(completed)));
}
void L2cap::CloseConnection(uint16_t raw_cid) {
ConnectionInterfaceDescriptor cid(raw_cid);
GetHandler()->Post(common::Bind(&L2cap::impl::CloseConnection, common::Unretained(pimpl_.get()), cid));
}
void L2cap::SetReadDataReadyCallback(uint16_t raw_cid, ReadDataReadyCallback on_data_ready) {
ConnectionInterfaceDescriptor cid(raw_cid);
GetHandler()->Post(
common::Bind(&L2cap::impl::SetReadDataReadyCallback, common::Unretained(pimpl_.get()), cid, on_data_ready));
}
void L2cap::SetConnectionClosedCallback(uint16_t raw_cid, ConnectionClosedCallback on_closed) {
ConnectionInterfaceDescriptor cid(raw_cid);
GetHandler()->Post(common::Bind(&L2cap::impl::SetConnectionClosedCallback, common::Unretained(pimpl_.get()), cid,
std::move(on_closed)));
}
void L2cap::Write(uint16_t raw_cid, const uint8_t* data, size_t len) {
ConnectionInterfaceDescriptor cid(raw_cid);
auto packet = MakeUniquePacket(data, len);
GetHandler()->Post(common::BindOnce(&L2cap::impl::Write, common::Unretained(pimpl_.get()), cid, std::move(packet)));
}
void L2cap::WriteFlushable(uint16_t raw_cid, const uint8_t* data, size_t len) {
LOG_WARN("UNIMPLEMENTED Write flushable");
return Write(raw_cid, data, len);
}
void L2cap::WriteNonFlushable(uint16_t raw_cid, const uint8_t* data, size_t len) {
LOG_WARN("UNIMPLEMENTED Write non flushable");
return Write(raw_cid, data, len);
}
void L2cap::SendLoopbackResponse(std::function<void()> function) {
GetHandler()->Post(common::BindOnce(&L2cap::impl::SendLoopbackResponse, common::Unretained(pimpl_.get()), function));
}
/**
* Module methods
*/
void L2cap::ListDependencies(ModuleList* list) {
list->add<l2cap::classic::L2capClassicModule>();
}
void L2cap::Start() {
pimpl_ = std::make_unique<impl>(*this, GetDependency<l2cap::classic::L2capClassicModule>());
}
void L2cap::Stop() {
pimpl_.reset();
}
} // namespace shim
} // namespace bluetooth