OSDN Git Service

btm: Initial Iso Manager implementation
authorJakub Tyszkowski <jakub.tyszkowski@codecoup.pl>
Thu, 20 Aug 2020 10:58:19 +0000 (10:58 +0000)
committerJakub Tyszkowski <jakub.tyszkowski@codecoup.pl>
Thu, 20 Aug 2020 10:59:04 +0000 (10:59 +0000)
This adds the ISO Manager used to manage the CIG/CIS and
in future patches also BIG/BIS channels. It defines an API
for the user to call and callback interfaces that the user
has to implement and register in the ISO Manager to use it.

Bug: 150670922
Tag: #feature
Test: atest --host net_test_btm_iso
Sponsor: jpawlowski@
Change-Id: I0f4719b6efb74a7350cf3d6d9263bf855d60ce6f

15 files changed:
TEST_MAPPING
stack/Android.bp
stack/BUILD.gn
stack/btm/btm_iso.cc [new file with mode: 0644]
stack/btm/btm_iso_impl.h [new file with mode: 0644]
stack/btu/btu_hcif.cc
stack/btu/btu_task.cc
stack/include/btm_iso_api.h [new file with mode: 0644]
stack/include/btm_iso_api_types.h [new file with mode: 0644]
stack/test/btm_iso_test.cc [new file with mode: 0644]
stack/test/common/mock_controller.cc [new file with mode: 0644]
stack/test/common/mock_controller.h [new file with mode: 0644]
stack/test/common/mock_hcic_layer.cc [new file with mode: 0644]
stack/test/common/mock_hcic_layer.h [new file with mode: 0644]
test/run_host_unit_tests.py

index cc24d8b..1ca5c4b 100755 (executable)
       "host" : true
     },
     {
+      "name" : "net_test_btm_iso",
+      "host" : true
+    },
+    {
       "name" : "net_test_btpackets",
       "host" : true
     },
index 86ba521..aab5b44 100644 (file)
@@ -118,6 +118,7 @@ cc_library_static {
         "btm/btm_main.cc",
         "acl/btm_pm.cc",
         "btm/btm_sco.cc",
+        "btm/btm_iso.cc",
         "btm/btm_sec.cc",
         "btu/btu_hcif.cc",
         "btu/btu_task.cc",
@@ -499,3 +500,42 @@ cc_test {
     ],
 }
 
