From 3a2b8c35a5bb21809e98e657bb8d8130dd4e782a Mon Sep 17 00:00:00 2001 From: Gene Gallagher Date: Thu, 2 Apr 2026 01:17:39 +0000 Subject: [PATCH] RDKEMW-13607: merge pair code with mac hash methods --- include/ctrlm_ipc.h | 1 - src/ble/ctrlm_ble_network.cpp | 28 +---- src/ble/ctrlm_ble_rcu_interface.cpp | 25 ---- src/ble/ctrlm_ble_rcu_interface.h | 1 - src/ble/hal/blercu/blercucontroller.cpp | 55 -------- src/ble/hal/blercu/blercucontroller.h | 1 - src/ble/hal/blercu/blercucontroller_p.h | 1 - .../hal/blercu/blercupairingstatemachine.cpp | 59 ++------- .../hal/blercu/blercupairingstatemachine.h | 1 - .../configsettings/configmodelsettings.cpp | 117 +++++++++++------- src/ctrlm_ir_controller.cpp | 1 - 11 files changed, 86 insertions(+), 204 deletions(-) diff --git a/include/ctrlm_ipc.h b/include/ctrlm_ipc.h index 341d4ff9..32d0dea1 100644 --- a/include/ctrlm_ipc.h +++ b/include/ctrlm_ipc.h @@ -753,7 +753,6 @@ typedef struct { typedef struct { unsigned char api_revision; ///< Revision of this API ctrlm_network_id_t network_id; ///< IN - Identifier of network - unsigned int key_code; ///< IN - Key code from device unsigned int pair_code; ///< IN - Pairing code from device ctrlm_iarm_call_result_t result; ///< OUT - return code of the operation } ctrlm_iarm_call_StartPairWithCode_params_t; diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index 03e1322f..daae2ffe 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -740,29 +740,13 @@ void ctrlm_obj_network_ble_t::req_process_pair_with_code(void *data, int size) { XLOGD_FATAL("Network is not ready!"); } else { if (ble_rcu_interface_) { - - if (dqm->params->key_code == KEY_BLUETOOTH) { - // KEY_BLUETOOTH means the pairing code is random 3 digit code embedded in the name - // so use pairWithCode - if (!ble_rcu_interface_->pairWithCode(dqm->params->pair_code)) { - // don't log error here, pairWithCode will handle printing the error. We do - // this because there is an error that is merely a warning that we don't want - // logged because it only confuses those analyzing the logs. - // XLOGD_ERROR("failed to start pairing with code"); - } else { - dqm->params->result = CTRLM_IARM_CALL_RESULT_SUCCESS; - } + if (!ble_rcu_interface_->pairWithCode(dqm->params->pair_code)) { + // don't log error here, pairWithCode will handle printing the error. We do + // this because there is an error that is merely a warning that we don't want + // logged because it only confuses those analyzing the logs. + // XLOGD_ERROR("failed to start pairing with code"); } else { - // if key_code is either not available or KEY_CONNECT, it means the pairing code is a - // hash of the MAC, so use pairWithMacHash - if (!ble_rcu_interface_->pairWithMacHash(dqm->params->pair_code)) { - // don't log error here, pairWithMacHash will handle printing the error. We do - // this because there is an error that is merely a warning that we don't want - // logged because it only confuses those analyzing the logs. - // XLOGD_ERROR("failed to start pairing with MAC hash"); - } else { - dqm->params->result = CTRLM_IARM_CALL_RESULT_SUCCESS; - } + dqm->params->result = CTRLM_IARM_CALL_RESULT_SUCCESS; } } } diff --git a/src/ble/ctrlm_ble_rcu_interface.cpp b/src/ble/ctrlm_ble_rcu_interface.cpp index 6abecad1..f90cdc67 100644 --- a/src/ble/ctrlm_ble_rcu_interface.cpp +++ b/src/ble/ctrlm_ble_rcu_interface.cpp @@ -697,31 +697,6 @@ bool ctrlm_ble_rcu_interface_t::pairWithCode(unsigned int code) XLOGD_ERROR("controller failed to start pairing, %s: %s", error.name().c_str(), error.message().c_str()); } return false; - } else { - XLOGD_INFO("started pairing with code %hhu", code); - } - return true; -} - -bool ctrlm_ble_rcu_interface_t::pairWithMacHash(unsigned int code) -{ - if (!m_controller) { - XLOGD_ERROR("m_controller is NULL!!!"); - return false; - } - - if (!m_controller->startPairingWithMacHash((uint8_t) code)) { - BleRcuError error = m_controller->lastError(); - - // Remote will continually send out IR pairing signals until the BLE pair request - // has been received. This means that the "Already in pairing state" error is normal. - // Let's omit this error print because it only serves to confuse those analyzing the logs. - if (error.message() != "Already in pairing state") { - XLOGD_ERROR("controller failed to start pairing, %s: %s", error.name().c_str(), error.message().c_str()); - } - return false; - } else { - XLOGD_INFO("started pairing with MAC hash 0x%X", code); } return true; } diff --git a/src/ble/ctrlm_ble_rcu_interface.h b/src/ble/ctrlm_ble_rcu_interface.h index a6526e1b..c9209c6f 100644 --- a/src/ble/ctrlm_ble_rcu_interface.h +++ b/src/ble/ctrlm_ble_rcu_interface.h @@ -92,7 +92,6 @@ class ctrlm_ble_rcu_interface_t void handleDeepsleep(bool wakingUp); bool pairWithCode(unsigned int code); - bool pairWithMacHash(unsigned int code); bool pairWithMacAddrs(const std::vector &macAddrList); bool pairAutoWithTimeout(int timeoutMs); bool pairCancel(); diff --git a/src/ble/hal/blercu/blercucontroller.cpp b/src/ble/hal/blercu/blercucontroller.cpp index eb1b39ac..7b669bda 100644 --- a/src/ble/hal/blercu/blercucontroller.cpp +++ b/src/ble/hal/blercu/blercucontroller.cpp @@ -271,61 +271,6 @@ bool BleRcuControllerImpl::startPairingWithCode(uint8_t pairingCode) return true; } -// ----------------------------------------------------------------------------- -/*! - \fn bool BleRcuController::startPairingWithMacHash(uint8_t macHash) - - Attempts to start the pairing procedure looking for a device that has - a MAC address matching the supplied MAC hash. - - The MAC hash is calculated by adding all the bytes of the MAC address - together, and masking with 0xFF. - e.g., MAC = AA:BB:CC:DD:EE:FF, hash = (AA+BB+CC+DD+EE+FF) & 0xFF - - If the controller is currently in pairing mode this method will fail and - return \c false. If the bluetooth adaptor is not available or not powered - then this function will also fail and return \c false. - - If \c false is returned use BleRcuController::lastError() to get the failure - reason. - - \note This object doesn't actually run the pairing procedure, instead it - just starts and stops the \l{BleRcuPairingStateMachine} object. - - \see cancelPairing(), isPairing() & pairingCode() - */ -bool BleRcuControllerImpl::startPairingWithMacHash(uint8_t macHash) -{ - if (m_pairingStateMachine.isRunning()) { - if (m_pairingStateMachine.isAutoPairing()) { - - XLOGD_WARN("received targeted pairing request in auto pair mode, cancelling auto pair first..."); - - m_ignorePairingFailure = true; - m_pairingStateMachine.stop(); - - m_lastError = BleRcuError(BleRcuError::Busy, "Cancelling auto pair operation, send another request."); - } else { - XLOGD_DEBUG("requested pairing while currently running a pairing operation, ignoring request"); - m_lastError = BleRcuError(BleRcuError::Busy, "Already in pairing state"); - } - return false; - } - - // check that the manager has powered on the adapter, without this we - // obviously can't scan / pair / etc. The only time the adaptor should - // (legitimately) be unavailable is right at start-up - if (!m_adapter->isAvailable() || !m_adapter->isPowered()) { - m_lastError = BleRcuError(BleRcuError::General, "Adaptor not available or not powered"); - return false; - } - - // start the pairing process - m_pairingStateMachine.startWithMacHash(macHash); - - return true; -} - // ----------------------------------------------------------------------------- /*! \fn bool BleRcuController::startPairingWithList(std::vector macAddrList) diff --git a/src/ble/hal/blercu/blercucontroller.h b/src/ble/hal/blercu/blercucontroller.h index a0b678cf..770d4d07 100644 --- a/src/ble/hal/blercu/blercucontroller.h +++ b/src/ble/hal/blercu/blercucontroller.h @@ -73,7 +73,6 @@ class BleRcuController virtual bool startPairingAutoWithTimeout(int timeoutMs) = 0; virtual bool startPairingWithCode(uint8_t pairingCode) = 0; - virtual bool startPairingWithMacHash(uint8_t macHash) = 0; virtual bool startPairingWithList(const std::vector &macAddrList) = 0; virtual bool cancelPairing() = 0; diff --git a/src/ble/hal/blercu/blercucontroller_p.h b/src/ble/hal/blercu/blercucontroller_p.h index 7d8411d3..ccbe62ea 100644 --- a/src/ble/hal/blercu/blercucontroller_p.h +++ b/src/ble/hal/blercu/blercucontroller_p.h @@ -61,7 +61,6 @@ class BleRcuControllerImpl final : public BleRcuController bool startPairingAutoWithTimeout(int timeoutMs) override; bool startPairingWithCode(uint8_t pairingCode) override; - bool startPairingWithMacHash(uint8_t macHash) override; bool startPairingWithList(const std::vector &macAddrList) override; bool cancelPairing() override; diff --git a/src/ble/hal/blercu/blercupairingstatemachine.cpp b/src/ble/hal/blercu/blercupairingstatemachine.cpp index bb769b41..925e053e 100644 --- a/src/ble/hal/blercu/blercupairingstatemachine.cpp +++ b/src/ble/hal/blercu/blercupairingstatemachine.cpp @@ -198,8 +198,7 @@ bool BleRcuPairingStateMachine::isRunning() const This special state is needed because auto pairing consists of running a scan for an undeterminate amount of time until one of the supported devices listed in the config file is found. We want to be able to cancel this operation if another - pair request comes in that targets a specific device (like pairWithCode or - pairWithMacHash) + pair request comes in that targets a specific device (like pairWithCode) */ bool BleRcuPairingStateMachine::isAutoPairing() const @@ -272,9 +271,10 @@ void BleRcuPairingStateMachine::startWithCode(uint8_t pairingCode) // clear the list of addresses to filter for m_pairingMacList.clear(); - // store the pairing code + // the pairing could be either the mac hash or the code embedded in the name, + // so store both for use when processing found devices m_pairingCode = pairingCode; - m_pairingMacHash = -1; + m_pairingMacHash = pairingCode; // create list of supported remotes regex to match to the name of the device m_targetedPairingNames.clear(); @@ -296,47 +296,7 @@ void BleRcuPairingStateMachine::startWithCode(uint8_t pairingCode) m_pairingAttempts++; m_pairingSucceeded = false; - XLOGD_INFO("started pairing using name prefix code %03d", m_pairingCode); -} - -// ----------------------------------------------------------------------------- -/*! - Starts the state machine using the supplied \a pairingCode and - \a namePrefixes. - - */ -void BleRcuPairingStateMachine::startWithMacHash(uint8_t macHash) -{ - // sanity check the statemachine is not already running - if (m_stateMachine.isRunning()) { - XLOGD_WARN("state machine already running"); - return; - } - - m_discoveryTimeout = m_discoveryTimeoutDefault; - m_isAutoPairing = false; - - // clear the target device - m_targetAddress.clear(); - - // clear the pairing code - m_pairingCode = -1; - - // clear the list of addresses to filter for - m_pairingMacList.clear(); - - // store the MAC hash - m_pairingMacHash = macHash; - - // clear the maps, we are trying to pair to a specific device using a hash of the MAC address - m_targetedPairingNames.clear(); - - // start the state machine - m_stateMachine.start(); - - m_pairingAttempts++; - m_pairingSucceeded = false; - XLOGD_INFO("started pairing, searching for device with MAC hash 0x%02X", m_pairingMacHash); + XLOGD_INFO("started pairing, searching for device with prefix code %03d or MAC hash 0x%02X", m_pairingCode, m_pairingMacHash); } // ----------------------------------------------------------------------------- @@ -869,23 +829,22 @@ void BleRcuPairingStateMachine::processDevice(const BleAddress &address, } if (it_name == m_targetedPairingNames.end()) { - // Device not found through conventional means, see if we are pairing based on MAC hash - // Because if we are pairing based on MAC hash, m_targetedPairingNames is first cleared if (m_pairingMacHash != -1) { + // Device not found through name match, see if there is a MAC hash match // Check if MAC hash matches int macHash = 0; for (int i = 0; i < 6; ++i) { macHash += (int)address[i]; } macHash &= 0xFF; - XLOGD_INFO("Pairing based on MAC hash, requested MAC hash = 0x%02X, this device = 0x%02X (%s, %s)", + XLOGD_INFO("Checking for MAC hash match, requested MAC hash = 0x%02X, this device = 0x%02X (%s, %s)", m_pairingMacHash, macHash, name.c_str(), address.toString().c_str()); if (m_pairingMacHash != macHash) { return; } - // Device not found through conventional means or MAC hash so let's check a mac address list - // Pairing via a mac address list clears supported names and the pairing mac hash } else if (m_pairingMacList.size() != 0) { + // Device not found through name match or MAC hash so let's check a mac address list + // Pairing via a mac address list clears supported names and the pairing mac hash if (m_pairingMacList.size() != 0) { // check if the mac address matches any of the ones in the filter list (if it exists) bool found = false; diff --git a/src/ble/hal/blercu/blercupairingstatemachine.h b/src/ble/hal/blercu/blercupairingstatemachine.h index d21b46b1..c90c31af 100644 --- a/src/ble/hal/blercu/blercupairingstatemachine.h +++ b/src/ble/hal/blercu/blercupairingstatemachine.h @@ -77,7 +77,6 @@ class BleRcuPairingStateMachine void start(const BleAddress &target, const std::string &name); void startAutoWithTimeout(int timeoutMs); void startWithCode(uint8_t pairingCode); - void startWithMacHash(uint8_t macHash); void startWithMacList(const std::vector &macList); void stop(); diff --git a/src/ble/hal/configsettings/configmodelsettings.cpp b/src/ble/hal/configsettings/configmodelsettings.cpp index de8c1dee..a9eeea56 100644 --- a/src/ble/hal/configsettings/configmodelsettings.cpp +++ b/src/ble/hal/configsettings/configmodelsettings.cpp @@ -137,47 +137,66 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) } } - // (optional) pairingFormat field - obj = json_object_get(json, "pairingNameFormat"); - if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid 'pairingNameFormat' field, not fatal, continuing to parse info"); - } else { - m_pairingNameFormat = json_string_value(obj); + // Advertising names field + json_t *advertisingNamesObj = json_object_get(json, "advertisingNames"); + if (!advertisingNamesObj || !json_is_object(advertisingNamesObj)) { + XLOGD_ERROR("missing or invalid 'advertisingNames' field"); + return; } - - // scanNameFormat field - obj = json_object_get(json, "scanNameFormat"); + // advertisingNames.regexPairing field + obj = json_object_get(advertisingNamesObj, "regexPairing"); if (!obj || !json_is_string(obj)) { - XLOGD_ERROR("invalid 'scanNameFormat' field, this is required so returning..."); + XLOGD_ERROR("invalid required 'advertisingNames.regexPairing' field, aborting..."); return; } m_scanNameMatcher = std::regex(json_string_value(obj), std::regex_constants::ECMAScript); - XLOGD_INFO("Pairing name regex <%s>", json_string_value(obj)); - - // (optional) connectNameFormat field - obj = json_object_get(json, "connectNameFormat"); - if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid 'connectNameFormat' field, not fatal, setting to scanNameFormat"); - m_connectNameMatcher = m_scanNameMatcher; + m_connectNameMatcher = m_scanNameMatcher; + XLOGD_INFO("Pairing and reconnect advertising name regex <%s>", json_string_value(obj)); + + + // advertisingNames.optional array + json_t *optionalNames = json_object_get(advertisingNamesObj, "optional"); + if (!optionalNames || !json_is_object(optionalNames)) { + XLOGD_INFO("missing or invalid 'optionalNames' field, not fatal, continuing..."); } else { - m_connectNameMatcher = std::regex(json_string_value(obj), std::regex_constants::ECMAScript); + + // (optional) formatSpecifierTargetedPairing field + obj = json_object_get(optionalNames, "formatSpecifierTargetedPairing"); + if (!obj || !json_is_string(obj)) { + XLOGD_INFO("invalid 'formatSpecifierTargetedPairing' field, not fatal, continuing..."); + } else { + m_pairingNameFormat = json_string_value(obj); + XLOGD_INFO("Pairing name printf format specifier for targeted pairing <%s>", m_pairingNameFormat.c_str()); + } + + // (optional) regexReconnect field + obj = json_object_get(optionalNames, "regexReconnect"); + if (!obj || !json_is_string(obj)) { + XLOGD_INFO("invalid or missing 'regexReconnect' field, not fatal, continuing..."); + } else { + m_connectNameMatcher = std::regex(json_string_value(obj), std::regex_constants::ECMAScript); + XLOGD_INFO("Reconnect advertising name regex overridden with <%s>", json_string_value(obj)); + } } + // (optional) otaProductName field obj = json_object_get(json, "otaProductName"); if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid 'otaProductName' field, not fatal, continuing to parse info"); + XLOGD_INFO("invalid 'otaProductName' field, not fatal, continuing to parse info"); } else { m_otaProductName = json_string_value(obj); + XLOGD_INFO("parsed 'otaProductName' field value <%s>", m_otaProductName.c_str()); } // (optional) typeZ field obj = json_object_get(json, "typeZ"); if (obj) { if (!json_is_boolean(obj)) { - XLOGD_WARN("invalid 'typeZ' field, not fatal, continuing to parse info"); + XLOGD_INFO("invalid 'typeZ' field, not fatal, continuing to parse info"); } else { m_typeZ = json_is_true(obj); + XLOGD_INFO("parsed 'typeZ' field value <%s>", m_typeZ ? "true" : "false"); } } @@ -185,9 +204,10 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) obj = json_object_get(json, "connParamUpdateBeforeOtaVersion"); if (obj) { if (!json_is_string(obj)) { - XLOGD_WARN("invalid 'connParamUpdateBeforeOtaVersion' field, not fatal, continuing to parse info"); + XLOGD_INFO("invalid 'connParamUpdateBeforeOtaVersion' field, not fatal, continuing to parse info"); } else { m_connParamUpdateBeforeOtaVersion = json_string_value(obj); + XLOGD_INFO("parsed 'connParamUpdateBeforeOtaVersion' field value <%s>", m_connParamUpdateBeforeOtaVersion.c_str()); } } @@ -195,9 +215,10 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) obj = json_object_get(json, "upgradePauseVersion"); if (obj) { if (!json_is_string(obj)) { - XLOGD_WARN("invalid 'upgradePauseVersion' field, not fatal, continuing to parse info"); + XLOGD_INFO("invalid 'upgradePauseVersion' field, not fatal, continuing to parse info"); } else { m_upgradePauseVersion = json_string_value(obj); + XLOGD_INFO("parsed 'upgradePauseVersion' field value <%s>", m_upgradePauseVersion.c_str()); } } @@ -205,18 +226,20 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) obj = json_object_get(json, "upgradeStuckVersion"); if (obj) { if (!json_is_string(obj)) { - XLOGD_WARN("invalid 'upgradeStuckVersion' field, not fatal, continuing to parse info"); + XLOGD_INFO("invalid 'upgradeStuckVersion' field, not fatal, continuing to parse info"); } else { m_upgradeStuckVersion = json_string_value(obj); + XLOGD_INFO("parsed 'upgradeStuckVersion' field value <%s>", m_upgradeStuckVersion.c_str()); } } // (optional) standbyMode field obj = json_object_get(json, "standbyMode"); if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid or missing 'standbyMode' field, not fatal, continuing to parse info"); + XLOGD_INFO("invalid or missing 'standbyMode' field, not fatal, continuing to parse info"); } else { m_standbyMode = json_string_value(obj); + XLOGD_INFO("parsed 'standbyMode' field value <%s>", m_standbyMode.c_str()); } // (optional) voiceKeyCode field @@ -231,6 +254,7 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) } else { m_voiceKeyCode = keyCode; m_voiceKeyCodePresent = true; + XLOGD_INFO("parsed 'voiceKeyCode' field value <%u>", m_voiceKeyCode); } } } @@ -248,6 +272,7 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) return; } string typeStr = json_string_value(obj); + XLOGD_INFO("parsed 'service.type' field value <%s>", typeStr.c_str()); if (typeStr.compare("gatt") == 0) { m_servicesType = ConfigModelSettings::GattServiceType; @@ -285,38 +310,38 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) } servicesRequiredStr = servicesRequiredStr.substr(0, servicesRequiredStr.length() - 2); // Remove the trailing comma and space - XLOGD_DEBUG("m_servicesRequired = <%s>", servicesRequiredStr.c_str()); + XLOGD_INFO("m_servicesRequired = <%s>", servicesRequiredStr.c_str()); // services.optional array json_t *optionalArray = json_object_get(servicesObj, "optional"); if (!optionalArray || !json_is_array(optionalArray)) { - XLOGD_ERROR( "missing or invalid 'services.optional' field"); - return; - } - array_size = json_array_size(optionalArray); + XLOGD_INFO( "missing or invalid 'services.optional' field, not fatal, continuing..."); + } else { + array_size = json_array_size(optionalArray); - for (unsigned int i = 0; i < array_size; i++) { - obj = json_array_get(optionalArray, i); - if (!obj || !json_is_string(obj)) { - XLOGD_ERROR("invalid 'services.optional' array entry"); - return; + for (unsigned int i = 0; i < array_size; i++) { + obj = json_array_get(optionalArray, i); + if (!obj || !json_is_string(obj)) { + XLOGD_ERROR("invalid 'services.optional' array entry"); + return; + } + string serviceStr = json_string_value(obj); + + if (!BleUuid().doesServiceExist(serviceStr)) { + XLOGD_ERROR("invalid service name <%s>", serviceStr.c_str()); + return; + } + m_servicesOptional.insert(serviceStr); } - string serviceStr = json_string_value(obj); - if (!BleUuid().doesServiceExist(serviceStr)) { - XLOGD_ERROR("invalid service name <%s>", serviceStr.c_str()); - return; + std::string servicesOptionalStr; + for (const auto& service : m_servicesOptional) { + servicesOptionalStr += service + ", "; } - m_servicesOptional.insert(serviceStr); - } + servicesOptionalStr = servicesOptionalStr.substr(0, servicesOptionalStr.length() - 2); // Remove the trailing comma and space - std::string servicesOptionalStr; - for (const auto& service : m_servicesOptional) { - servicesOptionalStr += service + ", "; + XLOGD_INFO("m_servicesOptional = <%s>\n", servicesOptionalStr.c_str()); } - servicesOptionalStr = servicesOptionalStr.substr(0, servicesOptionalStr.length() - 2); // Remove the trailing comma and space - - XLOGD_DEBUG("m_servicesOptional = <%s>\n", servicesOptionalStr.c_str()); // (optional) connectionParams // if (json.contains("connectionParams")) { diff --git a/src/ctrlm_ir_controller.cpp b/src/ctrlm_ir_controller.cpp index 17db165b..c42b8eae 100644 --- a/src/ctrlm_ir_controller.cpp +++ b/src/ctrlm_ir_controller.cpp @@ -206,7 +206,6 @@ void ctrlm_ir_controller_t::process_event_key(uint16_t key_code) { XLOGD_INFO("received IR key for BLE pairing (scan code 0x%06x : pairCode=%hhu)", scan_code_, commandCode); ctrlm_iarm_call_StartPairWithCode_params_t params; - params.key_code = key_code; params.pair_code = commandCode; ctrlm_main_queue_msg_pair_with_code_t msg;