OSDN Git Service

stack/eatt: Add initial implementation for EATT
authorŁukasz Rymanowski <lukasz.rymanowski@codecoup.pl>
Fri, 8 May 2020 23:26:11 +0000 (01:26 +0200)
committerJakub Pawlowski <jpawlowski@google.com>
Tue, 20 Oct 2020 12:36:19 +0000 (12:36 +0000)
Tag: #feature
Bug: 159786353
Sponsor: jpawlowski@
Test: atest --host net_test_eatt
Change-Id: I934153625e69b1e20af657ded8f17238484cb65e

31 files changed:
TEST_MAPPING
stack/Android.bp
stack/BUILD.gn
stack/eatt/eatt.cc [new file with mode: 0644]
stack/eatt/eatt.h [new file with mode: 0644]
stack/eatt/eatt_impl.h [new file with mode: 0644]
stack/gatt/gatt_api.cc
stack/gatt/gatt_cl.cc
stack/gatt/gatt_int.h
stack/gatt/gatt_main.cc
stack/gatt/gatt_sr.cc
stack/gatt/gatt_utils.cc
stack/include/bt_types.h
stack/include/btm_api_types.h
stack/test/common/mock_btif_storage.cc [new file with mode: 0644]
stack/test/common/mock_btif_storage.h [new file with mode: 0644]
stack/test/common/mock_btm_api_layer.cc [new file with mode: 0644]
stack/test/common/mock_btm_api_layer.h [new file with mode: 0644]
stack/test/common/mock_controller.cc
stack/test/common/mock_controller.h
stack/test/common/mock_eatt.cc [new file with mode: 0644]
stack/test/common/mock_eatt.h [new file with mode: 0644]
stack/test/common/mock_gatt_layer.cc [new file with mode: 0644]
stack/test/common/mock_gatt_layer.h [new file with mode: 0644]
stack/test/common/mock_l2cap_layer.cc
stack/test/common/mock_l2cap_layer.h
stack/test/eatt/eatt_test.cc [new file with mode: 0644]
stack/test/gatt/gatt_sr_test.cc
stack/test/gatt/mock_gatt_utils_ref.cc
stack/test/stack_gatt_sr_hash_test.cc
test/run_unit_tests.sh

index bbaf457..e3b4318 100755 (executable)
@@ -28,6 +28,9 @@
       "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
     },
index b6bf90c..bc3c27b 100644 (file)
@@ -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,
+    },
+}
index 8b9e74b..9bb356c 100644 (file)
@@ -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 (file)
index 0000000..573092b
--- /dev/null
@@ -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(&reg_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
diff --git a/stack/eatt/eatt.h b/stack/eatt/eatt.h
new file mode 100644 (file)
index 0000000..0af2fe3
--- /dev/null
@@ -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 <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
diff --git a/stack/eatt/eatt_impl.h b/stack/eatt/eatt_impl.h
new file mode 100644 (file)
index 0000000..5fd0df3
--- /dev/null
@@ -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 <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
index e7e3897..d40ad09 100644 (file)
@@ -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;
 }
index 899cfd2..519b8b8 100644 (file)
@@ -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<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();
index 4579d11..a040075 100644 (file)
@@ -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,
index 764f65d..c86af5e 100644 (file)
@@ -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<tGATT_HDL_LIST_ELEM>();
   gatt_cb.srv_list_info = new std::list<tGATT_SRV_LIST_ELEM>();
   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 */
index 60fe429..9685325 100644 (file)
 #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
index addab32..9eecedb 100644 (file)
 #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<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;
 
index eaf92b4..ac89c43 100644 (file)
@@ -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
  */
index 640f1dc..868be9b 100644 (file)
@@ -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 (file)
index 0000000..d246ba3
--- /dev/null
@@ -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 (file)
index 0000000..19a2de2
--- /dev/null
@@ -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 <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
diff --git a/stack/test/common/mock_btm_api_layer.cc b/stack/test/common/mock_btm_api_layer.cc
new file mode 100644 (file)
index 0000000..e2ac67f
--- /dev/null
@@ -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 (file)
index 0000000..0d7ee14
--- /dev/null
@@ -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 <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
index 253d4b5..5083425 100644 (file)
@@ -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;
 }
index a04bbc8..137ca2b 100644 (file)
@@ -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 (file)
index 0000000..c0cfc24
--- /dev/null
@@ -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<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
diff --git a/stack/test/common/mock_eatt.h b/stack/test/common/mock_eatt.h
new file mode 100644 (file)
index 0000000..9875c7b
--- /dev/null
@@ -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 <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);
+};
diff --git a/stack/test/common/mock_gatt_layer.cc b/stack/test/common/mock_gatt_layer.cc
new file mode 100644 (file)
index 0000000..01b7ef7
--- /dev/null
@@ -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<void(const RawAddress&, bool)> 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 (file)
index 0000000..255f43d
--- /dev/null
@@ -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 <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
index 33e8643..4131a20 100644 (file)
@@ -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<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
index 5b7ef69..4c29fd0 100644 (file)
@@ -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<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;
 };
 
@@ -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<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));
 };
 
 /**
diff --git a/stack/test/eatt/eatt_test.cc b/stack/test/eatt/eatt_test.cc
new file mode 100644 (file)
index 0000000..883caec
--- /dev/null
@@ -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 <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
index 037eeca..66f6b82 100644 (file)
@@ -20,6 +20,7 @@
 #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"
@@ -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 =
index d344641..75c99bc 100644 (file)
@@ -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 */
index b5f16a9..9d64baa 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest.h>
 
+#include "stack/test/common/mock_eatt.h"
 #include "crypto_toolbox/crypto_toolbox.h"
 #include "stack/gatt/gatt_int.h"
 
index 44135e0..3b17dd1 100755 (executable)
@@ -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