+
+// Iso manager unit tests
+cc_test {
+    name: "net_test_btm_iso",
+    test_suites: ["device-tests"],
+    host_supported: true,
+    defaults: ["fluoride_defaults"],
+    local_include_dirs: [
+        "btm",
+        "include",
+        "test/common",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/btcore/include",
+    ],
+    srcs: [
+        "btm/btm_iso.cc",
+        "test/btm_iso_test.cc",
+        "test/common/mock_controller.cc",
+        "test/common/mock_hcic_layer.cc",
+    ],
+    static_libs: [
+        "libbt-common",
+        "libgmock",
+        "liblog",
+        "libosi",
+    ],
+    sanitize: {
+        cfi: true,
+        scs: true,
+        address: true,
+        all_undefined: true,
+        integer_overflow: true,
+        diag: {
+            undefined : true
+        },
+    },
+}
index c75fdaf..ef837f1 100644 (file)
@@ -90,6 +90,7 @@ static_library("stack") {
     "btm/btm_dev.cc",
     "btm/btm_devctl.cc",
     "btm/btm_inq.cc",
+    "btm/btm_iso.cc",
     "btm/btm_main.cc",
     "btm/btm_pm.cc",
     "btm/btm_sco.cc",
diff --git a/stack/btm/btm_iso.cc b/stack/btm/btm_iso.cc
new file mode 100644 (file)
index 0000000..a57a4a5
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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 <memory>
+
+#include "btm_iso_api.h"
+#include "btm_iso_impl.h"
+#include "btu.h"
+
+using bluetooth::hci::iso_manager::CigCallbacks;
+using bluetooth::hci::iso_manager::iso_impl;
+
+namespace bluetooth {
+namespace hci {
+
+struct IsoManager::impl {
+  impl(const IsoManager& iso_manager) : iso_manager_(iso_manager) {}
+
+  void Start() {
+    LOG_ASSERT(!iso_impl_);
+    iso_impl_ = std::make_unique<iso_impl>();
+  }
+
+  void Stop() {
+    LOG_ASSERT(iso_impl_);
+    iso_impl_.reset();
+  }
+
+  bool IsRunning() { return iso_impl_ ? true : false; }
+
+  const IsoManager& iso_manager_;
+  std::unique_ptr<iso_impl> iso_impl_;
+};
+
+IsoManager::IsoManager() : pimpl_(std::make_unique<impl>(*this)) {}
+
+void IsoManager::RegisterCigCallbacks(CigCallbacks* callbacks) const {
+  pimpl_->iso_impl_->handle_register_cis_callbacks(callbacks);
+}
+
+void IsoManager::CreateCig(uint8_t cig_id,
+                           struct iso_manager::cig_create_params cig_params) {
+  pimpl_->iso_impl_->create_cig(cig_id, std::move(cig_params));
+}
+
+void IsoManager::ReconfigureCig(
+    uint8_t cig_id, struct iso_manager::cig_create_params cig_params) {
+  pimpl_->iso_impl_->reconfigure_cig(cig_id, std::move(cig_params));
+}
+
+void IsoManager::RemoveCig(uint8_t cig_id) {
+  pimpl_->iso_impl_->remove_cig(cig_id);
+}
+
+void IsoManager::EstablishCis(
+    struct iso_manager::cis_establish_params conn_params) {
+  pimpl_->iso_impl_->establish_cis(std::move(conn_params));
+}
+
+void IsoManager::DisconnectCis(uint16_t cis_handle, uint8_t reason) {
+  pimpl_->iso_impl_->disconnect_cis(cis_handle, reason);
+}
+
+void IsoManager::SetupIsoDataPath(
+    uint16_t iso_handle, struct iso_manager::iso_data_path_params path_params) {
+  pimpl_->iso_impl_->setup_iso_data_path(iso_handle, std::move(path_params));
+}
+
+void IsoManager::RemoveIsoDataPath(uint16_t iso_handle, uint8_t data_path_dir) {
+  pimpl_->iso_impl_->remove_iso_data_path(iso_handle, data_path_dir);
+}
+
+void IsoManager::SendIsoData(uint16_t iso_handle, const uint8_t* data,
+                             uint16_t data_len) {
+  pimpl_->iso_impl_->send_iso_data(iso_handle, data, data_len);
+}
+
+void IsoManager::HandleIsoData(void* p_msg) {
+  if (pimpl_->IsRunning())
+    pimpl_->iso_impl_->handle_iso_data(static_cast<BT_HDR*>(p_msg));
+}
+
+void IsoManager::HandleDisconnect(uint16_t handle, uint8_t reason) {
+  if (pimpl_->IsRunning())
+    pimpl_->iso_impl_->disconnection_complete(handle, reason);
+}
+
+void IsoManager::HandleNumComplDataPkts(uint8_t* p, uint8_t evt_len) {
+  if (pimpl_->IsRunning())
+    pimpl_->iso_impl_->handle_num_completed_pkts(p, evt_len);
+}
+
+void IsoManager::HandleHciEvent(uint8_t sub_code, uint8_t* params,
+                                uint16_t length) {
+  if (pimpl_->IsRunning())
+    pimpl_->iso_impl_->on_iso_event(sub_code, params, length);
+}
+
+void IsoManager::Start() { pimpl_->Start(); }
+
+void IsoManager::Stop() { pimpl_->Stop(); }
+
+IsoManager::~IsoManager() = default;
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/stack/btm/btm_iso_impl.h b/stack/btm/btm_iso_impl.h
new file mode 100644 (file)
index 0000000..4ed1782
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * 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 <map>
+#include <memory>
+#include <set>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "bt_types.h"
+#include "btm_iso_api.h"
+#include "btu.h"
+#include "common/time_util.h"
+#include "device/include/controller.h"
+#include "osi/include/log.h"
+
+namespace bluetooth {
+namespace hci {
+namespace iso_manager {
+static constexpr uint8_t kIsoDataInTsBtHdrOffset = 0x0C;
+static constexpr uint8_t kIsoHeaderWithTsLen = 12;
+static constexpr uint8_t kIsoHeaderWithoutTsLen = 8;
+
+static constexpr uint8_t kStateFlagsNone = 0x00;
+static constexpr uint8_t kStateFlagIsConnected = 0x01;
+static constexpr uint8_t kStateFlagHasDataPathSet = 0x02;
+
+struct iso_sync_info {
+  uint32_t first_sync_ts;
+  uint16_t seq_nb;
+};
+
+struct iso_base {
+  uint8_t cig_id;
+
+  struct iso_sync_info sync_info;
+  uint8_t state_flags;
+  uint32_t sdu_itv;
+};
+
+typedef iso_base iso_cis;
+
+struct iso_impl {
+  iso_impl() {
+    iso_credits_ = controller_get_interface()->get_iso_buffer_count();
+    iso_buffer_size_ = controller_get_interface()->get_iso_data_size();
+  }
+
+  ~iso_impl() {}
+
+  void handle_register_cis_callbacks(CigCallbacks* callbacks) {
+    LOG_ASSERT(callbacks != nullptr) << "Invalid CIG callbacks";
+    cig_callbacks_ = callbacks;
+  }
+
+  void on_set_cig_params(uint8_t cig_id, uint32_t sdu_itv_mtos, uint8_t* stream,
+                         uint16_t len) {
+    uint8_t cis_cnt;
+    uint16_t conn_handle;
+    cig_create_cmpl_evt evt;
+
+    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
+    LOG_ASSERT(len >= 3) << "Invalid packet length.";
+
+    STREAM_TO_UINT8(evt.status, stream);
+    STREAM_TO_UINT8(evt.cig_id, stream);
+    STREAM_TO_UINT8(cis_cnt, stream);
+
+    uint8_t evt_code = IsCigKnown(cig_id) ? kIsoEventCigOnReconfigureCmpl
+                                          : kIsoEventCigOnCreateCmpl;
+
+    if (evt.status == HCI_SUCCESS) {
+      LOG_ASSERT(len >= (3) + (cis_cnt * sizeof(uint16_t)))
+          << "Invalid CIS count.";
+
+      /* Remove entries for the reconfigured CIG */
+      if (evt_code == kIsoEventCigOnReconfigureCmpl) {
+        auto cis_it = conn_hdl_to_cis_map_.cbegin();
+        while (cis_it != conn_hdl_to_cis_map_.cend()) {
+          if (cis_it->second->cig_id == evt.cig_id)
+            cis_it = conn_hdl_to_cis_map_.erase(cis_it);
+          else
+            ++cis_it;
+        }
+      }
+
+      evt.conn_handles.reserve(cis_cnt);
+      for (int i = 0; i < cis_cnt; i++) {
+        STREAM_TO_UINT16(conn_handle, stream);
+
+        evt.conn_handles.push_back(conn_handle);
+        conn_hdl_to_cis_map_[conn_handle] = std::unique_ptr<iso_cis>(
+            new iso_cis({.sync_info = {.first_sync_ts = 0, .seq_nb = 0},
+                         .cig_id = cig_id,
+                         .state_flags = kStateFlagsNone,
+                         .sdu_itv = sdu_itv_mtos}));
+      }
+    }
+
+    cig_callbacks_->OnCigEvent(evt_code, &evt);
+  }
+
+  void create_cig(uint8_t cig_id,
+                  struct iso_manager::cig_create_params cig_params) {
+    LOG_ASSERT(!IsCigKnown(cig_id)) << "Invalid cig - already exists.";
+
+    btsnd_hcic_set_cig_params(
+        cig_id, cig_params.sdu_itv_mtos, cig_params.sdu_itv_stom,
+        cig_params.sca, cig_params.packing, cig_params.framing,
+        cig_params.max_trans_lat_stom, cig_params.max_trans_lat_mtos,
+        cig_params.cis_cfgs.size(), cig_params.cis_cfgs.data(),
+        base::BindOnce(&iso_impl::on_set_cig_params, base::Unretained(this),
+                       cig_id, cig_params.sdu_itv_mtos));
+  }
+
+  void reconfigure_cig(uint8_t cig_id,
+                       struct iso_manager::cig_create_params cig_params) {
+    LOG_ASSERT(IsCigKnown(cig_id)) << "No such cig";
+
+    btsnd_hcic_set_cig_params(
+        cig_id, cig_params.sdu_itv_mtos, cig_params.sdu_itv_stom,
+        cig_params.sca, cig_params.packing, cig_params.framing,
+        cig_params.max_trans_lat_stom, cig_params.max_trans_lat_mtos,
+        cig_params.cis_cfgs.size(), cig_params.cis_cfgs.data(),
+        base::BindOnce(&iso_impl::on_set_cig_params, base::Unretained(this),
+                       cig_id, cig_params.sdu_itv_mtos));
+  }
+
+  void on_remove_cig(uint8_t* stream, uint16_t len) {
+    cig_remove_cmpl_evt evt;
+
+    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
+    LOG_ASSERT(len == 2) << "Invalid packet length.";
+
+    STREAM_TO_UINT8(evt.status, stream);
+    STREAM_TO_UINT8(evt.cig_id, stream);
+
+    if (evt.status == HCI_SUCCESS) {
+      auto cis_it = conn_hdl_to_cis_map_.cbegin();
+      while (cis_it != conn_hdl_to_cis_map_.cend()) {
+        if (cis_it->second->cig_id == evt.cig_id)
+          cis_it = conn_hdl_to_cis_map_.erase(cis_it);
+        else
+          ++cis_it;
+      }
+    }
+
+    cig_callbacks_->OnCigEvent(kIsoEventCigOnRemoveCmpl, &evt);
+  }
+
+  void remove_cig(uint8_t cig_id) {
+    LOG_ASSERT(IsCigKnown(cig_id)) << "No such cig";
+
+    btsnd_hcic_remove_cig(cig_id, base::BindOnce(&iso_impl::on_remove_cig,
+                                                 base::Unretained(this)));
+  }
+
+  void establish_cis(struct iso_manager::cis_establish_params conn_params) {
+    for (auto& el : conn_params.conn_pairs) {
+      auto cis = GetCisIfKnown(el.cis_conn_handle);
+      LOG_ASSERT(cis) << "No such cis";
+      LOG_ASSERT(!(cis->state_flags & kStateFlagIsConnected))
+          << "Already connected";
+    }
+    btsnd_hcic_create_cis(conn_params.conn_pairs.size(),
+                          conn_params.conn_pairs.data());
+  }
+
+  void disconnect_cis(uint16_t cis_handle, uint8_t reason) {
+    auto cis = GetCisIfKnown(cis_handle);
+    LOG_ASSERT(cis) << "No such cis";
+    LOG_ASSERT(cis->state_flags & kStateFlagIsConnected) << "Not connected";
+    btsnd_hcic_disconnect(cis_handle, reason);
+  }
+
+  void on_setup_iso_data_path(uint8_t* stream, uint16_t len) {
+    uint8_t status;
+    uint16_t conn_handle;
+
+    STREAM_TO_UINT8(status, stream);
+    STREAM_TO_UINT16(conn_handle, stream);
+
+    iso_base* iso = GetIsoIfKnown(conn_handle);
+    LOG_ASSERT(iso != nullptr) << "Invalid connection handle: " << +conn_handle;
+
+    if (status == HCI_SUCCESS) iso->state_flags |= kStateFlagHasDataPathSet;
+
+    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
+    cig_callbacks_->OnSetupIsoDataPath(status, conn_handle, iso->cig_id);
+  }
+
+  void setup_iso_data_path(
+      uint16_t conn_handle,
+      struct iso_manager::iso_data_path_params path_params) {
+    iso_base* iso = GetIsoIfKnown(conn_handle);
+    LOG_ASSERT(iso != nullptr) << "No such iso connection: " << +conn_handle;
+
+    LOG_ASSERT(iso->state_flags & kStateFlagIsConnected)
+        << "CIS not established";
+
+    btsnd_hcic_setup_iso_data_path(
+        conn_handle, path_params.data_path_dir, path_params.data_path_id,
+        path_params.codec_id_format, path_params.codec_id_company,
+        path_params.codec_id_vendor, path_params.controller_delay,
+        std::move(path_params.codec_conf),
+        base::BindOnce(&iso_impl::on_setup_iso_data_path,
+                       base::Unretained(this)));
+  }
+
+  void on_remove_iso_data_path(uint8_t* stream, uint16_t len) {
+    uint8_t status;
+    uint16_t conn_handle;
+
+    STREAM_TO_UINT8(status, stream);
+    STREAM_TO_UINT16(conn_handle, stream);
+
+    iso_base* iso = GetIsoIfKnown(conn_handle);
+    LOG_ASSERT(iso != nullptr) << "Invalid connection handle: " << +conn_handle;
+
+    if (status == HCI_SUCCESS) iso->state_flags &= ~kStateFlagHasDataPathSet;
+
+    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
+    cig_callbacks_->OnRemoveIsoDataPath(status, conn_handle, iso->cig_id);
+  }
+
+  void remove_iso_data_path(uint16_t iso_handle, uint8_t data_path_dir) {
+    iso_base* iso = GetIsoIfKnown(iso_handle);
+    LOG_ASSERT(iso != nullptr) << "No such iso connection";
+    LOG_ASSERT((iso->state_flags & kStateFlagHasDataPathSet) ==
+               kStateFlagHasDataPathSet)
+        << "Data path not set";
+
+    btsnd_hcic_remove_iso_data_path(
+        iso_handle, data_path_dir,
+        base::BindOnce(&iso_impl::on_remove_iso_data_path,
+                       base::Unretained(this)));
+  }
+
+  BT_HDR* prepare_ts_hci_packet(uint16_t iso_handle, uint32_t ts,
+                                uint16_t seq_nb, uint16_t data_len) {
+    /* Add 2 for packet seq., 2 for length, 4 for the timestamp */
+    uint16_t iso_data_load_len = data_len + 8;
+
+    /* Add 2 for handle, 2 for length */
+    uint16_t iso_full_len = iso_data_load_len + 4;
+    BT_HDR* packet = (BT_HDR*)osi_malloc(iso_full_len + sizeof(BT_HDR));
+    packet->len = iso_full_len;
+    packet->offset = 0;
+    packet->event = MSG_STACK_TO_HC_HCI_ISO;
+    packet->layer_specific = 0;
+
+    uint8_t* packet_data = packet->data;
+    UINT16_TO_STREAM(packet_data, iso_handle);
+    UINT16_TO_STREAM(packet_data, iso_data_load_len);
+
+    packet->layer_specific |= BT_ISO_HDR_CONTAINS_TS;
+    UINT32_TO_STREAM(packet_data, ts);
+
+    UINT16_TO_STREAM(packet_data, seq_nb);
+    UINT16_TO_STREAM(packet_data, data_len);
+
+    return packet;
+  }
+
+  void send_iso_data_hci_packet(BT_HDR* packet) {
+    bte_main_hci_send(packet, MSG_STACK_TO_HC_HCI_ISO | 0x0001);
+  }
+
+  void send_iso_data(uint16_t iso_handle, const uint8_t* data,
+                     uint16_t data_len) {
+    iso_base* iso = GetIsoIfKnown(iso_handle);
+    LOG_ASSERT(iso != nullptr)
+        << "No such iso connection handle: " << +iso_handle;
+
+    LOG_ASSERT(iso->state_flags & kStateFlagIsConnected)
+        << "CIS not established";
+    LOG_ASSERT(iso->state_flags & kStateFlagHasDataPathSet)
+        << "Data path not set for handle: " << +iso_handle;
+
+    /* Calculate sequence number for the ISO data packet.
+     * It should be incremented by 1 every SDU Interval.
+     */
+    uint32_t ts = bluetooth::common::time_get_os_boottime_us();
+    iso->sync_info.seq_nb = (ts - iso->sync_info.first_sync_ts) / iso->sdu_itv;
+
+    if (iso_credits_ == 0 || data_len > iso_buffer_size_) {
+      LOG(WARNING) << __func__ << ", dropping ISO packet, len: "
+                   << static_cast<int>(data_len)
+                   << ", iso credits: " << static_cast<int>(iso_credits_);
+      return;
+    }
+
+    iso_credits_--;
+
+    BT_HDR* packet =
+        prepare_ts_hci_packet(iso_handle, ts, iso->sync_info.seq_nb, data_len);
+    memcpy(packet->data + kIsoDataInTsBtHdrOffset, data, data_len);
+    send_iso_data_hci_packet(packet);
+  }
+
+  void process_cis_est_pkt(uint8_t len, uint8_t* data) {
+    cis_establish_cmpl_evt evt;
+
+    LOG_ASSERT(len == 28) << "Invalid packet length";
+    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
+
+    STREAM_TO_UINT8(evt.status, data);
+    STREAM_TO_UINT16(evt.cis_conn_hdl, data);
+
+    auto cis = GetCisIfKnown(evt.cis_conn_hdl);
+    LOG_ASSERT(cis != nullptr) << "No such cis";
+
+    cis->sync_info.first_sync_ts = bluetooth::common::time_get_os_boottime_us();
+
+    STREAM_TO_UINT24(evt.cig_sync_delay, data);
+    STREAM_TO_UINT24(evt.cis_sync_delay, data);
+    STREAM_TO_UINT24(evt.trans_lat_mtos, data);
+    STREAM_TO_UINT24(evt.trans_lat_stom, data);
+    STREAM_TO_UINT8(evt.phy_mtos, data);
+    STREAM_TO_UINT8(evt.phy_stom, data);
+    STREAM_TO_UINT8(evt.nse, data);
+    STREAM_TO_UINT8(evt.bn_mtos, data);
+    STREAM_TO_UINT8(evt.bn_stom, data);
+    STREAM_TO_UINT8(evt.ft_mtos, data);
+    STREAM_TO_UINT8(evt.ft_stom, data);
+    STREAM_TO_UINT16(evt.max_pdu_mtos, data);
+    STREAM_TO_UINT16(evt.max_pdu_stom, data);
+    STREAM_TO_UINT16(evt.iso_itv, data);
+
+    if (evt.status == HCI_SUCCESS) cis->state_flags |= kStateFlagIsConnected;
+
+    evt.cig_id = cis->cig_id;
+    cig_callbacks_->OnCisEvent(kIsoEventCisEstablishCmpl, &evt);
+  }
+
+  void disconnection_complete(uint16_t handle, uint8_t reason) {
+    /* Check if this is an ISO handle */
+    auto cis = GetCisIfKnown(handle);
+    if (cis == nullptr) return;
+
+    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
+
+    LOG_INFO("%s flags: %d", __func__, +cis->state_flags);
+    if (cis->state_flags & kStateFlagIsConnected) {
+      cis_disconnected_evt evt = {
+          .reason = reason,
+          .cis_conn_hdl = handle,
+          .cig_id = cis->cig_id,
+      };
+
+      cig_callbacks_->OnCisEvent(kIsoEventCisDisconnected, &evt);
+      cis->state_flags &= ~kStateFlagIsConnected;
+      /* Data path is considered still valid, but can be reconfigured only once
+       * CIS is reestablished.
+       */
+    }
+  }
+
+  void handle_num_completed_pkts(uint8_t* p, uint8_t evt_len) {
+    uint8_t num_handles;
+
+    STREAM_TO_UINT8(num_handles, p);
+
+    LOG_ASSERT(evt_len == num_handles * 4 + 1);
+
+    for (int i = 0; i < num_handles; i++) {
+      uint16_t handle, num_sent;
+
+      STREAM_TO_UINT16(handle, p);
+      STREAM_TO_UINT16(num_sent, p);
+
+      if (conn_hdl_to_cis_map_.find(handle) == conn_hdl_to_cis_map_.end())
+        continue;
+
+      iso_credits_ += num_sent;
+    }
+  }
+
+  void on_iso_event(uint8_t code, uint8_t* packet, uint16_t packet_len) {
+    switch (code) {
+      case HCI_BLE_CIS_EST_EVT:
+        process_cis_est_pkt(packet_len, packet);
+        break;
+      case HCI_BLE_CREATE_BIG_CPL_EVT:
+        /* TODO: Implement */
+        break;
+      case HCI_BLE_TERM_BIG_CPL_EVT:
+        /* TODO: Implement */
+        break;
+      case HCI_BLE_CIS_REQ_EVT:
+        /* Not supported */
+        break;
+      case HCI_BLE_BIG_SYNC_EST_EVT:
+        /* Not supported */
+        break;
+      case HCI_BLE_BIG_SYNC_LOST_EVT:
+        /* Not supported */
+        break;
+      default:
+        LOG_ERROR("Unhandled event code %d", +code);
+    }
+  }
+
+  void handle_iso_data(BT_HDR* p_msg) {
+    const uint8_t* stream = p_msg->data;
+    cis_data_evt evt;
+    uint16_t handle, seq_nb;
+
+    if (p_msg->len <= ((p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS)
+                           ? kIsoHeaderWithTsLen
+                           : kIsoHeaderWithoutTsLen))
+      return;
+
+    LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
+
+    STREAM_TO_UINT16(handle, stream);
+    evt.cis_conn_hdl = HCID_GET_HANDLE(handle);
+
+    iso_base* iso = GetCisIfKnown(evt.cis_conn_hdl);
+    if (iso == nullptr) {
+      LOG(ERROR) << __func__ << ", received data for the non-registered CIS!";
+      return;
+    }
+
+    STREAM_SKIP_UINT16(stream);
+    if (p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS)
+      STREAM_TO_UINT32(evt.ts, stream);
+
+    STREAM_TO_UINT16(seq_nb, stream);
+
+    uint32_t ts = bluetooth::common::time_get_os_boottime_us();
+    uint32_t new_calc_seq_nb =
+        (ts - iso->sync_info.first_sync_ts) / iso->sdu_itv;
+    if (new_calc_seq_nb == iso->sync_info.seq_nb) ++new_calc_seq_nb;
+
+    if (iso->sync_info.seq_nb == 0) {
+      evt.evt_lost = 0;
+    } else {
+      evt.evt_lost = new_calc_seq_nb - iso->sync_info.seq_nb - 1;
+      if (evt.evt_lost > 0) {
+        LOG(WARNING) << evt.evt_lost << " packets possibly lost.";
+      }
+
+      if (new_calc_seq_nb != seq_nb) {
+        LOG(WARNING) << "Sequence number mismatch. "
+                        "Adjusting own time reference point.";
+        iso->sync_info.first_sync_ts = ts - (seq_nb * iso->sdu_itv);
+        new_calc_seq_nb = seq_nb;
+      }
+    }
+    iso->sync_info.seq_nb = new_calc_seq_nb;
+
+    evt.p_msg = p_msg;
+    evt.cig_id = iso->cig_id;
+    cig_callbacks_->OnCisEvent(kIsoEventCisDataAvailable, &evt);
+  }
+
+  iso_cis* GetCisIfKnown(uint16_t cis_conn_handle) {
+    auto cis_it = conn_hdl_to_cis_map_.find(cis_conn_handle);
+    return (cis_it != conn_hdl_to_cis_map_.end()) ? cis_it->second.get()
+                                                  : nullptr;
+  }
+
+  iso_base* GetIsoIfKnown(uint16_t iso_handle) {
+    return GetCisIfKnown(iso_handle);
+  }
+
+  bool IsCigKnown(uint8_t cig_id) const {
+    auto const cis_it =
+        std::find_if(conn_hdl_to_cis_map_.cbegin(), conn_hdl_to_cis_map_.cend(),
+                     [&cig_id](auto& kv_pair) {
+                       return (kv_pair.second->cig_id == cig_id);
+                     });
+    return (cis_it != conn_hdl_to_cis_map_.cend());
+  }
+
+  std::map<uint16_t, std::unique_ptr<iso_cis>> conn_hdl_to_cis_map_;
+
+  uint16_t iso_credits_;
+  uint16_t iso_buffer_size_;
+
+  CigCallbacks* cig_callbacks_ = nullptr;
+};
+
+}  // namespace iso_manager
+}  // namespace hci
+}  // namespace bluetooth
index 6eee125..a1b51b0 100644 (file)
@@ -47,6 +47,7 @@
 #include "btif_config.h"
 #include "btm_api.h"
 #include "btm_int.h"
+#include "btm_iso_api.h"
 #include "btu.h"
 #include "common/metrics.h"
 #include "device/include/controller.h"
@@ -61,6 +62,7 @@
 #include "stack/include/sec_hci_link_interface.h"
 
 using base::Location;
+using bluetooth::hci::IsoManager;
 
 extern void btm_process_cancel_complete(uint8_t status, uint8_t mode);
 extern void btm_process_inq_results2(uint8_t* p, uint8_t inq_res_mode);
@@ -335,6 +337,7 @@ void btu_hcif_process_event(UNUSED_ATTR uint8_t controller_id, BT_HDR* p_msg) {
       break;
     case HCI_NUM_COMPL_DATA_PKTS_EVT:
       l2c_link_process_num_completed_pkts(p, hci_evt_len);
+      IsoManager::GetInstance()->HandleNumComplDataPkts(p, hci_evt_len);
       break;
     case HCI_MODE_CHANGE_EVT:
       btu_hcif_mode_change_evt(p);
@@ -436,6 +439,16 @@ void btu_hcif_process_event(UNUSED_ATTR uint8_t controller_id, BT_HDR* p_msg) {
         case HCI_LE_ADVERTISING_SET_TERMINATED_EVT:
           btm_le_on_advertising_set_terminated(p, hci_evt_len);
           break;
+
+        case HCI_BLE_CIS_EST_EVT:
+        case HCI_BLE_CREATE_BIG_CPL_EVT:
+        case HCI_BLE_TERM_BIG_CPL_EVT:
+        case HCI_BLE_CIS_REQ_EVT:
+        case HCI_BLE_BIG_SYNC_EST_EVT:
+        case HCI_BLE_BIG_SYNC_LOST_EVT:
+          IsoManager::GetInstance()->HandleHciEvent(ble_sub_code, p,
+                                                    ble_evt_len);
+          break;
       }
       break;
     }
@@ -1021,8 +1034,11 @@ static void btu_hcif_disconnection_comp_evt(uint8_t* p) {
                     __func__, reason, handle);
   }
 
-  /* If L2CAP doesn't know about it, send it to SCO */
-  if (!l2c_link_hci_disc_comp(handle, reason)) btm_sco_removed(handle, reason);
+  /* If L2CAP or SCO doesn't know about it, send it to ISO */
+  if (!l2c_link_hci_disc_comp(handle, reason) &&
+      !btm_sco_removed(handle, reason)) {
+    IsoManager::GetInstance()->HandleDisconnect(handle, reason);
+  }
 
   /* Notify security manager */
   btm_sec_disconnected(handle, reason);
index c44203a..d52d647 100644 (file)
@@ -27,6 +27,7 @@
 #include "btcore/include/module.h"
 #include "bte.h"
 #include "btif/include/btif_common.h"
+#include "btm_iso_api.h"
 #include "common/message_loop_thread.h"
 #include "osi/include/osi.h"
 #include "stack/btm/btm_int.h"
@@ -39,6 +40,7 @@
 #include <base/threading/thread.h>
 
 using bluetooth::common::MessageLoopThread;
+using bluetooth::hci::IsoManager;
 
 /* Define BTU storage area */
 uint8_t btu_trace_level = HCI_INITIAL_TRACE_LEVEL;
@@ -72,7 +74,7 @@ void btu_hci_msg_process(BT_HDR* p_msg) {
       break;
 
     case BT_EVT_TO_BTU_HCI_ISO:
-      // TODO: implement handler
+      IsoManager::GetInstance()->HandleIsoData(p_msg);
       osi_free(p_msg);
       break;
 
diff --git a/stack/include/btm_iso_api.h b/stack/include/btm_iso_api.h
new file mode 100644 (file)
index 0000000..541a8d4
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <vector>
+
+#include "btm_iso_api_types.h"
+
+namespace bluetooth {
+namespace hci {
+namespace iso_manager {
+struct CigCallbacks {
+  virtual ~CigCallbacks() = default;
+  virtual void OnSetupIsoDataPath(uint8_t status, uint16_t conn_handle,
+                                  uint8_t cig_id) = 0;
+  virtual void OnRemoveIsoDataPath(uint8_t status, uint16_t conn_handle,
+                                   uint8_t cig_id) = 0;
+
+  virtual void OnCisEvent(uint8_t event, void* data) = 0;
+  virtual void OnCigEvent(uint8_t event, void* data) = 0;
+};
+}  // namespace iso_manager
+
+class IsoManager {
+ public:
+  IsoManager();
+  virtual ~IsoManager();
+
+  static IsoManager* GetInstance() {
+    static IsoManager* instance = new IsoManager();
+    return instance;
+  }
+
+  /**
+   * Set CIG and CIS related callbacks
+   *
+   * <p> Shall be set by the Le Audio Unicaster implementation
+   *
+   * @param callbacks CigCallbacks implementation
+   */
+  virtual void RegisterCigCallbacks(iso_manager::CigCallbacks* callbacks) const;
+
+  /**
+   * Creates connected isochronous group (CIG) according to given params.
+   *
+   * @param cig_id connected isochronous group id
+   * @param cig_params CIG parameters
+   */
+  virtual void CreateCig(uint8_t cig_id,
+                         struct iso_manager::cig_create_params cig_params);
+
+  /**
+   * Reconfigures connected isochronous group (CIG) according to given params.
+   *
+   * @param cig_id connected isochronous group id
+   * @param cig_params CIG parameters
+   */
+  virtual void ReconfigureCig(uint8_t cig_id,
+                              struct iso_manager::cig_create_params cig_params);
+
+  /**
+   * Initiates removing of connected isochronous group (CIG).
+   *
+   * @param cig_id connected isochronous group id
+   */
+  virtual void RemoveCig(uint8_t cig_id);
+
+  /**
+   * Initiates creation of connected isochronous stream (CIS).
+   *
+   * @param conn_params A set of cis and acl connection handles
+   */
+  virtual void EstablishCis(
+      struct iso_manager::cis_establish_params conn_params);
+
+  /**
+   * Initiates disconnection of connected isochronous stream (CIS).
+   *
+   * @param conn_handle CIS connection handle
+   * @param reason HCI reason for disconnection
+   */
+  virtual void DisconnectCis(uint16_t conn_handle, uint8_t reason);
+
+  /**
+   * Initiates creation of isochronous data path for connected isochronous
+   * stream.
+   *
+   * @param conn_handle handle of CIS connection
+   * @param path_params iso data path parameters
+   */
+  virtual void SetupIsoDataPath(
+      uint16_t conn_handle,
+      struct iso_manager::iso_data_path_params path_params);
+
+  /**
+   * Initiates removement of isochronous data path for connected isochronous
+   * stream.
+   *
+   * @param conn_handle handle of CIS connection
+   * @param data_path_dir iso data path direction
+   */
+  virtual void RemoveIsoDataPath(uint16_t conn_handle, uint8_t data_path_dir);
+
+  /**
+   * Sends iso data to the controller
+   *
+   * @param conn_handle handle of CIS connection
+   * @param data data buffer. The ownership of data is not being transferred.
+   * @param data_len data buffer length
+   */
+  virtual void SendIsoData(uint16_t conn_handle, const uint8_t* data,
+                           uint16_t data_len);
+
+  /* Below are defined handlers called by the legacy code in btu_hcif.cc */
+
+  /**
+   * Handles Iso Data packets from the controller
+   *
+   * @param p_msg raw data packet. The ownership of p_msg is not being
+   * transferred.
+   */
+  virtual void HandleIsoData(void* p_msg);
+
+  /**
+   * Handles disconnect HCI event
+   *
+   * <p> This callback can be called with handles other than ISO connection
+   * handles.
+   *
+   * @param conn_handle connection handle
+   * @param reason HCI reason for disconnection
+   */
+  virtual void HandleDisconnect(uint16_t conn_handle, uint8_t reason);
+
+  /**
+   * Handles HCI event for the number of completed packets
+   *
+   * @param p raw packet buffer for the event. The ownership of p is not being
+   * transferred.
+   * @param evt_len event packet buffer length
+   */
+  virtual void HandleNumComplDataPkts(uint8_t* p, uint8_t evt_len);
+
+  /**
+   * Handle CIS related HCI events
+   *
+   * @param sub_code ble subcode for the HCI event
+   * @param params raw packet buffer for the event. The ownership of params is
+   * not being transferred
+   * @param length event packet buffer length
+   */
+  virtual void HandleHciEvent(uint8_t sub_code, uint8_t* params,
+                              uint16_t length);
+
+  /**
+   * Starts the IsoManager module
+   */
+  void Start();
+
+  /**
+   * Stops the IsoManager module
+   */
+  void Stop();
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(IsoManager);
+};
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/stack/include/btm_iso_api_types.h b/stack/include/btm_iso_api_types.h
new file mode 100644 (file)
index 0000000..638a054
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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 "bt_types.h"
+#include "hcimsgs.h"
+
+namespace bluetooth {
+namespace hci {
+namespace iso_manager {
+
+constexpr uint8_t kIsoDataPathDirectionIn = 0x00;
+constexpr uint8_t kIsoDataPathDirectionOut = 0x01;
+
+constexpr uint8_t kIsoDataPathHci = 0x00;
+constexpr uint8_t kIsoDataPathDisabled = 0xFF;
+
+constexpr uint8_t kIsoSca251To500Ppm = 0x00;
+constexpr uint8_t kIsoSca151To250Ppm = 0x01;
+constexpr uint8_t kIsoSca101To150Ppm = 0x02;
+constexpr uint8_t kIsoSca76To100Ppm = 0x03;
+constexpr uint8_t kIsoSca51To75Ppm = 0x04;
+constexpr uint8_t kIsoSca31To50Ppm = 0x05;
+constexpr uint8_t kIsoSca21To30Ppm = 0x06;
+constexpr uint8_t kIsoSca0To20Ppm = 0x07;
+
+constexpr uint8_t kIsoEventCisDataAvailable = 0x00;
+constexpr uint8_t kIsoEventCisEstablishCmpl = 0x01;
+constexpr uint8_t kIsoEventCisDisconnected = 0x02;
+
+constexpr uint8_t kIsoEventCigOnCreateCmpl = 0x00;
+constexpr uint8_t kIsoEventCigOnReconfigureCmpl = 0x01;
+constexpr uint8_t kIsoEventCigOnRemoveCmpl = 0x02;
+
+struct cig_create_params {
+  uint32_t sdu_itv_mtos;
+  uint32_t sdu_itv_stom;
+  uint8_t sca;
+  uint8_t packing;
+  uint8_t framing;
+  uint16_t max_trans_lat_stom;
+  uint16_t max_trans_lat_mtos;
+  std::vector<EXT_CIS_CFG> cis_cfgs;
+};
+
+struct cig_remove_cmpl_evt {
+  uint8_t status;
+  uint8_t cig_id;
+};
+
+struct cig_create_cmpl_evt {
+  uint8_t status;
+  uint8_t cig_id;
+  std::vector<uint16_t> conn_handles;
+};
+
+struct cis_data_evt {
+  uint8_t cig_id;
+  uint16_t cis_conn_hdl;
+  uint32_t ts;
+  uint16_t evt_lost;
+  BT_HDR* p_msg;
+};
+
+struct cis_establish_params {
+  std::vector<EXT_CIS_CREATE_CFG> conn_pairs;
+};
+
+struct cis_establish_cmpl_evt {
+  uint8_t status;
+  uint8_t cig_id;
+  uint16_t cis_conn_hdl;
+  uint32_t cig_sync_delay;
+  uint32_t cis_sync_delay;
+  uint32_t trans_lat_mtos;
+  uint32_t trans_lat_stom;
+  uint8_t phy_mtos;
+  uint8_t phy_stom;
+  uint8_t nse;
+  uint8_t bn_mtos;
+  uint8_t bn_stom;
+  uint8_t ft_mtos;
+  uint8_t ft_stom;
+  uint16_t max_pdu_mtos;
+  uint16_t max_pdu_stom;
+  uint16_t iso_itv;
+};
+
+struct cis_disconnected_evt {
+  uint8_t reason;
+  uint8_t cig_id;
+  uint16_t cis_conn_hdl;
+};
+
+struct iso_data_path_params {
+  uint8_t data_path_dir;
+  uint8_t data_path_id;
+  uint8_t codec_id_format;
+  uint16_t codec_id_company;
+  uint16_t codec_id_vendor;
+  uint32_t controller_delay;
+  std::vector<uint8_t> codec_conf;
+};
+
+}  // namespace iso_manager
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/stack/test/btm_iso_test.cc b/stack/test/btm_iso_test.cc
new file mode 100644 (file)
index 0000000..e2596ab
--- /dev/null
@@ -0,0 +1,1503 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "btm_iso_api.h"
+#include "device/include/controller.h"
+#include "main/shim/shim.h"
+#include "mock_controller.h"
+#include "mock_hcic_layer.h"
+
+using bluetooth::hci::IsoManager;
+using testing::_;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::Eq;
+using testing::Matcher;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+using testing::Test;
+
+// Iso Manager currently works on top of the legacy HCI layer
+bool bluetooth::shim::is_gd_shim_enabled() { return false; }
+
+namespace bte {
+class BteInterface {
+ public:
+  virtual void HciSend(BT_HDR* p_msg, uint16_t event) = 0;
+  virtual ~BteInterface() = default;
+};
+
+class MockBteInterface : public BteInterface {
+ public:
+  MOCK_METHOD((void), HciSend, (BT_HDR * p_msg, uint16_t event), (override));
+};
+
+static MockBteInterface* bte_interface = nullptr;
+static void SetMockBteInterface(MockBteInterface* interface) {
+  bte_interface = interface;
+}
+}  // namespace bte
+
+void bte_main_hci_send(BT_HDR* p_msg, uint16_t event) {
+  bte::bte_interface->HciSend(p_msg, event);
+}
+
+namespace {
+class MockCigCallbacks : public bluetooth::hci::iso_manager::CigCallbacks {
+ public:
+  MockCigCallbacks() = default;
+  ~MockCigCallbacks() override = default;
+
+  MOCK_METHOD((void), OnSetupIsoDataPath,
+              (uint8_t status, uint16_t conn_handle, uint8_t cig_id),
+              (override));
+  MOCK_METHOD((void), OnRemoveIsoDataPath,
+              (uint8_t status, uint16_t conn_handle, uint8_t cig_id),
+              (override));
+
+  MOCK_METHOD((void), OnCisEvent, (uint8_t event, void* data), (override));
+  MOCK_METHOD((void), OnCigEvent, (uint8_t event, void* data), (override));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCigCallbacks);
+};
+}  // namespace
+
+class IsoManagerTest : public Test {
+ protected:
+  void SetUp() override {
+    bte::SetMockBteInterface(&bte_interface_);
+    hcic::SetMockHcicInterface(&hcic_interface_);
+    controller::SetMockControllerInterface(&controller_interface_);
+
+    cig_callbacks_.reset(new MockCigCallbacks());
+
+    EXPECT_CALL(controller_interface_, GetIsoBufferCount())
+        .Times(AtLeast(1))
+        .WillRepeatedly(Return(6));
+    EXPECT_CALL(controller_interface_, GetIsoDataSize())
+        .Times(AtLeast(1))
+        .WillRepeatedly(Return(1024));
+
+    InitIsoManager();
+  }
+
+  void TearDown() override {
+    CleanupIsoManager();
+
+    cig_callbacks_.reset();
+
+    bte::SetMockBteInterface(nullptr);
+    hcic::SetMockHcicInterface(nullptr);
+    controller::SetMockControllerInterface(nullptr);
+  }
+
+  virtual void InitIsoManager() {
+    manager_instance_ = IsoManager::GetInstance();
+    manager_instance_->Start();
+    manager_instance_->RegisterCigCallbacks(cig_callbacks_.get());
+
+    // Default mock SetCigParams action
+    volatile_test_cig_create_cmpl_evt_ = kDefaultCigParamsEvt;
+    ON_CALL(hcic_interface_, SetCigParams)
+        .WillByDefault([this](auto cig_id, auto,
+                              base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+          uint8_t hci_mock_rsp_buffer
+              [3 + sizeof(uint16_t) * this->volatile_test_cig_create_cmpl_evt_
+                                          .conn_handles.size()];
+          uint8_t* p = hci_mock_rsp_buffer;
+
+          UINT8_TO_STREAM(p, this->volatile_test_cig_create_cmpl_evt_.status);
+          UINT8_TO_STREAM(p, cig_id);
+          UINT8_TO_STREAM(
+              p, this->volatile_test_cig_create_cmpl_evt_.conn_handles.size());
+          for (auto handle :
+               this->volatile_test_cig_create_cmpl_evt_.conn_handles) {
+            UINT16_TO_STREAM(p, handle);
+          }
+
+          std::move(cb).Run(
+              hci_mock_rsp_buffer,
+              3 + sizeof(uint16_t) * this->volatile_test_cig_create_cmpl_evt_
+                                         .conn_handles.size());
+          return 0;
+        });
+
+    // Default mock CreateCis action
+    ON_CALL(hcic_interface_, CreateCis)
+        .WillByDefault([](uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg) {
+          for (const EXT_CIS_CREATE_CFG* cis = cis_cfg; num_cis != 0;
+               num_cis--, cis++) {
+            std::vector<uint8_t> buf(28);
+            uint8_t* p = buf.data();
+            UINT8_TO_STREAM(p, HCI_SUCCESS);
+            UINT16_TO_STREAM(p, cis->cis_conn_handle);
+            UINT24_TO_STREAM(p, 0xEA);    // CIG sync delay
+            UINT24_TO_STREAM(p, 0xEB);    // CIS sync delay
+            UINT24_TO_STREAM(p, 0xEC);    // transport latency mtos
+            UINT24_TO_STREAM(p, 0xED);    // transport latency stom
+            UINT8_TO_STREAM(p, 0x01);     // phy mtos
+            UINT8_TO_STREAM(p, 0x02);     // phy stom
+            UINT8_TO_STREAM(p, 0x01);     // nse
+            UINT8_TO_STREAM(p, 0x02);     // bn mtos
+            UINT8_TO_STREAM(p, 0x03);     // bn stom
+            UINT8_TO_STREAM(p, 0x04);     // ft mtos
+            UINT8_TO_STREAM(p, 0x05);     // ft stom
+            UINT16_TO_STREAM(p, 0x00FA);  // Max PDU mtos
+            UINT16_TO_STREAM(p, 0x00FB);  // Max PDU stom
+            UINT16_TO_STREAM(p, 0x0C60);  // ISO interval
+
+            IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT,
+                                                      buf.data(), buf.size());
+          }
+        });
+
+    // Default mock disconnect action
+    ON_CALL(hcic_interface_, Disconnect)
+        .WillByDefault([](uint16_t handle, uint8_t reason) {
+          IsoManager::GetInstance()->HandleDisconnect(handle, reason);
+        });
+
+    // Default mock SetupIsoDataPath HCI action
+    ON_CALL(hcic_interface_, SetupIsoDataPath)
+        .WillByDefault(
+            [](uint16_t iso_handle, uint8_t /* data_path_dir */,
+               uint8_t /* data_path_id */, uint8_t /* codec_id_format */,
+               uint16_t /* codec_id_company */, uint16_t /* codec_id_vendor */,
+               uint32_t /* controller_delay */,
+               std::vector<uint8_t> /* codec_conf */,
+               base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+              std::vector<uint8_t> buf(3);
+              uint8_t* p = buf.data();
+              UINT8_TO_STREAM(p, HCI_SUCCESS);
+              UINT16_TO_STREAM(p, iso_handle);
+
+              std::move(cb).Run(buf.data(), buf.size());
+            });
+
+    // Default mock RemoveIsoDataPath HCI action
+    ON_CALL(hcic_interface_, RemoveIsoDataPath)
+        .WillByDefault([](uint16_t iso_handle, uint8_t data_path_dir,
+                          base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+          std::vector<uint8_t> buf(3);
+          uint8_t* p = buf.data();
+          UINT8_TO_STREAM(p, HCI_SUCCESS);
+          UINT16_TO_STREAM(p, iso_handle);
+
+          std::move(cb).Run(buf.data(), buf.size());
+        });
+  }
+
+  virtual void CleanupIsoManager() {
+    manager_instance_->Stop();
+    manager_instance_ = nullptr;
+  }
+
+  static const bluetooth::hci::iso_manager::cig_create_params kDefaultCigParams;
+  static const bluetooth::hci::iso_manager::cig_create_params
+      kDefaultCigParams2;
+  static const bluetooth::hci::iso_manager::cig_create_cmpl_evt
+      kDefaultCigParamsEvt;
+  static const bluetooth::hci::iso_manager::iso_data_path_params
+      kDefaultIsoDataPathParams;
+
+  bluetooth::hci::iso_manager::cig_create_cmpl_evt
+      volatile_test_cig_create_cmpl_evt_;
+
+  IsoManager* manager_instance_;
+  bte::MockBteInterface bte_interface_;
+  hcic::MockHcicInterface hcic_interface_;
+  controller::MockControllerInterface controller_interface_;
+
+  std::unique_ptr<MockCigCallbacks> cig_callbacks_;
+};
+
+const bluetooth::hci::iso_manager::cig_create_cmpl_evt
+    IsoManagerTest::kDefaultCigParamsEvt = {
+        .cig_id = 128,
+        .status = 0x00,
+        .conn_handles = std::vector<uint16_t>({0x0EFF, 0x00FF}),
+};
+
+const bluetooth::hci::iso_manager::iso_data_path_params
+    IsoManagerTest::kDefaultIsoDataPathParams = {
+        .data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionOut,
+        .data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci,
+        .codec_id_format = 0x06,
+        .codec_id_company = 0,
+        .codec_id_vendor = 0,
+        .controller_delay = 0,
+        .codec_conf = {0x02, 0x01, 0x02},
+};
+
+const bluetooth::hci::iso_manager::cig_create_params
+    IsoManagerTest::kDefaultCigParams = {
+        .sdu_itv_mtos = 0x00002710,
+        .sdu_itv_stom = 0x00002711,
+        .sca = bluetooth::hci::iso_manager::kIsoSca0To20Ppm,
+        .packing = 0x00,
+        .framing = 0x01,
+        .max_trans_lat_stom = 0x000A,
+        .max_trans_lat_mtos = 0x0009,
+        .cis_cfgs =
+            {
+                // CIS #1
+                {
+                    .cis_id = 1,
+                    .max_sdu_size_mtos = 0x0028,
+                    .max_sdu_size_stom = 0x0027,
+                    .phy_mtos = 0x04,
+                    .phy_stom = 0x03,
+                    .rtn_mtos = 0x02,
+                    .rtn_stom = 0x01,
+                },
+                // CIS #2
+                {
+                    .cis_id = 2,
+                    .max_sdu_size_mtos = 0x0029,
+                    .max_sdu_size_stom = 0x002A,
+                    .phy_mtos = 0x09,
+                    .phy_stom = 0x08,
+                    .rtn_mtos = 0x07,
+                    .rtn_stom = 0x06,
+                },
+            },
+};
+
+const bluetooth::hci::iso_manager::cig_create_params
+    IsoManagerTest::kDefaultCigParams2 = {
+        .sdu_itv_mtos = 0x00002709,
+        .sdu_itv_stom = 0x00002700,
+        .sca = bluetooth::hci::iso_manager::kIsoSca0To20Ppm,
+        .packing = 0x01,
+        .framing = 0x00,
+        .max_trans_lat_stom = 0x000B,
+        .max_trans_lat_mtos = 0x0006,
+        .cis_cfgs =
+            {
+                // CIS #1
+                {
+                    .cis_id = 1,
+                    .max_sdu_size_mtos = 0x0022,
+                    .max_sdu_size_stom = 0x0022,
+                    .phy_mtos = 0x01,
+                    .phy_stom = 0x02,
+                    .rtn_mtos = 0x02,
+                    .rtn_stom = 0x01,
+                },
+                // CIS #2
+                {
+                    .cis_id = 2,
+                    .max_sdu_size_mtos = 0x002A,
+                    .max_sdu_size_stom = 0x002B,
+                    .phy_mtos = 0x06,
+                    .phy_stom = 0x06,
+                    .rtn_mtos = 0x07,
+                    .rtn_stom = 0x07,
+                },
+            },
+};
+
+class IsoManagerDeathTest : public IsoManagerTest {};
+
+class IsoManagerDeathTestNoInit : public IsoManagerTest {
+ protected:
+  void InitIsoManager() override { /* DO NOTHING */
+  }
+
+  void CleanupIsoManager() override { /* DO NOTHING */
+  }
+};
+
+bool operator==(const EXT_CIS_CFG& x, const EXT_CIS_CFG& y) {
+  return ((x.cis_id == y.cis_id) &&
+          (x.max_sdu_size_mtos == y.max_sdu_size_mtos) &&
+          (x.max_sdu_size_stom == y.max_sdu_size_stom) &&
+          (x.phy_mtos == y.phy_mtos) && (x.phy_stom == y.phy_stom) &&
+          (x.rtn_mtos == y.rtn_mtos) && (x.rtn_stom == y.rtn_stom));
+}
+
+bool operator==(
+    const struct bluetooth::hci::iso_manager::cig_create_params& x,
+    const struct bluetooth::hci::iso_manager::cig_create_params& y) {
+  return ((x.sdu_itv_mtos == y.sdu_itv_mtos) &&
+          (x.sdu_itv_stom == y.sdu_itv_stom) && (x.sca == y.sca) &&
+          (x.packing == y.packing) && (x.framing == y.framing) &&
+          (x.max_trans_lat_stom == y.max_trans_lat_stom) &&
+          (x.max_trans_lat_mtos == y.max_trans_lat_mtos) &&
+          std::is_permutation(x.cis_cfgs.begin(), x.cis_cfgs.end(),
+                              y.cis_cfgs.begin()));
+}
+
+namespace iso_matchers {
+MATCHER_P(Eq, value, "") { return (arg == value); }
+MATCHER_P2(EqPointedArray, value, len, "") {
+  return (!std::memcmp(arg, value, len));
+}
+}  // namespace iso_matchers
+
+TEST_F(IsoManagerTest, SingletonAccess) {
+  auto* iso_mgr = IsoManager::GetInstance();
+  ASSERT_EQ(manager_instance_, iso_mgr);
+}
+
+TEST_F(IsoManagerTest, RegisterCallbacks) {
+  auto* iso_mgr = IsoManager::GetInstance();
+  ASSERT_EQ(manager_instance_, iso_mgr);
+
+  iso_mgr->RegisterCigCallbacks(new MockCigCallbacks());
+}
+
+TEST_F(IsoManagerDeathTestNoInit, RegisterNullCigCallbacks) {
+  IsoManager::GetInstance()->Start();
+
+  ASSERT_EXIT(IsoManager::GetInstance()->RegisterCigCallbacks(nullptr),
+              ::testing::KilledBySignal(SIGABRT), "Invalid CIG callbacks");
+
+  // Manual cleanup as IsoManagerDeathTest has no 'generic' cleanup
+  IsoManager::GetInstance()->Stop();
+}
+
+TEST_F(IsoManagerDeathTestNoInit, DoubleStart) {
+  IsoManager::GetInstance()->Start();
+
+  ASSERT_EXIT(IsoManager::GetInstance()->Start(),
+              ::testing::KilledBySignal(SIGABRT), "");
+
+  // Manual cleanup as IsoManagerDeathTest has no 'generic' cleanup
+  IsoManager::GetInstance()->Stop();
+}
+
+TEST_F(IsoManagerDeathTestNoInit, DoubleStop) {
+  IsoManager::GetInstance()->Start();
+  IsoManager::GetInstance()->Stop();
+
+  ASSERT_EXIT(IsoManager::GetInstance()->Stop(),
+              ::testing::KilledBySignal(SIGABRT), "");
+}
+
+// Verify hci layer being called by the Iso Manager
+TEST_F(IsoManagerTest, CreateCigHciCall) {
+  for (uint8_t i = 220; i != 60; ++i) {
+    EXPECT_CALL(hcic_interface_,
+                SetCigParams(i, iso_matchers::Eq(kDefaultCigParams), _))
+        .Times(1)
+        .RetiresOnSaturation();
+    IsoManager::GetInstance()->CreateCig(i, kDefaultCigParams);
+  }
+}
+
+// Check handling create cig request twice with the same CIG id
+TEST_F(IsoManagerDeathTest, CreateSameCigTwice) {
+  bluetooth::hci::iso_manager::cig_create_cmpl_evt evt;
+  evt.status = 0x01;
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnCreateCmpl, _))
+      .WillOnce([&evt](uint8_t type, void* data) {
+        evt = *static_cast<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
+            data);
+        return 0;
+      });
+
+  volatile_test_cig_create_cmpl_evt_.cig_id = 127;
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+  ASSERT_EQ(evt.status, HCI_SUCCESS);
+
+  // Second call with the same CIG ID should fail
+  ASSERT_EXIT(IsoManager::GetInstance()->CreateCig(
+                  volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams),
+              ::testing::KilledBySignal(SIGABRT), "already exists");
+}
+
+// Check for handling invalid length response from the faulty controller
+TEST_F(IsoManagerDeathTest, CreateCigCallbackInvalidRspPacket) {
+  uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00};
+  ON_CALL(hcic_interface_, SetCigParams)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+
+  ASSERT_EXIT(IsoManager::GetInstance()->CreateCig(128, kDefaultCigParams),
+              ::testing::KilledBySignal(SIGABRT), "Invalid packet length");
+}
+
+// Check for handling invalid length response from the faulty controller
+TEST_F(IsoManagerDeathTest, CreateCigCallbackInvalidRspPacket2) {
+  uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00, 0x02, 0x01, 0x00};
+  ON_CALL(hcic_interface_, SetCigParams)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+
+  ASSERT_EXIT(IsoManager::GetInstance()->CreateCig(128, kDefaultCigParams),
+              ::testing::KilledBySignal(SIGABRT), "Invalid CIS count");
+}
+
+// Check if IsoManager properly handles error responses from HCI layer
+TEST_F(IsoManagerTest, CreateCigCallbackInvalidStatus) {
+  uint8_t rsp_cig_id = 128;
+  uint8_t rsp_status = 0x01;
+  uint8_t rsp_cis_cnt = 3;
+  uint8_t hci_mock_rsp_buffer[] = {rsp_status, rsp_cig_id, rsp_cis_cnt};
+
+  ON_CALL(hcic_interface_, SetCigParams)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+
+  bluetooth::hci::iso_manager::cig_create_cmpl_evt evt;
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnCreateCmpl, _))
+      .WillOnce([&evt](uint8_t type, void* data) {
+        evt = *static_cast<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
+            data);
+        return 0;
+      });
+
+  IsoManager::GetInstance()->CreateCig(rsp_cig_id, kDefaultCigParams);
+  ASSERT_EQ(evt.cig_id, rsp_cig_id);
+  ASSERT_EQ(evt.status, rsp_status);
+  ASSERT_TRUE(evt.conn_handles.empty());
+}
+
+// Check valid callback response
+TEST_F(IsoManagerTest, CreateCigCallbackValid) {
+  bluetooth::hci::iso_manager::cig_create_cmpl_evt evt;
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnCreateCmpl, _))
+      .WillOnce([&evt](uint8_t type, void* data) {
+        evt = *static_cast<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
+            data);
+        return 0;
+      });
+
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+  ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id);
+  ASSERT_EQ(evt.status, volatile_test_cig_create_cmpl_evt_.status);
+  ASSERT_EQ(evt.conn_handles.size(), 2u);
+  ASSERT_TRUE(
+      std::is_permutation(evt.conn_handles.begin(), evt.conn_handles.end(),
+                          std::vector<uint16_t>({0x0EFF, 0x00FF}).begin()));
+}
+
+// Check if CIG reconfigure triggers HCI layer call
+TEST_F(IsoManagerTest, ReconfigureCigHciCall) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  EXPECT_CALL(hcic_interface_,
+              SetCigParams(volatile_test_cig_create_cmpl_evt_.cig_id,
+                           iso_matchers::Eq(kDefaultCigParams), _))
+      .Times(1);
+  IsoManager::GetInstance()->ReconfigureCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+}
+
+// Verify handlidng invalid call - reconfiguring invalid CIG
+TEST_F(IsoManagerDeathTest, ReconfigureCigWithNoSuchCig) {
+  ASSERT_EXIT(IsoManager::GetInstance()->ReconfigureCig(128, kDefaultCigParams),
+              ::testing::KilledBySignal(SIGABRT), "No such cig");
+}
+
+TEST_F(IsoManagerDeathTest, ReconfigureCigInvalidRspPacket) {
+  uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00};
+
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  ON_CALL(hcic_interface_, SetCigParams)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+  ASSERT_EXIT(IsoManager::GetInstance()->ReconfigureCig(
+                  volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams),
+              ::testing::KilledBySignal(SIGABRT), "Invalid packet length");
+}
+
+TEST_F(IsoManagerDeathTest, ReconfigureCigInvalidRspPacket2) {
+  uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00, 0x02, 0x01, 0x00};
+
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  ON_CALL(hcic_interface_, SetCigParams)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+  ASSERT_EXIT(
+      IsoManager::GetInstance()->ReconfigureCig(
+          volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams2),
+      ::testing::KilledBySignal(SIGABRT), "Invalid CIS count");
+}
+
+TEST_F(IsoManagerTest, ReconfigureCigInvalidStatus) {
+  uint8_t rsp_cig_id = 128;
+  uint8_t rsp_status = 0x01;
+  uint8_t rsp_cis_cnt = 3;
+  uint8_t hci_mock_rsp_buffer[] = {rsp_status, rsp_cig_id, rsp_cis_cnt};
+
+  IsoManager::GetInstance()->CreateCig(rsp_cig_id, kDefaultCigParams);
+
+  // Set-up the invalid response
+  ON_CALL(hcic_interface_, SetCigParams)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+
+  bluetooth::hci::iso_manager::cig_create_cmpl_evt evt;
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnReconfigureCmpl, _))
+      .WillOnce([&evt](uint8_t type, void* data) {
+        evt = *static_cast<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
+            data);
+        return 0;
+      });
+  IsoManager::GetInstance()->ReconfigureCig(rsp_cig_id, kDefaultCigParams2);
+
+  ASSERT_EQ(evt.cig_id, rsp_cig_id);
+  ASSERT_EQ(evt.status, rsp_status);
+  ASSERT_TRUE(evt.conn_handles.empty());
+}
+
+TEST_F(IsoManagerTest, ReconfigureCigValid) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cig_create_cmpl_evt evt;
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnReconfigureCmpl, _))
+      .WillOnce([&evt](uint8_t type, void* data) {
+        evt = *static_cast<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
+            data);
+        return 0;
+      });
+
+  // Verify valid reconfiguration request
+  IsoManager::GetInstance()->ReconfigureCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams2);
+  ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id);
+  ASSERT_EQ(evt.status, volatile_test_cig_create_cmpl_evt_.status);
+  ASSERT_TRUE(std::is_permutation(
+      evt.conn_handles.begin(), evt.conn_handles.end(),
+      volatile_test_cig_create_cmpl_evt_.conn_handles.begin()));
+}
+
+TEST_F(IsoManagerTest, RemoveCigHciCall) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  EXPECT_CALL(hcic_interface_,
+              RemoveCig(volatile_test_cig_create_cmpl_evt_.cig_id, _))
+      .Times(1);
+  IsoManager::GetInstance()->RemoveCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id);
+}
+
+TEST_F(IsoManagerDeathTest, RemoveCigWithNoSuchCig) {
+  ASSERT_EXIT(IsoManager::GetInstance()->RemoveCig(
+                  volatile_test_cig_create_cmpl_evt_.cig_id),
+              ::testing::KilledBySignal(SIGABRT), "No such cig");
+}
+
+TEST_F(IsoManagerDeathTest, RemoveSameCigTwice) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  ON_CALL(hcic_interface_, RemoveCig)
+      .WillByDefault(
+          [this](auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            uint8_t hci_mock_rsp_buffer[2];
+            uint8_t* p = hci_mock_rsp_buffer;
+
+            UINT8_TO_STREAM(p, HCI_SUCCESS);
+            UINT8_TO_STREAM(p, this->volatile_test_cig_create_cmpl_evt_.cig_id);
+
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+
+  IsoManager::GetInstance()->RemoveCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id);
+
+  ASSERT_EXIT(IsoManager::GetInstance()->RemoveCig(
+                  volatile_test_cig_create_cmpl_evt_.cig_id),
+              ::testing::KilledBySignal(SIGABRT), "No such cig");
+}
+
+TEST_F(IsoManagerDeathTest, RemoveCigInvalidRspPacket) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  ON_CALL(hcic_interface_, RemoveCig)
+      .WillByDefault([](auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+        uint8_t hci_mock_rsp_buffer[] = {0x00};  // status byte only
+
+        std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+        return 0;
+      });
+  ASSERT_EXIT(IsoManager::GetInstance()->RemoveCig(
+                  volatile_test_cig_create_cmpl_evt_.cig_id),
+              ::testing::KilledBySignal(SIGABRT), "Invalid packet length");
+}
+
+TEST_F(IsoManagerTest, RemoveCigInvalidStatus) {
+  uint8_t rsp_status = 0x02;
+  uint8_t hci_mock_rsp_buffer[] = {rsp_status,
+                                   volatile_test_cig_create_cmpl_evt_.cig_id};
+
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  ON_CALL(hcic_interface_, RemoveCig)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+
+  bluetooth::hci::iso_manager::cig_remove_cmpl_evt evt;
+  ON_CALL(*cig_callbacks_,
+          OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnRemoveCmpl, _))
+      .WillByDefault([&evt](uint8_t type, void* data) {
+        evt = *static_cast<bluetooth::hci::iso_manager::cig_remove_cmpl_evt*>(
+            data);
+        return 0;
+      });
+
+  IsoManager::GetInstance()->RemoveCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id);
+  ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id);
+  ASSERT_EQ(evt.status, rsp_status);
+}
+
+TEST_F(IsoManagerTest, RemoveCigValid) {
+  uint8_t hci_mock_rsp_buffer[] = {HCI_SUCCESS,
+                                   volatile_test_cig_create_cmpl_evt_.cig_id};
+
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  ON_CALL(hcic_interface_, RemoveCig)
+      .WillByDefault(
+          [&hci_mock_rsp_buffer](
+              auto, base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+            std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer));
+            return 0;
+          });
+
+  bluetooth::hci::iso_manager::cig_remove_cmpl_evt evt;
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnRemoveCmpl, _))
+      .WillOnce([&evt](uint8_t type, void* data) {
+        evt = *static_cast<bluetooth::hci::iso_manager::cig_remove_cmpl_evt*>(
+            data);
+        return 0;
+      });
+
+  IsoManager::GetInstance()->RemoveCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id);
+  ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id);
+  ASSERT_EQ(evt.status, HCI_SUCCESS);
+}
+
+TEST_F(IsoManagerTest, EstablishCisHciCall) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+
+  EXPECT_CALL(hcic_interface_,
+              CreateCis(2, iso_matchers::EqPointedArray(
+                               params.conn_pairs.data(),
+                               params.conn_pairs.size() *
+                                   sizeof(params.conn_pairs.data()[0]))))
+      .Times(1);
+  IsoManager::GetInstance()->EstablishCis(params);
+}
+
+TEST_F(IsoManagerDeathTest, EstablishCisWithNoSuchCis) {
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+
+  ASSERT_EXIT(
+      IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis(
+          params),
+      ::testing::KilledBySignal(SIGABRT), "No such cis");
+}
+
+TEST_F(IsoManagerDeathTest, ConnectSameCisTwice) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  ASSERT_EXIT(
+      IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis(
+          params),
+      ::testing::KilledBySignal(SIGABRT), "Already connected");
+}
+
+TEST_F(IsoManagerDeathTest, EstablishCisInvalidResponsePacket) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  ON_CALL(hcic_interface_, CreateCis)
+      .WillByDefault([this](uint8_t num_cis,
+                            const EXT_CIS_CREATE_CFG* cis_cfg) {
+        for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+          std::vector<uint8_t> buf(27);
+          uint8_t* p = buf.data();
+          UINT8_TO_STREAM(p, HCI_SUCCESS);
+          UINT16_TO_STREAM(p, handle);
+          UINT24_TO_STREAM(p, 0xEA);    // CIG sync delay
+          UINT24_TO_STREAM(p, 0xEB);    // CIS sync delay
+          UINT24_TO_STREAM(p, 0xEC);    // transport latency mtos
+          UINT24_TO_STREAM(p, 0xED);    // transport latency stom
+          UINT8_TO_STREAM(p, 0x01);     // phy mtos
+          UINT8_TO_STREAM(p, 0x02);     // phy stom
+          UINT8_TO_STREAM(p, 0x01);     // nse
+          UINT8_TO_STREAM(p, 0x02);     // bn mtos
+          UINT8_TO_STREAM(p, 0x03);     // bn stom
+          UINT8_TO_STREAM(p, 0x04);     // ft mtos
+          UINT8_TO_STREAM(p, 0x05);     // ft stom
+          UINT16_TO_STREAM(p, 0x00FA);  // Max PDU mtos
+          UINT16_TO_STREAM(p, 0x00FB);  // Max PDU stom
+
+          IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT,
+                                                    buf.data(), buf.size());
+        }
+      });
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+
+  ASSERT_EXIT(
+      IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis(
+          params),
+      ::testing::KilledBySignal(SIGABRT), "Invalid packet length");
+}
+
+TEST_F(IsoManagerTest, EstablishCisInvalidStatus) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+  uint8_t invalid_status = 0x01;
+
+  ON_CALL(hcic_interface_, CreateCis)
+      .WillByDefault([this, invalid_status](uint8_t num_cis,
+                                            const EXT_CIS_CREATE_CFG* cis_cfg) {
+        for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+          std::vector<uint8_t> buf(28);
+          uint8_t* p = buf.data();
+          UINT8_TO_STREAM(p, invalid_status);
+          UINT16_TO_STREAM(p, handle);
+          UINT24_TO_STREAM(p, 0xEA);    // CIG sync delay
+          UINT24_TO_STREAM(p, 0xEB);    // CIS sync delay
+          UINT24_TO_STREAM(p, 0xEC);    // transport latency mtos
+          UINT24_TO_STREAM(p, 0xED);    // transport latency stom
+          UINT8_TO_STREAM(p, 0x01);     // phy mtos
+          UINT8_TO_STREAM(p, 0x02);     // phy stom
+          UINT8_TO_STREAM(p, 0x01);     // nse
+          UINT8_TO_STREAM(p, 0x02);     // bn mtos
+          UINT8_TO_STREAM(p, 0x03);     // bn stom
+          UINT8_TO_STREAM(p, 0x04);     // ft mtos
+          UINT8_TO_STREAM(p, 0x05);     // ft stom
+          UINT16_TO_STREAM(p, 0x00FA);  // Max PDU mtos
+          UINT16_TO_STREAM(p, 0x00FB);  // Max PDU stom
+          UINT16_TO_STREAM(p, 0x0C60);  // ISO interval
+
+          IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT,
+                                                    buf.data(), buf.size());
+        }
+      });
+
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _))
+      .Times(kDefaultCigParams.cis_cfgs.size())
+      .WillRepeatedly([this, invalid_status](uint8_t type, void* data) {
+        bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt =
+            static_cast<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
+                data);
+
+        ASSERT_EQ(evt->status, invalid_status);
+        ASSERT_TRUE(
+            std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(),
+                      volatile_test_cig_create_cmpl_evt_.conn_handles.end(),
+                      evt->cis_conn_hdl) !=
+            volatile_test_cig_create_cmpl_evt_.conn_handles.end());
+      });
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+}
+
+TEST_F(IsoManagerTest, EstablishCisValid) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _))
+      .Times(kDefaultCigParams.cis_cfgs.size())
+      .WillRepeatedly([this](uint8_t type, void* data) {
+        bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt =
+            static_cast<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
+                data);
+
+        ASSERT_EQ(evt->status, HCI_SUCCESS);
+        ASSERT_TRUE(
+            std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(),
+                      volatile_test_cig_create_cmpl_evt_.conn_handles.end(),
+                      evt->cis_conn_hdl) !=
+            volatile_test_cig_create_cmpl_evt_.conn_handles.end());
+      });
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+}
+
+TEST_F(IsoManagerTest, ReconnectCisValid) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  // trigger HCI disconnection event
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    IsoManager::GetInstance()->HandleDisconnect(handle, 0x16);
+  }
+
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _))
+      .Times(kDefaultCigParams.cis_cfgs.size())
+      .WillRepeatedly([this](uint8_t type, void* data) {
+        bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt =
+            static_cast<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
+                data);
+
+        ASSERT_EQ(evt->status, HCI_SUCCESS);
+        ASSERT_TRUE(
+            std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(),
+                      volatile_test_cig_create_cmpl_evt_.conn_handles.end(),
+                      evt->cis_conn_hdl) !=
+            volatile_test_cig_create_cmpl_evt_.conn_handles.end());
+      });
+  IsoManager::GetInstance()->EstablishCis(params);
+}
+
+TEST_F(IsoManagerTest, DisconnectCisHciCall) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    EXPECT_CALL(hcic_interface_, Disconnect(handle, 0x16))
+        .Times(1)
+        .RetiresOnSaturation();
+    IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(handle,
+                                                                        0x16);
+  }
+}
+
+TEST_F(IsoManagerDeathTest, DisconnectCisWithNoSuchCis) {
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    ASSERT_EXIT(
+        IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(
+            handle, 0x16),
+        ::testing::KilledBySignal(SIGABRT), "No such cis");
+  }
+}
+
+TEST_F(IsoManagerDeathTest, DisconnectSameCisTwice) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(handle,
+                                                                        0x16);
+  }
+
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    ASSERT_EXIT(
+        IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(
+            handle, 0x16),
+        ::testing::KilledBySignal(SIGABRT), "Not connected");
+  }
+}
+
+TEST_F(IsoManagerTest, DisconnectCisValid) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  uint8_t disconnect_reason = 0x16;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    EXPECT_CALL(*cig_callbacks_, OnCisEvent)
+        .WillOnce([this, handle, disconnect_reason](uint8_t event_code,
+                                                    void* data) {
+          ASSERT_EQ(event_code,
+                    bluetooth::hci::iso_manager::kIsoEventCisDisconnected);
+          auto* event =
+              static_cast<bluetooth::hci::iso_manager::cis_disconnected_evt*>(
+                  data);
+          ASSERT_EQ(event->reason, disconnect_reason);
+          ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id);
+          ASSERT_EQ(event->cis_conn_hdl, handle);
+        })
+        .RetiresOnSaturation();
+    IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(
+        handle, disconnect_reason);
+  }
+}
+
+// Check if we properly ignore not ISO related disconnect events
+TEST_F(IsoManagerDeathTest, DisconnectCisInvalidResponse) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  // Make the HCI layer send invalid handles in disconnect event
+  ON_CALL(hcic_interface_, Disconnect)
+      .WillByDefault([](uint16_t handle, uint8_t reason) {
+        IsoManager::GetInstance()->HandleDisconnect(handle + 1, reason);
+      });
+
+  // We don't expect any calls as these are not ISO handles
+  ON_CALL(*cig_callbacks_,
+          OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDisconnected, _))
+      .WillByDefault([](uint8_t event_code, void* data) { FAIL(); });
+
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(handle,
+                                                                        0x16);
+  }
+}
+
+TEST_F(IsoManagerTest, SetupIsoDataPathValid) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  bluetooth::hci::iso_manager::iso_data_path_params path_params =
+      kDefaultIsoDataPathParams;
+
+  // Setup data paths for all CISes
+  path_params.data_path_dir =
+      bluetooth::hci::iso_manager::kIsoDataPathDirectionIn;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    EXPECT_CALL(*cig_callbacks_,
+                OnSetupIsoDataPath(HCI_SUCCESS, handle,
+                                   volatile_test_cig_create_cmpl_evt_.cig_id))
+        .Times(1)
+        .RetiresOnSaturation();
+
+    path_params.data_path_dir =
+        (bluetooth::hci::iso_manager::kIsoDataPathDirectionIn + handle) % 2;
+
+    IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params);
+  }
+}
+
+TEST_F(IsoManagerTest, SetupIsoDataPathTwice) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish CISes
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  // Setup data paths for all CISes twice
+  bluetooth::hci::iso_manager::iso_data_path_params path_params =
+      kDefaultIsoDataPathParams;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params);
+    // Should be possible to reconfigure
+    IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params);
+  }
+}
+
+TEST_F(IsoManagerTest, SetupIsoDataPathInvalidStatus) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  bluetooth::hci::iso_manager::iso_data_path_params path_params =
+      kDefaultIsoDataPathParams;
+
+  uint8_t setup_datapath_rsp_status = HCI_SUCCESS;
+  ON_CALL(hcic_interface_, SetupIsoDataPath)
+      .WillByDefault([&setup_datapath_rsp_status](
+                         uint16_t iso_handle, uint8_t, uint8_t, uint8_t,
+                         uint16_t, uint16_t, uint32_t, std::vector<uint8_t>,
+                         base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+        std::vector<uint8_t> buf(3);
+        uint8_t* p = buf.data();
+        UINT8_TO_STREAM(p, setup_datapath_rsp_status);
+        UINT16_TO_STREAM(p, iso_handle);
+
+        std::move(cb).Run(buf.data(), buf.size());
+      });
+
+  // Try to setup data paths for all CISes
+  path_params.data_path_dir =
+      bluetooth::hci::iso_manager::kIsoDataPathDirectionIn;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    // Mock the response with status != HCI_SUCCESS
+    EXPECT_CALL(*cig_callbacks_,
+                OnSetupIsoDataPath(0x11, handle,
+                                   volatile_test_cig_create_cmpl_evt_.cig_id))
+        .Times(1)
+        .RetiresOnSaturation();
+    setup_datapath_rsp_status = 0x11;
+    IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params);
+
+    // It should be possible to retry on the same handle after the first
+    // failure
+    EXPECT_CALL(*cig_callbacks_,
+                OnSetupIsoDataPath(HCI_SUCCESS, handle,
+                                   volatile_test_cig_create_cmpl_evt_.cig_id))
+        .Times(1)
+        .RetiresOnSaturation();
+    setup_datapath_rsp_status = HCI_SUCCESS;
+    IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params);
+  }
+}
+
+TEST_F(IsoManagerTest, RemoveIsoDataPathValid) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  // Establish all CISes before setting up their data paths
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  bluetooth::hci::iso_manager::iso_data_path_params path_params =
+      kDefaultIsoDataPathParams;
+
+  // Setup and remove data paths for all CISes
+  path_params.data_path_dir =
+      bluetooth::hci::iso_manager::kIsoDataPathDirectionIn;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params);
+
+    EXPECT_CALL(*cig_callbacks_,
+                OnRemoveIsoDataPath(HCI_SUCCESS, handle,
+                                    volatile_test_cig_create_cmpl_evt_.cig_id))
+        .Times(1)
+        .RetiresOnSaturation();
+    IsoManager::GetInstance()->RemoveIsoDataPath(handle,
+                                                 path_params.data_path_dir);
+  }
+}
+
+TEST_F(IsoManagerDeathTest, RemoveIsoDataPathNoSuchPath) {
+  // Check on CIS
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+  uint16_t iso_handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
+  ASSERT_EXIT(
+      IsoManager::GetInstance()->RemoveIsoDataPath(
+          iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut),
+      ::testing::KilledBySignal(SIGABRT), "path not set");
+
+  IsoManager::GetInstance()->EstablishCis({.conn_pairs = {{iso_handle, 1}}});
+  ASSERT_EXIT(
+      IsoManager::GetInstance()->RemoveIsoDataPath(
+          iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut),
+      ::testing::KilledBySignal(SIGABRT), "path not set");
+}
+
+TEST_F(IsoManagerDeathTest, RemoveIsoDataPathTwice) {
+  // Check on CIS
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+  uint16_t iso_handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
+  IsoManager::GetInstance()->EstablishCis({.conn_pairs = {{iso_handle, 1}}});
+  IsoManager::GetInstance()->SetupIsoDataPath(iso_handle,
+                                              kDefaultIsoDataPathParams);
+  IsoManager::GetInstance()->RemoveIsoDataPath(
+      iso_handle, kDefaultIsoDataPathParams.data_path_dir);
+  ASSERT_EXIT(
+      IsoManager::GetInstance()->RemoveIsoDataPath(
+          iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut),
+      ::testing::KilledBySignal(SIGABRT), "path not set");
+}
+
+// Check if HCI status other than HCI_SUCCESS is being propagated to the caller
+TEST_F(IsoManagerTest, RemoveIsoDataPathInvalidStatus) {
+  // Mock invalid status response
+  uint8_t remove_datapath_rsp_status = 0x12;
+  ON_CALL(hcic_interface_, RemoveIsoDataPath)
+      .WillByDefault([&remove_datapath_rsp_status](
+                         uint16_t iso_handle, uint8_t data_path_dir,
+                         base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+        std::vector<uint8_t> buf(3);
+        uint8_t* p = buf.data();
+        UINT8_TO_STREAM(p, remove_datapath_rsp_status);
+        UINT16_TO_STREAM(p, iso_handle);
+
+        std::move(cb).Run(buf.data(), buf.size());
+      });
+
+  // Check on CIS
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+  uint16_t iso_handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
+  IsoManager::GetInstance()->EstablishCis({.conn_pairs = {{iso_handle, 1}}});
+  IsoManager::GetInstance()->SetupIsoDataPath(iso_handle,
+                                              kDefaultIsoDataPathParams);
+
+  EXPECT_CALL(*cig_callbacks_,
+              OnRemoveIsoDataPath(remove_datapath_rsp_status, iso_handle,
+                                  volatile_test_cig_create_cmpl_evt_.cig_id))
+      .Times(1);
+  IsoManager::GetInstance()->RemoveIsoDataPath(
+      iso_handle, kDefaultIsoDataPathParams.data_path_dir);
+}
+
+TEST_F(IsoManagerTest, SendIsoDataCigValid) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    bluetooth::hci::iso_manager::iso_data_path_params path_params =
+        kDefaultIsoDataPathParams;
+    path_params.data_path_dir =
+        bluetooth::hci::iso_manager::kIsoDataPathDirectionOut;
+    IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params);
+
+    for (uint8_t num_pkts = 2; num_pkts != 0; num_pkts--) {
+      constexpr uint8_t data_len = 108;
+
+      EXPECT_CALL(bte_interface_, HciSend)
+          .WillOnce([handle, data_len](BT_HDR* p_msg, uint16_t event) {
+            uint8_t* p = p_msg->data;
+            uint16_t msg_handle;
+            uint16_t iso_load_len;
+
+            ASSERT_TRUE((event & MSG_STACK_TO_HC_HCI_ISO) != 0);
+            ASSERT_NE(p_msg, nullptr);
+            ASSERT_EQ(p_msg->len, data_len + ((p_msg->layer_specific &
+                                               BT_ISO_HDR_CONTAINS_TS)
+                                                  ? 12
+                                                  : 8));
+
+            // Verify packet internals
+            STREAM_TO_UINT16(msg_handle, p);
+            ASSERT_EQ(msg_handle, handle);
+
+            STREAM_TO_UINT16(iso_load_len, p);
+            ASSERT_EQ(
+                iso_load_len,
+                data_len +
+                    ((p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) ? 8 : 4));
+
+            if (p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) {
+              STREAM_SKIP_UINT16(p);  // skip ts LSB halfword
+              STREAM_SKIP_UINT16(p);  // skip ts MSB halfword
+            }
+            STREAM_SKIP_UINT16(p);  // skip seq_nb
+
+            uint16_t msg_data_len;
+            STREAM_TO_UINT16(msg_data_len, p);
+            ASSERT_EQ(msg_data_len, data_len);
+          })
+          .RetiresOnSaturation();
+
+      std::vector<uint8_t> data_vec(data_len, 0);
+      IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(),
+                                             data_vec.size());
+    }
+  }
+}
+
+TEST_F(IsoManagerTest, SendIsoDataNoCredits) {
+  uint8_t num_buffers = controller_interface_.GetIsoBufferCount();
+  std::vector<uint8_t> data_vec(108, 0);
+
+  // Check on CIG
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  IsoManager::GetInstance()->SetupIsoDataPath(
+      volatile_test_cig_create_cmpl_evt_.conn_handles[0],
+      kDefaultIsoDataPathParams);
+
+  /* Try sending twice as much data as we can ignoring the credit limits and
+   * expect the redundant packets to be ignored and not propagated down to the
+   * HCI.
+   */
+  EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers);
+  for (uint8_t i = 0; i < (2 * num_buffers); i++) {
+    IsoManager::GetInstance()->SendIsoData(
+        volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(),
+        data_vec.size());
+  }
+}
+
+TEST_F(IsoManagerTest, SendIsoDataCreditsReturned) {
+  uint8_t num_buffers = controller_interface_.GetIsoBufferCount();
+  std::vector<uint8_t> data_vec(108, 0);
+
+  // Check on CIG
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  IsoManager::GetInstance()->SetupIsoDataPath(
+      volatile_test_cig_create_cmpl_evt_.conn_handles[0],
+      kDefaultIsoDataPathParams);
+
+  /* Try sending twice as much data as we can, ignoring the credits limit and
+   * expect the redundant packets to be ignored and not propagated down to the
+   * HCI.
+   */
+  EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers);
+  for (uint8_t i = 0; i < (2 * num_buffers); i++) {
+    IsoManager::GetInstance()->SendIsoData(
+        volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(),
+        data_vec.size());
+  }
+
+  // Return all credits for this one handle
+  uint8_t mock_rsp[5];
+  uint8_t* p = mock_rsp;
+  UINT8_TO_STREAM(p, 1);
+  UINT16_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles[0]);
+  UINT16_TO_STREAM(p, num_buffers);
+  IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp));
+
+  // Expect some more events go down the HCI
+  EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers);
+  for (uint8_t i = 0; i < (2 * num_buffers); i++) {
+    IsoManager::GetInstance()->SendIsoData(
+        volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(),
+        data_vec.size());
+  }
+}
+
+TEST_F(IsoManagerDeathTest, SendIsoDataWithNoDataPath) {
+  std::vector<uint8_t> data_vec(108, 0);
+
+  // Check on CIG
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& conn_handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({conn_handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  ASSERT_EXIT(IsoManager::GetInstance()->SendIsoData(
+                  volatile_test_cig_create_cmpl_evt_.conn_handles[0],
+                  data_vec.data(), data_vec.size()),
+              ::testing::KilledBySignal(SIGABRT), "Data path not set");
+}
+
+TEST_F(IsoManagerDeathTest, SendIsoDataWithNoCigHandle) {
+  std::vector<uint8_t> data_vec(108, 0);
+  ASSERT_EXIT(IsoManager::GetInstance()->SendIsoData(134, data_vec.data(),
+                                                     data_vec.size()),
+              ::testing::KilledBySignal(SIGABRT), "No such iso");
+}
+
+TEST_F(IsoManagerDeathTest, SendIsoDataWithNoCigConnected) {
+  std::vector<uint8_t> data_vec(108, 0);
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
+  ASSERT_EXIT(IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(),
+                                                     data_vec.size()),
+              ::testing::KilledBySignal(SIGABRT), "CIS not established");
+}
+
+TEST_F(IsoManagerTest, HandleDisconnectNoSuchHandle) {
+  // Don't expect any callbacks when connection handle is not for ISO.
+  EXPECT_CALL(*cig_callbacks_, OnCigEvent).Times(0);
+  EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0);
+
+  IsoManager::GetInstance()->HandleDisconnect(123, 16);
+}
+
+TEST_F(IsoManagerTest, HandleDisconnectValidCig) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
+  IsoManager::GetInstance()->EstablishCis({{{handle, 1}}});
+
+  EXPECT_CALL(*cig_callbacks_, OnCigEvent).Times(0);
+  EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0);
+
+  // Expect disconnect event exactly once
+  EXPECT_CALL(*cig_callbacks_, OnCisEvent)
+      .WillOnce([this, handle](uint8_t event_code, void* data) {
+        ASSERT_EQ(event_code,
+                  bluetooth::hci::iso_manager::kIsoEventCisDisconnected);
+        auto* event =
+            static_cast<bluetooth::hci::iso_manager::cis_disconnected_evt*>(
+                data);
+        ASSERT_EQ(event->reason, 16);
+        ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id);
+        ASSERT_EQ(event->cis_conn_hdl, handle);
+      });
+
+  IsoManager::GetInstance()->HandleDisconnect(handle, 16);
+}
+
+TEST_F(IsoManagerTest, HandleDisconnectDisconnectedCig) {
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
+  IsoManager::GetInstance()->EstablishCis({{{handle, 1}}});
+
+  EXPECT_CALL(*cig_callbacks_, OnCigEvent).Times(0);
+  EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0);
+
+  // Expect disconnect event exactly once
+  EXPECT_CALL(
+      *cig_callbacks_,
+      OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDisconnected, _))
+      .Times(1)
+      .RetiresOnSaturation();
+  IsoManager::GetInstance()->HandleDisconnect(handle, 16);
+
+  // This one was once connected - expect no events
+  IsoManager::GetInstance()->HandleDisconnect(handle, 16);
+
+  // This one was never connected - expect no events
+  handle = volatile_test_cig_create_cmpl_evt_.conn_handles[1];
+  IsoManager::GetInstance()->HandleDisconnect(handle, 16);
+}
diff --git a/stack/test/common/mock_controller.cc b/stack/test/common/mock_controller.cc
new file mode 100644 (file)
index 0000000..253d4b5
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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_controller.h"
+
+#include "device/include/controller.h"
+
+static controller::MockControllerInterface* controller_interface = nullptr;
+
+void controller::SetMockControllerInterface(
+    MockControllerInterface* interface) {
+  controller_interface = interface;
+}
+
+uint16_t get_iso_data_size(void) {
+  return controller_interface->GetIsoDataSize();
+}
+
+uint8_t get_iso_buffer_count(void) {
+  return controller_interface->GetIsoBufferCount();
+}
+
+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;
+
+  return controller_instance;
+}
diff --git a/stack/test/common/mock_controller.h b/stack/test/common/mock_controller.h
new file mode 100644 (file)
index 0000000..a04bbc8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 <base/callback.h>
+#include <gmock/gmock.h>
+
+#include "hcimsgs.h"
+
+namespace controller {
+class ControllerInterface {
+ public:
+  virtual uint8_t GetIsoBufferCount(void) = 0;
+  virtual uint16_t GetIsoDataSize(void) = 0;
+
+  virtual ~ControllerInterface() = default;
+};
+
+class MockControllerInterface : public ControllerInterface {
+ public:
+  MOCK_METHOD((uint8_t), GetIsoBufferCount, (), (override));
+  MOCK_METHOD((uint16_t), GetIsoDataSize, (), (override));
+};
+
+void SetMockControllerInterface(
+    MockControllerInterface* mock_controller_interface);
+}  // namespace controller
diff --git a/stack/test/common/mock_hcic_layer.cc b/stack/test/common/mock_hcic_layer.cc
new file mode 100644 (file)
index 0000000..4cdd418
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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_hcic_layer.h"
+
+static hcic::MockHcicInterface* hcic_interface = nullptr;
+
+void hcic::SetMockHcicInterface(MockHcicInterface* interface) {
+  hcic_interface = interface;
+}
+
+void btsnd_hcic_set_cig_params(
+    uint8_t cig_id, uint32_t sdu_itv_mtos, uint32_t sdu_itv_stom, uint8_t sca,
+    uint8_t packing, uint8_t framing, uint16_t max_trans_lat_stom,
+    uint16_t max_trans_lat_mtos, uint8_t cis_cnt, const EXT_CIS_CFG* cis_cfg,
+    base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+  struct bluetooth::hci::iso_manager::cig_create_params cig_params = {
+      .sdu_itv_mtos = sdu_itv_mtos,
+      .sdu_itv_stom = sdu_itv_stom,
+      .sca = sca,
+      .packing = packing,
+      .framing = framing,
+      .max_trans_lat_stom = max_trans_lat_stom,
+      .max_trans_lat_mtos = max_trans_lat_mtos,
+      .cis_cfgs = std::vector(cis_cfg, cis_cfg + cis_cnt),
+  };
+  hcic_interface->SetCigParams(cig_id, std::move(cig_params), std::move(cb));
+}
+
+void btsnd_hcic_remove_cig(uint8_t cig_id,
+                           base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+  hcic_interface->RemoveCig(cig_id, std::move(cb));
+}
+
+void btsnd_hcic_create_cis(uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg) {
+  hcic_interface->CreateCis(num_cis, cis_cfg);
+}
+
+void btsnd_hcic_disconnect(uint16_t handle, uint8_t reason) {
+  hcic_interface->Disconnect(handle, reason);
+}
+
+void btsnd_hcic_setup_iso_data_path(
+    uint16_t iso_handle, uint8_t data_path_dir, uint8_t data_path_id,
+    uint8_t codec_id_format, uint16_t codec_id_company,
+    uint16_t codec_id_vendor, uint32_t controller_delay,
+    std::vector<uint8_t> codec_conf,
+    base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+  hcic_interface->SetupIsoDataPath(iso_handle, data_path_dir, data_path_id,
+                                   codec_id_format, codec_id_company,
+                                   codec_id_vendor, controller_delay,
+                                   std::move(codec_conf), std::move(cb));
+}
+
+void btsnd_hcic_remove_iso_data_path(
+    uint16_t iso_handle, uint8_t data_path_dir,
+    base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
+  hcic_interface->RemoveIsoDataPath(iso_handle, data_path_dir, std::move(cb));
+}
diff --git a/stack/test/common/mock_hcic_layer.h b/stack/test/common/mock_hcic_layer.h
new file mode 100644 (file)
index 0000000..2e7ad0b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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 <base/callback.h>
+#include <gmock/gmock.h>
+
+#include <array>
+
+#include "btm_iso_api_types.h"
+#include "hcimsgs.h"
+
+namespace hcic {
+class HcicInterface {
+ public:
+  // iso_manager::cig_create_params is a workaround for the 10 params function
+  // limitation that gmock sets
+  virtual void SetCigParams(
+      uint8_t cig_id,
+      struct bluetooth::hci::iso_manager::cig_create_params cig_params,
+      base::OnceCallback<void(uint8_t*, uint16_t)> cb) = 0;
+
+  virtual void RemoveCig(uint8_t cig_id,
+                         base::OnceCallback<void(uint8_t*, uint16_t)> cb) = 0;
+
+  virtual void CreateCis(uint8_t num_cis,
+                         const EXT_CIS_CREATE_CFG* cis_create_cfg) = 0;
+
+  virtual void Disconnect(uint16_t handle, uint8_t reason) = 0;
+
+  virtual void SetupIsoDataPath(
+      uint16_t iso_handle, uint8_t data_path_dir, uint8_t data_path_id,
+      uint8_t codec_id_format, uint16_t codec_id_company,
+      uint16_t codec_id_vendor, uint32_t controller_delay,
+      std::vector<uint8_t> codec_conf,
+      base::OnceCallback<void(uint8_t*, uint16_t)> cb) = 0;
+
+  virtual void RemoveIsoDataPath(
+      uint16_t iso_handle, uint8_t data_path_dir,
+      base::OnceCallback<void(uint8_t*, uint16_t)> cb) = 0;
+
+  virtual ~HcicInterface() = default;
+};
+
+class MockHcicInterface : public HcicInterface {
+ public:
+  MOCK_METHOD((void), SetCigParams,
+              (uint8_t cig_id,
+               struct bluetooth::hci::iso_manager::cig_create_params cig_params,
+               base::OnceCallback<void(uint8_t*, uint16_t)> cb),
+              (override));
+
+  MOCK_METHOD((void), RemoveCig,
+              (uint8_t cig_id, base::OnceCallback<void(uint8_t*, uint16_t)> cb),
+              (override));
+
+  MOCK_METHOD((void), CreateCis,
+              (uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_create_cfg),
+              (override));
+
+  MOCK_METHOD((void), Disconnect, (uint16_t handle, uint8_t reason),
+              (override));
+
+  MOCK_METHOD((void), SetupIsoDataPath,
+              (uint16_t iso_handle, uint8_t data_path_dir, uint8_t data_path_id,
+               uint8_t codec_id_format, uint16_t codec_id_company,
+               uint16_t codec_id_vendor, uint32_t controller_delay,
+               std::vector<uint8_t> codec_conf,
+               base::OnceCallback<void(uint8_t*, uint16_t)> cb),
+              (override));
+
+  MOCK_METHOD((void), RemoveIsoDataPath,
+              (uint16_t iso_handle, uint8_t data_path_dir,
+               base::OnceCallback<void(uint8_t*, uint16_t)> cb),
+              (override));
+};
+
+void SetMockHcicInterface(MockHcicInterface* mock_hcic_interface);
+
+}  // namespace hcic
index 992ca31..4a17e23 100755 (executable)
@@ -26,6 +26,7 @@ HOST_TESTS = [
     'net_test_avrcp',
     'net_test_btcore',
     'net_test_types',
+    'net_test_btm_iso',
     'net_test_btpackets',
 ]