| /* |
| * 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 "main/shim/l2c_api.h" |
| |
| #include <future> |
| #include <unordered_map> |
| #include <unordered_set> |
| |
| #include "bta/include/bta_dm_acl.h" |
| #include "gd/l2cap/classic/l2cap_classic_module.h" |
| #include "gd/l2cap/le/l2cap_le_module.h" |
| #include "gd/os/log.h" |
| #include "gd/os/queue.h" |
| #include "main/shim/acl_api.h" |
| #include "main/shim/btm.h" |
| #include "main/shim/entry.h" |
| #include "main/shim/helpers.h" |
| #include "main/shim/stack.h" |
| #include "osi/include/allocator.h" |
| #include "stack/btm/btm_ble_int.h" |
| #include "stack/btm/btm_sec.h" |
| #include "stack/include/acl_hci_link_interface.h" |
| #include "stack/include/ble_acl_interface.h" |
| #include "stack/include/bt_hdr.h" |
| #include "stack/include/btm_api.h" |
| #include "stack/include/btu.h" |
| #include "stack/include/gatt_api.h" |
| #include "stack/include/sco_hci_link_interface.h" |
| #include "types/raw_address.h" |
| |
| #include <base/logging.h> |
| |
| extern void gatt_notify_conn_update(const RawAddress& remote, uint16_t interval, |
| uint16_t latency, uint16_t timeout, |
| tHCI_STATUS status); |
| extern void gatt_notify_phy_updated(tGATT_STATUS status, uint16_t handle, |
| uint8_t tx_phy, uint8_t rx_phy); |
| |
| void process_ssr_event(tHCI_STATUS status, uint16_t handle, uint16_t max_tx_lat, |
| uint16_t max_rx_lat); |
| |
| namespace bluetooth { |
| namespace shim { |
| |
| using bluetooth::hci::AddressWithType; |
| using namespace bluetooth::l2cap; |
| |
| // Classic Dynamic Channel Shim Helper |
| |
| namespace { |
| uint16_t classic_cid_token_counter_ = 0x41; |
| constexpr uint64_t kBrEdrNotSupportedMask = 0x0000002000000000; // Bit 37 |
| constexpr uint64_t kLeSupportedControllerMask = 0x0000004000000000; // Bit 38 |
| constexpr uint64_t kLeSupportedHostMask = 0x0000000000000002; // Bit 1 |
| |
| std::unordered_map<uint16_t /* token */, uint16_t /* psm */> |
| classic_cid_token_to_channel_map_; |
| |
| uint16_t add_classic_cid_token_entry(uint16_t psm) { |
| uint16_t new_token = classic_cid_token_counter_; |
| classic_cid_token_to_channel_map_[new_token] = psm; |
| classic_cid_token_counter_++; |
| if (classic_cid_token_counter_ == 0) classic_cid_token_counter_ = 0x41; |
| return new_token; |
| } |
| |
| void remove_classic_cid_token_entry(uint16_t cid_token) { |
| classic_cid_token_to_channel_map_.erase(cid_token); |
| } |
| |
| void remove_classic_dynamic_channel_helper(uint16_t psm); |
| |
| struct ClassicDynamicChannelHelper { |
| ClassicDynamicChannelHelper(uint16_t psm, tL2CAP_APPL_INFO appl_info, |
| classic::DynamicChannelConfigurationOption config, |
| classic::SecurityPolicy policy) |
| : psm_(psm), appl_info_(appl_info), config_(config), policy_(policy) {} |
| |
| uint16_t psm_; |
| tL2CAP_APPL_INFO appl_info_; |
| classic::DynamicChannelConfigurationOption config_; |
| classic::SecurityPolicy policy_; |
| |
| void Register() { |
| GetL2capClassicModule()->GetDynamicChannelManager()->RegisterService( |
| psm_, config_, policy_, |
| GetGdShimHandler()->BindOnceOn( |
| this, &ClassicDynamicChannelHelper::on_registration_complete), |
| GetGdShimHandler()->BindOn( |
| this, &ClassicDynamicChannelHelper::on_channel_open, 0)); |
| } |
| |
| void on_registration_complete( |
| classic::DynamicChannelManager::RegistrationResult result, |
| std::unique_ptr<classic::DynamicChannelService> service) { |
| if (result != classic::DynamicChannelManager::RegistrationResult::SUCCESS) { |
| LOG(ERROR) << "Channel is not registered. psm=" << +psm_ << (int)result; |
| return; |
| } |
| channel_service_ = std::move(service); |
| } |
| |
| std::unique_ptr<classic::DynamicChannelService> channel_service_ = nullptr; |
| |
| void Connect(uint16_t cid_token, bluetooth::hci::AddressWithType device) { |
| if (channel_service_ == nullptr) { |
| return; |
| } |
| initiated_by_us_[cid_token] = true; |
| GetL2capClassicModule()->GetDynamicChannelManager()->ConnectChannel( |
| device.GetAddress(), config_, psm_, |
| GetGdShimHandler()->BindOn( |
| this, &ClassicDynamicChannelHelper::on_channel_open, cid_token), |
| GetGdShimHandler()->BindOnceOn( |
| this, &ClassicDynamicChannelHelper::on_outgoing_connection_fail)); |
| } |
| |
| void Disconnect(uint16_t cid_token) { |
| if (channel_service_ == nullptr) { |
| return; |
| } |
| if (channels_.count(cid_token) == 0) { |
| return; |
| } |
| channels_[cid_token]->Close(); |
| } |
| |
| void Unregister() { |
| if (channel_service_ != nullptr) { |
| channel_service_->Unregister(GetGdShimHandler()->BindOnceOn( |
| this, &ClassicDynamicChannelHelper::on_unregistered)); |
| channel_service_ = nullptr; |
| } |
| } |
| |
| void on_unregistered() { |
| for (const auto& device : channels_) { |
| device.second->Close(); |
| } |
| remove_classic_dynamic_channel_helper(psm_); |
| } |
| |
| void on_channel_close(uint16_t cid_token, |
| bluetooth::hci::ErrorCode error_code) { |
| channel_enqueue_buffer_[cid_token] = nullptr; |
| channel_enqueue_buffer_.erase(cid_token); |
| channels_[cid_token]->GetQueueUpEnd()->UnregisterDequeue(); |
| channels_.erase(cid_token); |
| do_in_main_thread(FROM_HERE, base::Bind(appl_info_.pL2CA_DisconnectInd_Cb, |
| cid_token, false)); |
| |
| remove_classic_cid_token_entry(cid_token); |
| initiated_by_us_.erase(cid_token); |
| |
| if (channel_service_ == nullptr && channels_.empty()) { |
| // Try again |
| L2CA_Deregister(psm_); |
| } |
| } |
| |
| void on_channel_open(uint16_t cid_token, |
| std::unique_ptr<classic::DynamicChannel> channel) { |
| auto device = channel->GetDevice(); |
| auto address = bluetooth::ToRawAddress(device.GetAddress()); |
| bool initiator_local = (cid_token != 0); |
| if (cid_token == 0) { |
| cid_token = add_classic_cid_token_entry(psm_); |
| } |
| |
| channel->RegisterOnCloseCallback(GetGdShimHandler()->BindOnceOn( |
| this, &ClassicDynamicChannelHelper::on_channel_close, cid_token)); |
| |
| channel_enqueue_buffer_[cid_token] = std::make_unique< |
| bluetooth::os::EnqueueBuffer<bluetooth::packet::BasePacketBuilder>>( |
| channel->GetQueueUpEnd()); |
| |
| if (initiator_local) { |
| do_in_main_thread( |
| FROM_HERE, base::Bind(appl_info_.pL2CA_ConnectCfm_Cb, cid_token, 0)); |
| |
| tL2CAP_CFG_INFO cfg_info{}; |
| do_in_main_thread(FROM_HERE, base::Bind(appl_info_.pL2CA_ConfigCfm_Cb, |
| cid_token, L2CAP_INITIATOR_LOCAL, |
| base::Unretained(&cfg_info))); |
| } else { |
| if (appl_info_.pL2CA_ConnectInd_Cb == nullptr) { |
| Disconnect(cid_token); |
| return; |
| } |
| do_in_main_thread(FROM_HERE, base::Bind(appl_info_.pL2CA_ConnectInd_Cb, |
| address, cid_token, psm_, 0)); |
| |
| tL2CAP_CFG_INFO cfg_info{}; |
| do_in_main_thread(FROM_HERE, base::Bind(appl_info_.pL2CA_ConfigCfm_Cb, |
| cid_token, L2CAP_INITIATOR_LOCAL, |
| base::Unretained(&cfg_info))); |
| } |
| |
| channel->GetQueueUpEnd()->RegisterDequeue( |
| GetGdShimHandler(), |
| bluetooth::common::Bind(&ClassicDynamicChannelHelper::on_incoming_data, |
| bluetooth::common::Unretained(this), |
| cid_token)); |
| |
| channels_[cid_token] = std::move(channel); |
| } |
| |
| void on_incoming_data(uint16_t cid_token) { |
| auto channel = channels_.find(cid_token); |
| if (channel == channels_.end()) { |
| LOG_ERROR("Channel is not open"); |
| return; |
| } |
| auto packet = channel->second->GetQueueUpEnd()->TryDequeue(); |
| std::vector<uint8_t> packet_vector(packet->begin(), packet->end()); |
| BT_HDR* buffer = |
| static_cast<BT_HDR*>(osi_calloc(packet_vector.size() + sizeof(BT_HDR))); |
| std::copy(packet_vector.begin(), packet_vector.end(), buffer->data); |
| buffer->len = packet_vector.size(); |
| if (do_in_main_thread(FROM_HERE, |
| base::Bind(appl_info_.pL2CA_DataInd_Cb, cid_token, |
| base::Unretained(buffer))) != |
| BT_STATUS_SUCCESS) { |
| osi_free(buffer); |
| } |
| } |
| |
| void on_outgoing_connection_fail( |
| classic::DynamicChannelManager::ConnectionResult result) { |
| LOG(ERROR) << "Outgoing connection failed: " |
| << static_cast<int>(result.connection_result_code); |
| } |
| |
| bool send(uint16_t cid, |
| std::unique_ptr<bluetooth::packet::BasePacketBuilder> packet) { |
| auto buffer = channel_enqueue_buffer_.find(cid); |
| if (buffer == channel_enqueue_buffer_.end() || buffer->second == nullptr) { |
| LOG(ERROR) << "Channel is not open"; |
| return false; |
| } |
| buffer->second->Enqueue(std::move(packet), GetGdShimHandler()); |
| return true; |
| } |
| |
| uint16_t GetRemoteCid(uint16_t cid) { |
| auto channel = channels_.find(cid); |
| if (channel == channels_.end()) { |
| LOG_ERROR("Channel is not open"); |
| return 0; |
| } |
| return channel->second->HACK_GetRemoteCid(); |
| } |
| |
| bool SetChannelTxPriority(uint16_t cid, bool high_priority) { |
| auto channel = channels_.find(cid); |
| if (channel == channels_.end()) { |
| LOG_ERROR("Channel is not open"); |
| return false; |
| } |
| channel->second->HACK_SetChannelTxPriority(high_priority); |
| return true; |
| } |
| |
| void FlushChannel(uint16_t cid) { |
| auto buffer = channel_enqueue_buffer_.find(cid); |
| if (buffer == channel_enqueue_buffer_.end()) { |
| LOG_ERROR("Channel is not open"); |
| return; |
| } |
| buffer->second->Clear(); |
| } |
| |
| uint16_t GetNumBufferedPackets(uint16_t cid) { |
| auto buffer = channel_enqueue_buffer_.find(cid); |
| if (buffer == channel_enqueue_buffer_.end()) { |
| LOG_ERROR("Channel is not open"); |
| return 0; |
| } |
| return buffer->second->Size(); |
| } |
| |
| std::unordered_map<uint16_t, std::unique_ptr<classic::DynamicChannel>> |
| channels_; |
| std::unordered_map<uint16_t, std::unique_ptr<bluetooth::os::EnqueueBuffer< |
| bluetooth::packet::BasePacketBuilder>>> |
| channel_enqueue_buffer_; |
| std::unordered_map<uint16_t, bool> initiated_by_us_; |
| }; |
| |
| std::unordered_map<uint16_t, std::unique_ptr<ClassicDynamicChannelHelper>> |
| classic_dynamic_channel_helper_map_; |
| |
| void remove_classic_dynamic_channel_helper(uint16_t psm) { |
| if (classic_dynamic_channel_helper_map_.count(psm) != 0 && |
| classic_dynamic_channel_helper_map_[psm]->channels_.empty()) { |
| classic_dynamic_channel_helper_map_.erase(psm); |
| } |
| } |
| |
| // Helper: L2cap security enforcement shim |
| |
| std::unordered_map<intptr_t, |
| bluetooth::common::ContextualOnceCallback<void(bool)>> |
| security_enforce_callback_map = {}; |
| |
| class ClassicSecurityEnforcementShim |
| : public bluetooth::l2cap::classic::SecurityEnforcementInterface { |
| public: |
| static void security_enforce_result_callback(const RawAddress* bd_addr, |
| tBT_TRANSPORT trasnport, |
| void* p_ref_data, |
| tBTM_STATUS result) { |
| intptr_t counter = (intptr_t)p_ref_data; |
| if (security_enforce_callback_map.count(counter) == 0) { |
| LOG_ERROR("Received unexpected callback"); |
| return; |
| } |
| |
| auto& callback = security_enforce_callback_map[counter]; |
| std::move(callback).Invoke(result == BTM_SUCCESS); |
| security_enforce_callback_map.erase(counter); |
| } |
| |
| void Enforce(bluetooth::hci::AddressWithType remote, |
| bluetooth::l2cap::classic::SecurityPolicy policy, |
| ResultCallback result_callback) override { |
| uint16_t sec_mask = 0; |
| switch (policy) { |
| case bluetooth::l2cap::classic::SecurityPolicy:: |
| _SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK: |
| result_callback.Invoke(true); |
| return; |
| case bluetooth::l2cap::classic::SecurityPolicy::ENCRYPTED_TRANSPORT: |
| sec_mask = BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | |
| BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT; |
| break; |
| case bluetooth::l2cap::classic::SecurityPolicy::BEST: |
| case bluetooth::l2cap::classic::SecurityPolicy:: |
| AUTHENTICATED_ENCRYPTED_TRANSPORT: |
| sec_mask = BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | |
| BTM_SEC_IN_MITM | BTM_SEC_OUT_AUTHENTICATE | |
| BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_MITM; |
| break; |
| } |
| auto bd_addr = bluetooth::ToRawAddress(remote.GetAddress()); |
| security_enforce_callback_map[security_enforce_callback_counter_] = |
| std::move(result_callback); |
| btm_sec_l2cap_access_req_by_requirement( |
| bd_addr, sec_mask, true, security_enforce_result_callback, |
| (void*)security_enforce_callback_counter_); |
| security_enforce_callback_counter_++; |
| } |
| |
| intptr_t security_enforce_callback_counter_ = 100; |
| } security_enforcement_shim_; |
| |
| struct RemoteFeature { |
| uint8_t lmp_version = 0; |
| uint16_t manufacturer_name = 0; |
| uint16_t sub_version = 0; |
| uint8_t raw_remote_features[8]; |
| bool version_info_received = false; |
| bool role_switch_supported = false; |
| bool br_edr_supported = false; |
| bool le_supported_controller = false; |
| bool le_supported_host = false; |
| bool ssp_supported = false; |
| bool sc_supported = false; |
| bool received_page_0 = false; |
| bool received_page_1 = false; |
| }; |
| |
| std::unordered_map<RawAddress, RemoteFeature> remote_feature_map_; |
| |
| struct LinkPropertyListenerShim |
| : public bluetooth::l2cap::classic::LinkPropertyListener { |
| std::unordered_map<hci::Address, uint16_t> address_to_handle_; |
| |
| void OnLinkConnected(hci::Address remote, uint16_t handle) override { |
| address_to_handle_[remote] = handle; |
| } |
| |
| void OnLinkDisconnected(hci::Address remote) override { |
| address_to_handle_.erase(remote); |
| } |
| |
| void OnReadRemoteVersionInformation(hci::ErrorCode error_code, |
| hci::Address remote, uint8_t lmp_version, |
| uint16_t manufacturer_name, |
| uint16_t sub_version) override { |
| auto bda = bluetooth::ToRawAddress(remote); |
| auto& entry = remote_feature_map_[bda]; |
| entry.lmp_version = lmp_version; |
| entry.manufacturer_name = manufacturer_name; |
| entry.sub_version = sub_version; |
| entry.version_info_received = true; |
| } |
| |
| void OnReadRemoteExtendedFeatures(hci::Address remote, uint8_t page_number, |
| uint8_t max_page_number, |
| uint64_t features) override { |
| auto bda = bluetooth::ToRawAddress(remote); |
| auto& entry = remote_feature_map_[bda]; |
| if (page_number == 0) { |
| entry.received_page_0 = true; |
| if (features & 0x20) entry.role_switch_supported = true; |
| entry.br_edr_supported = !(features & kBrEdrNotSupportedMask); |
| entry.le_supported_controller = features & kLeSupportedControllerMask; |
| std::memcpy(entry.raw_remote_features, &features, 8); |
| } |
| if (page_number == 1) { |
| entry.received_page_1 = true; |
| if (features & 0x01) entry.ssp_supported = true; |
| entry.le_supported_host = features & kLeSupportedHostMask; |
| } |
| if (entry.received_page_0 && entry.received_page_1) { |
| const bool le_supported = |
| entry.le_supported_controller && entry.le_supported_host; |
| btm_sec_set_peer_sec_caps(address_to_handle_[remote], entry.ssp_supported, |
| false, entry.role_switch_supported, |
| entry.br_edr_supported, le_supported); |
| } |
| } |
| |
| void OnRoleChange(hci::ErrorCode error_code, hci::Address remote, |
| hci::Role role) override { |
| btm_rejectlist_role_change_device(ToRawAddress(remote), |
| ToLegacyHciErrorCode(error_code)); |
| btm_acl_role_changed(ToLegacyHciErrorCode(error_code), ToRawAddress(remote), |
| ToLegacyRole(role)); |
| } |
| |
| void OnReadClockOffset(hci::Address remote, uint16_t clock_offset) override { |
| btm_sec_update_clock_offset(address_to_handle_[remote], clock_offset); |
| } |
| |
| void OnModeChange(hci::ErrorCode error_code, hci::Address remote, |
| hci::Mode mode, uint16_t interval) override { |
| btm_sco_chk_pend_unpark(ToLegacyHciErrorCode(error_code), |
| address_to_handle_[remote]); |
| btm_pm_proc_mode_change(ToLegacyHciErrorCode(error_code), |
| address_to_handle_[remote], ToLegacyHciMode(mode), |
| interval); |
| } |
| |
| void OnSniffSubrating(hci::ErrorCode error_code, hci::Address remote, |
| uint16_t max_tx_lat, uint16_t max_rx_lat, |
| uint16_t min_remote_timeout, |
| uint16_t min_local_timeout) override { |
| process_ssr_event(ToLegacyHciErrorCode(error_code), |
| address_to_handle_[remote], max_tx_lat, max_rx_lat); |
| } |
| |
| } link_property_listener_shim_; |
| |
| class SecurityListenerShim |
| : public bluetooth::l2cap::classic::LinkSecurityInterfaceListener { |
| public: |
| void OnLinkConnected( |
| std::unique_ptr<bluetooth::l2cap::classic::LinkSecurityInterface> |
| interface) override { |
| auto bda = bluetooth::ToRawAddress(interface->GetRemoteAddress()); |
| |
| uint16_t handle = interface->GetAclHandle(); |
| address_to_handle_[bda] = handle; |
| btm_sec_connected(bda, handle, HCI_SUCCESS, 0); |
| BTM_PM_OnConnected(handle, bda); |
| BTA_dm_acl_up(bda, BT_TRANSPORT_BR_EDR); |
| address_to_interface_[bda] = std::move(interface); |
| } |
| |
| void OnAuthenticationComplete(hci::ErrorCode hci_status, |
| bluetooth::hci::Address remote) override { |
| // Note: if gd security is not enabled, we should use btu_hcif.cc path |
| auto bda = bluetooth::ToRawAddress(remote); |
| uint16_t handle = address_to_handle_[bda]; |
| btm_sec_auth_complete(handle, ToLegacyHciErrorCode(hci_status)); |
| } |
| |
| void OnLinkDisconnected(bluetooth::hci::Address remote) override { |
| auto bda = bluetooth::ToRawAddress(remote); |
| uint16_t handle = address_to_handle_[bda]; |
| address_to_handle_.erase(bda); |
| address_to_interface_.erase(bda); |
| btm_sec_disconnected(handle, HCI_ERR_PEER_USER); |
| BTA_dm_acl_down(bda, BT_TRANSPORT_BR_EDR); |
| BTM_PM_OnDisconnected(handle); |
| } |
| |
| void OnEncryptionChange(bluetooth::hci::Address remote, |
| bool encrypted) override { |
| // Note: if gd security is not enabled, we should use btu_hcif.cc path |
| auto bda = bluetooth::ToRawAddress(remote); |
| uint16_t handle = address_to_handle_[bda]; |
| btm_sec_encrypt_change(handle, HCI_SUCCESS, encrypted); |
| } |
| |
| void UpdateLinkHoldForSecurity(RawAddress remote, bool is_bonding) { |
| if (address_to_interface_.count(remote) == 0) { |
| return; |
| } |
| if (is_bonding) { |
| address_to_interface_[remote]->Hold(); |
| } else { |
| address_to_interface_[remote]->Release(); |
| } |
| } |
| |
| bool IsRoleCentral(RawAddress remote) { |
| if (address_to_interface_.count(remote) == 0) { |
| return false; |
| } |
| return address_to_interface_[remote]->GetRole() == |
| bluetooth::hci::Role::CENTRAL; |
| } |
| |
| void Disconnect(RawAddress remote) { |
| if (address_to_interface_.count(remote) == 0) { |
| return; |
| } |
| return address_to_interface_[remote]->Disconnect(); |
| } |
| |
| uint16_t GetNumAclLinks() { return address_to_handle_.size(); } |
| |
| bool IsLinkUp(RawAddress remote) { |
| return address_to_interface_.count(remote) != 0; |
| } |
| |
| std::unordered_map<RawAddress, uint16_t> address_to_handle_; |
| std::unordered_map< |
| RawAddress, |
| std::unique_ptr<bluetooth::l2cap::classic::LinkSecurityInterface>> |
| address_to_interface_; |
| } security_listener_shim_; |
| |
| bluetooth::l2cap::classic::SecurityInterface* security_interface_ = nullptr; |
| |
| struct LeLinkPropertyListenerShim |
| : public bluetooth::l2cap::le::LinkPropertyListener { |
| struct ConnectionInfo { |
| uint16_t handle; |
| hci::Role role; |
| AddressWithType address_with_type; |
| }; |
| std::unordered_map<hci::Address, ConnectionInfo> info_; |
| |
| void OnLinkConnected(AddressWithType remote, uint16_t handle, |
| hci::Role role) override { |
| info_[remote.GetAddress()] = {handle, role, remote}; |
| btm_ble_connected(ToRawAddress(remote.GetAddress()), handle, |
| HCI_ENCRYPT_MODE_DISABLED, static_cast<uint8_t>(role), |
| static_cast<tBLE_ADDR_TYPE>(remote.GetAddressType()), |
| false); |
| } |
| |
| void OnLinkDisconnected(hci::AddressWithType remote) override { |
| info_.erase(remote.GetAddress()); |
| } |
| |
| void OnReadRemoteVersionInformation(hci::ErrorCode hci_status, |
| hci::AddressWithType remote, |
| uint8_t lmp_version, |
| uint16_t manufacturer_name, |
| uint16_t sub_version) override { |
| auto bda = bluetooth::ToRawAddress(remote.GetAddress()); |
| auto& entry = remote_feature_map_[bda]; |
| entry.lmp_version = lmp_version; |
| entry.manufacturer_name = manufacturer_name; |
| entry.sub_version = sub_version; |
| entry.version_info_received = true; |
| } |
| |
| void OnConnectionUpdate(hci::AddressWithType remote, |
| uint16_t connection_interval, |
| uint16_t connection_latency, |
| uint16_t supervision_timeout) override { |
| acl_ble_update_event_received( |
| HCI_SUCCESS, info_[remote.GetAddress()].handle, connection_interval, |
| connection_latency, supervision_timeout); |
| } |
| |
| void OnPhyUpdate(hci::AddressWithType remote, uint8_t tx_phy, |
| uint8_t rx_phy) override { |
| gatt_notify_phy_updated(GATT_SUCCESS, info_[remote.GetAddress()].handle, |
| tx_phy, rx_phy); |
| } |
| |
| void OnDataLengthChange(hci::AddressWithType remote, uint16_t tx_octets, |
| uint16_t tx_time, uint16_t rx_octets, |
| uint16_t rx_time) override { |
| // Used by L2cap internal only. |
| } |
| } le_link_property_listener_shim_; |
| |
| std::unordered_map<intptr_t, |
| bluetooth::common::ContextualOnceCallback<void(bool)>> |
| le_security_enforce_callback_map = {}; |
| |
| class LeSecurityEnforcementShim |
| : public bluetooth::l2cap::le::SecurityEnforcementInterface { |
| public: |
| static void le_security_enforce_result_callback(const RawAddress* bd_addr, |
| tBT_TRANSPORT trasnport, |
| void* p_ref_data, |
| tBTM_STATUS result) { |
| intptr_t counter = (intptr_t)p_ref_data; |
| if (le_security_enforce_callback_map.count(counter) == 0) { |
| LOG_ERROR("Received unexpected callback"); |
| return; |
| } |
| auto& callback = le_security_enforce_callback_map[counter]; |
| std::move(callback).Invoke(result == BTM_SUCCESS); |
| le_security_enforce_callback_map.erase(counter); |
| } |
| |
| void Enforce(bluetooth::hci::AddressWithType remote, |
| bluetooth::l2cap::le::SecurityPolicy policy, |
| ResultCallback result_callback) override { |
| tBTM_BLE_SEC_ACT sec_act = BTM_BLE_SEC_NONE; |
| switch (policy) { |
| case bluetooth::l2cap::le::SecurityPolicy:: |
| NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK: |
| result_callback.Invoke(true); |
| return; |
| case bluetooth::l2cap::le::SecurityPolicy::ENCRYPTED_TRANSPORT: |
| sec_act = BTM_BLE_SEC_ENCRYPT; |
| break; |
| case bluetooth::l2cap::le::SecurityPolicy::BEST: |
| case bluetooth::l2cap::le::SecurityPolicy:: |
| AUTHENTICATED_ENCRYPTED_TRANSPORT: |
| sec_act = BTM_BLE_SEC_ENCRYPT_MITM; |
| break; |
| default: |
| result_callback.Invoke(false); |
| } |
| auto bd_addr = bluetooth::ToRawAddress(remote.GetAddress()); |
| le_security_enforce_callback_map[security_enforce_callback_counter_] = |
| std::move(result_callback); |
| BTM_SetEncryption(bd_addr, BT_TRANSPORT_LE, |
| le_security_enforce_result_callback, |
| (void*)security_enforce_callback_counter_, sec_act); |
| security_enforce_callback_counter_++; |
| } |
| |
| intptr_t security_enforce_callback_counter_ = 100; |
| } le_security_enforcement_shim_; |
| } // namespace |
| |
| bool L2CA_ReadRemoteVersion(const RawAddress& addr, uint8_t* lmp_version, |
| uint16_t* manufacturer, uint16_t* lmp_sub_version) { |
| auto& entry = remote_feature_map_[addr]; |
| if (!entry.version_info_received) { |
| return false; |
| } |
| if (lmp_version != nullptr) *lmp_version = entry.lmp_version; |
| if (manufacturer != nullptr) *manufacturer = entry.manufacturer_name; |
| if (lmp_sub_version != nullptr) *lmp_sub_version = entry.sub_version; |
| return true; |
| } |
| |
| uint8_t* L2CA_ReadRemoteFeatures(const RawAddress& addr) { |
| auto& entry = remote_feature_map_[addr]; |
| if (!entry.received_page_0) { |
| return nullptr; |
| } |
| return entry.raw_remote_features; |
| } |
| |
| static void on_sco_disconnect(uint16_t handle, uint8_t reason) { |
| GetGdShimHandler()->Post(base::BindOnce(base::IgnoreResult(&btm_sco_removed), |
| handle, |
| static_cast<tHCI_REASON>(reason))); |
| } |
| |
| void L2CA_UseLegacySecurityModule() { |
| LOG_INFO("GD L2cap is using legacy security module"); |
| GetL2capClassicModule()->SetLinkPropertyListener( |
| GetGdShimHandler(), &link_property_listener_shim_); |
| |
| GetL2capClassicModule()->InjectSecurityEnforcementInterface( |
| &security_enforcement_shim_); |
| security_interface_ = GetL2capClassicModule()->GetSecurityInterface( |
| GetGdShimHandler(), &security_listener_shim_); |
| |
| GetL2capLeModule()->SetLinkPropertyListener(GetGdShimHandler(), |
| &le_link_property_listener_shim_); |
| GetL2capLeModule()->InjectSecurityEnforcementInterface( |
| &le_security_enforcement_shim_); |
| |
| GetAclManager()->HACK_SetNonAclDisconnectCallback(on_sco_disconnect); |
| } |
| |
| /** |
| * Classic Service Registration APIs |
| */ |
| uint16_t L2CA_Register(uint16_t client_psm, const tL2CAP_APPL_INFO& callbacks, |
| bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info, |
| uint16_t my_mtu, uint16_t required_remote_mtu, |
| uint16_t sec_level) { |
| if (classic_dynamic_channel_helper_map_.count(client_psm) != 0) { |
| LOG(ERROR) << __func__ << "Already registered psm: " << client_psm; |
| return 0; |
| } |
| |
| classic::DynamicChannelConfigurationOption config; |
| config.minimal_remote_mtu = std::max<uint16_t>(required_remote_mtu, 48); |
| config.incoming_mtu = my_mtu; |
| config.channel_mode = |
| (p_ertm_info != nullptr && |
| p_ertm_info->preferred_mode == L2CAP_FCR_ERTM_MODE |
| ? classic::DynamicChannelConfigurationOption:: |
| RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION |
| : classic::DynamicChannelConfigurationOption:: |
| RetransmissionAndFlowControlMode::L2CAP_BASIC); |
| |
| classic::SecurityPolicy policy = |
| (client_psm == 1 |
| ? classic::SecurityPolicy:: |
| _SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK |
| : classic::SecurityPolicy::ENCRYPTED_TRANSPORT); |
| if (sec_level & (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_OUT_AUTHENTICATE)) { |
| policy = classic::SecurityPolicy::BEST; |
| } |
| |
| classic_dynamic_channel_helper_map_[client_psm] = |
| std::make_unique<ClassicDynamicChannelHelper>(client_psm, callbacks, |
| config, policy); |
| |
| classic_dynamic_channel_helper_map_[client_psm]->Register(); |
| return client_psm; |
| } |
| |
| void L2CA_Deregister(uint16_t psm) { |
| if (classic_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return; |
| } |
| classic_dynamic_channel_helper_map_[psm]->Unregister(); |
| } |
| |
| /** |
| * Classic Connection Oriented Channel APIS |
| */ |
| uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& raw_address) { |
| if (classic_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return 0; |
| } |
| uint16_t cid_token = add_classic_cid_token_entry(psm); |
| classic_dynamic_channel_helper_map_[psm]->Connect( |
| cid_token, ToAddressWithType(raw_address, BLE_ADDR_PUBLIC)); |
| return cid_token; |
| } |
| |
| bool L2CA_DisconnectReq(uint16_t cid) { |
| auto psm = classic_cid_token_to_channel_map_[cid]; |
| if (classic_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return false; |
| } |
| classic_dynamic_channel_helper_map_[psm]->Disconnect(cid); |
| return true; |
| } |
| |
| uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data) { |
| if (classic_cid_token_to_channel_map_.count(cid) == 0) { |
| LOG(ERROR) << __func__ << "Invalid cid: " << cid; |
| osi_free(p_data); |
| return 0; |
| } |
| auto psm = classic_cid_token_to_channel_map_[cid]; |
| if (classic_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| osi_free(p_data); |
| return 0; |
| } |
| auto len = p_data->len; |
| auto* data = p_data->data + p_data->offset; |
| uint8_t sent_length = |
| classic_dynamic_channel_helper_map_[psm]->send( |
| cid, MakeUniquePacket(data, len, IsPacketFlushable(p_data))) * |
| len; |
| osi_free(p_data); |
| return sent_length; |
| } |
| |
| bool L2CA_ReconfigCreditBasedConnsReq(const RawAddress& bd_addr, |
| std::vector<uint16_t>& lcids, |
| tL2CAP_LE_CFG_INFO* p_cfg) { |
| LOG_INFO("UNIMPLEMENTED %s addr: %s cfg:%p", __func__, |
| bd_addr.ToString().c_str(), p_cfg); |
| return false; |
| } |
| |
| std::vector<uint16_t> L2CA_ConnectCreditBasedReq(uint16_t psm, |
| const RawAddress& p_bd_addr, |
| tL2CAP_LE_CFG_INFO* p_cfg) { |
| LOG_INFO("UNIMPLEMENTED %s addr:%s", __func__, p_bd_addr.ToString().c_str()); |
| std::vector<uint16_t> result; |
| return result; |
| } |
| |
| bool L2CA_ConnectCreditBasedRsp(const RawAddress& bd_addr, uint8_t id, |
| std::vector<uint16_t>& accepted_lcids, |
| uint16_t result, tL2CAP_LE_CFG_INFO* p_cfg) { |
| LOG_INFO("UNIMPLEMENTED %s addr:%s", __func__, bd_addr.ToString().c_str()); |
| return false; |
| } |
| |
| /** |
| * Link APIs |
| */ |
| bool L2CA_SetIdleTimeoutByBdAddr(const RawAddress& bd_addr, uint16_t timeout, |
| tBT_TRANSPORT transport) { |
| if (transport == BT_TRANSPORT_BR_EDR) { |
| LOG_INFO("UNIMPLEMENTED %s", __func__); |
| return false; |
| } |
| if (timeout == 0 || timeout == GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP) { |
| bluetooth::shim::L2CA_RemoveFixedChnl(kLeAttributeCid, bd_addr); |
| return true; |
| } else { |
| LOG_INFO("UNIMPLEMENTED %s", __func__); |
| return false; |
| } |
| } |
| |
| bool L2CA_SetAclPriority(uint16_t handle, bool high_priority) { |
| GetAclManager()->HACK_SetAclTxPriority(handle, high_priority); |
| return true; |
| } |
| |
| bool L2CA_SetAclPriority(const RawAddress& bd_addr, tL2CAP_PRIORITY priority) { |
| uint16_t handle = security_listener_shim_.address_to_handle_[bd_addr]; |
| return L2CA_SetAclPriority(handle, priority == L2CAP_PRIORITY_HIGH); |
| } |
| |
| bool L2CA_GetPeerFeatures(const RawAddress& bd_addr, uint32_t* p_ext_feat, |
| uint8_t* p_chnl_mask) { |
| LOG_INFO("UNIMPLEMENTED %s", __func__); |
| return false; |
| } |
| |
| static constexpr uint16_t kAttCid = 4; |
| |
| struct LeFixedChannelHelper { |
| LeFixedChannelHelper(uint16_t cid) : cid_(cid) {} |
| |
| uint16_t cid_; |
| |
| void on_registration_complete( |
| le::FixedChannelManager::RegistrationResult result, |
| std::unique_ptr<le::FixedChannelService> service) { |
| if (result != le::FixedChannelManager::RegistrationResult::SUCCESS) { |
| LOG(ERROR) << "Channel is not registered. cid=" << +cid_; |
| return; |
| } |
| channel_service_ = std::move(service); |
| } |
| |
| std::unique_ptr<le::FixedChannelService> channel_service_ = nullptr; |
| |
| void on_channel_close(bluetooth::hci::Address device, |
| bluetooth::hci::ErrorCode error_code) { |
| auto address = bluetooth::ToRawAddress(device); |
| channel_enqueue_buffer_[device] = nullptr; |
| channels_[device]->GetQueueUpEnd()->UnregisterDequeue(); |
| channels_[device] = nullptr; |
| conn_parameters_.erase(device); |
| uint8_t error = static_cast<uint8_t>(error_code); |
| (freg_.pL2CA_FixedConn_Cb)(cid_, address, false, error, BT_TRANSPORT_LE); |
| } |
| |
| void on_channel_open(std::unique_ptr<le::FixedChannel> channel) { |
| auto remote = channel->GetDevice(); |
| auto device = remote.GetAddress(); |
| channel->RegisterOnCloseCallback( |
| GetGdShimHandler(), bluetooth::common::BindOnce( |
| &LeFixedChannelHelper::on_channel_close, |
| bluetooth::common::Unretained(this), device)); |
| if (cid_ == kAttCid) { |
| channel->Acquire(); |
| } |
| channel_enqueue_buffer_[device] = std::make_unique< |
| bluetooth::os::EnqueueBuffer<bluetooth::packet::BasePacketBuilder>>( |
| channel->GetQueueUpEnd()); |
| channel->GetQueueUpEnd()->RegisterDequeue( |
| GetGdShimHandler(), |
| bluetooth::common::Bind(&LeFixedChannelHelper::on_incoming_data, |
| bluetooth::common::Unretained(this), device)); |
| channels_[device] = std::move(channel); |
| conn_parameters_[device] = {}; |
| |
| auto address = bluetooth::ToRawAddress(device); |
| |
| (freg_.pL2CA_FixedConn_Cb)(cid_, address, true, 0, BT_TRANSPORT_LE); |
| } |
| |
| void on_incoming_data(bluetooth::hci::Address device) { |
| auto channel = channels_.find(device); |
| if (channel == channels_.end()) { |
| LOG_ERROR("Channel is not open"); |
| return; |
| } |
| auto packet = channel->second->GetQueueUpEnd()->TryDequeue(); |
| std::vector<uint8_t> packet_vector(packet->begin(), packet->end()); |
| BT_HDR* buffer = |
| static_cast<BT_HDR*>(osi_calloc(packet_vector.size() + sizeof(BT_HDR))); |
| std::copy(packet_vector.begin(), packet_vector.end(), buffer->data); |
| buffer->len = packet_vector.size(); |
| auto address = bluetooth::ToRawAddress(device); |
| freg_.pL2CA_FixedData_Cb(cid_, address, buffer); |
| } |
| |
| void on_outgoing_connection_fail( |
| RawAddress remote, le::FixedChannelManager::ConnectionResult result) { |
| LOG(ERROR) << "Outgoing connection failed"; |
| freg_.pL2CA_FixedConn_Cb(cid_, remote, true, 0, BT_TRANSPORT_LE); |
| } |
| |
| bool send(hci::Address remote, |
| std::unique_ptr<bluetooth::packet::BasePacketBuilder> packet) { |
| auto buffer = channel_enqueue_buffer_.find(remote); |
| if (buffer == channel_enqueue_buffer_.end() || buffer->second == nullptr) { |
| LOG(ERROR) << "Channel is not open for cid " << cid_; |
| return false; |
| } |
| buffer->second->Enqueue(std::move(packet), GetGdShimHandler()); |
| return true; |
| } |
| |
| std::unordered_map<hci::Address, std::unique_ptr<le::FixedChannel>> channels_; |
| std::unordered_map<hci::Address, std::unique_ptr<bluetooth::os::EnqueueBuffer< |
| bluetooth::packet::BasePacketBuilder>>> |
| channel_enqueue_buffer_; |
| |
| struct ConnectionParameter { |
| // Default values are from GD HCI_ACL le_impl. |
| uint16_t min_int = 0x0018; |
| uint16_t max_int = 0x0028; |
| uint16_t latency = 0x0000; |
| uint16_t timeout = 0x001f4; |
| uint16_t min_ce_len = 0x0000; |
| uint16_t max_ce_len = 0x0000; |
| bool update_allowed = true; |
| }; |
| std::unordered_map<hci::Address, ConnectionParameter> conn_parameters_; |
| tL2CAP_FIXED_CHNL_REG freg_; |
| }; |
| |
| static LeFixedChannelHelper att_helper{4}; |
| static LeFixedChannelHelper smp_helper{6}; |
| static std::unordered_map<uint16_t, LeFixedChannelHelper&> |
| le_fixed_channel_helper_{ |
| {4, att_helper}, |
| {6, smp_helper}, |
| }; |
| |
| /** |
| * Fixed Channel APIs. Note: Classic fixed channel (connectionless and BR SMP) |
| * is not supported |
| */ |
| bool L2CA_RegisterFixedChannel(uint16_t cid, tL2CAP_FIXED_CHNL_REG* p_freg) { |
| if (cid != kAttCid && cid != kSmpCid) { |
| LOG(ERROR) << "Invalid cid: " << cid; |
| return false; |
| } |
| auto* helper = &le_fixed_channel_helper_.find(cid)->second; |
| if (helper == nullptr) { |
| LOG(ERROR) << "Can't register cid " << cid; |
| return false; |
| } |
| GetL2capLeModule()->GetFixedChannelManager()->RegisterService( |
| cid, |
| common::BindOnce(&LeFixedChannelHelper::on_registration_complete, |
| common::Unretained(helper)), |
| common::Bind(&LeFixedChannelHelper::on_channel_open, |
| common::Unretained(helper)), |
| GetGdShimHandler()); |
| helper->freg_ = *p_freg; |
| return true; |
| } |
| |
| bool L2CA_ConnectFixedChnl(uint16_t cid, const RawAddress& rem_bda) { |
| if (cid != kAttCid && cid != kSmpCid) { |
| LOG(ERROR) << "Invalid cid " << cid; |
| return false; |
| } |
| |
| auto* helper = &le_fixed_channel_helper_.find(cid)->second; |
| auto remote = Btm::GetAddressAndType(rem_bda); |
| auto record = |
| le_link_property_listener_shim_.info_.find(ToGdAddress(rem_bda)); |
| if (record != le_link_property_listener_shim_.info_.end()) { |
| remote = record->second.address_with_type; |
| } |
| LOG(ERROR) << __func__ << remote.ToString(); |
| auto manager = GetL2capLeModule()->GetFixedChannelManager(); |
| manager->ConnectServices( |
| remote, |
| common::BindOnce(&LeFixedChannelHelper::on_outgoing_connection_fail, |
| common::Unretained(helper), rem_bda), |
| GetGdShimHandler()); |
| return true; |
| } |
| |
| uint16_t L2CA_SendFixedChnlData(uint16_t cid, const RawAddress& rem_bda, |
| BT_HDR* p_buf) { |
| if (cid != kAttCid && cid != kSmpCid) { |
| LOG(ERROR) << "Invalid cid " << cid; |
| osi_free(p_buf); |
| return L2CAP_DW_FAILED; |
| } |
| auto* helper = &le_fixed_channel_helper_.find(cid)->second; |
| auto len = p_buf->len; |
| auto* data = p_buf->data + p_buf->offset; |
| bool sent = |
| helper->send(ToGdAddress(rem_bda), |
| MakeUniquePacket(data, len, IsPacketFlushable(p_buf))); |
| osi_free(p_buf); |
| return sent ? L2CAP_DW_SUCCESS : L2CAP_DW_FAILED; |
| } |
| |
| bool L2CA_RemoveFixedChnl(uint16_t cid, const RawAddress& rem_bda) { |
| if (cid != kAttCid && cid != kSmpCid) { |
| LOG(ERROR) << "Invalid cid " << cid; |
| return false; |
| } |
| auto* helper = &le_fixed_channel_helper_.find(cid)->second; |
| auto channel = helper->channels_.find(ToGdAddress(rem_bda)); |
| if (channel == helper->channels_.end() || channel->second == nullptr) { |
| LOG(ERROR) << "Channel is not open"; |
| return false; |
| } |
| channel->second->Release(); |
| return true; |
| } |
| |
| uint16_t L2CA_GetLeHandle(const RawAddress& rem_bda) { |
| auto addr = ToGdAddress(rem_bda); |
| if (le_link_property_listener_shim_.info_.count(addr) == 0) { |
| return 0; |
| } |
| return le_link_property_listener_shim_.info_[addr].handle; |
| } |
| |
| void L2CA_LeConnectionUpdate(const RawAddress& rem_bda, uint16_t min_int, |
| uint16_t max_int, uint16_t latency, |
| uint16_t timeout, uint16_t min_ce_len, |
| uint16_t max_ce_len) { |
| auto* helper = &le_fixed_channel_helper_.find(kAttCid)->second; |
| auto channel = helper->channels_.find(ToGdAddress(rem_bda)); |
| if (channel == helper->channels_.end() || channel->second == nullptr) { |
| LOG(ERROR) << "Channel is not open"; |
| return; |
| } |
| |
| auto& parameter = helper->conn_parameters_[ToGdAddress(rem_bda)]; |
| |
| parameter.min_int = min_int; |
| parameter.max_int = max_int; |
| parameter.latency = latency; |
| parameter.timeout = timeout; |
| parameter.min_ce_len = min_ce_len; |
| parameter.max_ce_len = max_ce_len; |
| |
| if (parameter.update_allowed) { |
| channel->second->GetLinkOptions()->UpdateConnectionParameter( |
| min_int, max_int, latency, timeout, min_ce_len, max_ce_len); |
| } |
| // If update not allowed, don't update; instead cache the value, and update |
| // when update is allowed. |
| } |
| |
| bool L2CA_EnableUpdateBleConnParams(const RawAddress& rem_bda, bool enable) { |
| // When enable is false, we disallow remote connection update request, and |
| // we use default parameters temporarily. |
| auto* helper = &le_fixed_channel_helper_.find(kAttCid)->second; |
| auto channel = helper->channels_.find(ToGdAddress(rem_bda)); |
| if (channel == helper->channels_.end() || channel->second == nullptr) { |
| LOG(ERROR) << "Channel is not open"; |
| return false; |
| } |
| |
| auto& parameter = helper->conn_parameters_[ToGdAddress(rem_bda)]; |
| parameter.update_allowed = enable; |
| // TODO(hsz): Notify HCI_ACL LE to allow/disallow remote request. |
| |
| if (parameter.update_allowed) { |
| // Use cached values |
| uint16_t min_int = parameter.min_int; |
| uint16_t max_int = parameter.max_int; |
| uint16_t latency = parameter.latency; |
| uint16_t timeout = parameter.timeout; |
| uint16_t min_ce_len = parameter.min_ce_len; |
| uint16_t max_ce_len = parameter.max_ce_len; |
| channel->second->GetLinkOptions()->UpdateConnectionParameter( |
| min_int, max_int, latency, timeout, min_ce_len, max_ce_len); |
| } else { |
| // Use the value from legacy l2cble_start_conn_update |
| uint16_t min_int = BTM_BLE_CONN_INT_MIN; |
| uint16_t max_int = BTM_BLE_CONN_INT_MIN; |
| L2CA_AdjustConnectionIntervals(&min_int, &max_int, BTM_BLE_CONN_INT_MIN); |
| uint16_t latency = BTM_BLE_CONN_PERIPHERAL_LATENCY_DEF; |
| uint16_t timeout = BTM_BLE_CONN_TIMEOUT_DEF; |
| uint16_t min_ce_len = 0x0000; |
| uint16_t max_ce_len = 0x0000; |
| channel->second->GetLinkOptions()->UpdateConnectionParameter( |
| min_int, max_int, latency, timeout, min_ce_len, max_ce_len); |
| } |
| return true; |
| } |
| |
| /** |
| * Channel hygiene APIs |
| */ |
| bool L2CA_GetRemoteCid(uint16_t lcid, uint16_t* rcid) { |
| auto psm = classic_cid_token_to_channel_map_[lcid]; |
| if (classic_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return false; |
| } |
| *rcid = classic_dynamic_channel_helper_map_[psm]->GetRemoteCid(lcid); |
| return *rcid != 0; |
| } |
| |
| bool L2CA_SetTxPriority(uint16_t cid, tL2CAP_CHNL_PRIORITY priority) { |
| auto psm = classic_cid_token_to_channel_map_[cid]; |
| if (classic_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return false; |
| } |
| bool high_priority = priority == L2CAP_CHNL_PRIORITY_HIGH; |
| return classic_dynamic_channel_helper_map_[psm]->SetChannelTxPriority( |
| cid, high_priority); |
| } |
| |
| bool L2CA_SetLeGattTimeout(const RawAddress& rem_bda, uint16_t idle_tout) { |
| if (idle_tout == 0xffff) { |
| bluetooth::shim::L2CA_ConnectFixedChnl(kLeAttributeCid, rem_bda); |
| } else { |
| bluetooth::shim::L2CA_RemoveFixedChnl(kLeAttributeCid, rem_bda); |
| } |
| return true; |
| } |
| |
| bool L2CA_SetChnlFlushability(uint16_t cid, bool is_flushable) { |
| LOG_INFO("UNIMPLEMENTED %s", __func__); |
| return false; |
| } |
| |
| uint16_t L2CA_FlushChannel(uint16_t lcid, uint16_t num_to_flush) { |
| auto psm = classic_cid_token_to_channel_map_[lcid]; |
| if (classic_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return 0; |
| } |
| if (num_to_flush == L2CAP_FLUSH_CHANS_GET) { |
| return classic_dynamic_channel_helper_map_[psm]->GetNumBufferedPackets( |
| lcid); |
| } else { |
| classic_dynamic_channel_helper_map_[psm]->FlushChannel(lcid); |
| return 1; // Client doesn't care |
| } |
| // TODO: Implement LE part |
| } |
| |
| bool L2CA_IsLinkEstablished(const RawAddress& bd_addr, |
| tBT_TRANSPORT transport) { |
| if (transport == BT_TRANSPORT_BR_EDR) { |
| return security_listener_shim_.IsLinkUp(bd_addr); |
| } else { |
| return bluetooth::shim::L2CA_GetLeHandle(bd_addr) != 0; |
| } |
| } |
| |
| bool L2CA_IsLeLink(uint16_t acl_handle) { |
| for (const auto& entry : le_link_property_listener_shim_.info_) { |
| if (entry.second.handle == acl_handle) return true; |
| } |
| return false; |
| } |
| |
| void L2CA_ReadConnectionAddr(const RawAddress& pseudo_addr, |
| RawAddress& conn_addr, uint8_t* p_addr_type) { |
| auto* helper = &le_fixed_channel_helper_.find(kSmpCid)->second; |
| auto channel = helper->channels_.find(ToGdAddress(pseudo_addr)); |
| if (channel == helper->channels_.end() || channel->second == nullptr) { |
| LOG(ERROR) << "Channel is not open!"; |
| return; |
| } |
| auto local = channel->second->GetLinkOptions()->GetLocalAddress(); |
| conn_addr = ToRawAddress(local.GetAddress()); |
| *p_addr_type = static_cast<uint8_t>(local.GetAddressType()); |
| } |
| |
| bool L2CA_ReadRemoteConnectionAddr(const RawAddress& pseudo_addr, |
| RawAddress& conn_addr, |
| uint8_t* p_addr_type) { |
| auto remote = ToGdAddress(pseudo_addr); |
| if (le_link_property_listener_shim_.info_.count(remote) == 0) { |
| LOG(ERROR) << __func__ << ": Unknown address"; |
| return false; |
| } |
| auto info = le_link_property_listener_shim_.info_[remote].address_with_type; |
| conn_addr = ToRawAddress(info.GetAddress()); |
| *p_addr_type = static_cast<tBLE_ADDR_TYPE>(info.GetAddressType()); |
| return true; |
| } |
| |
| hci_role_t L2CA_GetBleConnRole(const RawAddress& bd_addr) { |
| auto remote = ToGdAddress(bd_addr); |
| if (le_link_property_listener_shim_.info_.count(remote) == 0) { |
| return HCI_ROLE_UNKNOWN; |
| } |
| return static_cast<hci_role_t>( |
| le_link_property_listener_shim_.info_[remote].role); |
| } |
| |
| void L2CA_ConnectForSecurity(const RawAddress& bd_addr) { |
| security_interface_->InitiateConnectionForSecurity( |
| bluetooth::ToGdAddress(bd_addr)); |
| } |
| |
| void L2CA_SetBondingState(const RawAddress& bd_addr, bool is_bonding) { |
| security_listener_shim_.UpdateLinkHoldForSecurity(bd_addr, is_bonding); |
| } |
| |
| void L2CA_DisconnectLink(const RawAddress& remote) { |
| security_listener_shim_.Disconnect(remote); |
| } |
| |
| uint16_t L2CA_GetNumLinks() { return security_listener_shim_.GetNumAclLinks(); } |
| |
| // LE COC Shim Helper |
| |
| namespace { |
| |
| uint16_t le_cid_token_counter_ = 0x41; |
| |
| std::unordered_map<uint16_t /* token */, uint16_t /* psm */> |
| le_cid_token_to_channel_map_; |
| |
| uint16_t add_le_cid_token_entry(uint16_t psm) { |
| uint16_t new_token = le_cid_token_counter_; |
| le_cid_token_to_channel_map_[new_token] = psm; |
| le_cid_token_counter_++; |
| if (le_cid_token_counter_ == 0) le_cid_token_counter_ = 0x41; |
| return new_token; |
| } |
| |
| void remove_le_cid_token_entry(uint16_t cid_token) { |
| le_cid_token_to_channel_map_.erase(cid_token); |
| } |
| |
| void remove_le_dynamic_channel_helper(uint16_t psm); |
| |
| struct LeDynamicChannelHelper { |
| LeDynamicChannelHelper(uint16_t psm, tL2CAP_APPL_INFO appl_info, |
| le::DynamicChannelConfigurationOption config, |
| le::SecurityPolicy policy) |
| : psm_(psm), appl_info_(appl_info), config_(config), policy_(policy) {} |
| |
| uint16_t psm_; |
| tL2CAP_APPL_INFO appl_info_; |
| le::DynamicChannelConfigurationOption config_; |
| le::SecurityPolicy policy_; |
| |
| void Register() { |
| std::promise<void> promise; |
| auto future = promise.get_future(); |
| GetL2capLeModule()->GetDynamicChannelManager()->RegisterService( |
| psm_, config_, policy_, |
| base::BindOnce(&LeDynamicChannelHelper::on_registration_complete, |
| base::Unretained(this), std::move(promise)), |
| base::Bind(&LeDynamicChannelHelper::on_channel_open, |
| base::Unretained(this), 0), |
| GetGdShimHandler()); |
| future.wait_for(std::chrono::milliseconds(300)); |
| } |
| |
| void on_registration_complete( |
| std::promise<void> promise, |
| le::DynamicChannelManager::RegistrationResult result, |
| std::unique_ptr<le::DynamicChannelService> service) { |
| if (result != le::DynamicChannelManager::RegistrationResult::SUCCESS) { |
| LOG(ERROR) << "Channel is not registered. psm=" << +psm_ << (int)result; |
| promise.set_value(); |
| return; |
| } |
| channel_service_ = std::move(service); |
| promise.set_value(); |
| } |
| |
| std::unique_ptr<le::DynamicChannelService> channel_service_ = nullptr; |
| |
| void Connect(uint16_t cid_token, bluetooth::hci::AddressWithType device) { |
| if (channel_service_ == nullptr) { |
| LOG(ERROR) << __func__ << "Not registered"; |
| return; |
| } |
| initiated_by_us_[cid_token] = true; |
| GetL2capLeModule()->GetDynamicChannelManager()->ConnectChannel( |
| device, config_, psm_, |
| base::Bind(&LeDynamicChannelHelper::on_channel_open, |
| base::Unretained(this), cid_token), |
| base::BindOnce(&LeDynamicChannelHelper::on_outgoing_connection_fail, |
| base::Unretained(this)), |
| GetGdShimHandler()); |
| } |
| |
| void Disconnect(uint16_t cid_token) { |
| if (channel_service_ == nullptr) { |
| return; |
| } |
| if (channels_.count(cid_token) == 0) { |
| return; |
| } |
| channels_[cid_token]->Close(); |
| } |
| |
| void Unregister() { |
| if (channel_service_ != nullptr) { |
| channel_service_->Unregister( |
| base::BindOnce(&LeDynamicChannelHelper::on_unregistered, |
| base::Unretained(this)), |
| GetGdShimHandler()); |
| channel_service_ = nullptr; |
| } |
| } |
| |
| void on_unregistered() { |
| for (const auto& device : channels_) { |
| device.second->Close(); |
| } |
| remove_le_dynamic_channel_helper(psm_); |
| } |
| |
| void on_channel_close(uint16_t cid_token, |
| bluetooth::hci::ErrorCode error_code) { |
| channel_enqueue_buffer_[cid_token] = nullptr; |
| channel_enqueue_buffer_.erase(cid_token); |
| channels_[cid_token]->GetQueueUpEnd()->UnregisterDequeue(); |
| channels_.erase(cid_token); |
| do_in_main_thread(FROM_HERE, base::Bind(appl_info_.pL2CA_DisconnectInd_Cb, |
| cid_token, false)); |
| |
| remove_le_cid_token_entry(cid_token); |
| initiated_by_us_.erase(cid_token); |
| |
| if (channel_service_ == nullptr && channels_.empty()) { |
| // Try again |
| L2CA_Deregister(psm_); |
| } |
| } |
| |
| void on_channel_open(uint16_t cid_token, |
| std::unique_ptr<le::DynamicChannel> channel) { |
| auto device = channel->GetDevice(); |
| auto address = bluetooth::ToRawAddress(device.GetAddress()); |
| bool initiator_local = (cid_token != 0); |
| if (cid_token == 0) { |
| cid_token = add_le_cid_token_entry(psm_); |
| } |
| |
| channel->RegisterOnCloseCallback(GetGdShimHandler()->BindOnceOn( |
| this, &LeDynamicChannelHelper::on_channel_close, cid_token)); |
| |
| channel->GetQueueUpEnd()->RegisterDequeue( |
| GetGdShimHandler(), |
| bluetooth::common::Bind(&LeDynamicChannelHelper::on_incoming_data, |
| bluetooth::common::Unretained(this), |
| cid_token)); |
| |
| channel_enqueue_buffer_[cid_token] = std::make_unique< |
| bluetooth::os::EnqueueBuffer<bluetooth::packet::BasePacketBuilder>>( |
| channel->GetQueueUpEnd()); |
| |
| channels_[cid_token] = std::move(channel); |
| |
| if (initiator_local) { |
| do_in_main_thread( |
| FROM_HERE, base::Bind(appl_info_.pL2CA_ConnectCfm_Cb, cid_token, 0)); |
| |
| } else { |
| if (appl_info_.pL2CA_ConnectInd_Cb == nullptr) { |
| Disconnect(cid_token); |
| return; |
| } |
| do_in_main_thread(FROM_HERE, base::Bind(appl_info_.pL2CA_ConnectInd_Cb, |
| address, cid_token, psm_, 0)); |
| } |
| } |
| |
| void on_incoming_data(uint16_t cid_token) { |
| auto channel = channels_.find(cid_token); |
| if (channel == channels_.end()) { |
| LOG_ERROR("Channel is not open"); |
| return; |
| } |
| auto packet = channel->second->GetQueueUpEnd()->TryDequeue(); |
| std::vector<uint8_t> packet_vector(packet->begin(), packet->end()); |
| BT_HDR* buffer = |
| static_cast<BT_HDR*>(osi_calloc(packet_vector.size() + sizeof(BT_HDR))); |
| std::copy(packet_vector.begin(), packet_vector.end(), buffer->data); |
| buffer->len = packet_vector.size(); |
| if (do_in_main_thread(FROM_HERE, |
| base::Bind(appl_info_.pL2CA_DataInd_Cb, cid_token, |
| base::Unretained(buffer))) != |
| BT_STATUS_SUCCESS) { |
| osi_free(buffer); |
| } |
| } |
| |
| void on_outgoing_connection_fail( |
| le::DynamicChannelManager::ConnectionResult result) { |
| LOG(ERROR) << "Outgoing connection failed"; |
| } |
| |
| bool send(uint16_t cid, |
| std::unique_ptr<bluetooth::packet::BasePacketBuilder> packet) { |
| auto buffer = channel_enqueue_buffer_.find(cid); |
| if (buffer == channel_enqueue_buffer_.end() || buffer->second == nullptr) { |
| LOG(ERROR) << "Channel is not open"; |
| return false; |
| } |
| buffer->second->Enqueue(std::move(packet), GetGdShimHandler()); |
| return true; |
| } |
| |
| uint16_t GetMtu(uint16_t cid_token) { |
| if (channels_.count(cid_token) == 0) { |
| return 0; |
| } |
| return static_cast<uint16_t>(channels_[cid_token]->GetMtu()); |
| } |
| |
| std::unordered_map<uint16_t, std::unique_ptr<le::DynamicChannel>> channels_; |
| std::unordered_map<uint16_t, std::unique_ptr<bluetooth::os::EnqueueBuffer< |
| bluetooth::packet::BasePacketBuilder>>> |
| channel_enqueue_buffer_; |
| std::unordered_map<uint16_t, bool> initiated_by_us_; |
| }; |
| |
| std::unordered_map<uint16_t, std::unique_ptr<LeDynamicChannelHelper>> |
| le_dynamic_channel_helper_map_; |
| |
| void remove_le_dynamic_channel_helper(uint16_t psm) { |
| if (le_dynamic_channel_helper_map_.count(psm) != 0 && |
| le_dynamic_channel_helper_map_[psm]->channels_.empty()) { |
| le_dynamic_channel_helper_map_.erase(psm); |
| } |
| } |
| |
| std::unordered_set<uint16_t> assigned_dynamic_le_psm_; |
| uint16_t next_assigned_dynamic_le_psm_ = 0x80; |
| } // namespace |
| |
| /** |
| * Le Connection Oriented Channel APIs |
| */ |
| |
| uint16_t L2CA_AllocateLePSM() { |
| if (le_dynamic_channel_helper_map_.size() > 100) { |
| LOG_ERROR("Why do we need more than 100 dynamic channel PSMs?"); |
| return 0; |
| } |
| while (le_dynamic_channel_helper_map_.count(next_assigned_dynamic_le_psm_)) { |
| next_assigned_dynamic_le_psm_++; |
| if (next_assigned_dynamic_le_psm_ > 0xff) { |
| next_assigned_dynamic_le_psm_ = 0x80; |
| } |
| } |
| assigned_dynamic_le_psm_.emplace(next_assigned_dynamic_le_psm_); |
| return next_assigned_dynamic_le_psm_; |
| } |
| |
| void L2CA_FreeLePSM(uint16_t psm) { assigned_dynamic_le_psm_.erase(psm); } |
| |
| uint16_t L2CA_RegisterLECoc(uint16_t psm, const tL2CAP_APPL_INFO& callbacks, |
| uint16_t sec_level, tL2CAP_LE_CFG_INFO cfg) { |
| if (le_dynamic_channel_helper_map_.count(psm) != 0) { |
| LOG(ERROR) << __func__ << "Already registered psm: " << psm; |
| return 0; |
| } |
| |
| le::DynamicChannelConfigurationOption config; |
| config.mtu = cfg.mtu; |
| le::SecurityPolicy policy = |
| le::SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK; |
| if (sec_level & (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_OUT_AUTHENTICATE)) { |
| policy = le::SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT; |
| } |
| |
| le_dynamic_channel_helper_map_[psm] = |
| std::make_unique<LeDynamicChannelHelper>(psm, callbacks, config, policy); |
| le_dynamic_channel_helper_map_[psm]->Register(); |
| return psm; |
| } |
| |
| void L2CA_DeregisterLECoc(uint16_t psm) { |
| if (le_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return; |
| } |
| le_dynamic_channel_helper_map_[psm]->Unregister(); |
| } |
| |
| uint16_t L2CA_ConnectLECocReq(uint16_t psm, const RawAddress& p_bd_addr, |
| tL2CAP_LE_CFG_INFO* p_cfg) { |
| if (le_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return 0; |
| } |
| uint16_t cid_token = add_le_cid_token_entry(psm); |
| auto remote = Btm::GetAddressAndType(p_bd_addr); |
| auto record = |
| le_link_property_listener_shim_.info_.find(ToGdAddress(p_bd_addr)); |
| if (record != le_link_property_listener_shim_.info_.end()) { |
| remote = record->second.address_with_type; |
| } |
| le_dynamic_channel_helper_map_[psm]->Connect(cid_token, remote); |
| return cid_token; |
| } |
| |
| bool L2CA_GetPeerLECocConfig(uint16_t cid, tL2CAP_LE_CFG_INFO* peer_cfg) { |
| if (le_cid_token_to_channel_map_.count(cid) == 0) { |
| LOG(ERROR) << __func__ << "Invalid cid: " << cid; |
| return false; |
| } |
| auto psm = le_cid_token_to_channel_map_[cid]; |
| if (le_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return false; |
| } |
| auto mtu = le_dynamic_channel_helper_map_[psm]->GetMtu(cid); |
| peer_cfg->mtu = mtu; |
| return mtu; |
| } |
| |
| bool L2CA_DisconnectLECocReq(uint16_t cid) { |
| if (le_cid_token_to_channel_map_.count(cid) == 0) { |
| LOG(ERROR) << __func__ << "Invalid cid: " << cid; |
| return false; |
| } |
| auto psm = le_cid_token_to_channel_map_[cid]; |
| if (le_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| return false; |
| } |
| le_dynamic_channel_helper_map_[psm]->Disconnect(cid); |
| return true; |
| } |
| |
| uint8_t L2CA_LECocDataWrite(uint16_t cid, BT_HDR* p_data) { |
| if (le_cid_token_to_channel_map_.count(cid) == 0) { |
| LOG(ERROR) << __func__ << "Invalid cid: " << cid; |
| osi_free(p_data); |
| return 0; |
| } |
| auto psm = le_cid_token_to_channel_map_[cid]; |
| if (le_dynamic_channel_helper_map_.count(psm) == 0) { |
| LOG(ERROR) << __func__ << "Not registered psm: " << psm; |
| osi_free(p_data); |
| return 0; |
| } |
| auto len = p_data->len; |
| auto* data = p_data->data + p_data->offset; |
| uint8_t sent_length = |
| le_dynamic_channel_helper_map_[psm]->send( |
| cid, MakeUniquePacket(data, len, IsPacketFlushable(p_data))) * |
| len; |
| osi_free(p_data); |
| return sent_length; |
| } |
| |
| void L2CA_SwitchRoleToCentral(const RawAddress& addr) { |
| GetAclManager()->SwitchRole(ToGdAddress(addr), bluetooth::hci::Role::CENTRAL); |
| } |
| |
| } // namespace shim |
| } // namespace bluetooth |