blob: 314cbbb048a381b1cc45a50b93a22d8af9fa7f79 [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_shim_l2cap"
#include <cstdint>
#include "main/shim/dumpsys.h"
#include "main/shim/entry.h"
#include "main/shim/l2cap.h"
#include "main/shim/shim.h"
#include "osi/include/allocator.h"
#include "osi/include/log.h"
#include "stack/include/btu.h"
#include "shim/l2cap.h"
namespace {
constexpr char kModuleName[] = "shim::legacy::L2cap";
constexpr bool kDisconnectResponseRequired = false;
constexpr size_t kBtHdrSize = sizeof(BT_HDR);
constexpr uint16_t kConnectionFail = 1;
constexpr uint16_t kConnectionSuccess = 0;
constexpr uint16_t kInvalidConnectionInterfaceDescriptor = 0;
constexpr uint8_t kUnusedId = 0;
} // namespace
bool bluetooth::shim::legacy::PsmManager::IsPsmRegistered(uint16_t psm) const {
return psm_to_callback_map_.find(psm) != psm_to_callback_map_.end();
}
bool bluetooth::shim::legacy::PsmManager::HasClient(uint16_t psm) const {
return IsPsmRegistered(psm) &&
psm_to_callback_map_.find(psm) != psm_to_callback_map_.end();
}
void bluetooth::shim::legacy::PsmManager::RegisterPsm(
uint16_t psm, const tL2CAP_APPL_INFO& callbacks) {
CHECK(!HasClient(psm));
psm_to_callback_map_.try_emplace(psm, tL2CAP_APPL_INFO(callbacks));
}
void bluetooth::shim::legacy::PsmManager::UnregisterPsm(uint16_t psm) {
CHECK(IsPsmRegistered(psm));
psm_to_callback_map_.erase(psm);
}
const tL2CAP_APPL_INFO bluetooth::shim::legacy::PsmManager::Callbacks(
uint16_t psm) {
CHECK(HasClient(psm));
return psm_to_callback_map_[psm];
}
bluetooth::shim::legacy::L2cap::L2cap()
: le_dynamic_psm_(kInitialLeDynamicPsm),
classic_virtual_psm_(kInitialClassicVirtualPsm) {
bluetooth::shim::RegisterDumpsysFunction(static_cast<void*>(this),
[this](int fd) { Dump(fd); });
}
bluetooth::shim::legacy::L2cap::~L2cap() {
bluetooth::shim::UnregisterDumpsysFunction(static_cast<void*>(this));
}
bluetooth::shim::legacy::PsmManager& bluetooth::shim::legacy::L2cap::Le() {
return le_;
}
bluetooth::shim::legacy::PsmManager& bluetooth::shim::legacy::L2cap::Classic() {
return classic_;
}
bool bluetooth::shim::legacy::L2cap::ConnectionExists(uint16_t cid) const {
return cid_to_psm_map_.find(cid) != cid_to_psm_map_.end();
}
uint16_t bluetooth::shim::legacy::L2cap::CidToPsm(uint16_t cid) const {
CHECK(ConnectionExists(cid));
return cid_to_psm_map_.at(cid);
}
uint16_t bluetooth::shim::legacy::L2cap::ConvertClientToRealPsm(
uint16_t client_psm, bool is_outgoing_only_connection) {
if (!is_outgoing_only_connection) {
return client_psm;
}
return GetNextVirtualPsm(client_psm);
}
uint16_t bluetooth::shim::legacy::L2cap::ConvertClientToRealPsm(
uint16_t client_psm) {
if (client_psm_to_real_psm_map_.find(client_psm) ==
client_psm_to_real_psm_map_.end()) {
return client_psm;
}
return client_psm_to_real_psm_map_.at(client_psm);
}
void bluetooth::shim::legacy::L2cap::RemoveClientPsm(uint16_t client_psm) {
if (client_psm_to_real_psm_map_.find(client_psm) !=
client_psm_to_real_psm_map_.end()) {
client_psm_to_real_psm_map_.erase(client_psm);
}
}
uint16_t bluetooth::shim::legacy::L2cap::GetNextVirtualPsm(uint16_t real_psm) {
if (real_psm < kInitialClassicDynamicPsm) {
return real_psm;
}
while (Classic().IsPsmRegistered(classic_virtual_psm_)) {
classic_virtual_psm_ += 2;
if (classic_virtual_psm_ >= kFinalClassicVirtualPsm) {
classic_virtual_psm_ = kInitialClassicVirtualPsm;
}
}
return classic_virtual_psm_;
}
uint16_t bluetooth::shim::legacy::L2cap::GetNextDynamicLePsm() {
while (Le().IsPsmRegistered(le_dynamic_psm_)) {
le_dynamic_psm_++;
if (le_dynamic_psm_ > kFinalLeDynamicPsm) {
le_dynamic_psm_ = kInitialLeDynamicPsm;
}
}
return le_dynamic_psm_;
}
uint16_t bluetooth::shim::legacy::L2cap::RegisterService(
uint16_t psm, const tL2CAP_APPL_INFO& callbacks, bool enable_snoop,
tL2CAP_ERTM_INFO* p_ertm_info, uint16_t my_mtu,
uint16_t required_remote_mtu) {
if (Classic().IsPsmRegistered(psm)) {
LOG_WARN("Service is already registered psm:%hd", psm);
return 0;
}
if (!enable_snoop) {
LOG_INFO("Disable snooping on psm basis unsupported psm:%d", psm);
}
LOG_INFO("Registering service on psm:%hd", psm);
RegisterServicePromise register_promise;
auto service_registered = register_promise.get_future();
bool use_ertm = false;
if (p_ertm_info != nullptr &&
p_ertm_info->preferred_mode == L2CAP_FCR_ERTM_MODE) {
use_ertm = true;
}
bluetooth::shim::GetL2cap()->RegisterClassicService(
psm, use_ertm, my_mtu, required_remote_mtu,
std::bind(
&bluetooth::shim::legacy::L2cap::OnRemoteInitiatedConnectionCreated,
this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4),
std::move(register_promise));
uint16_t registered_psm = service_registered.get();
if (registered_psm != psm) {
LOG_WARN("Unable to register psm:%hd", psm);
} else {
LOG_INFO("Successfully registered psm:%hd", psm);
Classic().RegisterPsm(registered_psm, callbacks);
}
return registered_psm;
}
void bluetooth::shim::legacy::L2cap::UnregisterService(uint16_t psm) {
if (!Classic().IsPsmRegistered(psm)) {
LOG_WARN("Service must be registered in order to unregister psm:%hd", psm);
return;
}
for (auto& entry : cid_to_psm_map_) {
if (entry.second == psm) {
LOG_WARN(" Unregistering service with active channels psm:%hd cid:%hd",
psm, entry.first);
}
}
LOG_INFO("Unregistering service on psm:%hd", psm);
UnregisterServicePromise unregister_promise;
auto service_unregistered = unregister_promise.get_future();
bluetooth::shim::GetL2cap()->UnregisterClassicService(
psm, std::move(unregister_promise));
service_unregistered.wait();
Classic().UnregisterPsm(psm);
}
uint16_t bluetooth::shim::legacy::L2cap::CreateConnection(
uint16_t psm, const RawAddress& raw_address) {
if (!Classic().IsPsmRegistered(psm)) {
LOG_WARN("Service must be registered in order to connect psm:%hd", psm);
return kInvalidConnectionInterfaceDescriptor;
}
CreateConnectionPromise create_promise;
auto created = create_promise.get_future();
LOG_INFO("Initiating local connection to psm:%hd address:%s", psm,
raw_address.ToString().c_str());
bluetooth::shim::GetL2cap()->CreateClassicConnection(
psm, raw_address.ToString(),
std::bind(
&bluetooth::shim::legacy::L2cap::OnLocalInitiatedConnectionCreated,
this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
std::move(create_promise));
uint16_t cid = created.get();
if (cid == kInvalidConnectionInterfaceDescriptor) {
LOG_WARN("Failed to initiate connection interface to psm:%hd address:%s",
psm, raw_address.ToString().c_str());
} else {
LOG_INFO(
"Successfully initiated connection to psm:%hd address:%s"
" connection_interface_descriptor:%hd",
psm, raw_address.ToString().c_str(), cid);
CHECK(!ConnectionExists(cid));
cid_to_psm_map_[cid] = psm;
}
return cid;
}
void bluetooth::shim::legacy::L2cap::OnLocalInitiatedConnectionCreated(
std::string string_address, uint16_t psm, uint16_t cid, uint16_t remote_cid,
bool connected) {
cid_to_remote_cid_map_[cid] = remote_cid;
if (cid_closing_set_.count(cid) == 0) {
if (connected) {
SetDownstreamCallbacks(cid);
} else {
LOG_WARN("Failed intitiating connection remote:%s psm:%hd cid:%hd",
string_address.c_str(), psm, cid);
}
do_in_main_thread(
FROM_HERE,
base::Bind(classic_.Callbacks(psm).pL2CA_ConnectCfm_Cb, cid,
connected ? (kConnectionSuccess) : (kConnectionFail)));
tL2CAP_CFG_INFO cfg_info{};
do_in_main_thread(
FROM_HERE,
base::Bind(classic_.Callbacks(CidToPsm(cid)).pL2CA_ConfigCfm_Cb, cid,
L2CAP_INITIATOR_LOCAL, base::Unretained(&cfg_info)));
} else {
LOG_INFO("Connection Closed before presentation to upper layer");
if (connected) {
SetDownstreamCallbacks(cid);
bluetooth::shim::GetL2cap()->CloseClassicConnection(cid);
} else {
LOG_INFO("Connection failed after initiator closed");
}
}
}
void bluetooth::shim::legacy::L2cap::OnRemoteInitiatedConnectionCreated(
std::string string_address, uint16_t psm, uint16_t cid,
uint16_t remote_cid) {
RawAddress raw_address;
RawAddress::FromString(string_address, raw_address);
LOG_INFO(
"Sending connection indicator to upper stack from device:%s "
"psm:%hd cid:%hd",
string_address.c_str(), psm, cid);
CHECK(!ConnectionExists(cid));
cid_to_psm_map_[cid] = psm;
cid_to_remote_cid_map_[cid] = remote_cid;
SetDownstreamCallbacks(cid);
do_in_main_thread(
FROM_HERE,
base::Bind(classic_.Callbacks(CidToPsm(cid)).pL2CA_ConnectInd_Cb,
raw_address, cid, psm, kUnusedId));
tL2CAP_CFG_INFO cfg_info{};
do_in_main_thread(
FROM_HERE,
base::Bind(classic_.Callbacks(CidToPsm(cid)).pL2CA_ConfigCfm_Cb, cid,
L2CAP_INITIATOR_REMOTE, base::Unretained(&cfg_info)));
}
bool bluetooth::shim::legacy::L2cap::Write(uint16_t cid, BT_HDR* bt_hdr) {
CHECK(bt_hdr != nullptr);
const uint8_t* data = bt_hdr->data + bt_hdr->offset;
size_t len = bt_hdr->len;
if (!ConnectionExists(cid) || len == 0) {
return false;
}
LOG_INFO("Writing data cid:%hd len:%zd", cid, len);
bluetooth::shim::GetL2cap()->Write(cid, data, len);
return true;
}
void bluetooth::shim::legacy::L2cap::SetDownstreamCallbacks(uint16_t cid) {
bluetooth::shim::GetL2cap()->SetReadDataReadyCallback(
cid, [this](uint16_t cid, std::vector<const uint8_t> data) {
LOG_INFO("OnDataReady cid:%hd len:%zd", cid, data.size());
BT_HDR* bt_hdr =
static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
std::copy(data.begin(), data.end(), bt_hdr->data);
bt_hdr->len = data.size();
do_in_main_thread(
FROM_HERE,
base::Bind(classic_.Callbacks(CidToPsm(cid)).pL2CA_DataInd_Cb, cid,
base::Unretained(bt_hdr)));
});
bluetooth::shim::GetL2cap()->SetConnectionClosedCallback(
cid, [this](uint16_t cid, int error_code) {
LOG_INFO("OnChannel closed callback cid:%hd", cid);
if (!ConnectionExists(cid)) {
LOG_WARN("%s Unexpected channel closure cid:%hd", __func__, cid);
return;
}
if (cid_closing_set_.count(cid) == 1) {
cid_closing_set_.erase(cid);
} else {
do_in_main_thread(
FROM_HERE,
base::Bind(
classic_.Callbacks(CidToPsm(cid)).pL2CA_DisconnectInd_Cb, cid,
kDisconnectResponseRequired));
}
cid_to_psm_map_.erase(cid);
cid_to_remote_cid_map_.erase(cid);
});
}
bool bluetooth::shim::legacy::L2cap::DisconnectRequest(uint16_t cid) {
CHECK(ConnectionExists(cid));
if (cid_closing_set_.find(cid) != cid_closing_set_.end()) {
LOG_WARN("%s Channel already in closing state cid:%hu", __func__, cid);
return false;
}
LOG_INFO("%s initiated locally cid:%hu", __func__, cid);
cid_closing_set_.insert(cid);
bluetooth::shim::GetL2cap()->CloseClassicConnection(cid);
return true;
}
void bluetooth::shim::legacy::L2cap::Dump(int fd) {
if (cid_to_psm_map_.empty()) {
dprintf(fd, "%s No active l2cap channels\n", kModuleName);
} else {
for (auto& connection : cid_to_psm_map_) {
dprintf(fd, "%s active l2cap channel cid:%hd psm:%hd\n", kModuleName,
connection.first, connection.second);
}
}
}
bool bluetooth::shim::legacy::L2cap::GetRemoteCid(uint16_t cid,
uint16_t* remote_cid) {
auto it = cid_to_remote_cid_map_.find(cid);
if (it == cid_to_remote_cid_map_.end()) {
return false;
}
*remote_cid = it->second;
return true;
}