| /* |
| * 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) { |
| printf("%s: peer_bd_addr=%s,l2cap_disconnect_reason=0x%04x\n", __func__, |
| mcap_disconnect_ind->bd_addr.ToString().c_str(), |
| mcap_disconnect_ind->reason); |
| } |
| |
| static void print_mcap_event(const tMCA_CONNECT_IND* mcap_connect_ind) { |
| printf("%s: peer_bd_addr=%s, peer_mtu=%d \n", __func__, |
| mcap_connect_ind->bd_addr.ToString().c_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 RawAddress& 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 RawAddress& bd_addr) { |
| for (McapMcl& mcl : _mcl_list) { |
| if (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; |
| RawAddress bd_addr = 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); |
| RawAddress bd_addr = p_data->disconnect_ind.bd_addr; |
| mcap_mcl = FindMclByPeerAddress(bd_addr); |
| if (!mcap_mcl) { |
| LOG(ERROR) << "No MCL for BD addr " << bd_addr; |
| break; |
| } |
| if (!mcap_mcl->IsConnected()) { |
| LOG(WARNING) << "MCL for " << bd_addr << " 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 |