MCAP: Add a test tool for MCAP

* Add a command line tool for MCAP related tests
* This tool can access the Bluetooth stack via a command line program
* Type "help" to obtain a list of available console commands in this
  Bluetooth
* Bluetooth must be disabled when this tool is running

Bug: 37867299
Test: make, PTS MCAP test
Change-Id: I106e6625ee8a29b7fdad88cdfeb99419054ef771
diff --git a/Android.bp b/Android.bp
index 69ed54b..dae6460 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,4 +15,5 @@
     "vendor_libs",
     "test",
     "udrv",
+    "tools",
 ]
diff --git a/BUILD.gn b/BUILD.gn
index 3cb29e1..618de1c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -39,3 +39,10 @@
     "//device:net_test_device",
   ]
 }
+
+group("test_tools") {
+  testonly = true
+  deps = [
+    "//tools/mcap_tool:mcap_tool"
+  ]
+}
diff --git a/tools/Android.bp b/tools/Android.bp
new file mode 100644
index 0000000..1357a93
--- /dev/null
+++ b/tools/Android.bp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+subdirs = [
+    "mcap_tool",
+]
diff --git a/tools/mcap_tool/Android.bp b/tools/mcap_tool/Android.bp
new file mode 100644
index 0000000..aca07dd
--- /dev/null
+++ b/tools/mcap_tool/Android.bp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+cc_binary {
+    name: "mcap_tool",
+    defaults : ["fluoride_defaults"],
+    srcs: [
+      "mcap_test_app.cc",
+      "mcap_test_mcl.cc",
+      "mcap_test_mdep.cc",
+      "mcap_test_mdl.cc",
+      "mcap_tool.cc",
+    ],
+    include_dirs: [
+      "system/bt",
+      "system/bt/include",
+      "system/bt/stack/include",
+      "system/bt/btcore/include",
+    ],
+    tags: ["debug", "optional"],
+    shared_libs: [
+      "libcutils",
+      "libutils",
+      "libhardware",
+    ],
+    static_libs: [
+      "libbtcore",
+      "libosi",
+    ]
+}
diff --git a/tools/mcap_tool/BUILD.gn b/tools/mcap_tool/BUILD.gn
new file mode 100644
index 0000000..a5fef31
--- /dev/null
+++ b/tools/mcap_tool/BUILD.gn
@@ -0,0 +1,43 @@
+#
+#  Copyright (C) 2017 Google, Inc.
+#
+#  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.
+#
+executable("mcap_tool") {
+  testonly = true
+  sources = [
+    "mcap_tool.cc",
+    "mcap_test_app.cc",
+    "mcap_test_mcl.cc",
+    "mcap_test_mdep.cc",
+    "mcap_test_mdl.cc",
+  ]
+  include_dirs = [
+    "//",
+    "//include",
+    "//stack/include",
+    "//btif/include",
+    "//btcore/include",
+    "//tools/mcap_tool",
+  ]
+  libs = [
+    "-lpthread",
+    "-lrt",
+    "-ldl",
+  ]
+  deps = [
+    "//btcore",
+    "//osi",
+    "//third_party/libchrome:base",
+  ]
+}
diff --git a/tools/mcap_tool/mcap_test_app.cc b/tools/mcap_tool/mcap_test_app.cc
new file mode 100644
index 0000000..29e50dc
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_app.cc
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "mcap_test_app.h"
+#include "mca_defs.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+#define CASE_RETURN_STR(const) \
+  case const:                  \
+    return #const;
+
+static const char* dump_mcap_events(const uint8_t event) {
+  switch (event) {
+    CASE_RETURN_STR(MCA_ERROR_RSP_EVT)
+    CASE_RETURN_STR(MCA_CREATE_IND_EVT)
+    CASE_RETURN_STR(MCA_CREATE_CFM_EVT)
+    CASE_RETURN_STR(MCA_RECONNECT_IND_EVT)
+    CASE_RETURN_STR(MCA_RECONNECT_CFM_EVT)
+    CASE_RETURN_STR(MCA_ABORT_IND_EVT)
+    CASE_RETURN_STR(MCA_ABORT_CFM_EVT)
+    CASE_RETURN_STR(MCA_DELETE_IND_EVT)
+    CASE_RETURN_STR(MCA_DELETE_CFM_EVT)
+    CASE_RETURN_STR(MCA_SYNC_CAP_IND_EVT)
+    CASE_RETURN_STR(MCA_SYNC_CAP_CFM_EVT)
+    CASE_RETURN_STR(MCA_SYNC_SET_IND_EVT)
+    CASE_RETURN_STR(MCA_SYNC_SET_CFM_EVT)
+    CASE_RETURN_STR(MCA_SYNC_INFO_IND_EVT)
+    CASE_RETURN_STR(MCA_CONNECT_IND_EVT)
+    CASE_RETURN_STR(MCA_DISCONNECT_IND_EVT)
+    CASE_RETURN_STR(MCA_OPEN_IND_EVT)
+    CASE_RETURN_STR(MCA_OPEN_CFM_EVT)
+    CASE_RETURN_STR(MCA_CLOSE_IND_EVT)
+    CASE_RETURN_STR(MCA_CLOSE_CFM_EVT)
+    CASE_RETURN_STR(MCA_CONG_CHG_EVT)
+    CASE_RETURN_STR(MCA_RSP_TOUT_IND_EVT)
+    default:
+      return "Unknown event";
+  }
+}
+
+static void print_mcap_event(const tMCA_DISCONNECT_IND* mcap_disconnect_ind) {
+  bdstr_t bd_addr_str;
+  printf("%s: peer_bd_addr=%s,l2cap_disconnect_reason=0x%04x\n", __func__,
+         bdaddr_to_string((bt_bdaddr_t*)mcap_disconnect_ind->bd_addr,
+                          bd_addr_str, sizeof(bd_addr_str)),
+         mcap_disconnect_ind->reason);
+}
+
+static void print_mcap_event(const tMCA_CONNECT_IND* mcap_connect_ind) {
+  bdstr_t bd_addr_str;
+  printf("%s: peer_bd_addr=%s, peer_mtu=%d \n", __func__,
+         bdaddr_to_string((bt_bdaddr_t*)mcap_connect_ind->bd_addr, bd_addr_str,
+                          sizeof(bd_addr_str)),
+         mcap_connect_ind->mtu);
+}
+
+static void print_mcap_event(const tMCA_RSP_EVT* mcap_rsp) {
+  printf("%s: response, mdl_id=%d, op_code=0x%02x, rsp_code=0x%02x\n", __func__,
+         mcap_rsp->mdl_id, mcap_rsp->op_code, mcap_rsp->rsp_code);
+}
+
+static void print_mcap_event(const tMCA_EVT_HDR* mcap_evt_hdr) {
+  printf("%s: event, mdl_id=%d, op_code=0x%02x\n", __func__,
+         mcap_evt_hdr->mdl_id, mcap_evt_hdr->op_code);
+}
+
+static void print_mcap_event(const tMCA_CREATE_IND* mcap_create_ind) {
+  printf("%s: mdl_id=%d, op_code=0x%02x, dep_id=%d, cfg=0x%02x\n", __func__,
+         mcap_create_ind->mdl_id, mcap_create_ind->op_code,
+         mcap_create_ind->dep_id, mcap_create_ind->cfg);
+}
+
+static void print_mcap_event(const tMCA_CREATE_CFM* mcap_create_cfm) {
+  printf("%s: mdl_id=%d, op_code=0x%02x, rsp_code=%d, cfg=0x%02x\n", __func__,
+         mcap_create_cfm->mdl_id, mcap_create_cfm->op_code,
+         mcap_create_cfm->rsp_code, mcap_create_cfm->cfg);
+}
+
+static void print_mcap_event(const tMCA_DL_OPEN* mcap_dl_open) {
+  printf("%s: mdl_id=%d, mdl_handle=%d, mtu=%d\n", __func__,
+         mcap_dl_open->mdl_id, mcap_dl_open->mdl, mcap_dl_open->mtu);
+}
+
+static void print_mcap_event(const tMCA_DL_CLOSE* mcap_dl_close) {
+  printf("%s: mdl_id=%d, mdl_handle=%d, l2cap_disconnect_reason=0x%04x\n",
+         __func__, mcap_dl_close->mdl_id, mcap_dl_close->mdl,
+         mcap_dl_close->reason);
+}
+
+static void print_mcap_event(const tMCA_CONG_CHG* mcap_congestion_change) {
+  printf("%s: mdl_id=%d, mdl_handle=%d, congested=%d\n", __func__,
+         mcap_congestion_change->mdl_id, mcap_congestion_change->mdl,
+         mcap_congestion_change->cong);
+}
+
+McapTestApp::McapTestApp(btmcap_test_interface_t* mcap_test_interface)
+    : _mcl_list(), _mdep_list() {
+  _mcap_test_interface = mcap_test_interface;
+}
+
+btmcap_test_interface_t* McapTestApp::GetInterface() {
+  return _mcap_test_interface;
+}
+
+bool McapTestApp::Register(uint16_t ctrl_psm, uint16_t data_psm,
+                           uint16_t sec_mask, tMCA_CTRL_CBACK* callback) {
+  if (!callback) {
+    LOG(ERROR) << "callback is null";
+    return false;
+  }
+  _mca_reg.rsp_tout = 5000;
+  _mca_reg.ctrl_psm = ctrl_psm;
+  _mca_reg.data_psm = data_psm;
+  _mca_reg.sec_mask = sec_mask;
+  _mcap_handle =
+      _mcap_test_interface->register_application(&_mca_reg, callback);
+  return _mcap_handle > 0;
+}
+
+void McapTestApp::Deregister() {
+  _mcap_test_interface->deregister_application(_mcap_handle);
+  _mcap_handle = 0;
+}
+
+bool McapTestApp::Registered() { return _mcap_handle > 0; }
+
+bool McapTestApp::ConnectMcl(const bt_bdaddr_t& bd_addr, uint16_t ctrl_psm,
+                             uint16_t sec_mask) {
+  if (!Registered()) {
+    LOG(ERROR) << "Application not registered";
+    return false;
+  }
+  McapMcl* mcap_mcl = FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(INFO) << "MCL does not exist, creating new MCL";
+    _mcl_list.push_back(McapMcl(_mcap_test_interface, _mcap_handle, bd_addr));
+    mcap_mcl = &_mcl_list[_mcl_list.size() - 1];
+  }
+  if (mcap_mcl->GetHandle() != 0) {
+    LOG(ERROR) << "MCL is still active, cannot make another connection";
+    return false;
+  }
+  return mcap_mcl->Connect(ctrl_psm, sec_mask);
+}
+
+bool McapTestApp::CreateMdep(uint8_t type, uint8_t max_mdl,
+                             tMCA_DATA_CBACK* data_callback) {
+  if (!data_callback) {
+    LOG(ERROR) << "Data callback is null";
+    return false;
+  }
+  _mdep_list.push_back(McapMdep(_mcap_test_interface, _mcap_handle, type,
+                                max_mdl, data_callback));
+  return _mdep_list[_mdep_list.size() - 1].Create();
+}
+
+uint8_t McapTestApp::GetHandle() { return _mcap_handle; }
+
+McapMcl* McapTestApp::FindMclByPeerAddress(const bt_bdaddr_t& bd_addr) {
+  for (McapMcl& mcl : _mcl_list) {
+    if (bdaddr_equals(&mcl.GetPeerAddress(), &bd_addr)) {
+      return &mcl;
+    }
+  }
+  return nullptr;
+}
+
+McapMcl* McapTestApp::FindMclByHandle(tMCA_CL mcl_handle) {
+  for (McapMcl& mcl : _mcl_list) {
+    if (mcl.GetHandle() == mcl_handle) {
+      return &mcl;
+    }
+  }
+  return nullptr;
+}
+
+McapMdep* McapTestApp::FindMdepByHandle(tMCA_DEP mdep_handle) {
+  for (McapMdep& mdep : _mdep_list) {
+    if (mdep.GetHandle() == mdep_handle) {
+      return &mdep;
+    }
+  }
+  return nullptr;
+}
+
+void McapTestApp::RemoveMclByHandle(tMCA_CL mcl_handle) {
+  LOG(INFO) << "Removing MCL handle " << (int)mcl_handle;
+  for (std::vector<McapMcl>::iterator it = _mcl_list.begin();
+       it != _mcl_list.end(); ++it) {
+    if (it->GetHandle() == mcl_handle) {
+      _mcl_list.erase(it);
+      LOG(INFO) << "Removed MCL handle " << (int)mcl_handle;
+      return;
+    }
+  }
+}
+
+bool McapTestApp::IsRegistered() { return _mcap_handle > 0; }
+
+void McapTestApp::ControlCallback(tMCA_HANDLE handle, tMCA_CL mcl,
+                                  uint8_t event, tMCA_CTRL* p_data) {
+  McapMcl* mcap_mcl = FindMclByHandle(mcl);
+  McapMdl* mcap_mdl = nullptr;
+  printf("%s: mcap_handle=%d, mcl_handle=%d, event=%s (0x%02x)\n", __func__,
+         handle, mcl, dump_mcap_events(event), event);
+  if (_mcap_handle != handle) {
+    LOG(ERROR) << "MCAP handle mismatch, self=" << _mcap_handle
+               << ", other=" << handle;
+    return;
+  }
+  switch (event) {
+    case MCA_ERROR_RSP_EVT:
+      print_mcap_event(&p_data->rsp);
+      break;
+
+    case MCA_CREATE_CFM_EVT:
+      // Called when MCA_CreateMdl succeeded step 1 when response is received
+      print_mcap_event(&p_data->create_cfm);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+        break;
+      }
+      mcap_mdl = mcap_mcl->FindMdlById(p_data->create_cfm.mdl_id);
+      if (!mcap_mdl) {
+        LOG(ERROR) << "MDL not found for id " << p_data->create_cfm.mdl_id;
+        break;
+      }
+      if (mcap_mdl->GetResponseCode() >= 0) {
+        LOG(ERROR) << "MDL already got response " << mcap_mdl->GetResponseCode()
+                   << " for id " << p_data->create_cfm.mdl_id;
+        break;
+      }
+      mcap_mdl->SetResponseCode(p_data->create_cfm.rsp_code);
+      break;
+
+    case MCA_CREATE_IND_EVT: {
+      // Should be replied with MCA_CreateMdlRsp
+      print_mcap_event(&p_data->create_ind);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+        break;
+      }
+      McapMdep* mcap_mdep = FindMdepByHandle(p_data->create_ind.dep_id);
+      if (!mcap_mdep) {
+        LOG(ERROR) << "MDEP ID " << (int)p_data->create_ind.dep_id
+                   << " does not exist";
+        _mcap_test_interface->create_mdl_response(
+            mcl, p_data->create_ind.dep_id, p_data->create_ind.mdl_id, 0,
+            MCA_RSP_BAD_MDEP, get_test_channel_config());
+        break;
+      }
+      bool ret = mcap_mcl->CreateMdlResponse(
+          mcap_mdep->GetHandle(), p_data->create_ind.mdl_id,
+          p_data->create_ind.dep_id, p_data->create_ind.cfg);
+      LOG(INFO) << (ret ? "SUCCESS" : "FAIL");
+      if (!ret) {
+        _mcap_test_interface->create_mdl_response(
+            mcl, p_data->create_ind.dep_id, p_data->create_ind.mdl_id, 0,
+            MCA_RSP_NO_RESOURCE, get_test_channel_config());
+      }
+      break;
+    }
+    case MCA_RECONNECT_IND_EVT: {
+      // Called when remote device asks to reconnect
+      // reply with MCA_ReconnectMdlRsp
+      print_mcap_event(&p_data->reconnect_ind);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+        break;
+      }
+      mcap_mdl = mcap_mcl->FindMdlById(p_data->reconnect_ind.mdl_id);
+      if (mcap_mdl && !mcap_mdl->IsConnected()) {
+        LOG(INFO) << "Creating reconnect response for MDL "
+                  << (int)p_data->reconnect_ind.mdl_id;
+        mcap_mdl->ReconnectResponse();
+        break;
+      }
+      LOG_IF(WARNING, mcap_mdl && mcap_mdl->IsConnected())
+          << "MDL ID " << (int)p_data->reconnect_ind.mdl_id
+          << " is already connected";
+      LOG_IF(WARNING, !mcap_mdl) << "No MDL for mdl_id "
+                                 << p_data->reconnect_ind.mdl_id;
+      tMCA_DEP mdep_handle = 0;
+      if (_mdep_list.size() > 0) {
+        mdep_handle = _mdep_list[0].GetHandle();
+      } else {
+        LOG(ERROR) << "Cannot find any available MDEP";
+      }
+      tMCA_RESULT ret = _mcap_test_interface->reconnect_mdl_response(
+          mcl, mdep_handle, p_data->reconnect_ind.mdl_id, MCA_RSP_BAD_MDL,
+          get_test_channel_config());
+      LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << ret;
+      break;
+    }
+    case MCA_RECONNECT_CFM_EVT:
+      // Called when MCA_ReconnectMdl step 1, receives a response
+      print_mcap_event(&p_data->reconnect_cfm);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+        break;
+      }
+      mcap_mdl = mcap_mcl->FindMdlById(p_data->reconnect_cfm.mdl_id);
+      if (!mcap_mdl) {
+        LOG(ERROR) << "MDL not found for id " << p_data->reconnect_cfm.mdl_id;
+        break;
+      }
+      if (mcap_mdl->GetResponseCode() >= 0) {
+        LOG(ERROR) << "MDL already got response " << mcap_mdl->GetResponseCode()
+                   << " for id " << p_data->reconnect_cfm.mdl_id;
+        break;
+      }
+      mcap_mdl->SetResponseCode(p_data->reconnect_cfm.rsp_code);
+      break;
+
+    case MCA_ABORT_IND_EVT:
+      print_mcap_event(&p_data->abort_ind);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+        break;
+      }
+      mcap_mdl = mcap_mcl->FindMdlById(p_data->abort_ind.mdl_id);
+      if (!mcap_mdl) {
+        LOG(ERROR) << "MDL not found for id " << (int)p_data->abort_ind.mdl_id;
+        break;
+      }
+      if (mcap_mdl->IsConnected()) {
+        LOG(ERROR) << "MDL is already connected for id "
+                   << (int)p_data->abort_ind.mdl_id;
+      }
+      mcap_mcl->RemoveMdl(p_data->abort_ind.mdl_id);
+      break;
+
+    case MCA_ABORT_CFM_EVT:
+      // Called when MCA_Abort succeeded
+      print_mcap_event(&p_data->abort_cfm);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+        break;
+      }
+      mcap_mdl = mcap_mcl->FindMdlById(p_data->abort_cfm.mdl_id);
+      if (!mcap_mdl) {
+        LOG(ERROR) << "MDL not found for id " << (int)p_data->abort_cfm.mdl_id;
+        break;
+      }
+      if (mcap_mdl->IsConnected()) {
+        LOG(ERROR) << "MDL is already connected for id "
+                   << (int)p_data->abort_cfm.mdl_id;
+      }
+      mcap_mcl->RemoveMdl(p_data->abort_cfm.mdl_id);
+      break;
+
+    case MCA_DELETE_IND_EVT:
+      print_mcap_event(&p_data->delete_ind);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+        break;
+      }
+      if (p_data->delete_ind.mdl_id == MCA_ALL_MDL_ID) {
+        mcap_mcl->RemoveAllMdl();
+      } else {
+        mcap_mcl->RemoveMdl(p_data->delete_ind.mdl_id);
+      }
+      break;
+
+    case MCA_DELETE_CFM_EVT:
+      // Called when MCA_Delete succeeded
+      print_mcap_event(&p_data->delete_cfm);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL " << (int)mcl << " not connected";
+        break;
+      }
+      if (p_data->delete_cfm.rsp_code) {
+        LOG(ERROR) << "No success response " << (int)p_data->delete_cfm.rsp_code
+                   << " when deleting MDL_ID "
+                   << (int)p_data->delete_cfm.mdl_id;
+        break;
+      }
+      if (p_data->delete_cfm.mdl_id == MCA_ALL_MDL_ID) {
+        mcap_mcl->RemoveAllMdl();
+      } else {
+        mcap_mcl->RemoveMdl(p_data->delete_cfm.mdl_id);
+      }
+      break;
+
+    case MCA_CONNECT_IND_EVT: {
+      // Called when MCA_ConnectReq succeeded
+      print_mcap_event(&p_data->connect_ind);
+      LOG(INFO) << "Received MCL handle " << (int)mcl;
+      bt_bdaddr_t bd_addr = *((bt_bdaddr_t*)p_data->connect_ind.bd_addr);
+      mcap_mcl = FindMclByPeerAddress(bd_addr);
+      if (!mcap_mcl) {
+        LOG(INFO) << "Creating new MCL for ID " << (int)mcl;
+        _mcl_list.push_back(
+            McapMcl(_mcap_test_interface, _mcap_handle, bd_addr));
+        mcap_mcl = &_mcl_list[_mcl_list.size() - 1];
+      }
+      if (mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL is already connected for handle " << (int)mcl;
+        break;
+      }
+      mcap_mcl->SetHandle(mcl);
+      mcap_mcl->SetMtu(p_data->connect_ind.mtu);
+      break;
+    }
+    case MCA_DISCONNECT_IND_EVT: {
+      // Called when MCA_ConnectReq failed or MCA_DisconnectReq succeeded
+      print_mcap_event(&p_data->disconnect_ind);
+      bt_bdaddr_t bd_addr = *((bt_bdaddr_t*)p_data->disconnect_ind.bd_addr);
+      mcap_mcl = FindMclByPeerAddress(bd_addr);
+      if (!mcap_mcl) {
+        bdstr_t bd_addr_str;
+        LOG(ERROR) << "No MCL for BD addr "
+                   << bdaddr_to_string(&bd_addr, bd_addr_str,
+                                       sizeof(bd_addr_str));
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        bdstr_t bd_addr_str;
+        LOG(WARNING) << "MCL for " << bdaddr_to_string(&bd_addr, bd_addr_str,
+                                                       sizeof(bd_addr_str))
+                     << " is already disconnected";
+      }
+      mcap_mcl->SetHandle(0);
+      mcap_mcl->SetMtu(0);
+      mcap_mcl->ResetAllMdl();
+      break;
+    }
+    case MCA_OPEN_IND_EVT:
+    // Called when MCA_CreateMdlRsp succeeded step 2, data channel is open
+    // Called when MCA_ReconnectMdlRsp succeeded step 2, data channel is open
+    case MCA_OPEN_CFM_EVT:
+      // Called when MCA_CreateMdl succeeded step 2, data channel is open
+      // Called when MCA_ReconnectMdl succeeded step 2, data channel is open
+      // Called when MCA_DataChnlCfg succeeded
+      print_mcap_event(&p_data->open_ind);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+        break;
+      }
+      mcap_mdl = mcap_mcl->FindMdlById(p_data->open_ind.mdl_id);
+      if (mcap_mdl) {
+        if (mcap_mdl->IsConnected()) {
+          LOG(ERROR) << "MDL is already connected for mcl_handle "
+                     << (int)p_data->open_ind.mdl_id;
+          break;
+        }
+        mcap_mdl->SetMtu(p_data->open_ind.mtu);
+        mcap_mdl->SetHandle(p_data->open_ind.mdl);
+      } else {
+        LOG(ERROR) << "No MDL for mdl_id " << (int)p_data->reconnect_ind.mdl_id;
+      }
+      break;
+
+    case MCA_CLOSE_IND_EVT:
+    case MCA_CLOSE_CFM_EVT:
+      // Called when MCA_CloseReq is successful
+      print_mcap_event(&p_data->close_cfm);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+        break;
+      }
+      mcap_mdl = mcap_mcl->FindMdlById(p_data->close_cfm.mdl_id);
+      if (mcap_mdl) {
+        mcap_mdl->SetMtu(0);
+        mcap_mdl->SetHandle(0);
+      } else {
+        LOG(WARNING) << "No MDL for mdl_id " << (int)p_data->close_cfm.mdl_id;
+      }
+      break;
+
+    case MCA_CONG_CHG_EVT:
+      print_mcap_event(&p_data->cong_chg);
+      if (!mcap_mcl) {
+        LOG(ERROR) << "No MCL for mcl_handle " << (int)mcl;
+        break;
+      }
+      if (!mcap_mcl->IsConnected()) {
+        LOG(ERROR) << "MCL not connected for mcl_handle " << (int)mcl;
+        break;
+      }
+      break;
+
+    case MCA_RSP_TOUT_IND_EVT:
+    case MCA_SYNC_CAP_IND_EVT:
+    case MCA_SYNC_CAP_CFM_EVT:
+    case MCA_SYNC_SET_IND_EVT:
+    case MCA_SYNC_SET_CFM_EVT:
+    case MCA_SYNC_INFO_IND_EVT:
+      print_mcap_event(&p_data->hdr);
+      break;
+  }
+}
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_app.h b/tools/mcap_tool/mcap_test_app.h
new file mode 100644
index 0000000..23dfa37
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_app.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "bdaddr.h"
+#include "mca_api.h"
+
+#include "mcap_test_mcl.h"
+#include "mcap_test_mdep.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+extern const tMCA_CHNL_CFG MCAP_TEST_CHANNEL_CONFIG;
+
+class McapTestApp {
+ public:
+  /**
+   * McapTestApp is the root node for an MCAP application
+   *
+   * @param mcap_test_interface interface to MCAP APIs on Bluetooth stack
+   *                            from get_test_interface()
+   */
+  McapTestApp(btmcap_test_interface_t* mcap_test_interface);
+  btmcap_test_interface_t* GetInterface();
+  /**
+   * Register an application with the Bluetooth stack
+   * @param ctrl_psm Control channel L2CAP PSM
+   * @param data_psm Data channel L2CAP PSM
+   * @param sec_mask Security Mask
+   * @param callback Control channel callback
+   * @return
+   */
+  bool Register(uint16_t ctrl_psm, uint16_t data_psm, uint16_t sec_mask,
+                tMCA_CTRL_CBACK* callback);
+  /**
+   * De-register the current application
+   */
+  void Deregister();
+  /**
+   * Check if current application is registered
+   * @return True if registered
+   */
+  bool Registered();
+  /**
+   * Create MCAP Communication Link
+   * @param bd_addr Peer Bluetooth Address
+   * @param ctrl_psm Control channel L2CAP PSM, should be the same as registered
+   *                 value for most cases
+   * @param sec_mask Security mask
+   * @return True on success
+   */
+  bool ConnectMcl(const bt_bdaddr_t& bd_addr, uint16_t ctrl_psm,
+                  uint16_t sec_mask);
+  /**
+   * Create MCAP Data End Point
+   * @param type 0 - MCA_TDEP_ECHO, 1 - MCA_TDEP_DATA
+   * @param max_mdl Maximum number of data channels for this end point
+   * @param data_callback Data callback
+   * @return True on success
+   */
+  bool CreateMdep(uint8_t type, uint8_t max_mdl,
+                  tMCA_DATA_CBACK* data_callback);
+  // Simple methods that are self-explanatory
+  uint8_t GetHandle();
+  McapMcl* FindMclByPeerAddress(const bt_bdaddr_t& bd_addr);
+  McapMcl* FindMclByHandle(tMCA_CL mcl_handle);
+  McapMdep* FindMdepByHandle(tMCA_DEP mdep_handle);
+  void RemoveMclByHandle(tMCA_CL mcl_handle);
+  bool IsRegistered();
+  /**
+   * Callback function for control channel, need to be called by an external
+   * function registered during McapTestApp::Register()
+   * @param handle MCAP application handle, should be the same as GetHandle()
+   * @param mcl MCL handle, FindMclByHandle(mcl) should return non-null value
+   * @param event Control event
+   * @param p_data Control data
+   */
+  void ControlCallback(tMCA_HANDLE handle, tMCA_CL mcl, uint8_t event,
+                       tMCA_CTRL* p_data);
+
+ private:
+  // Initialized during start up
+  tMCA_REG _mca_reg;
+  btmcap_test_interface_t* _mcap_test_interface = nullptr;
+  std::vector<McapMcl> _mcl_list;
+  std::vector<McapMdep> _mdep_list;
+
+  // Initialized later
+  tMCA_HANDLE _mcap_handle = 0;
+};
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mcl.cc b/tools/mcap_tool/mcap_test_mcl.cc
new file mode 100644
index 0000000..8842c04
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mcl.cc
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cstring>
+
+#include <base/logging.h>
+
+#include "bdaddr.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mcap_test_mcl.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+McapMcl::McapMcl(btmcap_test_interface_t* mcap_test_interface,
+                 tMCA_HANDLE mcap_handle, const bt_bdaddr_t& peer_bd_addr)
+    : _mdl_list() {
+  _mcap_handle = mcap_handle;
+  _mcap_test_interface = mcap_test_interface;
+  memcpy(_peer_bd_addr.address, peer_bd_addr.address,
+         sizeof(_peer_bd_addr.address));
+}
+
+bool McapMcl::Connect(uint16_t ctrl_psm, uint16_t sec_mask) {
+  tMCA_RESULT ret = _mcap_test_interface->connect_mcl(
+      _mcap_handle, _peer_bd_addr.address, ctrl_psm, sec_mask);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMcl::Disconnect() {
+  if (!IsConnected()) {
+    LOG(ERROR) << "MCL is not connected";
+    return false;
+  }
+  tMCA_RESULT ret = _mcap_test_interface->disconnect_mcl(_mcl_handle);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+McapMdl* McapMcl::AllocateMdl(tMCA_DEP mdep_handle, uint16_t mdl_id,
+                              uint8_t dep_id, uint8_t cfg) {
+  if (!IsConnected()) {
+    LOG(ERROR) << "MCL is not connected";
+    return nullptr;
+  }
+  if (FindMdlById(mdl_id) != nullptr) {
+    LOG(ERROR) << "mdl_id=" << mdl_id << "already exists";
+    return nullptr;
+  }
+  if (!HasAvailableMdl()) {
+    LOG(ERROR) << "No more avaible MDL, currently " << _mdl_list.size();
+    return nullptr;
+  }
+  _mdl_list.push_back(McapMdl(_mcap_test_interface, _mcl_handle, mdep_handle,
+                              mdl_id, dep_id, cfg));
+  return &_mdl_list[_mdl_list.size() - 1];
+}
+
+bool McapMcl::CreateMdl(tMCA_DEP mdep_handle, uint16_t data_psm,
+                        uint16_t mdl_id, uint8_t peer_dep_id, uint8_t cfg,
+                        bool should_connect) {
+  if (!IsConnected()) {
+    LOG(ERROR) << "MCL is not connected";
+    return false;
+  }
+  McapMdl* mcap_mdl = FindMdlById(mdl_id);
+  if (!mcap_mdl) {
+    LOG(INFO) << "mdl_id=" << mdl_id << "does not exists, creating new one";
+    mcap_mdl = AllocateMdl(mdep_handle, mdl_id, peer_dep_id, cfg);
+    if (!mcap_mdl) {
+      return false;
+    }
+  }
+  if (mcap_mdl->IsConnected()) {
+    LOG(ERROR) << "mdl_id=" << mdl_id << "is already connected with handle "
+               << (int)mcap_mdl->GetHandle();
+    return false;
+  }
+  return mcap_mdl->Create(data_psm, should_connect);
+}
+
+bool McapMcl::DataChannelConfig() {
+  tMCA_RESULT ret = _mcap_test_interface->data_channel_config(
+      _mcl_handle, get_test_channel_config());
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMcl::CreateMdlResponse(tMCA_DEP mdep_handle, uint16_t mdl_id,
+                                uint8_t my_dep_id, uint8_t cfg) {
+  if (!IsConnected()) {
+    LOG(ERROR) << "MCL is not connected";
+    return false;
+  }
+  McapMdl* mcap_mdl = FindMdlById(mdl_id);
+  if (!mcap_mdl) {
+    LOG(INFO) << "mdl_id=" << mdl_id << " does not exists, creating new one";
+    mcap_mdl = AllocateMdl(mdep_handle, mdl_id, my_dep_id, cfg);
+    if (!mcap_mdl) {
+      LOG(ERROR) << "MDL cannot be created";
+      return false;
+    }
+  }
+  if (mcap_mdl->IsConnected()) {
+    LOG(INFO) << "mdl_id=" << mdl_id << " is already connected with handle "
+              << (int)mcap_mdl->GetHandle() << ", updating context";
+    mcap_mdl->UpdateContext(mdep_handle, my_dep_id, cfg);
+  }
+  return mcap_mdl->CreateResponse();
+}
+
+bool McapMcl::AbortMdl() {
+  tMCA_RESULT ret = _mcap_test_interface->abort_mdl(_mcl_handle);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMcl::DeleteMdl(uint16_t mdl_id) {
+  tMCA_RESULT ret = _mcap_test_interface->delete_mdl(_mcl_handle, mdl_id);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bt_bdaddr_t& McapMcl::GetPeerAddress() { return _peer_bd_addr; }
+
+void McapMcl::SetHandle(tMCA_CL handle) { _mcl_handle = handle; }
+
+tMCA_CL McapMcl::GetHandle() const { return _mcl_handle; }
+
+void McapMcl::SetMtu(uint16_t mtu) { _control_mtu = mtu; }
+
+uint16_t McapMcl::GetMtu() { return _control_mtu; }
+
+McapMdl* McapMcl::FindMdlById(uint16_t mdl_id) {
+  for (McapMdl& mdl : _mdl_list) {
+    if (mdl.GetId() == mdl_id) {
+      return &mdl;
+    }
+  }
+  return nullptr;
+}
+
+McapMdl* McapMcl::FindMdlByHandle(tMCA_DL mdl_handle) {
+  for (McapMdl& mdl : _mdl_list) {
+    if (mdl.GetHandle() == mdl_handle) {
+      return &mdl;
+    }
+  }
+  return nullptr;
+}
+
+void McapMcl::RemoveAllMdl() { _mdl_list.clear(); }
+
+void McapMcl::RemoveMdl(uint16_t mdl_id) {
+  LOG(INFO) << "Removing MDL id " << (int)mdl_id;
+  for (std::vector<McapMdl>::iterator it = _mdl_list.begin();
+       it != _mdl_list.end(); ++it) {
+    if (it->GetId() == mdl_id) {
+      _mdl_list.erase(it);
+      LOG(INFO) << "Removed MDL id " << (int)mdl_id;
+      return;
+    }
+  }
+}
+
+void McapMcl::ResetAllMdl() {
+  for (McapMdl& mcap_mdl : _mdl_list) {
+    mcap_mdl.SetHandle(0);
+    mcap_mdl.SetMtu(0);
+    mcap_mdl.SetResponseCode(-1);
+  }
+}
+
+void McapMcl::ResetMdl(uint16_t mdl_id) {
+  LOG(INFO) << "Closing MDL id " << (int)mdl_id;
+  McapMdl* mcap_mdl = FindMdlById(mdl_id);
+  if (!mcap_mdl) {
+    LOG(ERROR) << "Cannot find MDL for id " << (int)mdl_id;
+    return;
+  }
+  if (mcap_mdl->IsConnected()) {
+    LOG(ERROR) << "MDL " << (int)mdl_id << " is still connected";
+    return;
+  }
+  mcap_mdl->SetHandle(0);
+  mcap_mdl->SetMtu(0);
+  mcap_mdl->SetResponseCode(-1);
+}
+
+bool McapMcl::IsConnected() { return _mcl_handle > 0; }
+
+int McapMcl::ConnectedMdlCount() {
+  int count = 0;
+  for (McapMdl& mcap_mdl : _mdl_list) {
+    if (mcap_mdl.IsConnected()) {
+      count++;
+    }
+  }
+  return count;
+}
+
+bool McapMcl::HasAvailableMdl() { return ConnectedMdlCount() < MCA_NUM_MDLS; }
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mcl.h b/tools/mcap_tool/mcap_test_mcl.h
new file mode 100644
index 0000000..2d71ee6
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mcl.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <vector>
+
+#include "bdaddr.h"
+#include "mca_api.h"
+#include "mcap_test_mdl.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+class McapMcl {
+ public:
+  /**
+   * A controller for a MCAP Communication Link (MCL)
+   * @param mcap_test_interface Underlining interface to Bluetooth stack
+   * @param mcap_handle Parent application handle
+   * @param peer_bd_addr Peer Bluetooth MAC address
+   */
+  McapMcl(btmcap_test_interface_t* mcap_test_interface, tMCA_HANDLE mcap_handle,
+          const bt_bdaddr_t& peer_bd_addr);
+  /**
+   * Connect this MCL's control channel
+   * @param ctrl_psm Control channel L2CAP PSM
+   * @param sec_mask Security mask
+   * @return True on success
+   */
+  bool Connect(uint16_t ctrl_psm, uint16_t sec_mask);
+  /**
+   * Disconnect from control channel
+   * @return
+   */
+  bool Disconnect();
+  /**
+   * Close this MCL connection
+   * @return
+   */
+  bool Close();
+  /**
+   * Allocate an MCAP Data Link (MDL) object and save to this MCL object
+   * @param mdep_handle MDEP handle for data, MDEP must be prior created
+   * @param mdl_id Desired MDL ID, user supported
+   * @param dep_id Peer MDEP ID
+   * @param cfg Configuration flags
+   * @return True on success
+   */
+  McapMdl* AllocateMdl(tMCA_DEP mdep_handle, uint16_t mdl_id, uint8_t dep_id,
+                       uint8_t cfg);
+  /**
+   * Send CREATE_MDL message to peer device, will allocate an MDL if the MDL for
+   * corresponding MDL ID was not allocated before
+   * @param mdep_handle MDEP handle for data, MDEP must be prior created
+   * @param data_psm Data channel L2CAP PSM
+   * @param mdl_id Desired MDL ID, user supported
+   * @param peer_dep_id Peer MDEP ID
+   * @param cfg Configuration flags
+   * @param should_connect whether we should connect L2CAP immediately
+   * @return True on success
+   */
+  bool CreateMdl(tMCA_DEP mdep_handle, uint16_t data_psm, uint16_t mdl_id,
+                 uint8_t peer_dep_id, uint8_t cfg, bool should_connect);
+  /**
+   * Configure data channel, unblock any pending MDL L2CAP connection requests
+   * @return True on Success
+   */
+  bool DataChannelConfig();
+  /**
+   * Respond to CREATE_MDL message received from peer device, will allocate an
+   * MDL if the MDL for corresponding MDL ID was not allocated before
+   * @param mdep_handle MDEP handle for data, MDEP must be prior created
+   * @param mdl_id Desired MDL ID, peer supported
+   * @param my_dep_id My MDEP ID
+   * @param cfg Configuration flags
+   * @return True on success
+   */
+  bool CreateMdlResponse(tMCA_DEP mdep_handle, uint16_t mdl_id,
+                         uint8_t my_dep_id, uint8_t cfg);
+  /**
+   * Send ABORT_MDL request, aborting all pending CREATE_MDL requests
+   * @return
+   */
+  bool AbortMdl();
+  /**
+   * Send DELETE_MDL request to remote
+   * @param mdl_id None zero value mdl_id, 0xFFFF for all remote MDLs
+   * @return True on success
+   */
+  bool DeleteMdl(uint16_t mdl_id);
+  // Simple methods that are self-explanatory
+  bt_bdaddr_t& GetPeerAddress();
+  void SetHandle(tMCA_CL handle);
+  tMCA_CL GetHandle() const;
+  void SetMtu(uint16_t mtu);
+  uint16_t GetMtu();
+  McapMdl* FindMdlById(uint16_t mdl_id);
+  McapMdl* FindMdlByHandle(tMCA_DL mdl_handle);
+  void RemoveAllMdl();
+  void RemoveMdl(uint16_t mdl_id);
+  void ResetAllMdl();
+  void ResetMdl(uint16_t mdl_id);
+  bool IsConnected();
+  int ConnectedMdlCount();
+  bool HasAvailableMdl();
+
+ private:
+  // Initialized during start up
+  btmcap_test_interface_t* _mcap_test_interface;
+  tMCA_HANDLE _mcap_handle;
+  bt_bdaddr_t _peer_bd_addr;
+  std::vector<McapMdl> _mdl_list;
+
+  // Initialized later
+  tMCA_CL _mcl_handle = 0;
+  uint16_t _control_mtu = 0;
+};
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdep.cc b/tools/mcap_tool/mcap_test_mdep.cc
new file mode 100644
index 0000000..1fb6412
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdep.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <base/logging.h>
+
+#include "mcap_test_mdep.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+McapMdep::McapMdep(btmcap_test_interface_t* mcap_test_interface,
+                   tMCA_HANDLE mcap_handle, uint8_t type, uint8_t max_mdl,
+                   tMCA_DATA_CBACK* data_callback) {
+  _mcap_test_interface = mcap_test_interface;
+  _mcap_handle = mcap_handle;
+  _mca_cs.type = (0 == type) ? MCA_TDEP_ECHO : MCA_TDEP_DATA;
+  _mca_cs.max_mdl = max_mdl;
+  _mca_cs.p_data_cback = data_callback;
+}
+
+bool McapMdep::Create() {
+  tMCA_RESULT ret =
+      _mcap_test_interface->create_mdep(_mcap_handle, &_mdep_handle, &_mca_cs);
+  LOG(INFO) << "mdep_handle=" << (int)_mdep_handle;
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMdep::Delete() {
+  tMCA_RESULT ret =
+      _mcap_test_interface->create_mdep(_mcap_handle, &_mdep_handle, &_mca_cs);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+tMCA_DEP McapMdep::GetHandle() const { return _mdep_handle; }
+
+bool McapMdep::IsRegistered() { return _mdep_handle > 0; }
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdep.h b/tools/mcap_tool/mcap_test_mdep.h
new file mode 100644
index 0000000..a852d52
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdep.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "mca_api.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+class McapMdep {
+ public:
+  /**
+   * A control abstraction for a MCAP Data End Point (MDEP)
+   * @param mcap_test_interface Underlining MCAP interface to Bluetooth stack
+   * @param mcap_handle Parent MCAP application handle
+   * @param type 0 for Echo, 1 for Normal, nothing else
+   * @param max_mdl Maximum number of MDL's allowed
+   * @param data_callback Data channel callback function
+   */
+  McapMdep(btmcap_test_interface_t* mcap_test_interface,
+           tMCA_HANDLE mcap_handle, uint8_t type, uint8_t max_mdl,
+           tMCA_DATA_CBACK* data_callback);
+  /**
+   * Actually create the MDEP in the stack
+   * @return True if success
+   */
+  bool Create();
+  /**
+   * Destroy the MDEP in the stack
+   * @return True if success
+   */
+  bool Delete();
+  // Simple methods that are self-explanatory
+  tMCA_DEP GetHandle() const;
+  bool IsRegistered();
+
+ private:
+  // Initialized during start up
+  btmcap_test_interface_t* _mcap_test_interface;
+  tMCA_HANDLE _mcap_handle;
+  tMCA_CS _mca_cs;
+
+  // Initialized later
+  tMCA_DEP _mdep_handle = 0;
+};
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdl.cc b/tools/mcap_tool/mcap_test_mdl.cc
new file mode 100644
index 0000000..e7b8083
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdl.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <base/logging.h>
+
+#include "mca_defs.h"
+#include "mcap_test_mdl.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+/* Test MCAP Channel Configurations */
+const tMCA_CHNL_CFG MCAP_TEST_CHANNEL_CONFIG = {
+    .fcr_opt =
+        {
+            L2CAP_FCR_ERTM_MODE,
+            MCA_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */
+            /* Maximum transmissions before disconnecting */
+            MCA_FCR_OPT_MAX_TX_B4_DISCNT,
+            MCA_FCR_OPT_RETX_TOUT,    /* retransmission timeout (2 secs) */
+            MCA_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
+            MCA_FCR_OPT_MPS_SIZE,     /* MPS segment size */
+        },
+    .user_rx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+    .user_tx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+    .fcr_rx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+    .fcr_tx_buf_size = BT_DEFAULT_BUFFER_SIZE,
+    .fcs = MCA_FCS_NONE,
+    .data_mtu = 572 /* L2CAP MTU of the MCAP data channel */
+};
+
+const tMCA_CHNL_CFG* get_test_channel_config() {
+  return &MCAP_TEST_CHANNEL_CONFIG;
+}
+
+McapMdl::McapMdl(btmcap_test_interface_t* mcap_test_interface,
+                 tMCA_CL mcl_handle, tMCA_DEP mdep_handle, uint16_t mdl_id,
+                 uint8_t dep_id, uint8_t cfg) {
+  _mcap_test_interface = mcap_test_interface;
+  _mcl_handle = mcl_handle;
+  _mdep_handle = mdep_handle;
+  _mdl_id = mdl_id;
+  _dep_id = dep_id;
+  _cfg = cfg;
+}
+
+bool McapMdl::UpdateContext(tMCA_DEP mdep_handle, uint8_t dep_id, uint8_t cfg) {
+  if (!_mdl_handle) {
+    LOG(ERROR) << "MDL handle not initialized";
+  }
+  _mdep_handle = mdep_handle;
+  _dep_id = dep_id;
+  _cfg = cfg;
+  tMCA_RESULT ret = _mcap_test_interface->close_mdl_request(_mdl_handle);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  if (ret != MCA_SUCCESS) return false;
+  SetHandle(0);
+  SetResponseCode(-1);
+  SetMtu(0);
+  return true;
+}
+
+bool McapMdl::Create(uint16_t data_psm, bool should_connect) {
+  tMCA_RESULT ret = _mcap_test_interface->create_mdl_request(
+      _mcl_handle, _mdep_handle, data_psm, _mdl_id, _dep_id, _cfg,
+      should_connect ? &MCAP_TEST_CHANNEL_CONFIG : nullptr);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::Close() {
+  if (!_mdl_handle) {
+    LOG(ERROR) << "MDL handle not initialized";
+    return false;
+  }
+  tMCA_RESULT ret = _mcap_test_interface->close_mdl_request(_mdl_handle);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::Reconnect(uint16_t data_psm) {
+  tMCA_RESULT ret = _mcap_test_interface->reconnect_mdl_request(
+      _mcl_handle, _mdep_handle, data_psm, _mdl_id, &MCAP_TEST_CHANNEL_CONFIG);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::ReconnectResponse() {
+  tMCA_RESULT ret = _mcap_test_interface->reconnect_mdl_response(
+      _mcl_handle, _mdep_handle, _mdl_id, MCA_RSP_SUCCESS,
+      &MCAP_TEST_CHANNEL_CONFIG);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::CreateResponse() {
+  tMCA_RESULT ret = _mcap_test_interface->create_mdl_response(
+      _mcl_handle, _dep_id, _mdl_id, _cfg, MCA_SUCCESS,
+      &MCAP_TEST_CHANNEL_CONFIG);
+  LOG_IF(INFO, ret != MCA_SUCCESS) << "ret=" << (int)ret;
+  return ret == MCA_SUCCESS;
+}
+
+bool McapMdl::IsConnected() { return _mdl_handle > 0; }
+
+uint16_t McapMdl::GetId() { return _mdl_id; }
+
+int32_t McapMdl::GetResponseCode() { return _mdl_rsp_code; }
+
+void McapMdl::SetResponseCode(int32_t rsp_code) { _mdl_rsp_code = rsp_code; }
+
+void McapMdl::SetHandle(tMCA_DL mdl_handle) { _mdl_handle = mdl_handle; }
+
+tMCA_DL McapMdl::GetHandle() { return _mdl_handle; }
+
+void McapMdl::SetMtu(uint16_t mtu) { _data_mtu = mtu; }
+
+uint16_t McapMdl::GetMtu() { return _data_mtu; }
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_test_mdl.h b/tools/mcap_tool/mcap_test_mdl.h
new file mode 100644
index 0000000..7dadc34
--- /dev/null
+++ b/tools/mcap_tool/mcap_test_mdl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "mca_api.h"
+
+namespace SYSTEM_BT_TOOLS_MCAP_TOOL {
+
+const tMCA_CHNL_CFG* get_test_channel_config();
+
+class McapMdl {
+ public:
+  /**
+   * An abstraction for the MCAP Data Link (MDL)
+   * @param mcap_test_interface Underlining MCAP interface to Bluetooth stack
+   * @param mcl_handle Parent MCL handle
+   * @param mdep_handle Associated MDEP handle
+   * @param mdl_id Desired MDL ID, application supported
+   * @param dep_id Peer or self MDEP ID
+   * @param cfg Configuration flags
+   */
+  McapMdl(btmcap_test_interface_t* mcap_test_interface, tMCA_CL mcl_handle,
+          tMCA_DEP mdep_handle, uint16_t mdl_id, uint8_t dep_id, uint8_t cfg);
+  /**
+   * Update this MDL's context so that it can be reused for a new connection
+   * This will close this MDL connection at the same time
+   * @param mdep_handle Associated MDEP handle
+   * @param dep_id Peer or self MDEP ID
+   * @param cfg Configuration flags
+   * @return True on success
+   */
+  bool UpdateContext(tMCA_DEP mdep_handle, uint8_t dep_id, uint8_t cfg);
+  /**
+   * Request to create this MDL to remote device through MCL
+   * The create command won't initiate an L2CAP connection unless a non-null
+   * config is given
+   * @param data_psm Data channel L2CAP PSM
+   * @return True on success
+   */
+  bool Create(uint16_t data_psm, bool should_connect);
+  /**
+   * Connect this MDL to remote by configuring the data channel
+   * @return True on success
+   */
+  bool Connect();
+  /**
+   * Close this MDL connection
+   * @return True on success
+   */
+  bool Close();
+  /**
+   * Request to reconnect connect this MDL to remote device through MCL
+   * @param data_psm Data channel L2CAP PSM
+   * @return True on success
+   */
+  bool Reconnect(uint16_t data_psm);
+  /**
+   * Respond to a reconnect request from peer
+   * @return True on success
+   */
+  bool ReconnectResponse();
+  /**
+   * Respond to a connect request from peer
+   * @return True on success
+   */
+  bool CreateResponse();
+  bool IsConnected();
+  int32_t GetResponseCode();
+  void SetResponseCode(int32_t rsp_code);
+  uint16_t GetId();
+  void SetHandle(tMCA_DL mdl_handle);
+  tMCA_DL GetHandle();
+  void SetMtu(uint16_t mtu);
+  uint16_t GetMtu();
+
+ private:
+  // Initialized at start up
+  btmcap_test_interface_t* _mcap_test_interface;
+  tMCA_CL _mcl_handle;
+  tMCA_DEP _mdep_handle;
+  uint16_t _mdl_id;
+  uint8_t _dep_id;
+  uint8_t _cfg;
+
+  // Initialized later
+  tMCA_DL _mdl_handle = 0;
+  uint16_t _data_mtu = 0;
+  int32_t _mdl_rsp_code = -1;
+};
+
+}  // namespace SYSTEM_BT_TOOLS_MCAP_TOOL
diff --git a/tools/mcap_tool/mcap_tool.cc b/tools/mcap_tool/mcap_tool.cc
new file mode 100644
index 0000000..e84c35e
--- /dev/null
+++ b/tools/mcap_tool/mcap_tool.cc
@@ -0,0 +1,973 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2015, The linux Foundation. All rights reserved.
+ *
+ *  Not a Contribution.
+ *
+ *  Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+/************************************************************************************
+ *
+ *  Filename:      mcap_tool.cc
+ *
+ *  Description:   Fluoride MCAP Test Tool application
+ *
+ ***********************************************************************************/
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/hardware.h>
+#ifndef OS_GENERIC
+#include <private/android_filesystem_config.h>
+#endif
+#include <base/logging.h>
+
+#include "bdaddr.h"
+#include "bt_types.h"
+#include "l2c_api.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "osi/include/compat.h"
+#if defined(OS_GENERIC)
+#include "hal_util.h"
+#endif
+#include "mcap_test_app.h"
+#include "mcap_test_mcl.h"
+#include "mcap_test_mdep.h"
+#include "mcap_test_mdl.h"
+
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapTestApp;
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMcl;
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMdep;
+using SYSTEM_BT_TOOLS_MCAP_TOOL::McapMdl;
+
+/******************************************************************************
+ *  Constants & Macros
+ *****************************************************************************/
+#define PID_FILE "/data/.bdt_pid"
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#define CASE_RETURN_STR(const) \
+  case const:                  \
+    return #const;
+
+#ifndef OS_GENERIC
+/* Permission Groups */
+static gid_t groups[] = {AID_NET_BT,    AID_INET, AID_NET_BT_ADMIN,
+                         AID_SYSTEM,    AID_MISC, AID_SDCARD_RW,
+                         AID_NET_ADMIN, AID_VPN};
+#endif
+/******************************************************************************
+ *  Static variables
+ *****************************************************************************/
+/* Console loop states */
+static bool global_main_done = false;
+static bt_status_t global_status;
+static bool global_strict_mode = false;
+
+/* Device and Profile Interfaces */
+static bluetooth_device_t* sBtDevice = nullptr;
+const bt_interface_t* sBtInterface = nullptr;
+static btmcap_test_interface_t* sMcapTestInterface = nullptr;
+static McapTestApp* sMcapTestApp = nullptr;
+
+/* Bluetooth stack states */
+static bool global_bt_enabled = false;
+static int global_adapter_state = BT_STATE_OFF;
+static int global_pair_state = BT_BOND_STATE_NONE;
+/************************************************************************************
+**  Static functions
+************************************************************************************/
+static void process_cmd(char* p, bool is_job);
+
+/*******************************************************************************
+ ** Misc helper functions
+ *******************************************************************************/
+static const char* dump_bt_status(const bt_status_t status) {
+  switch (status) {
+    CASE_RETURN_STR(BT_STATUS_SUCCESS)
+    CASE_RETURN_STR(BT_STATUS_FAIL)
+    CASE_RETURN_STR(BT_STATUS_NOT_READY)
+    CASE_RETURN_STR(BT_STATUS_NOMEM)
+    CASE_RETURN_STR(BT_STATUS_BUSY)
+    CASE_RETURN_STR(BT_STATUS_UNSUPPORTED)
+    default:
+      return "unknown status code";
+  }
+}
+
+/************************************************************************************
+**  MCAP Callbacks
+************************************************************************************/
+static void mcap_ctrl_callback(tMCA_HANDLE handle, tMCA_CL mcl, uint8_t event,
+                               tMCA_CTRL* p_data) {
+  sMcapTestApp->ControlCallback(handle, mcl, event, p_data);
+}
+
+static void mcap_data_cb(tMCA_DL mdl, BT_HDR* p_pkt) {
+  printf("%s: mdl=%d, event=%d, len=%d, offset=%d, layer_specific=%d\n",
+         __func__, mdl, p_pkt->event, p_pkt->len, p_pkt->offset,
+         p_pkt->layer_specific);
+  printf("%s: HEXDUMP OF DATA LENGTH %u:\n", __func__, p_pkt->len);
+  printf("=========================Begin=========================\n");
+  bool newline = false;
+  for (int i = 0; i < p_pkt->len; ++i) {
+    printf("%02x", p_pkt->data[i]);
+    if (i > 0 && (i % 25) == 0) {
+      printf("\n");
+      newline = true;
+    } else {
+      printf(" ");
+      newline = false;
+    }
+  }
+  if (!newline) printf("\n");
+  printf("=========================End===========================\n");
+}
+
+/************************************************************************************
+**  Shutdown helper functions
+************************************************************************************/
+
+static void console_shutdown(void) {
+  LOG(INFO) << __func__ << ": Shutdown Fluoride MCAP test app";
+  global_main_done = true;
+}
+
+/*****************************************************************************
+** Android's init.rc does not yet support applying linux capabilities
+*****************************************************************************/
+
+#ifndef OS_GENERIC
+static void config_permissions(void) {
+  struct __user_cap_header_struct header;
+  struct __user_cap_data_struct cap[2];
+
+  printf("set_aid_and_cap : pid %d, uid %d gid %d", getpid(), getuid(),
+         getgid());
+
+  header.pid = 0;
+
+  prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+  setuid(AID_BLUETOOTH);
+  setgid(AID_BLUETOOTH);
+
+  header.version = _LINUX_CAPABILITY_VERSION_3;
+
+  cap[CAP_TO_INDEX(CAP_NET_RAW)].permitted |= CAP_TO_MASK(CAP_NET_RAW);
+  cap[CAP_TO_INDEX(CAP_NET_ADMIN)].permitted |= CAP_TO_MASK(CAP_NET_ADMIN);
+  cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].permitted |=
+      CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+  cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted |= CAP_TO_MASK(CAP_SYS_RAWIO);
+  cap[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE);
+  cap[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+  cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].permitted |= CAP_TO_MASK(CAP_WAKE_ALARM);
+
+  cap[CAP_TO_INDEX(CAP_NET_RAW)].effective |= CAP_TO_MASK(CAP_NET_RAW);
+  cap[CAP_TO_INDEX(CAP_NET_ADMIN)].effective |= CAP_TO_MASK(CAP_NET_ADMIN);
+  cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].effective |=
+      CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+  cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective |= CAP_TO_MASK(CAP_SYS_RAWIO);
+  cap[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE);
+  cap[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
+  cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].effective |= CAP_TO_MASK(CAP_WAKE_ALARM);
+
+  capset(&header, &cap[0]);
+  setgroups(sizeof(groups) / sizeof(groups[0]), groups);
+}
+#endif
+
+/*******************************************************************************
+ ** Console helper functions
+ *******************************************************************************/
+
+void skip_blanks(char** p) {
+  while (**p == ' ') (*p)++;
+}
+
+uint32_t get_int(char** p, int DefaultValue) {
+  uint32_t Value = 0;
+  unsigned char UseDefault;
+
+  UseDefault = 1;
+  skip_blanks(p);
+
+  while (((**p) <= '9' && (**p) >= '0')) {
+    Value = Value * 10 + (**p) - '0';
+    UseDefault = 0;
+    (*p)++;
+  }
+
+  if (UseDefault)
+    return DefaultValue;
+  else
+    return Value;
+}
+
+int get_signed_int(char** p, int DefaultValue) {
+  int Value = 0;
+  unsigned char UseDefault;
+  unsigned char NegativeNum = 0;
+
+  UseDefault = 1;
+  skip_blanks(p);
+
+  if ((**p) == '-') {
+    NegativeNum = 1;
+    (*p)++;
+  }
+  while (((**p) <= '9' && (**p) >= '0')) {
+    Value = Value * 10 + (**p) - '0';
+    UseDefault = 0;
+    (*p)++;
+  }
+
+  if (UseDefault)
+    return DefaultValue;
+  else
+    return ((NegativeNum == 0) ? Value : -Value);
+}
+
+void get_str(char** p, char* Buffer) {
+  skip_blanks(p);
+
+  while (**p != 0 && **p != ' ') {
+    *Buffer = **p;
+    (*p)++;
+    Buffer++;
+  }
+
+  *Buffer = 0;
+}
+
+uint32_t get_hex_any(char** p, int DefaultValue, unsigned int NumOfNibble) {
+  uint32_t Value = 0;
+  unsigned char UseDefault;
+  // unsigned char   NumOfNibble = 8;  //Since we are returning uint32, max
+  // allowed is 4 bytes(8 nibbles).
+
+  UseDefault = 1;
+  skip_blanks(p);
+
+  while ((NumOfNibble) &&
+         (((**p) <= '9' && (**p) >= '0') || ((**p) <= 'f' && (**p) >= 'a') ||
+          ((**p) <= 'F' && (**p) >= 'A'))) {
+    if (**p >= 'a')
+      Value = Value * 16 + (**p) - 'a' + 10;
+    else if (**p >= 'A')
+      Value = Value * 16 + (**p) - 'A' + 10;
+    else
+      Value = Value * 16 + (**p) - '0';
+    UseDefault = 0;
+    (*p)++;
+    NumOfNibble--;
+  }
+
+  if (UseDefault)
+    return DefaultValue;
+  else
+    return Value;
+}
+uint32_t get_hex(char** p, int DefaultValue) {
+  return get_hex_any(p, DefaultValue, 8);
+}
+uint8_t get_hex_byte(char** p, int DefaultValue) {
+  return get_hex_any(p, DefaultValue, 2);
+}
+
+bool is_cmd(const char* cmd, const char* str) {
+  return (strlen(str) == strlen(cmd)) && (strncmp(cmd, str, strlen(str)) == 0);
+}
+
+typedef void(console_cmd_handler_t)(char* p);
+
+typedef struct {
+  const char* name;
+  console_cmd_handler_t* handler;
+  const char* help;
+  bool is_job;
+} cmd_t;
+
+extern const cmd_t console_cmd_list[];
+static int console_cmd_maxlen = 0;
+
+static void* cmdjob_handler(void* param) {
+  char* job_cmd = (char*)param;
+  LOG(INFO) << "cmdjob starting: " << job_cmd;
+  process_cmd(job_cmd, true);
+  LOG(INFO) << "cmdjob terminating";
+  free(job_cmd);
+  return nullptr;
+}
+
+static int create_cmdjob(char* cmd) {
+  CHECK(cmd);
+  char* job_cmd = (char*)calloc(1, strlen(cmd) + 1); /* freed in job handler */
+  if (job_cmd) {
+    strlcpy(job_cmd, cmd, strlen(job_cmd) + 1);
+    pthread_t thread_id;
+    int ret =
+        pthread_create(&thread_id, nullptr, cmdjob_handler, (void*)job_cmd);
+    LOG_IF(ERROR, ret != 0) << "Error during pthread_create";
+  } else {
+    LOG(INFO) << "Cannot Allocate memory for cmdjob: " << cmd;
+  }
+  return 0;
+}
+
+/*******************************************************************************
+ ** Load stack lib
+ *******************************************************************************/
+
+int HAL_load(void) {
+  int err = 0;
+  hw_module_t* module;
+  hw_device_t* device;
+  LOG(INFO) << "Loading HAL library and extensions";
+#if defined(OS_GENERIC)
+  err = hal_util_load_bt_library((hw_module_t const**)&module);
+#else
+  err = hw_get_module(BT_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
+#endif
+  if (!err) {
+    err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
+    if (!err) {
+      sBtDevice = (bluetooth_device_t*)device;
+      sBtInterface = sBtDevice->get_bluetooth_interface();
+    }
+  }
+  LOG(INFO) << "HAL library loaded, status: " << strerror(err);
+  return err;
+}
+
+int HAL_unload(void) {
+  int err = 0;
+  LOG(INFO) << "Unloading HAL lib";
+  sBtInterface = nullptr;
+  LOG(INFO) << "HAL library unloaded, status: " << strerror(err);
+  return err;
+}
+
+/*******************************************************************************
+ ** HAL test functions & callbacks
+ *******************************************************************************/
+
+void setup_test_env(void) {
+  int i = 0;
+  while (console_cmd_list[i].name) {
+    console_cmd_maxlen =
+        MAX(console_cmd_maxlen, (int)strlen(console_cmd_list[i].name));
+    i++;
+  }
+}
+
+void check_return_status(bt_status_t status) {
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(INFO) << "HAL REQUEST FAILED status : " << status << " ("
+              << dump_bt_status(status) << ")";
+  } else {
+    LOG(INFO) << "HAL REQUEST SUCCESS";
+  }
+}
+
+static void adapter_state_changed(bt_state_t state) {
+  int V1 = 1000, V2 = 2;
+  bt_property_t property = {BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, 4, &V1};
+  bt_property_t property1 = {BT_PROPERTY_ADAPTER_SCAN_MODE, 2, &V2};
+  bt_property_t property2 = {BT_PROPERTY_BDNAME, 6, (void*)"Fluoride_Test"};
+
+  global_adapter_state = state;
+
+  if (state == BT_STATE_ON) {
+    global_bt_enabled = true;
+    global_status = (bt_status_t)sBtInterface->set_adapter_property(&property1);
+    global_status = (bt_status_t)sBtInterface->set_adapter_property(&property);
+    global_status = (bt_status_t)sBtInterface->set_adapter_property(&property2);
+  } else {
+    global_bt_enabled = false;
+  }
+}
+
+static void adapter_properties_changed(bt_status_t status, int num_properties,
+                                       bt_property_t* properties) {
+  bt_bdaddr_t bd_addr;
+  bdstr_t bd_addr_str;
+  if (!properties) {
+    printf("properties is null\n");
+    return;
+  }
+  switch (properties->type) {
+    case BT_PROPERTY_BDADDR:
+      memcpy(bd_addr.address, properties->val,
+             MIN((size_t)properties->len, sizeof(bd_addr)));
+      bdaddr_to_string(&bd_addr, bd_addr_str, sizeof(bd_addr_str));
+      LOG(INFO) << "Local Bd Addr = " << bd_addr_str;
+      break;
+    default:
+      break;
+  }
+  return;
+}
+
+static void discovery_state_changed(bt_discovery_state_t state) {
+  LOG(INFO) << "Discovery State Updated: "
+            << (state == BT_DISCOVERY_STOPPED ? "STOPPED" : "STARTED");
+}
+
+static void pin_request_cb(bt_bdaddr_t* remote_bd_addr, bt_bdname_t* bd_name,
+                           uint32_t cod, bool min_16_digit) {
+  bt_pin_code_t pincode = {{0x31, 0x32, 0x33, 0x34}};
+
+  if (BT_STATUS_SUCCESS !=
+      sBtInterface->pin_reply(remote_bd_addr, true, 4, &pincode)) {
+    LOG(INFO) << "Pin Reply failed";
+  }
+}
+
+static void ssp_request_cb(bt_bdaddr_t* remote_bd_addr, bt_bdname_t* bd_name,
+                           uint32_t cod, bt_ssp_variant_t pairing_variant,
+                           uint32_t pass_key) {
+  LOG(INFO) << __func__ << ": device_name:" << bd_name->name
+            << ", pairing_variant: " << (int)pairing_variant
+            << ", passkey: " << unsigned(pass_key);
+  if (BT_STATUS_SUCCESS !=
+      sBtInterface->ssp_reply(remote_bd_addr, pairing_variant, true,
+                              pass_key)) {
+    LOG(ERROR) << "SSP Reply failed";
+  }
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+                                  bt_bdaddr_t* remote_bd_addr,
+                                  bt_bond_state_t state) {
+  LOG(INFO) << "Bond State Changed = " << state;
+  global_pair_state = state;
+}
+
+static void acl_state_changed(bt_status_t status, bt_bdaddr_t* remote_bd_addr,
+                              bt_acl_state_t state) {
+  bdstr_t bd_addr_str;
+  bdaddr_to_string(remote_bd_addr, bd_addr_str, sizeof(bd_addr_str));
+  LOG(INFO) << __func__ << ": remote_bd_addr=" << bd_addr_str << ", acl status="
+            << (state == BT_ACL_STATE_CONNECTED ? "ACL Connected"
+                                                : "ACL Disconnected");
+}
+
+static void dut_mode_recv(uint16_t opcode, uint8_t* buf, uint8_t len) {
+  LOG(INFO) << "DUT MODE RECV : NOT IMPLEMENTED";
+}
+
+static bt_callbacks_t bt_callbacks = {
+    sizeof(bt_callbacks_t),
+    adapter_state_changed,
+    adapter_properties_changed, /*adapter_properties_cb */
+    nullptr,                    /* remote_device_properties_cb */
+    nullptr,                    /* device_found_cb */
+    discovery_state_changed,    /* discovery_state_changed_cb */
+    pin_request_cb,             /* pin_request_cb  */
+    ssp_request_cb,             /* ssp_request_cb  */
+    bond_state_changed_cb,      /*bond_state_changed_cb */
+    acl_state_changed,          /* acl_state_changed_cb */
+    nullptr,                    /* thread_evt_cb */
+    dut_mode_recv,              /*dut_mode_recv_cb */
+    nullptr,                    /* le_test_mode_cb */
+    nullptr                     /* energy_info_cb */
+};
+
+static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb,
+                           void* data) {
+  static timer_t timer;
+  static bool timer_created;
+
+  if (!timer_created) {
+    struct sigevent sigevent;
+    memset(&sigevent, 0, sizeof(sigevent));
+    sigevent.sigev_notify = SIGEV_THREAD;
+    sigevent.sigev_notify_function = (void (*)(union sigval))cb;
+    sigevent.sigev_value.sival_ptr = data;
+    timer_create(CLOCK_MONOTONIC, &sigevent, &timer);
+    timer_created = true;
+  }
+
+  struct itimerspec new_value;
+  new_value.it_value.tv_sec = delay_millis / 1000;
+  new_value.it_value.tv_nsec = (delay_millis % 1000) * 1000 * 1000;
+  new_value.it_interval.tv_sec = 0;
+  new_value.it_interval.tv_nsec = 0;
+  timer_settime(timer, 0, &new_value, nullptr);
+
+  return true;
+}
+
+static int acquire_wake_lock(const char* lock_name) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int release_wake_lock(const char* lock_name) {
+  return BT_STATUS_SUCCESS;
+}
+
+static bt_os_callouts_t callouts = {
+    sizeof(bt_os_callouts_t), set_wake_alarm, acquire_wake_lock,
+    release_wake_lock,
+};
+
+void adapter_init(void) {
+  LOG(INFO) << __func__;
+  global_status = (bt_status_t)sBtInterface->init(&bt_callbacks);
+  if (global_status == BT_STATUS_SUCCESS) {
+    global_status = (bt_status_t)sBtInterface->set_os_callouts(&callouts);
+  }
+  check_return_status(global_status);
+}
+
+void adapter_enable(void) {
+  LOG(INFO) << __func__;
+  if (global_bt_enabled) {
+    LOG(INFO) << __func__ << ": Bluetooth is already enabled";
+    return;
+  }
+  global_status = (bt_status_t)sBtInterface->enable(global_strict_mode);
+  check_return_status(global_status);
+}
+
+void adapter_disable(void) {
+  LOG(INFO) << __func__;
+  if (!global_bt_enabled) {
+    LOG(INFO) << __func__ << ": Bluetooth is already disabled";
+    return;
+  }
+  global_status = (bt_status_t)sBtInterface->disable();
+  check_return_status(global_status);
+}
+void adapter_dut_mode_configure(char* p) {
+  LOG(INFO) << __func__;
+  if (!global_bt_enabled) {
+    LOG(INFO) << __func__
+              << ": Bluetooth must be enabled for test_mode to work.";
+    return;
+  }
+  int32_t mode = get_signed_int(&p, -1);  // arg1
+  if ((mode != 0) && (mode != 1)) {
+    LOG(INFO) << __func__ << "Please specify mode: 1 to enter, 0 to exit";
+    return;
+  }
+  global_status = (bt_status_t)sBtInterface->dut_mode_configure(mode);
+  check_return_status(global_status);
+}
+
+void adapter_cleanup(void) {
+  LOG(INFO) << __func__;
+  sBtInterface->cleanup();
+}
+
+/*******************************************************************************
+ ** Console commands
+ *******************************************************************************/
+
+void do_help(char* p) {
+  int i = 0;
+  char line[128];
+  int pos = 0;
+
+  while (console_cmd_list[i].name != nullptr) {
+    pos = snprintf(line, sizeof(line), "%s", (char*)console_cmd_list[i].name);
+    printf("%s %s\n", (char*)line, (char*)console_cmd_list[i].help);
+    i++;
+  }
+}
+
+void do_quit(char* p) { console_shutdown(); }
+
+/*******************************************************************
+ *
+ *  BT TEST  CONSOLE COMMANDS
+ *
+ *  Parses argument lists and passes to API test function
+ *
+ */
+
+void do_init(char* p) { adapter_init(); }
+
+void do_enable(char* p) { adapter_enable(); }
+
+void do_disable(char* p) { adapter_disable(); }
+
+void do_cleanup(char* p) { adapter_cleanup(); }
+
+/**
+ * MCAP API commands
+ */
+void do_mcap_register(char* p) {
+  uint16_t ctrl_psm = get_hex(&p, 0);  // arg1
+  uint16_t data_psm = get_hex(&p, 0);  // arg2
+  uint16_t sec_mask = get_int(&p, 0);  // arg3
+  printf("%s: ctrl_psm=0x%04x, data_psm=0x%04x, sec_mask=0x%04x\n", __func__,
+         ctrl_psm, data_psm, sec_mask);
+  if (!ctrl_psm || !data_psm) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  sMcapTestApp->Register(ctrl_psm, data_psm, sec_mask, mcap_ctrl_callback);
+  printf("%s: mcap_handle=%d\n", __func__, sMcapTestApp->GetHandle());
+}
+
+void do_mcap_deregister(char* p) {
+  printf("%s: mcap_handle=%d\n", __func__, sMcapTestApp->GetHandle());
+  sMcapTestApp->Deregister();
+  printf("%s: handle=%d\n", __func__, sMcapTestApp->GetHandle());
+}
+
+void do_mcap_create_mdep(char* p) {
+  int type = get_int(&p, -1);  // arg1
+  printf("%s: mcap_handle=%d, type=%d\n", __func__, sMcapTestApp->GetHandle(),
+         type);
+  bool ret = sMcapTestApp->CreateMdep(type, MCA_NUM_MDLS, mcap_data_cb);
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_delete_mdep(char* p) {
+  uint8_t mdep_handle = get_int(&p, 0);
+  printf("%s: mcap_handle=%d, mdep_handle=%d\n", __func__,
+         sMcapTestApp->GetHandle(), mdep_handle);
+  if (!mdep_handle) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMdep* mcap_mdep = sMcapTestApp->FindMdepByHandle(mdep_handle);
+  if (!mcap_mdep) {
+    LOG(ERROR) << "No MDEP for handle " << (int)mdep_handle;
+    return;
+  }
+  bool ret = mcap_mdep->Delete();
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_connect_mcl(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  uint16_t ctrl_psm = get_hex(&p, 0);  // arg2
+  uint16_t sec_mask = get_int(&p, 0);  // arg3
+  printf("%s: mcap_handle=%d, ctrl_psm=0x%04x, secMask=0x%04x, bd_addr=%s\n",
+         __func__, sMcapTestApp->GetHandle(), ctrl_psm, sec_mask, buf);
+  if (!ctrl_psm || !valid_bd_addr) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  bool ret = sMcapTestApp->ConnectMcl(bd_addr, ctrl_psm, sec_mask);
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_disconnect_mcl(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  printf("%s: bd_addr=%s\n", __func__, buf);
+  if (!valid_bd_addr) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(ERROR) << "No MCL for bd_addr " << buf;
+    return;
+  }
+  bool ret = mcap_mcl->Disconnect();
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_create_mdl(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  uint16_t mdep_handle = get_int(&p, 0);  // arg2
+  uint16_t data_psm = get_hex(&p, 0);     // arg3
+  uint16_t mdl_id = get_int(&p, 0);       // arg4
+  uint8_t peer_dep_id = get_int(&p, 0);   // arg5
+  uint8_t cfg = get_hex(&p, 0);           // arg6
+  int do_not_connect = get_int(&p, 0);    // arg7
+  printf(
+      "%s: bd_addr=%s, mdep_handle=%d, data_psm=0x%04x, mdl_id=%d,"
+      " peer_dep_id=%d, cfg=0x%02x, do_not_connect=%d\n",
+      __func__, buf, mdep_handle, data_psm, mdl_id, peer_dep_id, cfg,
+      do_not_connect);
+  if (!data_psm || !peer_dep_id || !valid_bd_addr || !mdep_handle) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(ERROR) << "No MCL for bd_addr " << buf;
+    return;
+  }
+  bool ret = mcap_mcl->CreateMdl(mdep_handle, data_psm, mdl_id, peer_dep_id,
+                                 cfg, !do_not_connect);
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_data_channel_config(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  printf("%s: bd_addr=%s\n", __func__, buf);
+  if (!valid_bd_addr) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(ERROR) << "No MCL for bd_addr " << buf;
+    return;
+  }
+  bool ret = mcap_mcl->DataChannelConfig();
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_abort_mdl(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  printf("%s: bd_addr=%s\n", __func__, buf);
+  if (!valid_bd_addr) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(ERROR) << "No MCL for bd_addr " << buf;
+    return;
+  }
+  bool ret = mcap_mcl->AbortMdl();
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_delete_mdl(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  uint16_t mdl_id = get_int(&p, 0);  // arg2
+  printf("%s: bd_addr=%s, mdl_id=%d\n", __func__, buf, mdl_id);
+  if (!valid_bd_addr) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(ERROR) << "No MCL for bd_addr " << buf;
+    return;
+  }
+  bool ret = mcap_mcl->DeleteMdl(mdl_id);
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_close_mdl(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  uint16_t mdl_id = get_int(&p, 0);  // arg2
+  printf("%s: bd_addr=%s, mdl_id=%d\n", __func__, buf, mdl_id);
+  if (!valid_bd_addr || !mdl_id) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(ERROR) << "No MCL for bd_addr " << buf;
+    return;
+  }
+  McapMdl* mcap_mdl = mcap_mcl->FindMdlById(mdl_id);
+  if (!mcap_mdl) {
+    LOG(ERROR) << "No MDL for ID " << (int)mdl_id;
+    return;
+  }
+  bool ret = mcap_mdl->Close();
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_mcap_reconnect_mdl(char* p) {
+  char buf[64];
+  get_str(&p, buf);  // arg1
+  bt_bdaddr_t bd_addr;
+  bool valid_bd_addr = string_to_bdaddr(buf, &bd_addr);
+  uint16_t data_psm = get_hex(&p, 0);  // arg1
+  uint16_t mdl_id = get_int(&p, 0);    // arg2
+  printf("%s: data_psm=0x%04x, mdl_id=%d\n", __func__, data_psm, mdl_id);
+  if (!valid_bd_addr) {
+    printf("%s: Invalid Parameters\n", __func__);
+    return;
+  }
+  McapMcl* mcap_mcl = sMcapTestApp->FindMclByPeerAddress(bd_addr);
+  if (!mcap_mcl) {
+    LOG(ERROR) << "No MCL for bd_addr " << buf;
+    return;
+  }
+  McapMdl* mcap_mdl = mcap_mcl->FindMdlById(mdl_id);
+  if (!mcap_mdl) {
+    LOG(ERROR) << "No MDL for ID " << (int)mdl_id;
+    return;
+  }
+  bool ret = mcap_mdl->Reconnect(data_psm);
+  printf("%s: %s\n", __func__, ret ? "SUCCESS" : "FAIL");
+}
+
+static void do_pairing(char* p) {
+  bt_bdaddr_t bd_addr;
+  if (!string_to_bdaddr(p, &bd_addr)) {
+    LOG(ERROR) << "Invalid Bluetooth address " << p;
+    return;
+  }
+  if (BT_STATUS_SUCCESS !=
+      sBtInterface->create_bond(&bd_addr, BT_TRANSPORT_BR_EDR)) {
+    LOG(ERROR) << "Failed to Initiate Pairing";
+    return;
+  }
+}
+
+/** CONSOLE COMMAND TABLE */
+
+const cmd_t console_cmd_list[] = {
+    /* INTERNAL */
+    {"help", do_help, "", 0},
+    {"quit", do_quit, "", 0},
+    /* API CONSOLE COMMANDS */
+    /* Init and Cleanup shall be called automatically */
+    {"enable_bluetooth", do_enable, "", 0},
+    {"disable_bluetooth", do_disable, "", 0},
+    {"pair", do_pairing, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+    {"register", do_mcap_register,
+     "ctrl_psm<hex> data_psm<hex> security_mask<0-10>", 0},
+    {"deregister", do_mcap_deregister, "", 0},
+    {"create_mdep", do_mcap_create_mdep, "type<0-Echo, 1-Normal>", 0},
+    {"delete_mdep", do_mcap_delete_mdep, "mdep_handle<int>", 0},
+    {"connect_mcl", do_mcap_connect_mcl,
+     "BD_ADDR<xx:xx:xx:xx:xx:xx> ctrl_psm<hex> security_mask<0-10>", 0},
+    {"disconnect_mcl", do_mcap_disconnect_mcl, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+    {"create_mdl", do_mcap_create_mdl,
+     "BD_ADDR<xx:xx:xx:xx:xx:xx> mdep_handle<int> data_psm<hex> mdl_id<int> "
+     "peer_dep_id<int> cfg<hex> "
+     "do_not_connect<0-connect,1-wait_for_data_channel_config>",
+     0},
+    {"data_channel_config", do_mcap_data_channel_config,
+     "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+    {"abort_mdl", do_mcap_abort_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx>", 0},
+    {"close_mdl", do_mcap_close_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx> mdl_id<int>",
+     0},
+    {"delete_mdl", do_mcap_delete_mdl, "BD_ADDR<xx:xx:xx:xx:xx:xx> mdl_id<int>",
+     0},
+    {"reconnect_mdl", do_mcap_reconnect_mdl,
+     "BD_ADDR<xx:xx:xx:xx:xx:xx> data_psm<hex> mdl_id<int>", 0},
+    /* last entry */
+    {nullptr, nullptr, "", 0},
+};
+
+/** Main console command handler */
+
+static void process_cmd(char* p, bool is_job) {
+  char cmd[2048];
+  int i = 0;
+  char* p_saved = p;
+
+  get_str(&p, cmd);  // arg1
+
+  /* table commands */
+  while (console_cmd_list[i].name != nullptr) {
+    if (is_cmd(cmd, console_cmd_list[i].name)) {
+      if (!is_job && console_cmd_list[i].is_job)
+        create_cmdjob(p_saved);
+      else {
+        console_cmd_list[i].handler(p);
+      }
+      return;
+    }
+    i++;
+  }
+  LOG(ERROR) << "Unknown command: " << p_saved;
+  do_help(nullptr);
+}
+
+int main(int argc, char* argv[]) {
+  setbuf(stdout, NULL);
+#if !defined(OS_GENERIC)
+  config_permissions();
+#endif
+  LOG(INFO) << "Fluoride MCAP test app is starting";
+
+  if (HAL_load() < 0) {
+    fprintf(stderr, "%s: HAL failed to initialize, exit\n", __func__);
+    unlink(PID_FILE);
+    exit(0);
+  }
+
+  setup_test_env();
+
+  /* Automatically perform the init */
+  adapter_init();
+  sleep(2);
+  adapter_enable();
+  sleep(2);
+  sMcapTestInterface =
+      (btmcap_test_interface_t*)sBtInterface->get_profile_interface(
+          BT_TEST_INTERFACE_MCAP_ID);
+  sMcapTestInterface->init();
+  sMcapTestApp = new McapTestApp(sMcapTestInterface);
+
+  /* Main loop */
+  char line[2048];
+  while (!global_main_done) {
+    memset(line, '\0', sizeof(line));
+    /* command prompt */
+    printf(">");
+    fflush(stdout);
+    fgets(line, sizeof(line), stdin);
+    if (line[0] != '\0') {
+      /* Remove line feed */
+      line[strlen(line) - 1] = 0;
+      if (strlen(line) != 0) process_cmd(line, false);
+    }
+  }
+  adapter_cleanup();
+  HAL_unload();
+  LOG(INFO) << "Fluoride MCAP test app is terminating";
+
+  return 0;
+}