"name" : "net_test_device"
},
{
+ "name" : "net_test_eatt"
+ },
+ {
"name" : "net_test_hci"
},
{
"host" : true
},
{
+ "name" : "net_test_eatt",
+ "host" : true
+ },
+ {
"name" : "net_test_types",
"host" : true
},
"avrc",
"l2cap",
"avdt",
+ "eatt",
"gatt",
"gap",
"pan",
"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",
include_dirs: [
"system/bt",
"system/bt/stack/include",
+ "system/bt/stack/eatt",
"system/bt/stack/l2cap",
"system/bt/stack/btm",
"system/bt/utils/include",
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",
"libosi",
"libbt-common",
"libbt-protos-lite",
+ "libgmock",
"libosi-AllocationTestHarness",
],
sanitize: {
include_dirs: [
"system/bt",
"system/bt/stack/btm",
+ "system/bt/stack/eatt",
"system/bt/stack/include",
"system/bt/utils/include",
],
"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: [
"libbt-common",
"libbt-protos-lite",
"liblog",
+ "libgmock",
"libosi",
],
}
"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: [
},
},
}
+
+// 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,
+ },
+}
"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",
"gap",
"pan",
"bnep",
+ "eatt",
"hid",
"sdp",
"smp",
include_dirs = [
"include",
"//",
+ "//bta/eatt",
"//bta/include",
"//bta/sys",
"//btcore/include",
--- /dev/null
+/*
+ * 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<eatt_impl>();
+ }
+ }
+
+ 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<uint16_t>& 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> 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<impl>()) {}
+
+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
--- /dev/null
+/*
+ * 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 <queue>
+
+#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<tGATT_CMD_Q> 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<tGATT_CMD_Q>();
+ 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<impl> pimpl_;
+
+ DISALLOW_COPY_AND_ASSIGN(EattExtension);
+};
+
+} // namespace eatt
+} // namespace bluetooth
\ No newline at end of file
--- /dev/null
+/*
+ * 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 <map>
+#include <queue>
+
+#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<uint16_t, std::shared_ptr<EattChannel>> 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<eatt_device> 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<uint16_t>& 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<EattChannel>(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<uint8_t>(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<uint16_t> 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<EattChannel>(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<uint16_t, std::shared_ptr<EattChannel>>& 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<uint16_t, std::shared_ptr<EattChannel>>& 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<uint16_t, std::shared_ptr<EattChannel>>& 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<uint16_t, std::shared_ptr<EattChannel>>& 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<uint16_t, std::shared_ptr<EattChannel>>& 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<uint16_t, std::shared_ptr<EattChannel>>& 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<uint16_t, std::shared_ptr<EattChannel>>& 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<uint16_t> 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<uint16_t> 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
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;
}
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);
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;
}
#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"
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 *
bool gatt_cl_send_next_cmd_inq(tGATT_TCB& tcb) {
std::queue<tGATT_CMD_Q>* 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();
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;
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);
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);
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);
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,
#include "bt_common.h"
#include "bt_utils.h"
+#include "eatt.h"
#include "btif_storage.h"
#include "btm_ble_int.h"
#include "btm_int.h"
#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 */
gatt_cb.hdl_list_info = new std::list<tGATT_HDL_LIST_ELEM>();
gatt_cb.srv_list_info = new std::list<tGATT_SRV_LIST_ELEM>();
gatt_profile_db_init();
+
+ EattExtension::GetInstance()->Start();
}
/*******************************************************************************
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();
}
/*******************************************************************************
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 = "
gatt_chk_srv_chg(p_srv_chg_clt);
}
}
+
+ EattExtension::GetInstance()->Connect(bd_addr);
}
/** This function is called to process the congestion callback from lcb */
#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;
/*******************************************************************************
*
******************************************************************************/
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;
* 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);
+}
/*******************************************************************************
*
* 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
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;
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)
}
#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,
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;
return;
}
- alarm_cancel(tcb.conf_timer);
+ gatt_stop_conf_timer(tcb, cid);
bool continue_processing = gatts_proc_ind_ack(tcb, handle);
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
#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))
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);
* 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);
}
/*******************************************************************************
******************************************************************************/
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
* 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);
} else
status = GATT_INSUF_RESOURCE;
- if (deq) gatt_dequeue_sr_cmd(tcb);
+ if (deq)
+ gatt_dequeue_sr_cmd(tcb, cid);
return status;
}
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;
}
/*******************************************************************************
******************************************************************************/
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();
}
/*******************************************************************************
*
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;
}
}
*
* 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;
}
/*******************************************************************************
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;
}
*
******************************************************************************/
-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;
+}
/*******************************************************************************
*
*
******************************************************************************/
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_;
}
/*******************************************************************************
******************************************************************************/
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_;
}
/*******************************************************************************
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;
}
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_;
}
/*******************************************************************************
*
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;
}
}
}
/* 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
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);
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<tGATT_CMD_Q>* cl_cmd_q_p = &tcb.cl_cmd_q;
+ std::queue<tGATT_CMD_Q>* 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;
#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
*/
#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
--- /dev/null
+/******************************************************************************
+ *
+ * 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();
+}
--- /dev/null
+/******************************************************************************
+ *
+ * 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 <gmock/gmock.h>
+
+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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <gmock/gmock.h>
+
+#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
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;
}
public:
virtual uint8_t GetIsoBufferCount(void) = 0;
virtual uint16_t GetIsoDataSize(void) = 0;
+ virtual uint16_t GetAclDataSizeBle(void) = 0;
virtual ~ControllerInterface() = default;
};
public:
MOCK_METHOD((uint8_t), GetIsoBufferCount, (), (override));
MOCK_METHOD((uint16_t), GetIsoDataSize, (), (override));
+ MOCK_METHOD((uint16_t), GetAclDataSizeBle, (), (override));
};
void SetMockControllerInterface(
--- /dev/null
+/*
+ * 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<impl>()) {}
+
+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<impl>();
+ }
+
+ 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
--- /dev/null
+/*
+ * 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 <gmock/gmock.h>
+
+#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);
+};
--- /dev/null
+/*
+ * 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<void(const RawAddress&, bool)> cb) {
+ return gatt_interface->GetEattSupport(peer_bda, std::move(cb));
+}
--- /dev/null
+/*
+ * 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 <gmock/gmock.h>
+
+#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<void(const RawAddress&, bool)> cb) = 0;
+ virtual ~GattInterface() = default;
+};
+
+class MockGattInterface : public GattInterface {
+ public:
+ MOCK_METHOD2(GetEattSupport,
+ bool(const RawAddress& peer_bda,
+ base::OnceCallback<void(const RawAddress&, bool)> 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
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<uint16_t> 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<uint16_t> &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<uint16_t> &lcids,
+ tL2CAP_LE_CFG_INFO* peer_cfg) {
+ return l2cap_interface->ReconfigCreditBasedConnsReq(bd_addr, lcids, peer_cfg);
+}
\ No newline at end of file
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<uint16_t> &lcids,
+ uint16_t result,
+ tL2CAP_LE_CFG_INFO* p_cfg) = 0;
+ virtual std::vector<uint16_t> ConnectCreditBasedReq(uint16_t psm,
+ const RawAddress& bd_addr,
+ tL2CAP_LE_CFG_INFO* p_cfg) = 0;
+ virtual bool ReconfigCreditBasedConnsReq(const RawAddress& bd_addr, std::vector<uint16_t> &lcids,
+ tL2CAP_LE_CFG_INFO* peer_cfg) = 0;
virtual ~L2capInterface() = default;
};
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<uint16_t> &lcids,
+ uint16_t result,
+ tL2CAP_LE_CFG_INFO* p_cfg));
+ MOCK_METHOD3(ConnectCreditBasedReq,
+ std::vector<uint16_t> (uint16_t psm,
+ const RawAddress& bd_addr,
+ tL2CAP_LE_CFG_INFO* p_cfg));
+ MOCK_METHOD3(ReconfigCreditBasedConnsReq,
+ bool(const RawAddress& p_bd_addr, std::vector<uint16_t> &lcids, tL2CAP_LE_CFG_INFO* peer_cfg));
};
/**
--- /dev/null
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#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<void(const RawAddress&, bool)> cb) {
+ std::move(cb).Run(addr, true);
+ return true;
+ });
+
+ std::vector<uint16_t> 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<uint16_t> 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<void(const RawAddress&, bool)> 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<uint16_t> 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<uint16_t> 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<uint16_t> 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<uint16_t> 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
#include <cstdint>
#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"
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 =
/** 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 */
#include <gtest/gtest.h>
+#include "stack/test/common/mock_eatt.h"
#include "crypto_toolbox/crypto_toolbox.h"
#include "stack/gatt/gatt_int.h"
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