From: Ɓukasz Rymanowski Date: Fri, 8 May 2020 23:26:11 +0000 (+0200) Subject: stack/eatt: Add initial implementation for EATT X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=b6317be537;p=android-x86%2Fsystem-bt.git stack/eatt: Add initial implementation for EATT Tag: #feature Bug: 159786353 Sponsor: jpawlowski@ Test: atest --host net_test_eatt Change-Id: I934153625e69b1e20af657ded8f17238484cb65e --- diff --git a/TEST_MAPPING b/TEST_MAPPING index bbaf4576f..e3b431871 100755 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -28,6 +28,9 @@ "name" : "net_test_device" }, { + "name" : "net_test_eatt" + }, + { "name" : "net_test_hci" }, { @@ -78,6 +81,10 @@ "host" : true }, { + "name" : "net_test_eatt", + "host" : true + }, + { "name" : "net_test_types", "host" : true }, diff --git a/stack/Android.bp b/stack/Android.bp index b6bf90ce4..bc3c27b75 100644 --- a/stack/Android.bp +++ b/stack/Android.bp @@ -25,6 +25,7 @@ cc_library_static { "avrc", "l2cap", "avdt", + "eatt", "gatt", "gap", "pan", @@ -125,6 +126,7 @@ cc_library_static { "btm/btm_scn.cc", "btu/btu_hcif.cc", "btu/btu_task.cc", + "eatt/eatt.cc", "gap/gap_ble.cc", "gap/gap_conn.cc", "gatt/att_protocol.cc", @@ -453,6 +455,7 @@ cc_test { include_dirs: [ "system/bt", "system/bt/stack/include", + "system/bt/stack/eatt", "system/bt/stack/l2cap", "system/bt/stack/btm", "system/bt/utils/include", @@ -460,6 +463,8 @@ cc_test { srcs: [ "test/gatt/gatt_sr_test.cc", "gatt/gatt_utils.cc", + "test/common/mock_eatt.cc", + "test/common/mock_gatt_layer.cc", ], shared_libs: [ "libcutils", @@ -471,6 +476,7 @@ cc_test { "libosi", "libbt-common", "libbt-protos-lite", + "libgmock", "libosi-AllocationTestHarness", ], sanitize: { @@ -489,6 +495,7 @@ cc_test { include_dirs: [ "system/bt", "system/bt/stack/btm", + "system/bt/stack/eatt", "system/bt/stack/include", "system/bt/utils/include", ], @@ -497,6 +504,8 @@ cc_test { "gatt/gatt_sr_hash.cc", "gatt/gatt_utils.cc", "test/gatt/mock_gatt_utils_ref.cc", + "test/common/mock_eatt.cc", + "test/common/mock_gatt_layer.cc", "test/stack_gatt_sr_hash_test.cc", ], shared_libs: [ @@ -508,6 +517,7 @@ cc_test { "libbt-common", "libbt-protos-lite", "liblog", + "libgmock", "libosi", ], } @@ -531,6 +541,7 @@ cc_test { "btm/btm_iso.cc", "test/btm_iso_test.cc", "test/common/mock_controller.cc", + "test/common/mock_gatt_layer.cc", "test/common/mock_hcic_layer.cc", ], static_libs: [ @@ -550,3 +561,51 @@ cc_test { }, }, } + +// EATT unit tests +cc_test { + name: "net_test_eatt", + test_suites: ["device-tests"], + host_supported: true, + defaults: [ + "fluoride_defaults", + "clang_coverage_bin", + ], + local_include_dirs: [ + "include", + "btm", + "eatt", + "gatt", + "l2cap", + "test/common", + ], + include_dirs:[ + "system/bt", + "system/bt/btcore/include", + ], + srcs: [ + "eatt/eatt.cc", + "test/common/mock_btm_api_layer.cc", + "test/common/mock_btif_storage.cc", + "test/common/mock_controller.cc", + "test/common/mock_gatt_layer.cc", + "test/common/mock_l2cap_layer.cc", + "test/gatt/mock_gatt_utils_ref.cc", + "test/eatt/eatt_test.cc", + ], + shared_libs: [ + "libcutils", + "libcrypto", + "libprotobuf-cpp-lite", + ], + static_libs: [ + "libbt-common", + "libbt-protos-lite", + "liblog", + "libgmock", + "libosi", + ], + sanitize: { + cfi: false, + }, +} diff --git a/stack/BUILD.gn b/stack/BUILD.gn index 8b9e74b6e..9bb356ce6 100644 --- a/stack/BUILD.gn +++ b/stack/BUILD.gn @@ -98,6 +98,7 @@ static_library("stack") { "btm/btm_sec.cc", "btu/btu_hcif.cc", "btu/btu_task.cc", + "eatt/eatt.cc", "gap/gap_ble.cc", "gap/gap_conn.cc", "gatt/att_protocol.cc", @@ -166,6 +167,7 @@ static_library("stack") { "gap", "pan", "bnep", + "eatt", "hid", "sdp", "smp", @@ -208,6 +210,7 @@ executable("stack_unittests") { include_dirs = [ "include", "//", + "//bta/eatt", "//bta/include", "//bta/sys", "//btcore/include", diff --git a/stack/eatt/eatt.cc b/stack/eatt/eatt.cc new file mode 100644 index 000000000..573092b1d --- /dev/null +++ b/stack/eatt/eatt.cc @@ -0,0 +1,207 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA - + * www.ehima.com + * + * 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. + */ + +#include "eatt_impl.h" +#include "stack/l2cap/l2c_int.h" + +using bluetooth::eatt::eatt_impl; + +namespace bluetooth { +namespace eatt { + +struct EattExtension::impl { + impl() = default; + ~impl() = default; + + void Start() { + if (eatt_impl_) { + LOG(ERROR) << "Eatt already started"; + return; + }; + + /* Register server for Eatt */ + memset(®_info_, 0, sizeof(reg_info_)); + reg_info_.pL2CA_CreditBasedConnectInd_Cb = eatt_connect_ind; + reg_info_.pL2CA_CreditBasedConnectCfm_Cb = eatt_connect_cfm; + reg_info_.pL2CA_CreditBasedReconfigCompleted_Cb = eatt_reconfig_completed; + reg_info_.pL2CA_DisconnectInd_Cb = eatt_disconnect_ind; + reg_info_.pL2CA_Error_Cb = eatt_error_cb; + reg_info_.pL2CA_DataInd_Cb = eatt_data_ind; + + if (L2CA_RegisterLECoc(BT_PSM_EATT, reg_info_, BTM_SEC_NONE) == 0) { + LOG(ERROR) << __func__ << " cannot register EATT"; + } else { + eatt_impl_ = std::make_unique(); + } + } + + void Stop() { + if (!eatt_impl_) { + LOG(ERROR) << "Eatt not started"; + return; + } + eatt_impl_.reset(nullptr); + L2CA_DeregisterLECoc(BT_PSM_EATT); + } + + bool IsRunning() { return eatt_impl_ ? true : false; } + + static eatt_impl* GetImplInstance(void) { + auto* instance = EattExtension::GetInstance(); + return instance->pimpl_->eatt_impl_.get(); + } + + static void eatt_connect_ind(const RawAddress& bda, + std::vector& lcids, uint16_t psm, + uint16_t peer_mtu, uint8_t identifier) { + auto p_eatt_impl = GetImplInstance(); + if (p_eatt_impl) + p_eatt_impl->eatt_l2cap_connect_ind(bda, lcids, psm, peer_mtu, + identifier); + } + + static void eatt_connect_cfm(const RawAddress& bda, uint16_t lcid, + uint16_t peer_mtu, uint16_t result) { + auto p_eatt_impl = GetImplInstance(); + if (p_eatt_impl) + p_eatt_impl->eatt_l2cap_connect_cfm(bda, lcid, peer_mtu, result); + } + + static void eatt_reconfig_completed(const RawAddress& bda, uint16_t lcid, + bool is_local_cfg, + tL2CAP_LE_CFG_INFO* p_cfg) { + auto p_eatt_impl = GetImplInstance(); + if (p_eatt_impl) + p_eatt_impl->eatt_l2cap_reconfig_completed(bda, lcid, is_local_cfg, + p_cfg); + } + + static void eatt_error_cb(uint16_t lcid, uint16_t reason) { + auto p_eatt_impl = GetImplInstance(); + if (p_eatt_impl) p_eatt_impl->eatt_l2cap_error_cb(lcid, reason); + } + + static void eatt_disconnect_ind(uint16_t lcid, bool please_confirm) { + auto p_eatt_impl = GetImplInstance(); + if (p_eatt_impl) + p_eatt_impl->eatt_l2cap_disconnect_ind(lcid, please_confirm); + } + + static void eatt_data_ind(uint16_t lcid, BT_HDR* data_p) { + auto p_eatt_impl = GetImplInstance(); + if (p_eatt_impl) p_eatt_impl->eatt_l2cap_data_ind(lcid, data_p); + } + + std::unique_ptr eatt_impl_; + tL2CAP_APPL_INFO reg_info_; +}; + +void EattExtension::AddFromStorage(const RawAddress& bd_addr) { + eatt_impl* p_eatt_impl = EattExtension::impl::GetImplInstance(); + if (p_eatt_impl) p_eatt_impl->add_from_storage(bd_addr); +} + +EattExtension::EattExtension() : pimpl_(std::make_unique()) {} + +bool EattExtension::IsEattSupportedByPeer(const RawAddress& bd_addr) { + return pimpl_->eatt_impl_->is_eatt_supported_by_peer(bd_addr); +} + +void EattExtension::Connect(const RawAddress& bd_addr) { + pimpl_->eatt_impl_->connect(bd_addr); +} + +void EattExtension::Disconnect(const RawAddress& bd_addr) { + pimpl_->eatt_impl_->disconnect(bd_addr); +} + +void EattExtension::Reconfigure(const RawAddress& bd_addr, uint16_t cid, + uint16_t mtu) { + pimpl_->eatt_impl_->reconfigure(bd_addr, cid, mtu); +} +void EattExtension::ReconfigureAll(const RawAddress& bd_addr, uint16_t mtu) { + pimpl_->eatt_impl_->reconfigure_all(bd_addr, mtu); +} + +EattChannel* EattExtension::FindEattChannelByCid(const RawAddress& bd_addr, + uint16_t cid) { + return pimpl_->eatt_impl_->find_eatt_channel_by_cid(bd_addr, cid); +} + +EattChannel* EattExtension::FindEattChannelByTransId(const RawAddress& bd_addr, + uint32_t trans_id) { + return pimpl_->eatt_impl_->find_eatt_channel_by_transid(bd_addr, trans_id); +} + +bool EattExtension::IsIndicationPending(const RawAddress& bd_addr, + uint16_t indication_handle) { + return pimpl_->eatt_impl_->is_indication_pending(bd_addr, indication_handle); +} + +EattChannel* EattExtension::GetChannelAvailableForIndication( + const RawAddress& bd_addr) { + return pimpl_->eatt_impl_->get_channel_available_for_indication(bd_addr); +} + +void EattExtension::FreeGattResources(const RawAddress& bd_addr) { + pimpl_->eatt_impl_->free_gatt_resources(bd_addr); +} + +bool EattExtension::IsOutstandingMsgInSendQueue(const RawAddress& bd_addr) { + return pimpl_->eatt_impl_->is_outstanding_msg_in_send_queue(bd_addr); +} + +EattChannel* EattExtension::GetChannelWithQueuedData( + const RawAddress& bd_addr) { + return pimpl_->eatt_impl_->get_channel_with_queued_data(bd_addr); +} + +EattChannel* EattExtension::GetChannelAvailableForClientRequest( + const RawAddress& bd_addr) { + return pimpl_->eatt_impl_->get_channel_available_for_client_request(bd_addr); +} + +/* Start stop GATT indication timer per CID */ +void EattExtension::StartIndicationConfirmationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->eatt_impl_->start_indication_confirm_timer(bd_addr, cid); +} + +void EattExtension::StopIndicationConfirmationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->eatt_impl_->stop_indication_confirm_timer(bd_addr, cid); +} + +/* Start stop application indication timeout */ +void EattExtension::StartAppIndicationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->eatt_impl_->start_app_indication_timer(bd_addr, cid); +} + +void EattExtension::StopAppIndicationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->eatt_impl_->stop_app_indication_timer(bd_addr, cid); +} + +void EattExtension::Start() { pimpl_->Start(); } + +void EattExtension::Stop() { pimpl_->Stop(); } + +EattExtension::~EattExtension() = default; + +} // namespace eatt +} // namespace bluetooth diff --git a/stack/eatt/eatt.h b/stack/eatt/eatt.h new file mode 100644 index 000000000..0af2fe3de --- /dev/null +++ b/stack/eatt/eatt.h @@ -0,0 +1,284 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA - + * www.ehima.com + * + * 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. + */ + +#pragma once + +#include + +#include "stack/gatt/gatt_int.h" + +#define EATT_MIN_MTU_MPS (64) +#define EATT_DEFAULT_MTU (256) + +namespace bluetooth { +namespace eatt { + +/* Enums */ +enum class EattChannelState : uint8_t { + EATT_CHANNEL_PENDING = 0x00, + EATT_CHANNEL_OPENED, + EATT_CHANNEL_RECONFIGURING, +}; + +class EattChannel { + public: + /* Pointer to EattDevice */ + RawAddress bda_; + uint16_t cid_; + uint16_t tx_mtu_; + uint16_t rx_mtu_; + EattChannelState state_; + + /* Used to keep server commands */ + tGATT_SR_CMD server_outstanding_cmd_; + /* Used to veryfy indication confirmation*/ + uint16_t indicate_handle_; + /* local app confirm to indication timer */ + alarm_t* ind_ack_timer_; + /* indication confirmation timer */ + alarm_t* ind_confirmation_timer_; + /* GATT client command queue */ + std::queue cl_cmd_q_; + + EattChannel(RawAddress& bda, uint16_t cid, uint16_t tx_mtu, uint16_t rx_mtu) + : bda_(bda), + cid_(cid), + tx_mtu_(tx_mtu), + rx_mtu_(rx_mtu), + state_(EattChannelState::EATT_CHANNEL_PENDING), + indicate_handle_(0), + ind_ack_timer_(NULL), + ind_confirmation_timer_(NULL) {} + + ~EattChannel() { + if (ind_ack_timer_ != NULL) { + alarm_free(ind_ack_timer_); + } + + if (ind_confirmation_timer_ != NULL) { + alarm_free(ind_confirmation_timer_); + } + } + + void EattChannelSetState(EattChannelState state) { + if (state_ == EattChannelState::EATT_CHANNEL_PENDING) { + if (state == EattChannelState::EATT_CHANNEL_OPENED) { + cl_cmd_q_ = std::queue(); + memset(&server_outstanding_cmd_, 0, sizeof(tGATT_SR_CMD)); + char name[64]; + sprintf(name, "eatt_ind_ack_timer_%s_cid_0x%04x", + bda_.ToString().c_str(), cid_); + ind_ack_timer_ = alarm_new(name); + + sprintf(name, "eatt_ind_conf_timer_%s_cid_0x%04x", + bda_.ToString().c_str(), cid_); + ind_confirmation_timer_ = alarm_new(name); + } + } + state_ = state; + } + void EattChannelSetTxMTU(uint16_t tx_mtu) { this->tx_mtu_ = tx_mtu; } +}; + +/* Interface class */ +class EattExtension { + public: + EattExtension(); + virtual ~EattExtension(); + + static EattExtension* GetInstance() { + static EattExtension* instance = new EattExtension(); + return instance; + } + + static void AddFromStorage(const RawAddress& bd_addr); + + /** + * Checks if EATT is supported on peer device. + * + * @param bd_addr peer device address + */ + virtual bool IsEattSupportedByPeer(const RawAddress& bd_addr); + + /** + * Connect at maximum 5 EATT channels to peer device. + * + * @param bd_addr peer device address + */ + virtual void Connect(const RawAddress& bd_addr); + + /** + * Disconnect all EATT channels to peer device. + * + * @param bd_addr peer device address + */ + virtual void Disconnect(const RawAddress& bd_addr); + + /** + * Reconfigure EATT channel for give CID + * + * @param bd_addr peer device address + * @param cid channel id + * @param mtu new maximum transmit unit available of local device + */ + virtual void Reconfigure(const RawAddress& bd_addr, uint16_t cid, + uint16_t mtu); + + /** + * Reconfigure all EATT channels to peer device. + * + * @param bd_addr peer device address + * @param mtu new maximum transmit unit available of local device + */ + virtual void ReconfigureAll(const RawAddress& bd_addr, uint16_t mtu); + + /* Below methods required by GATT implementation */ + + /** + * Find EATT channel by cid. + * + * @param bd_addr peer device address + * @param cid channel id + * + * @return Eatt Channel instance. + */ + virtual EattChannel* FindEattChannelByCid(const RawAddress& bd_addr, + uint16_t cid); + + /** + * Find EATT channel by transaction id. + * + * @param bd_addr peer device address + * @param trans_id transaction id + * + * @return pointer to EATT channel. + */ + virtual EattChannel* FindEattChannelByTransId(const RawAddress& bd_addr, + uint32_t trans_id); + + /** + * Check if EATT channel on given handle is waiting for a indication + * confirmation + * + * @param bd_addr peer device address + * @param indication_handle handle of the pending indication + * + * @return true if confirmation is pending false otherwise + */ + virtual bool IsIndicationPending(const RawAddress& bd_addr, + uint16_t indication_handle); + + /** + * Get EATT channel available for indication. + * + * @param bd_addr peer device address + * + * @return pointer to EATT channel. + */ + virtual EattChannel* GetChannelAvailableForIndication( + const RawAddress& bd_addr); + + /** + * Free Resources. + * + * (Maybe not needed) + * @param bd_addr peer device address + * + */ + virtual void FreeGattResources(const RawAddress& bd_addr); + + /** + * Check if there is any EATT channels having some msg in its send queue + * + * @param bd_addr peer device address + * + * @return true when there is at least one EATT channel ready to send + */ + virtual bool IsOutstandingMsgInSendQueue(const RawAddress& bd_addr); + + /** + * Get EATT channel ready to send. + * + * @param bd_addr peer device address + * + * @return pointer to EATT channel. + */ + virtual EattChannel* GetChannelWithQueuedData(const RawAddress& bd_addr); + + /** + * Get EATT channel available to send GATT request. + * + * @param bd_addr peer device address + * + * @return pointer to EATT channel. + */ + virtual EattChannel* GetChannelAvailableForClientRequest( + const RawAddress& bd_addr); + + /** + * Start GATT indication timer per CID. + * + * @param bd_addr peer device address + * @param cid channel id + */ + virtual void StartIndicationConfirmationTimer(const RawAddress& bd_addr, + uint16_t cid); + + /** + * Stop GATT indication timer per CID. + * + * @param bd_addr peer device address + * @param cid channel id + */ + virtual void StopIndicationConfirmationTimer(const RawAddress& bd_addr, + uint16_t cid); + + /** + * Start application time for incoming indication on given CID + * + * @param bd_addr peer device address + * @param cid channel id + */ + virtual void StartAppIndicationTimer(const RawAddress& bd_addr, uint16_t cid); + + /** + * Stop application time for incoming indication on given CID + * + * @param bd_addr peer device address + * @param cid channel id + */ + virtual void StopAppIndicationTimer(const RawAddress& bd_addr, uint16_t cid); + + /** + * Starts the EattExtension module + */ + void Start(); + + /** + * Stops the EattExtension module + */ + void Stop(); + + private: + struct impl; + std::unique_ptr pimpl_; + + DISALLOW_COPY_AND_ASSIGN(EattExtension); +}; + +} // namespace eatt +} // namespace bluetooth \ No newline at end of file diff --git a/stack/eatt/eatt_impl.h b/stack/eatt/eatt_impl.h new file mode 100644 index 000000000..5fd0df34f --- /dev/null +++ b/stack/eatt/eatt_impl.h @@ -0,0 +1,637 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA - + * www.ehima.com + * + * 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. + */ + +#include +#include + +#include "acl_api.h" +#include "base/bind_helpers.h" +#include "bt_types.h" +#include "device/include/controller.h" +#include "eatt.h" +#include "l2c_api.h" +#include "osi/include/alarm.h" +#include "stack/btm/btm_sec.h" +#include "stack/gatt/gatt_int.h" +#include "stack/l2cap/l2c_int.h" + +namespace bluetooth { +namespace eatt { + +class eatt_device { + public: + RawAddress bda_; + uint16_t rx_mtu_; + uint16_t rx_mps_; + + tGATT_TCB* eatt_tcb_; + + std::map> eatt_channels; + + eatt_device(const RawAddress& bd_addr, uint16_t mtu, uint16_t mps) + : rx_mtu_(mtu), rx_mps_(mps), eatt_tcb_(nullptr) { + bda_ = bd_addr; + } +}; + +struct eatt_impl { + std::vector devices_; + uint16_t psm_; + uint16_t default_mtu_; + uint16_t max_mps_; + tL2CAP_APPL_INFO reg_info_; + + eatt_impl() { + default_mtu_ = EATT_DEFAULT_MTU; + max_mps_ = EATT_MIN_MTU_MPS; + psm_ = BT_PSM_EATT; + }; + + ~eatt_impl() = default; + + eatt_device* find_device_by_cid(uint16_t lcid) { + /* This works only because Android CIDs are unique across the ACL + * connections */ + auto iter = find_if(devices_.begin(), devices_.end(), + [&lcid](const eatt_device& ed) { + auto it = ed.eatt_channels.find(lcid); + return it != ed.eatt_channels.end(); + }); + + return (iter == devices_.end()) ? nullptr : &(*iter); + } + + EattChannel* find_channel_by_cid(uint16_t lcid) { + eatt_device* eatt_dev = find_device_by_cid(lcid); + if (!eatt_dev) return nullptr; + + auto it = eatt_dev->eatt_channels.find(lcid); + return (it == eatt_dev->eatt_channels.end()) ? nullptr : it->second.get(); + } + + EattChannel* find_channel_by_cid(const RawAddress& bdaddr, uint16_t lcid) { + eatt_device* eatt_dev = find_device_by_address(bdaddr); + if (!eatt_dev) return nullptr; + + auto it = eatt_dev->eatt_channels.find(lcid); + return (it == eatt_dev->eatt_channels.end()) ? nullptr : it->second.get(); + } + + void remove_channel_by_cid(eatt_device* eatt_dev, uint16_t lcid) { + eatt_dev->eatt_channels.erase(lcid); + eatt_dev->eatt_tcb_->eatt--; + + if (eatt_dev->eatt_channels.size() == 0) eatt_dev->eatt_tcb_ = NULL; + } + + void remove_channel_by_cid(uint16_t lcid) { + eatt_device* eatt_dev = find_device_by_cid(lcid); + if (!eatt_dev) return; + + remove_channel_by_cid(eatt_dev, lcid); + } + + void eatt_l2cap_connect_ind(const RawAddress& bda, + std::vector& lcids, uint16_t psm, + uint16_t peer_mtu, uint8_t identifier) { + /* The assumption is that L2CAP layer already check parameters etc. + * Get our capabilities and accept all the channels. + */ + eatt_device* eatt_dev = this->find_device_by_address(bda); + if (!eatt_dev) { + LOG(ERROR) << __func__ << " unknown device: " << bda; + L2CA_ConnectCreditBasedRsp(bda, identifier, lcids, + L2CAP_CONN_NO_RESOURCES, NULL); + return; + } + + uint16_t max_mps = controller_get_interface()->get_acl_data_size_ble(); + + tL2CAP_LE_CFG_INFO local_coc_cfg = { + .mtu = eatt_dev->rx_mtu_, + .mps = eatt_dev->rx_mps_ < max_mps ? eatt_dev->rx_mps_ : max_mps, + .credits = L2CAP_LE_CREDIT_DEFAULT}; + + if (!L2CA_ConnectCreditBasedRsp(bda, identifier, lcids, L2CAP_CONN_OK, + &local_coc_cfg)) + return; + + for (uint16_t cid : lcids) { + EattChannel* channel = find_eatt_channel_by_cid(bda, cid); + CHECK(!channel); + + auto chan = std::make_shared(eatt_dev->bda_, cid, peer_mtu, + eatt_dev->rx_mtu_); + eatt_dev->eatt_channels.insert({cid, chan}); + + chan->EattChannelSetState(EattChannelState::EATT_CHANNEL_OPENED); + + CHECK(eatt_dev->eatt_tcb_); + eatt_dev->eatt_tcb_->eatt++; + + LOG(INFO) << __func__ << " Channel connected CID " << loghex(cid); + } + } + + void eatt_l2cap_connect_cfm(const RawAddress& bda, uint16_t lcid, + uint16_t peer_mtu, uint16_t result) { + LOG(INFO) << __func__ << " bda: " << bda << " cid: " << +lcid + << "peer mtu: " << +peer_mtu << " result " << +result; + + eatt_device* eatt_dev = find_device_by_address(bda); + if (!eatt_dev) { + LOG(ERROR) << __func__ << " unknown device"; + return; + } + + EattChannel* channel = this->find_channel_by_cid(bda, lcid); + if (!channel) { + LOG(ERROR) << __func__ << " unknown cid: " << loghex(lcid); + return; + } + + if (result != L2CAP_CONN_OK) { + LOG(ERROR) << __func__ + << " Could not connect CoC result: " << loghex(result); + remove_channel_by_cid(eatt_dev, lcid); + return; + } + + channel->EattChannelSetState(EattChannelState::EATT_CHANNEL_OPENED); + channel->EattChannelSetTxMTU(peer_mtu); + + CHECK(eatt_dev->eatt_tcb_); + CHECK(eatt_dev->bda_ == channel->bda_); + eatt_dev->eatt_tcb_->eatt++; + + LOG(INFO) << __func__ << " Channel connected CID " << loghex(lcid); + } + + void eatt_l2cap_reconfig_completed(const RawAddress& bda, uint16_t lcid, + bool is_local_cfg, + tL2CAP_LE_CFG_INFO* p_cfg) { + LOG(INFO) << __func__ << "lcid: " << loghex(lcid) + << " local cfg?: " << is_local_cfg; + + if (p_cfg->result != L2CAP_CFG_OK) { + LOG(INFO) << __func__ << " reconfig failed lcid: " << loghex(lcid) + << " result: " << loghex(p_cfg->result); + return; + } + + EattChannel* channel = find_channel_by_cid(bda, lcid); + if (!channel) return; + + /* On this layer we don't care about mps as this is handled in L2CAP layer + */ + if (is_local_cfg) + channel->rx_mtu_ = p_cfg->mtu; + else + channel->tx_mtu_ = p_cfg->mtu; + + /* Go back to open state */ + channel->EattChannelSetState(EattChannelState::EATT_CHANNEL_OPENED); + } + + void eatt_l2cap_error_cb(uint16_t lcid, uint16_t reason) { + LOG(INFO) << __func__ << " cid: " << loghex(lcid) << " reason " + << loghex(reason); + + /*TODO: provide address in the L2CAP callback */ + + EattChannel* channel = find_channel_by_cid(lcid); + if (!channel) { + LOG(ERROR) << __func__ << "Unknown lcid"; + return; + } + + eatt_device* eatt_dev = find_device_by_address(channel->bda_); + + switch (channel->state_) { + case EattChannelState::EATT_CHANNEL_PENDING: + LOG(ERROR) << "Connecting failed"; + remove_channel_by_cid(eatt_dev, lcid); + break; + case EattChannelState::EATT_CHANNEL_RECONFIGURING: + /* Just go back to open state */ + LOG(ERROR) << "Reconfig failed"; + channel->EattChannelSetState(EattChannelState::EATT_CHANNEL_OPENED); + break; + default: + LOG(ERROR) << __func__ << "Invalid state: " + << static_cast(channel->state_); + break; + } + } + + void eatt_l2cap_disconnect_ind(uint16_t lcid, bool please_confirm) { + LOG(INFO) << __func__ << " cid: " << loghex(lcid); + remove_channel_by_cid(lcid); + } + + void eatt_l2cap_data_ind(uint16_t lcid, BT_HDR* data_p) { + LOG(INFO) << __func__ << " cid: " << loghex(lcid); + eatt_device* eatt_dev = find_device_by_cid(lcid); + if (!eatt_dev) { + LOG(ERROR) << __func__ << " unknown cid: " << loghex(lcid); + return; + } + + EattChannel* channel = find_channel_by_cid(eatt_dev->bda_, lcid); + if (!channel) { + LOG(ERROR) << __func__ << "Received data on closed channel " + << loghex(lcid); + return; + } + + gatt_data_process(*eatt_dev->eatt_tcb_, channel->cid_, data_p); + osi_free(data_p); + } + + bool is_eatt_supported_by_peer(const RawAddress& bd_addr) { + /* For now on the list we have only devices which does support eatt */ + eatt_device* eatt_dev = find_device_by_address(bd_addr); + return eatt_dev ? true : false; + } + + eatt_device* find_device_by_address(const RawAddress& bd_addr) { + auto iter = find_if( + devices_.begin(), devices_.end(), + [&bd_addr](const eatt_device& ed) { return ed.bda_ == bd_addr; }); + + return iter == devices_.end() ? nullptr : &(*iter); + } + + eatt_device* add_eatt_device(const RawAddress& bd_addr) { + devices_.push_back(eatt_device(bd_addr, default_mtu_, max_mps_)); + eatt_device* eatt_dev = &devices_.back(); + return eatt_dev; + } + + void connect_eatt(eatt_device* eatt_dev) { + /* Let us use maximum possible mps */ + if (eatt_dev->rx_mps_ == EATT_MIN_MTU_MPS) + eatt_dev->rx_mps_ = controller_get_interface()->get_acl_data_size_ble(); + + tL2CAP_LE_CFG_INFO local_coc_cfg = { + .mtu = eatt_dev->rx_mtu_, + .mps = eatt_dev->rx_mps_, + .credits = L2CAP_LE_CREDIT_DEFAULT, + }; + + /* Warning! CIDs in Android are unique across the ACL connections */ + std::vector connecting_cids = + L2CA_ConnectCreditBasedReq(psm_, eatt_dev->bda_, &local_coc_cfg); + + if (connecting_cids.size() == 0) { + LOG(ERROR) << "Unable to get cid"; + return; + } + + LOG(INFO) << __func__ + << "Successfully sent CoC request, number of channel: " + << +connecting_cids.size(); + + for (uint16_t cid : connecting_cids) { + LOG(INFO) << " /n/t cid: " << loghex(cid); + + auto chan = std::make_shared(eatt_dev->bda_, cid, 0, + eatt_dev->rx_mtu_); + eatt_dev->eatt_channels.insert({cid, chan}); + } + + if (eatt_dev->eatt_tcb_) { + LOG(INFO) << __func__ << " has tcb ? " << eatt_dev->eatt_tcb_; + return; + } + + eatt_dev->eatt_tcb_ = + gatt_find_tcb_by_addr(eatt_dev->bda_, BT_TRANSPORT_LE); + CHECK(eatt_dev->eatt_tcb_); + } + + EattChannel* find_eatt_channel_by_cid(const RawAddress& bd_addr, + uint16_t cid) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + + LOG(INFO) << __func__ << bd_addr << " " << +cid; + + if (!eatt_dev) return nullptr; + + auto iter = find_if( + eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), + [&cid](const std::pair>& el) { + return el.first == cid; + }); + + return iter == eatt_dev->eatt_channels.end() ? nullptr : iter->second.get(); + } + + EattChannel* find_eatt_channel_by_transid(const RawAddress& bd_addr, + uint32_t trans_id) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) return nullptr; + + auto iter = find_if( + eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), + [&trans_id]( + const std::pair>& el) { + return el.second->server_outstanding_cmd_.trans_id == trans_id; + }); + + return iter == eatt_dev->eatt_channels.end() ? nullptr : iter->second.get(); + } + + bool is_indication_pending(const RawAddress& bd_addr, + uint16_t indication_handle) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) return false; + + auto iter = find_if( + eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), + [&indication_handle]( + const std::pair>& el) { + return el.second->indicate_handle_ == indication_handle; + }); + + return (iter != eatt_dev->eatt_channels.end()); + }; + + EattChannel* get_channel_available_for_indication(const RawAddress& bd_addr) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + auto iter = find_if( + eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), + [](const std::pair>& el) { + return !GATT_HANDLE_IS_VALID(el.second->indicate_handle_); + }); + + return (iter == eatt_dev->eatt_channels.end()) ? nullptr + : iter->second.get(); + }; + + EattChannel* get_channel_available_for_client_request( + const RawAddress& bd_addr) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) return nullptr; + + auto iter = find_if( + eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), + [](const std::pair>& el) { + return el.second->cl_cmd_q_.empty(); + }); + + return (iter == eatt_dev->eatt_channels.end()) ? nullptr + : iter->second.get(); + } + + void free_gatt_resources(const RawAddress& bd_addr) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) return; + + auto iter = eatt_dev->eatt_channels.begin(); + while (iter != eatt_dev->eatt_channels.end()) { + EattChannel* channel = iter->second.get(); + + fixed_queue_free(channel->server_outstanding_cmd_.multi_rsp_q, NULL); + channel->server_outstanding_cmd_.multi_rsp_q = NULL; + } + } + + bool is_outstanding_msg_in_send_queue(const RawAddress& bd_addr) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) return false; + + auto iter = find_if( + eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), + [](const std::pair>& el) { + return !el.second->cl_cmd_q_.empty(); + }); + return (iter != eatt_dev->eatt_channels.end()); + } + + EattChannel* get_channel_with_queued_data(const RawAddress& bd_addr) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) return nullptr; + + auto iter = find_if( + eatt_dev->eatt_channels.begin(), eatt_dev->eatt_channels.end(), + [](const std::pair>& el) { + return !el.second->cl_cmd_q_.empty(); + }); + return (iter == eatt_dev->eatt_channels.end()) ? nullptr + : iter->second.get(); + } + + static void eatt_ind_ack_timeout(void* data) { + EattChannel* channel = (EattChannel*)data; + tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(channel->bda_, BT_TRANSPORT_LE); + + LOG(WARNING) << __func__ << ": send ack now"; + attp_send_cl_confirmation_msg(*p_tcb, channel->cid_); + } + + static void eatt_ind_confirmation_timeout(void* data) { + EattChannel* channel = (EattChannel*)data; + tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(channel->bda_, BT_TRANSPORT_LE); + + LOG(WARNING) << __func__ << " disconnecting..."; + gatt_disconnect(p_tcb); + } + + void start_indication_confirm_timer(const RawAddress& bd_addr, uint16_t cid) { + EattChannel* channel = find_eatt_channel_by_cid(bd_addr, cid); + if (!channel) { + LOG(ERROR) << __func__ << "Unknown cid: " << loghex(cid) << " or device " + << bd_addr; + return; + } + + alarm_set_on_mloop(channel->ind_confirmation_timer_, + GATT_WAIT_FOR_RSP_TIMEOUT_MS, + eatt_ind_confirmation_timeout, channel); + } + + void stop_indication_confirm_timer(const RawAddress& bd_addr, uint16_t cid) { + EattChannel* channel = find_eatt_channel_by_cid(bd_addr, cid); + if (!channel) { + LOG(ERROR) << __func__ << "Unknown cid: " << loghex(cid) << " or device " + << bd_addr; + return; + } + + alarm_cancel(channel->ind_confirmation_timer_); + } + + void start_app_indication_timer(const RawAddress& bd_addr, uint16_t cid) { + EattChannel* channel = find_eatt_channel_by_cid(bd_addr, cid); + if (!channel) { + LOG(ERROR) << __func__ << "Unknown cid: " << loghex(cid) << " or device " + << bd_addr; + return; + } + + alarm_set_on_mloop(channel->ind_ack_timer_, GATT_WAIT_FOR_RSP_TIMEOUT_MS, + eatt_ind_ack_timeout, channel); + } + + void stop_app_indication_timer(const RawAddress& bd_addr, uint16_t cid) { + EattChannel* channel = find_eatt_channel_by_cid(bd_addr, cid); + if (!channel) { + LOG(ERROR) << __func__ << "Unknown cid: " << loghex(cid) << " or device " + << bd_addr; + return; + } + + alarm_cancel(channel->ind_ack_timer_); + } + + void reconfigure(const RawAddress& bd_addr, uint16_t cid, uint16_t new_mtu) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) { + LOG(ERROR) << __func__ << "Unknown device " << bd_addr; + return; + } + + EattChannel* channel = find_eatt_channel_by_cid(bd_addr, cid); + if (!channel) { + LOG(ERROR) << __func__ << "Unknown cid: " << loghex(cid) << " or device " + << bd_addr; + return; + } + + if (new_mtu <= channel->rx_mtu_) { + LOG(ERROR) << __func__ << "Invalid mtu: " << loghex(new_mtu); + return; + } + + std::vector cids = {cid}; + + tL2CAP_LE_CFG_INFO cfg = {.mps = eatt_dev->rx_mps_, .mtu = new_mtu}; + + if (!L2CA_ReconfigCreditBasedConnsReq(eatt_dev->bda_, cids, &cfg)) + LOG(ERROR) << __func__ << "Could not start reconfig cid: " << loghex(cid) + << " or device " << bd_addr; + } + + void reconfigure_all(const RawAddress& bd_addr, uint16_t new_mtu) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) { + LOG(ERROR) << __func__ << "Unknown device " << bd_addr; + return; + } + + uint8_t num_of_channels = eatt_dev->eatt_channels.size(); + if (num_of_channels == 0) { + LOG(ERROR) << __func__ << "No channels for device " << bd_addr; + return; + } + + std::vector cids; + + auto iter = eatt_dev->eatt_channels.begin(); + while (iter != eatt_dev->eatt_channels.end()) { + uint16_t cid = iter->first; + cids.push_back(cid); + iter++; + } + + if (new_mtu <= EATT_MIN_MTU_MPS) { + LOG(ERROR) << __func__ << "Invalid mtu: " << loghex(new_mtu); + return; + } + + tL2CAP_LE_CFG_INFO cfg = {.mps = eatt_dev->rx_mps_, .mtu = new_mtu}; + + if (!L2CA_ReconfigCreditBasedConnsReq(eatt_dev->bda_, cids, &cfg)) + LOG(ERROR) << __func__ << "Could not start reconfig for device " + << bd_addr; + } + + void eatt_is_supported_cb(const RawAddress& bd_addr, bool is_eatt_supported) { + LOG(INFO) << __func__ << " " << bd_addr + << " is_eatt_supported = " << int(is_eatt_supported); + if (!is_eatt_supported) return; + + eatt_device* eatt_dev = add_eatt_device(bd_addr); + connect_eatt(eatt_dev); + } + + void disconnect_channel(uint16_t cid) { L2CA_DisconnectReq(cid); } + + void disconnect(const RawAddress& bd_addr) { + LOG(INFO) << __func__ << " " << bd_addr; + + eatt_device* eatt_dev = find_device_by_address(bd_addr); + if (!eatt_dev) return; + + auto iter = eatt_dev->eatt_channels.begin(); + while (iter != eatt_dev->eatt_channels.end()) { + uint16_t cid = iter->first; + disconnect_channel(cid); + iter++; + } + } + + void connect(const RawAddress& bd_addr) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + + uint8_t role = acl_link_role(bd_addr, BT_TRANSPORT_LE); + if (role == HCI_ROLE_UNKNOWN) { + LOG(ERROR) << __func__ << "Could not get device role" << bd_addr; + return; + } + + LOG(INFO) << __func__ << " device " << bd_addr << " role" + << (role == HCI_ROLE_CENTRAL ? "central" : "peripheral"); + + if (role != HCI_ROLE_CENTRAL) { + /* TODO For now do nothing, we could run a timer here and start EATT if + * not started by central */ + LOG(INFO) + << " EATT Should be connected by the central. Let's wait for it."; + return; + } + + if (eatt_dev) { + /* We are reconnecting device we know that support EATT. + * Just connect CoC + */ + LOG(INFO) << __func__ << " Known device, connect eCoC"; + connect_eatt(eatt_dev); + return; + } + + /* For new device, first check if EATT is supported. */ + if (gatt_profile_get_eatt_support( + bd_addr, base::BindOnce(&eatt_impl::eatt_is_supported_cb, + base::Unretained(this))) == false) { + LOG(INFO) << __func__ << "Eatt is not supported. Checked for device " + << bd_addr; + } + } + + void add_from_storage(const RawAddress& bd_addr) { + eatt_device* eatt_dev = find_device_by_address(bd_addr); + + LOG(INFO) << __func__ << ", restoring: " << bd_addr; + + if (!eatt_dev) add_eatt_device(bd_addr); + } +}; + +} // namespace eatt +} // namespace bluetooth diff --git a/stack/gatt/gatt_api.cc b/stack/gatt/gatt_api.cc index e7e389719..d40ad0994 100644 --- a/stack/gatt/gatt_api.cc +++ b/stack/gatt/gatt_api.cc @@ -460,7 +460,7 @@ tGATT_STATUS GATTS_HandleValueIndication(uint16_t conn_id, uint16_t attr_handle, tGATT_STATUS cmd_status = attp_send_sr_msg(*p_tcb, cid, p_msg); if (cmd_status == GATT_SUCCESS || cmd_status == GATT_CONGESTED) { *indicate_handle_p = indication.handle; - gatt_start_conf_timer(p_tcb); + gatt_start_conf_timer(p_tcb, cid); } return cmd_status; } @@ -509,7 +509,7 @@ tGATT_STATUS GATTS_HandleValueNotification(uint16_t conn_id, tGATT_SR_MSG gatt_sr_msg; gatt_sr_msg.attr_value = notif; - uint16_t cid = gatt_tcb_get_att_cid(*p_tcb); + uint16_t cid = gatt_tcb_get_att_cid(*p_tcb, p_reg->eatt_support); BT_HDR* p_buf = attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_NOTIF, &gatt_sr_msg); @@ -903,14 +903,13 @@ tGATT_STATUS GATTC_SendHandleValueConfirm(uint16_t conn_id, uint16_t cid) { return GATT_SUCCESS; } - /*TODO Introduce timer per CID */ - alarm_cancel(p_tcb->ind_ack_timer); + gatt_stop_ind_ack_timer(p_tcb, cid); VLOG(1) << "notif_count= " << p_tcb->ind_count; /* send confirmation now */ tGATT_STATUS ret = attp_send_cl_confirmation_msg(*p_tcb, cid); - p_tcb->ind_count = 0; + return ret; } diff --git a/stack/gatt/gatt_cl.cc b/stack/gatt/gatt_cl.cc index 899cfd2ef..519b8b88a 100644 --- a/stack/gatt/gatt_cl.cc +++ b/stack/gatt/gatt_cl.cc @@ -29,6 +29,7 @@ #include "bt_common.h" #include "bt_target.h" #include "bt_utils.h" +#include "eatt.h" #include "gatt_int.h" #include "l2c_api.h" #include "log/log.h" @@ -48,6 +49,8 @@ using base::StringPrintf; using bluetooth::Uuid; +using bluetooth::eatt::EattExtension; +using bluetooth::eatt::EattChannel; /******************************************************************************* * G L O B A L G A T T D A T A * @@ -1053,9 +1056,14 @@ uint8_t gatt_cmd_to_rsp_code(uint8_t cmd_code) { bool gatt_cl_send_next_cmd_inq(tGATT_TCB& tcb) { std::queue* cl_cmd_q; - while (!tcb.cl_cmd_q.empty()) { + while (!tcb.cl_cmd_q.empty() || + EattExtension::GetInstance()->IsOutstandingMsgInSendQueue(tcb.peer_bda)) { if (!tcb.cl_cmd_q.empty()) { cl_cmd_q = &tcb.cl_cmd_q; + } else { + EattChannel* channel = + EattExtension::GetInstance()->GetChannelWithQueuedData(tcb.peer_bda); + cl_cmd_q = &channel->cl_cmd_q_; } tGATT_CMD_Q& cmd = cl_cmd_q->front(); diff --git a/stack/gatt/gatt_int.h b/stack/gatt/gatt_int.h index 4579d1130..a040075ec 100644 --- a/stack/gatt/gatt_int.h +++ b/stack/gatt/gatt_int.h @@ -275,6 +275,9 @@ typedef struct { tBT_TRANSPORT transport; uint32_t trans_id; + /* Indicates number of available eatt channels */ + uint8_t eatt; + uint16_t att_lcid; /* L2CAP channel ID for ATT */ uint16_t payload_size; @@ -462,11 +465,13 @@ extern void gatt_sr_get_sec_info(const RawAddress& rem_bda, uint8_t* p_key_size); extern void gatt_start_rsp_timer(tGATT_CLCB* p_clcb); extern void gatt_stop_rsp_timer(tGATT_CLCB* p_clcb); -extern void gatt_start_conf_timer(tGATT_TCB* p_tcb); +extern void gatt_start_conf_timer(tGATT_TCB* p_tcb, uint16_t cid); +extern void gatt_stop_conf_timer(tGATT_TCB& tcb, uint16_t cid); extern void gatt_rsp_timeout(void* data); extern void gatt_indication_confirmation_timeout(void* data); extern void gatt_ind_ack_timeout(void* data); extern void gatt_start_ind_ack_timer(tGATT_TCB& tcb, uint16_t cid); +extern void gatt_stop_ind_ack_timer(tGATT_TCB* p_tcb, uint16_t cid); extern tGATT_STATUS gatt_send_error_rsp(tGATT_TCB& tcb, uint16_t cid, uint8_t err_code, uint8_t op_code, uint16_t handle, bool deq); @@ -526,7 +531,7 @@ extern bool gatt_tcb_get_cid_available_for_indication( uint16_t* cid_p); extern bool gatt_tcb_find_indicate_handle(tGATT_TCB& tcb, uint16_t cid, uint16_t* indicated_handle_p); -extern uint16_t gatt_tcb_get_att_cid(tGATT_TCB& tcb); +extern uint16_t gatt_tcb_get_att_cid(tGATT_TCB& tcb, bool eatt_support); extern uint16_t gatt_tcb_get_payload_size_tx(tGATT_TCB& tcb, uint16_t cid); extern uint16_t gatt_tcb_get_payload_size_rx(tGATT_TCB& tcb, uint16_t cid); extern void gatt_clcb_dealloc(tGATT_CLCB* p_clcb); @@ -539,6 +544,7 @@ extern void gatt_sr_reset_prep_cnt(tGATT_TCB& tcb); extern tGATT_SR_CMD* gatt_sr_get_cmd_by_trans_id(tGATT_TCB* p_tcb, uint32_t trans_id); extern tGATT_SR_CMD* gatt_sr_get_cmd_by_cid(tGATT_TCB& tcb, uint16_t cid); +extern tGATT_READ_MULTI* gatt_sr_get_read_multi(tGATT_TCB& tcb, uint16_t cid); extern void gatt_sr_update_cback_cnt(tGATT_TCB& p_tcb, uint16_t cid, tGATT_IF gatt_if, bool is_inc, bool is_reset_first); @@ -556,7 +562,7 @@ extern bool gatt_send_ble_burst_data(const RawAddress& remote_bda, BT_HDR* p_buf); /* GATT client functions */ -extern void gatt_dequeue_sr_cmd(tGATT_TCB& tcb); +extern void gatt_dequeue_sr_cmd(tGATT_TCB& tcb, uint16_t cid); extern tGATT_STATUS gatt_send_write_msg(tGATT_TCB& p_tcb, tGATT_CLCB* p_clcb, uint8_t op_code, uint16_t handle, uint16_t len, uint16_t offset, diff --git a/stack/gatt/gatt_main.cc b/stack/gatt/gatt_main.cc index 764f65dec..c86af5e93 100644 --- a/stack/gatt/gatt_main.cc +++ b/stack/gatt/gatt_main.cc @@ -26,6 +26,7 @@ #include "bt_common.h" #include "bt_utils.h" +#include "eatt.h" #include "btif_storage.h" #include "btm_ble_int.h" #include "btm_int.h" @@ -38,6 +39,7 @@ #include "stack/btm/btm_sec.h" using base::StringPrintf; +using bluetooth::eatt::EattExtension; /******************************************************************************/ /* L O C A L F U N C T I O N P R O T O T Y P E S */ @@ -124,6 +126,8 @@ void gatt_init(void) { gatt_cb.hdl_list_info = new std::list(); gatt_cb.srv_list_info = new std::list(); gatt_profile_db_init(); + + EattExtension::GetInstance()->Start(); } /******************************************************************************* @@ -157,12 +161,17 @@ void gatt_free(void) { fixed_queue_free(gatt_cb.tcb[i].sr_cmd.multi_rsp_q, NULL); gatt_cb.tcb[i].sr_cmd.multi_rsp_q = NULL; + + if (gatt_cb.tcb[i].eatt) + EattExtension::GetInstance()->FreeGattResources(gatt_cb.tcb[i].peer_bda); } gatt_cb.hdl_list_info->clear(); gatt_cb.hdl_list_info = nullptr; gatt_cb.srv_list_info->clear(); gatt_cb.srv_list_info = nullptr; + + EattExtension::GetInstance()->Stop(); } /******************************************************************************* @@ -320,6 +329,10 @@ void gatt_update_app_use_link_flag(tGATT_IF gatt_if, tGATT_TCB* p_tcb, if (p_tcb->app_hold_link.empty()) { // acl link is connected but no application needs to use the link if (p_tcb->att_lcid == L2CAP_ATT_CID && is_valid_handle) { + + /* Drop EATT before closing ATT */ + EattExtension::GetInstance()->Disconnect(p_tcb->peer_bda); + /* for fixed channel, set the timeout value to GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP seconds */ VLOG(1) << " start link idle timer = " @@ -439,6 +452,8 @@ static void gatt_le_connect_cback(uint16_t chan, const RawAddress& bd_addr, gatt_chk_srv_chg(p_srv_chg_clt); } } + + EattExtension::GetInstance()->Connect(bd_addr); } /** This function is called to process the congestion callback from lcb */ diff --git a/stack/gatt/gatt_sr.cc b/stack/gatt/gatt_sr.cc index 60fe429cb..9685325d3 100644 --- a/stack/gatt/gatt_sr.cc +++ b/stack/gatt/gatt_sr.cc @@ -30,10 +30,13 @@ #include "gatt_int.h" #include "l2c_api.h" #include "l2c_int.h" +#include "stack/eatt/eatt.h" #define GATT_MTU_REQ_MIN_LEN 2 using base::StringPrintf; using bluetooth::Uuid; +using bluetooth::eatt::EattExtension; +using bluetooth::eatt::EattChannel; /******************************************************************************* * @@ -47,7 +50,15 @@ using bluetooth::Uuid; ******************************************************************************/ uint32_t gatt_sr_enqueue_cmd(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t handle) { - tGATT_SR_CMD* p_cmd = &tcb.sr_cmd; + tGATT_SR_CMD* p_cmd; + + if (cid == tcb.att_lcid) { + p_cmd = &tcb.sr_cmd; + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + p_cmd = &channel->server_outstanding_cmd_; + } uint32_t trans_id = 0; @@ -81,7 +92,14 @@ uint32_t gatt_sr_enqueue_cmd(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, * Returns true if empty, false if there is pending command. * ******************************************************************************/ -bool gatt_sr_cmd_empty(tGATT_TCB& tcb) { return (tcb.sr_cmd.op_code == 0); } +bool gatt_sr_cmd_empty(tGATT_TCB& tcb, uint16_t cid) { + if (cid == tcb.att_lcid) return (tcb.sr_cmd.op_code == 0); + + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + + return (channel->server_outstanding_cmd_.op_code == 0); +} /******************************************************************************* * @@ -92,19 +110,30 @@ bool gatt_sr_cmd_empty(tGATT_TCB& tcb) { return (tcb.sr_cmd.op_code == 0); } * Returns void * ******************************************************************************/ -void gatt_dequeue_sr_cmd(tGATT_TCB& tcb) { +void gatt_dequeue_sr_cmd(tGATT_TCB& tcb, uint16_t cid) { + tGATT_SR_CMD* p_cmd; + + if (cid == tcb.att_lcid) { + p_cmd = &tcb.sr_cmd; + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + + p_cmd = &channel->server_outstanding_cmd_; + } + /* Double check in case any buffers are queued */ - VLOG(1) << "gatt_dequeue_sr_cmd"; - if (tcb.sr_cmd.p_rsp_msg) - LOG(ERROR) << "free tcb.sr_cmd.p_rsp_msg = " << tcb.sr_cmd.p_rsp_msg; - osi_free_and_reset((void**)&tcb.sr_cmd.p_rsp_msg); - - while (!fixed_queue_is_empty(tcb.sr_cmd.multi_rsp_q)) - osi_free(fixed_queue_try_dequeue(tcb.sr_cmd.multi_rsp_q)); - fixed_queue_free(tcb.sr_cmd.multi_rsp_q, NULL); - memset(&tcb.sr_cmd, 0, sizeof(tGATT_SR_CMD)); + VLOG(1) << "gatt_dequeue_sr_cmd cid: " << loghex(cid); + if (p_cmd->p_rsp_msg) + LOG(ERROR) << "free tcb.sr_cmd.p_rsp_msg = " + << p_cmd->p_rsp_msg; + osi_free_and_reset((void**)&p_cmd->p_rsp_msg); + + while (!fixed_queue_is_empty(p_cmd->multi_rsp_q)) + osi_free(fixed_queue_try_dequeue(p_cmd->multi_rsp_q)); + fixed_queue_free(p_cmd->multi_rsp_q, NULL); + memset(p_cmd, 0, sizeof(tGATT_SR_CMD)); } - /******************************************************************************* * * Function process_read_multi_rsp @@ -271,7 +300,7 @@ tGATT_STATUS gatt_sr_process_app_rsp(tGATT_TCB& tcb, tGATT_IF gatt_if, sr_res_p->handle, false); } - gatt_dequeue_sr_cmd(tcb); + gatt_dequeue_sr_cmd(tcb, sr_res_p->cid); } VLOG(1) << __func__ << " ret_code=" << +ret_code; @@ -364,8 +393,9 @@ void gatt_process_read_multi_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint8_t sec_flag, key_size; VLOG(1) << __func__; - tcb.sr_cmd.multi_req.num_handles = 0; + tGATT_READ_MULTI* multi_req = gatt_sr_get_read_multi(tcb, cid); + multi_req->num_handles = 0; gatt_sr_get_sec_info(tcb.peer_bda, tcb.transport, &sec_flag, &key_size); #if (GATT_CONFORMANCE_TESTING == TRUE) @@ -382,13 +412,12 @@ void gatt_process_read_multi_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, } #endif - while (ll >= 2 && - tcb.sr_cmd.multi_req.num_handles < GATT_MAX_READ_MULTI_HANDLES) { + while (ll >= 2 && multi_req->num_handles < GATT_MAX_READ_MULTI_HANDLES) { STREAM_TO_UINT16(handle, p); auto it = gatt_sr_find_i_rcb_by_handle(handle); if (it != gatt_cb.srv_list_info->end()) { - tcb.sr_cmd.multi_req.handles[tcb.sr_cmd.multi_req.num_handles++] = handle; + multi_req->handles[multi_req->num_handles++] = handle; /* check read permission */ err = gatts_read_attr_perm_check(it->p_db, false, handle, sec_flag, @@ -409,20 +438,19 @@ void gatt_process_read_multi_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, LOG(ERROR) << "max attribute handle reached in ReadMultiple Request."; } - if (tcb.sr_cmd.multi_req.num_handles == 0) err = GATT_INVALID_HANDLE; + if (multi_req->num_handles == 0) err = GATT_INVALID_HANDLE; if (err == GATT_SUCCESS) { - trans_id = - gatt_sr_enqueue_cmd(tcb, cid, op_code, tcb.sr_cmd.multi_req.handles[0]); + trans_id = gatt_sr_enqueue_cmd(tcb, cid, op_code, multi_req->handles[0]); if (trans_id != 0) { tGATT_SR_CMD* sr_cmd_p = gatt_sr_get_cmd_by_cid(tcb, cid); gatt_sr_reset_cback_cnt(tcb, cid); /* read multiple use multi_rsp_q's count*/ - for (ll = 0; ll < tcb.sr_cmd.multi_req.num_handles; ll++) { + for (ll = 0; ll < multi_req->num_handles; ll++) { tGATTS_RSP* p_msg = (tGATTS_RSP*)osi_calloc(sizeof(tGATTS_RSP)); - handle = tcb.sr_cmd.multi_req.handles[ll]; + handle = multi_req->handles[ll]; auto it = gatt_sr_find_i_rcb_by_handle(handle); p_msg->attr_value.handle = handle; @@ -1186,7 +1214,7 @@ void gatts_process_value_conf(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code) { return; } - alarm_cancel(tcb.conf_timer); + gatt_stop_conf_timer(tcb, cid); bool continue_processing = gatts_proc_ind_ack(tcb, handle); @@ -1209,7 +1237,7 @@ void gatt_server_handle_client_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len, uint8_t* p_data) { /* there is pending command, discard this one */ - if (!gatt_sr_cmd_empty(tcb) && op_code != GATT_HANDLE_VALUE_CONF) return; + if (!gatt_sr_cmd_empty(tcb, cid) && op_code != GATT_HANDLE_VALUE_CONF) return; /* the size of the message may not be bigger than the local max PDU size*/ /* The message has to be smaller than the agreed MTU, len does not include op diff --git a/stack/gatt/gatt_utils.cc b/stack/gatt/gatt_utils.cc index addab3237..9eecedb53 100644 --- a/stack/gatt/gatt_utils.cc +++ b/stack/gatt/gatt_utils.cc @@ -37,10 +37,13 @@ #include "l2cdefs.h" #include "sdp_api.h" #include "stack/btm/btm_sec.h" +#include "stack/eatt/eatt.h" #include "stack/gatt/connection_manager.h" using base::StringPrintf; using bluetooth::Uuid; +using bluetooth::eatt::EattExtension; +using bluetooth::eatt::EattChannel; /* check if [x, y] and [a, b] have overlapping range */ #define GATT_VALIDATE_HANDLE_RANGE(x, y, a, b) ((y) >= (a) && (x) <= (b)) @@ -284,6 +287,10 @@ bool gatt_is_srv_chg_ind_pending(tGATT_TCB* p_tcb) { if (p_tcb->indicate_handle == gatt_cb.handle_of_h_r) return true; + if (p_tcb->eatt && EattExtension::GetInstance()->IsIndicationPending( + p_tcb->peer_bda, gatt_cb.handle_of_h_r)) + return true; + if (fixed_queue_is_empty(p_tcb->pending_ind_q)) return false; list_t* list = fixed_queue_get_list(p_tcb->pending_ind_q); @@ -557,9 +564,30 @@ void gatt_stop_rsp_timer(tGATT_CLCB* p_clcb) { * Returns void * ******************************************************************************/ -void gatt_start_conf_timer(tGATT_TCB* p_tcb) { - alarm_set_on_mloop(p_tcb->conf_timer, GATT_WAIT_FOR_RSP_TIMEOUT_MS, - gatt_indication_confirmation_timeout, p_tcb); +void gatt_start_conf_timer(tGATT_TCB* p_tcb, uint16_t cid) { + /* start notification cache timer */ + if (p_tcb->eatt && cid != L2CAP_ATT_CID) + EattExtension::GetInstance()->StartIndicationConfirmationTimer(p_tcb->peer_bda, cid); + else + alarm_set_on_mloop(p_tcb->conf_timer, GATT_WAIT_FOR_RSP_TIMEOUT_MS, + gatt_indication_confirmation_timeout, p_tcb); +} + +/******************************************************************************* + * + * Function gatt_stop_conf_timer + * + * Description Start a wait_for_confirmation timer. + * + * Returns void + * + ******************************************************************************/ +void gatt_stop_conf_timer(tGATT_TCB& tcb, uint16_t cid) { + /* start notification cache timer */ + if (tcb.eatt && cid != L2CAP_ATT_CID) + EattExtension::GetInstance()->StopIndicationConfirmationTimer(tcb.peer_bda, cid); + else + alarm_cancel(tcb.conf_timer); } /******************************************************************************* @@ -573,13 +601,33 @@ void gatt_start_conf_timer(tGATT_TCB* p_tcb) { ******************************************************************************/ void gatt_start_ind_ack_timer(tGATT_TCB& tcb, uint16_t cid) { /* start notification cache timer */ - /* TODO: set timer per CID */ - alarm_set_on_mloop(tcb.ind_ack_timer, GATT_WAIT_FOR_RSP_TIMEOUT_MS, - gatt_ind_ack_timeout, &tcb); + if (tcb.eatt && cid != L2CAP_ATT_CID) + EattExtension::GetInstance()->StartAppIndicationTimer(tcb.peer_bda, cid); + else + alarm_set_on_mloop(tcb.ind_ack_timer, GATT_WAIT_FOR_RSP_TIMEOUT_MS, + gatt_ind_ack_timeout, &tcb); } /******************************************************************************* * + * Function gatt_stop_ind_ack_timer + * + * Description stop the application ack timer + * + * Returns void + * + ******************************************************************************/ +void gatt_stop_ind_ack_timer(tGATT_TCB* p_tcb, uint16_t cid) { + /* start notification cache timer */ + if (p_tcb->eatt && cid != L2CAP_ATT_CID) { + EattExtension::GetInstance()->StopAppIndicationTimer(p_tcb->peer_bda, cid); + } else { + alarm_cancel(p_tcb->ind_ack_timer); + p_tcb->ind_count = 0; + } +} +/******************************************************************************* + * * Function gatt_rsp_timeout * * Description Called when GATT wait for ATT command response timer expires @@ -659,7 +707,7 @@ void gatt_indication_confirmation_timeout(void* data) { * Returns void * ******************************************************************************/ -void gatt_ind_ack_timeout(void* data) { +void gatt_ind_ack_timeout(void* data) { tGATT_TCB* p_tcb = (tGATT_TCB*)data; CHECK(p_tcb); @@ -765,7 +813,8 @@ tGATT_STATUS gatt_send_error_rsp(tGATT_TCB& tcb, uint16_t cid, uint8_t err_code, } else status = GATT_INSUF_RESOURCE; - if (deq) gatt_dequeue_sr_cmd(tcb); + if (deq) + gatt_dequeue_sr_cmd(tcb, cid); return status; } @@ -894,16 +943,24 @@ tGATT_REG* gatt_get_regcb(tGATT_IF gatt_if) { bool gatt_is_clcb_allocated(uint16_t conn_id) { uint8_t i = 0; - bool is_allocated = false; + uint8_t num_of_allocated = 0; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB* p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG* p_reg = gatt_get_regcb(gatt_if); + int possible_plcb = 1; + + if (p_reg->eatt_support) possible_plcb += p_tcb->eatt; + /* With eatt number of active clcbs can me up to 1 + number of eatt channels + */ for (i = 0; i < GATT_CL_MAX_LCB; i++) { if (gatt_cb.clcb[i].in_use && (gatt_cb.clcb[i].conn_id == conn_id)) { - is_allocated = true; - break; + if (++num_of_allocated == possible_plcb) return true; } } - return is_allocated; + return false; } /******************************************************************************* @@ -917,7 +974,13 @@ bool gatt_is_clcb_allocated(uint16_t conn_id) { ******************************************************************************/ bool gatt_tcb_is_cid_busy(tGATT_TCB& tcb, uint16_t cid) { - return !tcb.cl_cmd_q.empty(); + if (cid == tcb.att_lcid) return !tcb.cl_cmd_q.empty(); + + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + if (!channel) return false; + + return !channel->cl_cmd_q_.empty(); } /******************************************************************************* * @@ -945,7 +1008,10 @@ tGATT_CLCB* gatt_clcb_alloc(uint16_t conn_id) { p_clcb->conn_id = conn_id; p_clcb->p_reg = p_reg; p_clcb->p_tcb = p_tcb; - p_clcb->cid = gatt_tcb_get_att_cid(*p_tcb); + + /* Use eatt only when clients wants that */ + p_clcb->cid = gatt_tcb_get_att_cid(*p_tcb, p_reg->eatt_support); + break; } } @@ -959,20 +1025,31 @@ tGATT_CLCB* gatt_clcb_alloc(uint16_t conn_id) { * * Description This function checks if indication can be send * - * Returns None + * Returns true when stack is busy with waiting on indication + * confirmation, false otherwise * ******************************************************************************/ bool gatt_tcb_get_cid_available_for_indication(tGATT_TCB* p_tcb, bool eatt_support, uint16_t** indicated_handle_p, uint16_t* cid_p) { - if (GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) { - return false; + if (p_tcb->eatt && eatt_support) { + EattChannel* channel = + EattExtension::GetInstance()->GetChannelAvailableForIndication(p_tcb->peer_bda); + if (channel) { + *indicated_handle_p = &channel->indicate_handle_; + *cid_p = channel->cid_; + return true; + } } - *indicated_handle_p = &p_tcb->indicate_handle; - *cid_p = p_tcb->att_lcid; - return true; + if (!GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) { + *indicated_handle_p = &p_tcb->indicate_handle; + *cid_p = p_tcb->att_lcid; + return true; + } + + return false; } /******************************************************************************* @@ -992,6 +1069,16 @@ bool gatt_tcb_find_indicate_handle(tGATT_TCB& tcb, uint16_t cid, return true; } + if (tcb.eatt) { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + if (channel) { + *indicated_handle_p = channel->indicate_handle_; + channel->indicate_handle_ = 0; + return true; + } + } + return false; } @@ -1005,7 +1092,16 @@ bool gatt_tcb_find_indicate_handle(tGATT_TCB& tcb, uint16_t cid, * ******************************************************************************/ -uint16_t gatt_tcb_get_att_cid(tGATT_TCB& tcb) { return tcb.att_lcid; } +uint16_t gatt_tcb_get_att_cid(tGATT_TCB& tcb, bool eatt_support) { + if (eatt_support && tcb.eatt) { + EattChannel* channel = + EattExtension::GetInstance()->GetChannelAvailableForClientRequest(tcb.peer_bda); + if (channel) { + return channel->cid_; + } + } + return tcb.att_lcid; +} /******************************************************************************* * @@ -1017,7 +1113,12 @@ uint16_t gatt_tcb_get_att_cid(tGATT_TCB& tcb) { return tcb.att_lcid; } * ******************************************************************************/ uint16_t gatt_tcb_get_payload_size_tx(tGATT_TCB& tcb, uint16_t cid) { - return tcb.payload_size; + if (!tcb.eatt || (cid == tcb.att_lcid)) return tcb.payload_size; + + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + + return channel->tx_mtu_; } /******************************************************************************* @@ -1031,7 +1132,12 @@ uint16_t gatt_tcb_get_payload_size_tx(tGATT_TCB& tcb, uint16_t cid) { ******************************************************************************/ uint16_t gatt_tcb_get_payload_size_rx(tGATT_TCB& tcb, uint16_t cid) { - return tcb.payload_size; + if (!tcb.eatt || (cid == tcb.att_lcid)) return tcb.payload_size; + + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + + return channel->rx_mtu_; } /******************************************************************************* @@ -1066,7 +1172,10 @@ tGATT_TCB* gatt_find_tcb_by_cid(uint16_t lcid) { tGATT_TCB* p_tcb = NULL; for (xx = 0; xx < GATT_MAX_PHY_CHANNEL; xx++) { - if (gatt_cb.tcb[xx].in_use && gatt_cb.tcb[xx].att_lcid == lcid) { + if (gatt_cb.tcb[xx].in_use && + ((gatt_cb.tcb[xx].att_lcid == lcid) || + ((EattExtension::GetInstance()->FindEattChannelByCid(gatt_cb.tcb[xx].peer_bda, + lcid) != nullptr)))) { p_tcb = &gatt_cb.tcb[xx]; break; } @@ -1104,7 +1213,13 @@ void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB& tcb) { tGATT_SR_CMD* gatt_sr_get_cmd_by_trans_id(tGATT_TCB* p_tcb, uint32_t trans_id) { if (p_tcb->sr_cmd.trans_id == trans_id) return &p_tcb->sr_cmd; - return nullptr; + if (!p_tcb->eatt) return nullptr; + + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByTransId(p_tcb->peer_bda, trans_id); + if (!channel) return nullptr; + + return &channel->server_outstanding_cmd_; } /******************************************************************************* * @@ -1155,6 +1270,10 @@ void gatt_sr_reset_cback_cnt(tGATT_TCB& tcb, uint16_t cid) { for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { if (cid == tcb.att_lcid) { tcb.sr_cmd.cback_cnt[i] = 0; + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + channel->server_outstanding_cmd_.cback_cnt[i] = 0; } } } @@ -1176,8 +1295,36 @@ void gatt_sr_reset_prep_cnt(tGATT_TCB& tcb) { /* Get pointer to server command on given cid */ tGATT_SR_CMD* gatt_sr_get_cmd_by_cid(tGATT_TCB& tcb, uint16_t cid) { - return &tcb.sr_cmd; + tGATT_SR_CMD* sr_cmd_p; + + LOG(INFO) << __func__ << " cid: " << int(cid) << " tcb cid " << tcb.att_lcid; + if (cid == tcb.att_lcid) { + sr_cmd_p = &tcb.sr_cmd; + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + sr_cmd_p = &channel->server_outstanding_cmd_; + } + + return sr_cmd_p; } + +/* Get pointer to the context of outstanding multi request */ +tGATT_READ_MULTI* gatt_sr_get_read_multi(tGATT_TCB& tcb, uint16_t cid) { + tGATT_READ_MULTI* read_multi_p; + + LOG(INFO) << __func__ << " cid: " << int(cid) << " tcb cid " << tcb.att_lcid; + if (cid == tcb.att_lcid) { + read_multi_p = &tcb.sr_cmd.multi_req; + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + read_multi_p = &channel->server_outstanding_cmd_.multi_req; + } + + return read_multi_p; +} + /******************************************************************************* * * Function gatt_sr_update_cback_cnt @@ -1190,7 +1337,15 @@ tGATT_SR_CMD* gatt_sr_get_cmd_by_cid(tGATT_TCB& tcb, uint16_t cid) { void gatt_sr_update_cback_cnt(tGATT_TCB& tcb, uint16_t cid, tGATT_IF gatt_if, bool is_inc, bool is_reset_first) { uint8_t idx = ((uint8_t)gatt_if) - 1; - tGATT_SR_CMD* sr_cmd_p = &tcb.sr_cmd; + tGATT_SR_CMD* sr_cmd_p; + + if (cid == tcb.att_lcid) { + sr_cmd_p = &tcb.sr_cmd; + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + sr_cmd_p = &channel->server_outstanding_cmd_; + } if (is_reset_first) { gatt_sr_reset_cback_cnt(tcb, cid); @@ -1261,12 +1416,28 @@ void gatt_cmd_enq(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, bool to_send, cmd.p_clcb = p_clcb; cmd.cid = p_clcb->cid; - tcb.cl_cmd_q.push(cmd); + if (p_clcb->cid == tcb.att_lcid) { + tcb.cl_cmd_q.push(cmd); + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cmd.cid); + CHECK(channel); + channel->cl_cmd_q_.push(cmd); + } } /** dequeue the command in the client CCB command queue */ tGATT_CLCB* gatt_cmd_dequeue(tGATT_TCB& tcb, uint16_t cid, uint8_t* p_op_code) { - std::queue* cl_cmd_q_p = &tcb.cl_cmd_q; + std::queue* cl_cmd_q_p; + + if (cid == tcb.att_lcid) { + cl_cmd_q_p = &tcb.cl_cmd_q; + } else { + EattChannel* channel = + EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + CHECK(channel); + cl_cmd_q_p = &channel->cl_cmd_q_; + } if (cl_cmd_q_p->empty()) return nullptr; diff --git a/stack/include/bt_types.h b/stack/include/bt_types.h index eaf92b4e3..ac89c4349 100644 --- a/stack/include/bt_types.h +++ b/stack/include/bt_types.h @@ -248,6 +248,7 @@ typedef struct { #define BT_PSM_UDI_CP \ 0x001D /* Unrestricted Digital Information Profile C-Plane */ #define BT_PSM_ATT 0x001F /* Attribute Protocol */ +#define BT_PSM_EATT 0x0027 /* These macros extract the HCI opcodes from a buffer */ diff --git a/stack/include/btm_api_types.h b/stack/include/btm_api_types.h index 640f1dcdb..868be9b18 100644 --- a/stack/include/btm_api_types.h +++ b/stack/include/btm_api_types.h @@ -536,9 +536,10 @@ typedef uint8_t tBTM_LINK_KEY_TYPE; #define BTM_SEC_SERVICE_RFC_MUX 42 #define BTM_SEC_SERVICE_HEARING_AID_LEFT 54 #define BTM_SEC_SERVICE_HEARING_AID_RIGHT 55 +#define BTM_SEC_SERVICE_EATT 56 /* Update these as services are added */ -#define BTM_SEC_SERVICE_FIRST_EMPTY 56 +#define BTM_SEC_SERVICE_FIRST_EMPTY 57 #ifndef BTM_SEC_MAX_SERVICES #define BTM_SEC_MAX_SERVICES 75 diff --git a/stack/test/common/mock_btif_storage.cc b/stack/test/common/mock_btif_storage.cc new file mode 100644 index 000000000..d246ba382 --- /dev/null +++ b/stack/test/common/mock_btif_storage.cc @@ -0,0 +1,31 @@ +/****************************************************************************** + * + * Copyright 2020 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. + * + ******************************************************************************/ + +#include "mock_btif_storage.h" + +static bluetooth::manager::MockBtifStorageInterface* btif_storage_interface = + nullptr; + +void bluetooth::manager::SetMockBtifStorageInterface( + MockBtifStorageInterface* mock_btif_storage_interface) { + btif_storage_interface = mock_btif_storage_interface; +} + +void btif_storage_load_bonded_eatt(void) { + btif_storage_interface->LoadBondedEatt(); +} diff --git a/stack/test/common/mock_btif_storage.h b/stack/test/common/mock_btif_storage.h new file mode 100644 index 000000000..19a2de213 --- /dev/null +++ b/stack/test/common/mock_btif_storage.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * + * Copyright 2020 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. + * + ******************************************************************************/ +#pragma once + +#include + +namespace bluetooth { +namespace manager { + +class BtifStorageInterface { + public: + virtual void LoadBondedEatt(void) = 0; + virtual ~BtifStorageInterface() = default; +}; + +class MockBtifStorageInterface : public BtifStorageInterface { + public: + MOCK_METHOD0(LoadBondedEatt, void(void)); +}; + +/** + * Set the {@link MockBifStorageInterface} for testing + * + * @param mock_btif_storage_interface pointer to mock btm security + * internal interface, could be null + */ +void SetMockBtifStorageInterface( + MockBtifStorageInterface* mock_btif_storage_interface); + +} // namespace manager +} // namespace bluetooth \ No newline at end of file diff --git a/stack/test/common/mock_btm_api_layer.cc b/stack/test/common/mock_btm_api_layer.cc new file mode 100644 index 000000000..e2ac67f5e --- /dev/null +++ b/stack/test/common/mock_btm_api_layer.cc @@ -0,0 +1,38 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.dk. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ + +#include "mock_btm_api_layer.h" + +static bluetooth::manager::MockBtmApiInterface* btm_api_interface = nullptr; + +void bluetooth::manager::SetMockBtmApiInterface( + MockBtmApiInterface* mock_btm_api_interface) { + btm_api_interface = mock_btm_api_interface; +} + +bool BTM_SetSecurityLevel(bool is_originator, const char* p_name, + uint8_t service_id, uint16_t sec_level, uint16_t psm, + uint32_t mx_proto_id, uint32_t mx_chan_id) { + return btm_api_interface->SetSecurityLevel(is_originator, p_name, service_id, + sec_level, psm, mx_proto_id, + mx_chan_id); +} + +uint8_t acl_link_role(const RawAddress& remote_bd_addr, + tBT_TRANSPORT transport) { + return btm_api_interface->acl_link_role(remote_bd_addr, transport); +} \ No newline at end of file diff --git a/stack/test/common/mock_btm_api_layer.h b/stack/test/common/mock_btm_api_layer.h new file mode 100644 index 000000000..0d7ee14c5 --- /dev/null +++ b/stack/test/common/mock_btm_api_layer.h @@ -0,0 +1,55 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.dk. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ +#pragma once + +#include + +#include "btm_api.h" + +namespace bluetooth { +namespace manager { + +class BtmApiInterface { + public: + virtual bool SetSecurityLevel(bool is_originator, const char* p_name, + uint8_t service_id, uint16_t sec_level, + uint16_t psm, uint32_t mx_proto_id, + uint32_t mx_chan_id) = 0; + virtual uint8_t acl_link_role(const RawAddress& remote_bd_addr, + tBT_TRANSPORT transport) = 0; + virtual ~BtmApiInterface() = default; +}; + +class MockBtmApiInterface : public BtmApiInterface { + public: + MOCK_METHOD7(SetSecurityLevel, + bool(bool is_originator, const char* p_name, uint8_t service_id, + uint16_t sec_level, uint16_t psm, uint32_t mx_proto_id, + uint32_t mx_chan_id)); + MOCK_METHOD2(acl_link_role, uint8_t(const RawAddress& remote_bd_addr, + tBT_TRANSPORT transport)); +}; + +/** + * Set the {@link MockBtmApiInterface} for testing + * + * @param mock_btm_api_interface pointer to mock btm interface, could be null + */ +void SetMockBtmApiInterface(MockBtmApiInterface* mock_btm_interface); + +} // namespace manager +} // namespace bluetooth \ No newline at end of file diff --git a/stack/test/common/mock_controller.cc b/stack/test/common/mock_controller.cc index 253d4b598..5083425ab 100644 --- a/stack/test/common/mock_controller.cc +++ b/stack/test/common/mock_controller.cc @@ -33,12 +33,16 @@ uint16_t get_iso_data_size(void) { uint8_t get_iso_buffer_count(void) { return controller_interface->GetIsoBufferCount(); } +uint16_t get_acl_data_size_ble(void) { + return controller_interface->GetAclDataSizeBle(); +} const controller_t* controller_get_interface() { static controller_t* controller_instance = new controller_t(); controller_instance->get_iso_data_size = &get_iso_data_size; controller_instance->get_iso_buffer_count = &get_iso_buffer_count; + controller_instance->get_acl_data_size_ble = &get_acl_data_size_ble; return controller_instance; } diff --git a/stack/test/common/mock_controller.h b/stack/test/common/mock_controller.h index a04bbc80a..137ca2bcb 100644 --- a/stack/test/common/mock_controller.h +++ b/stack/test/common/mock_controller.h @@ -26,6 +26,7 @@ class ControllerInterface { public: virtual uint8_t GetIsoBufferCount(void) = 0; virtual uint16_t GetIsoDataSize(void) = 0; + virtual uint16_t GetAclDataSizeBle(void) = 0; virtual ~ControllerInterface() = default; }; @@ -34,6 +35,7 @@ class MockControllerInterface : public ControllerInterface { public: MOCK_METHOD((uint8_t), GetIsoBufferCount, (), (override)); MOCK_METHOD((uint16_t), GetIsoDataSize, (), (override)); + MOCK_METHOD((uint16_t), GetAclDataSizeBle, (), (override)); }; void SetMockControllerInterface( diff --git a/stack/test/common/mock_eatt.cc b/stack/test/common/mock_eatt.cc new file mode 100644 index 000000000..c0cfc2413 --- /dev/null +++ b/stack/test/common/mock_eatt.cc @@ -0,0 +1,143 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA - + * www.ehima.com + * + * 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. + */ + +#include "mock_eatt.h" + +MockEattExtension* mock_pimpl_; +MockEattExtension* MockEattExtension::GetInstance() { + bluetooth::eatt::EattExtension::GetInstance(); + return mock_pimpl_; +} + +namespace bluetooth { +namespace eatt { + +struct EattExtension::impl : public MockEattExtension { + impl() = default; + ~impl() = default; + bool IsRunning() { return mock_pimpl_ ? true : false; } +}; + +void EattExtension::AddFromStorage(const RawAddress& bd_addr) {} + +EattExtension::EattExtension() : pimpl_(std::make_unique()) {} + +bool EattExtension::IsEattSupportedByPeer(const RawAddress& bd_addr) { + return pimpl_->IsEattSupportedByPeer(bd_addr); +} + +void EattExtension::Connect(const RawAddress& bd_addr) { + pimpl_->Connect(bd_addr); +} + +void EattExtension::Disconnect(const RawAddress& bd_addr) { + pimpl_->Disconnect(bd_addr); +} + +void EattExtension::Reconfigure(const RawAddress& bd_addr, uint16_t cid, + uint16_t mtu) { + pimpl_->Reconfigure(bd_addr, cid, mtu); +} +void EattExtension::ReconfigureAll(const RawAddress& bd_addr, uint16_t mtu) { + pimpl_->ReconfigureAll(bd_addr, mtu); +} + +EattChannel* EattExtension::FindEattChannelByCid(const RawAddress& bd_addr, + uint16_t cid) { + return pimpl_->FindEattChannelByCid(bd_addr, cid); +} + +EattChannel* EattExtension::FindEattChannelByTransId(const RawAddress& bd_addr, + uint32_t trans_id) { + return pimpl_->FindEattChannelByTransId(bd_addr, trans_id); +} + +bool EattExtension::IsIndicationPending(const RawAddress& bd_addr, + uint16_t indication_handle) { + return pimpl_->IsIndicationPending(bd_addr, indication_handle); +} + +EattChannel* EattExtension::GetChannelAvailableForIndication( + const RawAddress& bd_addr) { + return pimpl_->GetChannelAvailableForIndication(bd_addr); +} + +void EattExtension::FreeGattResources(const RawAddress& bd_addr) { + pimpl_->FreeGattResources(bd_addr); +} + +bool EattExtension::IsOutstandingMsgInSendQueue(const RawAddress& bd_addr) { + return pimpl_->IsOutstandingMsgInSendQueue(bd_addr); +} + +EattChannel* EattExtension::GetChannelWithQueuedData( + const RawAddress& bd_addr) { + return pimpl_->GetChannelWithQueuedData(bd_addr); +} + +EattChannel* EattExtension::GetChannelAvailableForClientRequest( + const RawAddress& bd_addr) { + return pimpl_->GetChannelAvailableForClientRequest(bd_addr); +} + +/* Start stop GATT indication timer per CID */ +void EattExtension::StartIndicationConfirmationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->StartIndicationConfirmationTimer(bd_addr, cid); +} + +void EattExtension::StopIndicationConfirmationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->StopIndicationConfirmationTimer(bd_addr, cid); +} + +/* Start stop application indication timeout */ +void EattExtension::StartAppIndicationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->StartAppIndicationTimer(bd_addr, cid); +} + +void EattExtension::StopAppIndicationTimer(const RawAddress& bd_addr, + uint16_t cid) { + pimpl_->StopAppIndicationTimer(bd_addr, cid); +} + +void EattExtension::Start() { + // It is needed here as IsoManager which is a singleton creates it, but in + // this mock we want to destroy and recreate the mock on each test case. + if (!pimpl_) { + pimpl_ = std::make_unique(); + } + + mock_pimpl_ = pimpl_.get(); + pimpl_->Start(); +} + +void EattExtension::Stop() { + // It is needed here as IsoManager which is a singleton creates it, but in + // this mock we want to destroy and recreate the mock on each test case. + if (pimpl_) { + pimpl_->Stop(); + pimpl_.reset(); + } + + mock_pimpl_ = nullptr; +} + +EattExtension::~EattExtension() = default; +} // namespace eatt +} // namespace bluetooth diff --git a/stack/test/common/mock_eatt.h b/stack/test/common/mock_eatt.h new file mode 100644 index 000000000..9875c7bb4 --- /dev/null +++ b/stack/test/common/mock_eatt.h @@ -0,0 +1,70 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ +#pragma once + +#include + +#include "stack/eatt/eatt.h" + +using bluetooth::eatt::EattChannel; +using bluetooth::eatt::EattExtension; + +class MockEattExtension : public EattExtension { + public: + MockEattExtension() = default; + ~MockEattExtension() override = default; + + static MockEattExtension* GetInstance(); + + MOCK_METHOD((void), Connect, (const RawAddress& bd_addr)); + MOCK_METHOD((void), Disconnect, (const RawAddress& bd_addr)); + MOCK_METHOD((void), Reconfigure, + (const RawAddress& bd_addr, uint16_t cid, uint16_t mtu)); + MOCK_METHOD((void), ReconfigureAll, + (const RawAddress& bd_addr, uint16_t mtu)); + + MOCK_METHOD((bool), IsEattSupportedByPeer, (const RawAddress& bd_addr)); + MOCK_METHOD((EattChannel*), FindEattChannelByCid, + (const RawAddress& bd_addr, uint16_t cid)); + MOCK_METHOD((EattChannel*), FindEattChannelByTransId, + (const RawAddress& bd_addr, uint32_t trans_id)); + MOCK_METHOD((bool), IsIndicationPending, + (const RawAddress& bd_addr, uint16_t indication_handle)); + MOCK_METHOD((EattChannel*), GetChannelAvailableForIndication, + (const RawAddress& bd_addr)); + MOCK_METHOD((void), FreeGattResources, (const RawAddress& bd_addr)); + MOCK_METHOD((bool), IsOutstandingMsgInSendQueue, (const RawAddress& bd_addr)); + MOCK_METHOD((EattChannel*), GetChannelWithQueuedData, + (const RawAddress& bd_addr)); + MOCK_METHOD((EattChannel*), GetChannelAvailableForClientRequest, + (const RawAddress& bd_addr)); + MOCK_METHOD((void), StartIndicationConfirmationTimer, + (const RawAddress& bd_addr, uint16_t cid)); + MOCK_METHOD((void), StopIndicationConfirmationTimer, + (const RawAddress& bd_addr, uint16_t cid)); + + MOCK_METHOD((void), StartAppIndicationTimer, + (const RawAddress& bd_addr, uint16_t cid)); + MOCK_METHOD((void), StopAppIndicationTimer, + (const RawAddress& bd_addr, uint16_t cid)); + + MOCK_METHOD((void), Start, ()); + MOCK_METHOD((void), Stop, ()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockEattExtension); +}; diff --git a/stack/test/common/mock_gatt_layer.cc b/stack/test/common/mock_gatt_layer.cc new file mode 100644 index 000000000..01b7ef73a --- /dev/null +++ b/stack/test/common/mock_gatt_layer.cc @@ -0,0 +1,31 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ + +#include "mock_gatt_layer.h" + +static bluetooth::gatt::MockGattInterface* gatt_interface = nullptr; + +void bluetooth::gatt::SetMockGattInterface( + MockGattInterface* mock_gatt_interface) { + gatt_interface = mock_gatt_interface; +} + +bool gatt_profile_get_eatt_support( + const RawAddress& peer_bda, + base::OnceCallback cb) { + return gatt_interface->GetEattSupport(peer_bda, std::move(cb)); +} diff --git a/stack/test/common/mock_gatt_layer.h b/stack/test/common/mock_gatt_layer.h new file mode 100644 index 000000000..255f43d1e --- /dev/null +++ b/stack/test/common/mock_gatt_layer.h @@ -0,0 +1,50 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ +#pragma once + +#include + +#include "base/bind_helpers.h" +#include "stack/gatt/gatt_int.h" + +namespace bluetooth { +namespace gatt { + +class GattInterface { + public: + virtual bool GetEattSupport( + const RawAddress& peer_bda, + base::OnceCallback cb) = 0; + virtual ~GattInterface() = default; +}; + +class MockGattInterface : public GattInterface { + public: + MOCK_METHOD2(GetEattSupport, + bool(const RawAddress& peer_bda, + base::OnceCallback cb)); +}; + +/** + * Set the {@link MockGattInterface} for testing + * + * @param mock_gatt_interface pointer to mock gatt interface, could be null + */ +void SetMockGattInterface(MockGattInterface* mock_gatt_interface); + +} // namespace gatt +} // namespace bluetooth diff --git a/stack/test/common/mock_l2cap_layer.cc b/stack/test/common/mock_l2cap_layer.cc index 33e864312..4131a20f2 100644 --- a/stack/test/common/mock_l2cap_layer.cc +++ b/stack/test/common/mock_l2cap_layer.cc @@ -59,3 +59,29 @@ bool L2CA_ConfigRsp(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) { uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data) { return l2cap_interface->DataWrite(cid, p_data); } + +uint16_t L2CA_RegisterLECoc(uint16_t psm, const tL2CAP_APPL_INFO &cb_info, uint16_t sec_level) { + return l2cap_interface->RegisterLECoc(psm, cb_info, sec_level); +} + +void L2CA_DeregisterLECoc(uint16_t psm) { + return l2cap_interface->DeregisterLECoc(psm); +} + +std::vector L2CA_ConnectCreditBasedReq(uint16_t psm, + const RawAddress& bd_addr, + tL2CAP_LE_CFG_INFO* p_cfg) { + return l2cap_interface->ConnectCreditBasedReq(psm, bd_addr, p_cfg); +} + +bool L2CA_ConnectCreditBasedRsp(const RawAddress& bd_addr, uint8_t id, + std::vector &lcids, + uint16_t result, + tL2CAP_LE_CFG_INFO* p_cfg) { + return l2cap_interface->ConnectCreditBasedRsp(bd_addr, id, lcids, result, p_cfg); +} + +bool L2CA_ReconfigCreditBasedConnsReq(const RawAddress& bd_addr, std::vector &lcids, + tL2CAP_LE_CFG_INFO* peer_cfg) { + return l2cap_interface->ReconfigCreditBasedConnsReq(bd_addr, lcids, peer_cfg); +} \ No newline at end of file diff --git a/stack/test/common/mock_l2cap_layer.h b/stack/test/common/mock_l2cap_layer.h index 5b7ef690f..4c29fd00e 100644 --- a/stack/test/common/mock_l2cap_layer.h +++ b/stack/test/common/mock_l2cap_layer.h @@ -38,6 +38,17 @@ class L2capInterface { virtual bool ConfigRequest(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) = 0; virtual bool ConfigResponse(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) = 0; virtual uint8_t DataWrite(uint16_t cid, BT_HDR* p_data) = 0; + virtual uint16_t RegisterLECoc(uint16_t psm, const tL2CAP_APPL_INFO &cb_info, uint16_t sec_level) = 0; + virtual void DeregisterLECoc(uint16_t psm) = 0; + virtual bool ConnectCreditBasedRsp(const RawAddress& bd_addr, uint8_t id, + std::vector &lcids, + uint16_t result, + tL2CAP_LE_CFG_INFO* p_cfg) = 0; + virtual std::vector ConnectCreditBasedReq(uint16_t psm, + const RawAddress& bd_addr, + tL2CAP_LE_CFG_INFO* p_cfg) = 0; + virtual bool ReconfigCreditBasedConnsReq(const RawAddress& bd_addr, std::vector &lcids, + tL2CAP_LE_CFG_INFO* peer_cfg) = 0; virtual ~L2capInterface() = default; }; @@ -56,6 +67,20 @@ class MockL2capInterface : public L2capInterface { MOCK_METHOD2(ConfigRequest, bool(uint16_t cid, tL2CAP_CFG_INFO* p_cfg)); MOCK_METHOD2(ConfigResponse, bool(uint16_t cid, tL2CAP_CFG_INFO* p_cfg)); MOCK_METHOD2(DataWrite, uint8_t(uint16_t cid, BT_HDR* p_data)); + MOCK_METHOD3(RegisterLECoc, + uint16_t(uint16_t psm, const tL2CAP_APPL_INFO &cb_info, uint16_t sec_level)); + MOCK_METHOD1(DeregisterLECoc, void(uint16_t psm)); + MOCK_METHOD5(ConnectCreditBasedRsp, + bool(const RawAddress& p_bd_addr, uint8_t id, + std::vector &lcids, + uint16_t result, + tL2CAP_LE_CFG_INFO* p_cfg)); + MOCK_METHOD3(ConnectCreditBasedReq, + std::vector (uint16_t psm, + const RawAddress& bd_addr, + tL2CAP_LE_CFG_INFO* p_cfg)); + MOCK_METHOD3(ReconfigCreditBasedConnsReq, + bool(const RawAddress& p_bd_addr, std::vector &lcids, tL2CAP_LE_CFG_INFO* peer_cfg)); }; /** diff --git a/stack/test/eatt/eatt_test.cc b/stack/test/eatt/eatt_test.cc new file mode 100644 index 000000000..883caec00 --- /dev/null +++ b/stack/test/eatt/eatt_test.cc @@ -0,0 +1,366 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.dk. + * Represented by EHIMA - www.ehima.com + * + * 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. + */ + +#include +#include + +#include + +#include "base/bind_helpers.h" +#include "btm_api.h" +#include "l2c_api.h" +#include "mock_btif_storage.h" +#include "mock_btm_api_layer.h" +#include "mock_controller.h" +#include "mock_eatt.h" +#include "mock_gatt_layer.h" +#include "mock_l2cap_layer.h" + +using testing::_; +using testing::DoAll; +using testing::MockFunction; +using testing::NiceMock; +using testing::NotNull; +using testing::Return; +using testing::SaveArg; +using testing::SaveArgPointee; +using testing::StrictMock; + +using bluetooth::eatt::EattChannel; +using bluetooth::eatt::EattChannelState; + +/* Needed for testing context */ +static tGATT_TCB test_tcb; +void btif_storage_add_eatt_supported(const RawAddress& addr) { return; } +void gatt_data_process(tGATT_TCB& tcb, uint16_t cid, BT_HDR* p_buf) { return; } +tGATT_TCB* gatt_find_tcb_by_addr(const RawAddress& bda, + tBT_TRANSPORT transport) { + LOG(INFO) << __func__; + return &test_tcb; +} + +namespace { +const RawAddress test_address({0x11, 0x11, 0x11, 0x11, 0x11, 0x11}); + +class EattTest : public testing::Test { + protected: + void ConnectDeviceEattSupported(int num_of_accepted_connections) { + ON_CALL(gatt_interface_, GetEattSupport) + .WillByDefault( + [](const RawAddress& addr, + base::OnceCallback cb) { + std::move(cb).Run(addr, true); + return true; + }); + + std::vector test_local_cids{61, 62, 63, 64, 65}; + EXPECT_CALL(l2cap_interface_, + ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) + .WillOnce(Return(test_local_cids)); + + eatt_instance_->Connect(test_address); + + int i = 0; + for (uint16_t cid : test_local_cids) { + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel != nullptr); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_PENDING); + + if (i < num_of_accepted_connections) { + l2cap_app_info_.pL2CA_CreditBasedConnectCfm_Cb( + test_address, cid, EATT_MIN_MTU_MPS, L2CAP_CONN_OK); + connected_cids_.push_back(cid); + + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + } else { + l2cap_app_info_.pL2CA_Error_Cb(cid, L2CAP_CONN_NO_RESOURCES); + + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel == nullptr); + } + i++; + } + } + + void DisconnectEattDevice(void) { + EXPECT_CALL(l2cap_interface_, DisconnectRequest(_)) + .Times(connected_cids_.size()); + eatt_instance_->Disconnect(test_address); + } + + void SetUp() override { + bluetooth::l2cap::SetMockInterface(&l2cap_interface_); + bluetooth::manager::SetMockBtmApiInterface(&btm_api_interface_); + bluetooth::manager::SetMockBtifStorageInterface(&btif_storage_interface_); + bluetooth::gatt::SetMockGattInterface(&gatt_interface_); + controller::SetMockControllerInterface(&controller_interface); + + EXPECT_CALL(l2cap_interface_, RegisterLECoc(BT_PSM_EATT, _, _)) + .WillOnce(DoAll(SaveArg<1>(&l2cap_app_info_), Return(BT_PSM_EATT))); + + ON_CALL(btif_storage_interface_, LoadBondedEatt).WillByDefault([]() { + return; + }); + + ON_CALL(btm_api_interface_, acl_link_role(_, BT_TRANSPORT_LE)) + .WillByDefault(DoAll(Return(hci_role_))); + + ON_CALL(controller_interface, GetAclDataSizeBle()) + .WillByDefault(Return(128)); + + hci_role_ = HCI_ROLE_CENTRAL; + + eatt_instance_ = EattExtension::GetInstance(); + eatt_instance_->Start(); + + Test::SetUp(); + } + + void TearDown() override { + EXPECT_CALL(l2cap_interface_, DeregisterLECoc(BT_PSM_EATT)).Times(1); + + eatt_instance_->Stop(); + eatt_instance_ = nullptr; + hci_role_ = 0; + connected_cids_.clear(); + + bluetooth::gatt::SetMockGattInterface(nullptr); + bluetooth::l2cap::SetMockInterface(nullptr); + bluetooth::manager::SetMockBtifStorageInterface(nullptr); + bluetooth::manager::SetMockBtmApiInterface(nullptr); + controller::SetMockControllerInterface(nullptr); + + Test::TearDown(); + } + + bluetooth::manager::MockBtifStorageInterface btif_storage_interface_; + bluetooth::manager::MockBtmApiInterface btm_api_interface_; + bluetooth::l2cap::MockL2capInterface l2cap_interface_; + bluetooth::gatt::MockGattInterface gatt_interface_; + controller::MockControllerInterface controller_interface; + + tL2CAP_APPL_INFO l2cap_app_info_; + EattExtension* eatt_instance_; + std::vector connected_cids_; + uint8_t hci_role_; +}; + +TEST_F(EattTest, ConnectSucceed) { + ConnectDeviceEattSupported(1); + DisconnectEattDevice(); +} + +TEST_F(EattTest, ConnectSucceedMultipleChannels) { + ConnectDeviceEattSupported(5); + DisconnectEattDevice(); +} + +TEST_F(EattTest, ConnectFailedEattNotSupported) { + ON_CALL(gatt_interface_, GetEattSupport) + .WillByDefault([](const RawAddress& addr, + base::OnceCallback cb) { + std::move(cb).Run(addr, false); + return true; + }); + + EXPECT_CALL(l2cap_interface_, + ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) + .Times(0); + eatt_instance_->Connect(test_address); + ASSERT_TRUE(eatt_instance_->IsEattSupportedByPeer(test_address) == false); +} + +TEST_F(EattTest, ConnectFailedSlaveOnTheLink) { + EXPECT_CALL(l2cap_interface_, + ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) + .Times(0); + + /* This shall be HCI_ROLE_PERIPHERAL when it gets there + * TODO: fix when merge conflic fixed, go/aog/1459349 + */ + hci_role_ = 0x01; + eatt_instance_->Connect(test_address); + + /* Back to default btm role */ + hci_role_ = HCI_ROLE_CENTRAL; +} + +TEST_F(EattTest, DisonnectByPeerSucceed) { + ConnectDeviceEattSupported(1); + + uint16_t cid = connected_cids_[0]; + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + + l2cap_app_info_.pL2CA_DisconnectInd_Cb(cid, true); + + channel = eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel == nullptr); +} + +TEST_F(EattTest, ReconfigAllSucceed) { + ConnectDeviceEattSupported(3); + + std::vector cids; + EXPECT_CALL(l2cap_interface_, ReconfigCreditBasedConnsReq(_, _, _)) + .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); + + uint16_t new_mtu = 300; + eatt_instance_->ReconfigureAll(test_address, new_mtu); + + ASSERT_TRUE(cids.size() == connected_cids_.size()); + + tL2CAP_LE_CFG_INFO cfg = {.result = L2CAP_CFG_OK, .mtu = new_mtu}; + + for (uint16_t cid : cids) { + l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, + true, &cfg); + + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + ASSERT_TRUE(channel->rx_mtu_ == new_mtu); + } + + DisconnectEattDevice(); +} + +TEST_F(EattTest, ReconfigAllFailed) { + ConnectDeviceEattSupported(4); + + std::vector cids; + EXPECT_CALL(l2cap_interface_, ReconfigCreditBasedConnsReq(_, _, _)) + .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); + + uint16_t new_mtu = 300; + eatt_instance_->ReconfigureAll(test_address, new_mtu); + + ASSERT_TRUE(cids.size() == connected_cids_.size()); + + tL2CAP_LE_CFG_INFO cfg = {.result = L2CAP_CFG_FAILED_NO_REASON, + .mtu = new_mtu}; + + for (uint16_t cid : cids) { + l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, + true, &cfg); + + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + ASSERT_TRUE(channel->rx_mtu_ != new_mtu); + } + + DisconnectEattDevice(); +} + +TEST_F(EattTest, ReconfigSingleSucceed) { + ConnectDeviceEattSupported(2); + + std::vector cids; + EXPECT_CALL(l2cap_interface_, ReconfigCreditBasedConnsReq(_, _, _)) + .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); + + uint16_t new_mtu = 300; + eatt_instance_->Reconfigure(test_address, connected_cids_[1], new_mtu); + + ASSERT_TRUE(cids.size() == 1); + + tL2CAP_LE_CFG_INFO cfg = {.result = L2CAP_CFG_OK, .mtu = new_mtu}; + + auto it = std::find(connected_cids_.begin(), connected_cids_.end(), cids[0]); + ASSERT_TRUE(it != connected_cids_.end()); + + l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cids[0], + true, &cfg); + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cids[0]); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + ASSERT_TRUE(channel->rx_mtu_ == new_mtu); + + DisconnectEattDevice(); +} + +TEST_F(EattTest, ReconfigSingleFailed) { + ConnectDeviceEattSupported(2); + + std::vector cids; + EXPECT_CALL(l2cap_interface_, ReconfigCreditBasedConnsReq(_, _, _)) + .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); + + uint16_t new_mtu = 300; + eatt_instance_->ReconfigureAll(test_address, new_mtu); + + ASSERT_TRUE(cids.size() == connected_cids_.size()); + + tL2CAP_LE_CFG_INFO cfg = {.result = L2CAP_CFG_FAILED_NO_REASON, + .mtu = new_mtu}; + + auto it = std::find(connected_cids_.begin(), connected_cids_.end(), cids[0]); + ASSERT_TRUE(it != connected_cids_.end()); + + l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cids[0], + true, &cfg); + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cids[0]); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + ASSERT_TRUE(channel->rx_mtu_ != new_mtu); + + DisconnectEattDevice(); +} + +TEST_F(EattTest, ReconfigPeerSucceed) { + ConnectDeviceEattSupported(3); + + uint16_t new_mtu = 300; + tL2CAP_LE_CFG_INFO cfg = {.result = L2CAP_CFG_OK, .mtu = new_mtu}; + + for (uint16_t cid : connected_cids_) { + l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, + false, &cfg); + + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + ASSERT_TRUE(channel->tx_mtu_ == new_mtu); + } + + DisconnectEattDevice(); +} + +TEST_F(EattTest, ReconfigPeerFailed) { + ConnectDeviceEattSupported(2); + + uint16_t new_mtu = 300; + tL2CAP_LE_CFG_INFO cfg = {.result = L2CAP_CFG_FAILED_NO_REASON, + .mtu = new_mtu}; + + for (uint16_t cid : connected_cids_) { + l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, + false, &cfg); + + EattChannel* channel = + eatt_instance_->FindEattChannelByCid(test_address, cid); + ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); + ASSERT_TRUE(channel->tx_mtu_ != new_mtu); + } + + DisconnectEattDevice(); +} +} // namespace diff --git a/stack/test/gatt/gatt_sr_test.cc b/stack/test/gatt/gatt_sr_test.cc index 037eecad3..66f6b82d8 100644 --- a/stack/test/gatt/gatt_sr_test.cc +++ b/stack/test/gatt/gatt_sr_test.cc @@ -20,6 +20,7 @@ #include #include "osi/test/AllocationTestHarness.h" +#include "stack/test/common/mock_eatt.h" #include "stack/gatt/gatt_int.h" #undef LOG_TAG #include "stack/gatt/gatt_sr.cc" @@ -167,6 +168,7 @@ class GattSrTest : public AllocationTestHarness { memset(&el_, 0, sizeof(el_)); tcb_.trans_id = 0x12345677; + tcb_.att_lcid = L2CAP_ATT_CID; el_.gatt_if = 1; gatt_cb.cl_rcb[el_.gatt_if - 1].in_use = true; gatt_cb.cl_rcb[el_.gatt_if - 1].app_cb.p_req_cb = diff --git a/stack/test/gatt/mock_gatt_utils_ref.cc b/stack/test/gatt/mock_gatt_utils_ref.cc index d344641d9..75c99bcc7 100644 --- a/stack/test/gatt/mock_gatt_utils_ref.cc +++ b/stack/test/gatt/mock_gatt_utils_ref.cc @@ -71,7 +71,7 @@ void gatt_set_ch_state(tGATT_TCB* p_tcb, tGATT_CH_STATE ch_state) {} /** stack/gatt/gatt_sr.cc */ uint32_t gatt_sr_enqueue_cmd(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t handle) { return 0x0000; } -void gatt_dequeue_sr_cmd(tGATT_TCB& tcb) {} +void gatt_dequeue_sr_cmd(tGATT_TCB& tcb, uint16_t cid) {} /** stack/l2cap/l2c_ble.cc */ diff --git a/stack/test/stack_gatt_sr_hash_test.cc b/stack/test/stack_gatt_sr_hash_test.cc index b5f16a9ba..9d64baae9 100644 --- a/stack/test/stack_gatt_sr_hash_test.cc +++ b/stack/test/stack_gatt_sr_hash_test.cc @@ -18,6 +18,7 @@ #include +#include "stack/test/common/mock_eatt.h" #include "crypto_toolbox/crypto_toolbox.h" #include "stack/gatt/gatt_int.h" diff --git a/test/run_unit_tests.sh b/test/run_unit_tests.sh index 44135e047..3b17dd159 100755 --- a/test/run_unit_tests.sh +++ b/test/run_unit_tests.sh @@ -14,6 +14,7 @@ known_tests=( net_test_btif_profile_queue net_test_btif_config_cache net_test_device + net_test_eatt net_test_hci net_test_stack net_test_stack_multi_adv