From 76e6e78369996294c07f37a7b6fccf618c4d97f4 Mon Sep 17 00:00:00 2001 From: dwolaver <44593664+dwolaver@users.noreply.github.com> Date: Fri, 15 May 2026 11:23:07 -0400 Subject: [PATCH 01/15] RDKEMW-18299 : only register SIGQUIT when process is interactive (#229) --- src/ctrlm_main.cpp | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/ctrlm_main.cpp b/src/ctrlm_main.cpp index 54ddb884..becaac7a 100644 --- a/src/ctrlm_main.cpp +++ b/src/ctrlm_main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -350,7 +351,6 @@ static gboolean ctrlm_ntp_check(gpointer user_data); static void ctrlm_signals_register(void); static void ctrlm_signal_handler(int signal); static gboolean ctrlm_unix_signal_terminate(gpointer user_data); -static gboolean ctrlm_unix_signal_quit(gpointer user_data); static void ctrlm_main_iarm_call_status_get_(ctrlm_main_iarm_call_status_t *status); static void ctrlm_main_iarm_call_property_get_(ctrlm_main_iarm_call_property_t *property); @@ -1111,14 +1111,6 @@ static gboolean ctrlm_unix_signal_terminate(gpointer user_data) { return G_SOURCE_CONTINUE; } -static gboolean ctrlm_unix_signal_quit(gpointer user_data) { - XLOGD_INFO("Received SIGQUIT"); -#ifdef BREAKPAD_SUPPORT - ctrlm_crash(); -#endif - return G_SOURCE_CONTINUE; -} - void ctrlm_signals_register(void) { // Use g_unix_signal_add() so callbacks run inside the GLib main loop context // rather than from an async signal handler, avoiding undefined behavior from @@ -1131,9 +1123,15 @@ void ctrlm_signals_register(void) { if(0 == g_unix_signal_add(SIGTERM, ctrlm_unix_signal_terminate, GINT_TO_POINTER(SIGTERM))) { XLOGD_ERROR("Unable to register for SIGTERM."); } - XLOGD_INFO("Registering SIGQUIT..."); - if(0 == g_unix_signal_add(SIGQUIT, ctrlm_unix_signal_quit, NULL)) { - XLOGD_ERROR("Unable to register for SIGQUIT."); + bool interactive = isatty(STDIN_FILENO); + if(!interactive) { + XLOGD_INFO("Skipping SIGQUIT registration."); + } else { + XLOGD_INFO("Registering SIGQUIT..."); + if(signal(SIGQUIT, ctrlm_signal_handler) == SIG_ERR) { // SIGQUIT is not supported by g_unix_signal_add, so use signal() with a direct handler + int errsv = errno; + XLOGD_ERROR("Unable to register for SIGQUIT <%s>", strerror(errsv)); + } } // Ignore SIGPIPE — broken-pipe errors are handled at the call site via errno. XLOGD_INFO("Ignoring SIGPIPE..."); @@ -1144,29 +1142,22 @@ void ctrlm_signals_register(void) { } } -// Direct-call fallback only (e.g. from ctrlm_on_network_assert when kill() fails). -// No longer registered as an OS signal handler, so non-async-signal-safe calls are safe. +// Registered as an OS signal handler (must use async-signal-safe calls only). static void ctrlm_signal_handler(int signal) { switch(signal) { - case SIGTERM: - case SIGINT: { - XLOGD_INFO("Received %s", signal == SIGINT ? "SIGINT" : "SIGTERM"); - ctrlm_quit_main_loop(); - break; - } case SIGQUIT: { - XLOGD_INFO("Received SIGQUIT"); -#ifdef BREAKPAD_SUPPORT + XLOGD_SAFE_INFO("Received SIGQUIT"); + #ifdef BREAKPAD_SUPPORT ctrlm_crash(); -#endif + #endif break; } case SIGPIPE: { - XLOGD_ERROR("Received SIGPIPE. Pipe is broken"); + XLOGD_SAFE_ERROR("Received SIGPIPE. Pipe is broken"); break; } default: - XLOGD_ERROR("Received unhandled signal %d", signal); + XLOGD_SAFE_ERROR("Received unhandled signal"); break; } } @@ -1262,7 +1253,7 @@ void ctrlm_on_network_assert(ctrlm_network_id_t network_id) { g_ctrlm.main_thread = NULL; } // g_main_loop_quit() will be called when the SIGTERM GLib source fires, - // or directly via ctrlm_signal_handler() in the kill() fallback below. + // or directly in the kill() fallback below. g_ctrlm.return_code = -1; errno = 0; @@ -1270,7 +1261,7 @@ void ctrlm_on_network_assert(ctrlm_network_id_t network_id) { if(rc != 0) { int errsv = errno; XLOGD_ERROR("Failed to send SIGTERM to self <%s> - invoking shutdown handler directly", strerror(errsv)); - ctrlm_signal_handler(SIGTERM); + ctrlm_quit_main_loop(); } // give main() time to clean up From 76fe3577890fcc9441912521a932b8b310466a44 Mon Sep 17 00:00:00 2001 From: dwolaver <44593664+dwolaver@users.noreply.github.com> Date: Fri, 15 May 2026 11:46:34 -0400 Subject: [PATCH 02/15] RDKEMW-17620 : ctrlm rfc value groups not updating (#219) --- src/config/ctrlm_config.cpp | 12 +++++- src/config/ctrlm_config.h | 14 ++++++- src/ctrlm_main.cpp | 5 ++- src/rfc/ctrlm_rfc_attr.cpp | 8 ++-- src/voice/ctrlm_voice_obj.cpp | 73 +++++++++++++++++++++-------------- 5 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/config/ctrlm_config.cpp b/src/config/ctrlm_config.cpp index eb0ed60e..4d8f5404 100644 --- a/src/config/ctrlm_config.cpp +++ b/src/config/ctrlm_config.cpp @@ -45,6 +45,7 @@ void ctrlm_config_t::destroy_instance() { ctrlm_config_t::ctrlm_config_t() { this->root = NULL; + this->local_config = false; } ctrlm_config_t::~ctrlm_config_t() { @@ -52,9 +53,10 @@ ctrlm_config_t::~ctrlm_config_t() { json_decref(this->root); this->root = NULL; } + this->local_config = false; } -bool ctrlm_config_t::load_config(const std::string &file_path, bool verbose) { +bool ctrlm_config_t::load_config(const std::string &file_path, bool local_config, bool verbose) { bool ret = false; std::string contents = file_to_string(file_path); if(this->root) { @@ -73,6 +75,7 @@ bool ctrlm_config_t::load_config(const std::string &file_path, bool verbose) { if(verbose) { XLOGD_INFO("config loaded successfully as JSON for <%s>", file_path.c_str()); } + this->local_config = local_config; ret = true; } else { XLOGD_ERROR("JSON ERROR: Line <%u> Column <%u> Text <%s> Contents <%s>", json_error.line, json_error.column, json_error.text, contents.c_str()); @@ -83,7 +86,7 @@ bool ctrlm_config_t::load_config(const std::string &file_path, bool verbose) { return(ret); } -bool ctrlm_config_t::append_config(const std::string &file_path, bool verbose) { +bool ctrlm_config_t::append_config(const std::string &file_path, bool local_config, bool verbose) { if(this->root == NULL) { XLOGD_ERROR("no config file loaded"); return(false); @@ -109,6 +112,7 @@ bool ctrlm_config_t::append_config(const std::string &file_path, bool verbose) { if(verbose) { XLOGD_INFO("config appended successfully as JSON for <%s>", file_path.c_str()); } + this->local_config |= local_config; ret = true; } json_decref(append_root); @@ -121,6 +125,10 @@ bool ctrlm_config_t::path_exists(const std::string &path) { return(ctrlm_utils_json_from_path(this->root, path, false) != NULL ? true : false); } +bool ctrlm_config_t::has_local_config() const { + return(this->local_config); +} + json_t *ctrlm_config_t::json_from_path(const std::string &path, bool add_ref) { return(ctrlm_utils_json_from_path(this->root, path, add_ref)); } diff --git a/src/config/ctrlm_config.h b/src/config/ctrlm_config.h index 755e5b4b..52951db9 100644 --- a/src/config/ctrlm_config.h +++ b/src/config/ctrlm_config.h @@ -47,15 +47,24 @@ class ctrlm_config_t { /** * Function which loads a configuration file into the configuration object. * @param file_path The path to the configuration file + * @param local_config True if the configuration file is a local config, else False + * @param verbose True to enable verbose logging, else False * @return True on success, otherwise False */ - bool load_config(const std::string &file_path, bool verbose = false); + bool load_config(const std::string &file_path, bool local_config = false, bool verbose = false); /** * Function which appends a configuration file into the configuration object. * @param file_path The path to the configuration file + * @param local_config True if the configuration file is a local config, else False + * @param verbose True to enable verbose logging, else False * @return True on success, otherwise False */ - bool append_config(const std::string &file_path, bool verbose = false); + bool append_config(const std::string &file_path, bool local_config = false, bool verbose = false); + /** + * Function to check if local config exists + * @return True if the local config exists, else False + */ + bool has_local_config() const; /** * Function to check if object exists from a path * @param path A period seperated string used to navigate a JSON object i.e. "network_rf4ce.polling.enabled" @@ -83,6 +92,7 @@ class ctrlm_config_t { private: json_t *root; + bool local_config; }; #endif diff --git a/src/ctrlm_main.cpp b/src/ctrlm_main.cpp index becaac7a..902b4331 100644 --- a/src/ctrlm_main.cpp +++ b/src/ctrlm_main.cpp @@ -1614,16 +1614,17 @@ gboolean ctrlm_load_config(json_t **json_obj_root, json_t **json_obj_net_rf4ce, // Check for OPT config file override if(opt_append) { + bool local_config = true; if(!oem_append) { XLOGD_INFO("Loading OPT configuration from <%s>", config_fn_opt.c_str()); - if(!ctrlm_config->load_config(config_fn_opt)) { + if(!ctrlm_config->load_config(config_fn_opt, local_config)) { XLOGD_ERROR("Failed to load OPT configuration from <%s>", config_fn_opt.c_str()); return(false); } } else { XLOGD_INFO("Appending OPT configuration from <%s>", config_fn_opt.c_str()); - if(!ctrlm_config->append_config(config_fn_opt)) { + if(!ctrlm_config->append_config(config_fn_opt, local_config)) { XLOGD_ERROR("Failed to append OPT configuration from <%s>", config_fn_opt.c_str()); return(false); } diff --git a/src/rfc/ctrlm_rfc_attr.cpp b/src/rfc/ctrlm_rfc_attr.cpp index 263d9d6c..f9e996bb 100644 --- a/src/rfc/ctrlm_rfc_attr.cpp +++ b/src/rfc/ctrlm_rfc_attr.cpp @@ -99,9 +99,11 @@ bool ctrlm_rfc_attr_t::check_config_file(const std::string &path) const { bool ret = false; ctrlm_config_t *config = ctrlm_config_t::get_instance(); if(config) { - ret = config->path_exists(this->config_key + std::string(JSON_PATH_SEPERATOR) + path); - if(ret) { - XLOGD_DEBUG("%s exists in the config file", path.c_str()); + if(config->has_local_config()) { + ret = config->path_exists(this->config_key + std::string(JSON_PATH_SEPERATOR) + path); + if(ret) { + XLOGD_WARN("%s exists in the config file", path.c_str()); + } } } else { XLOGD_ERROR("config object is NULL"); diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index a6787b1e..d8d87159 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -3959,7 +3959,8 @@ xrsr_power_mode_t voice_xrsr_power_map(ctrlm_power_state_t ctrlm_power_state) { void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { bool enabled = true; - bool reroute = false; + + XLOGD_INFO("processing RFC values"); attr.get_rfc_value(JSON_INT_NAME_VOICE_VREX_REQUEST_TIMEOUT, this->prefs.timeout_vrex_connect,0); attr.get_rfc_value(JSON_INT_NAME_VOICE_VREX_RESPONSE_TIMEOUT, this->prefs.timeout_vrex_session,0); @@ -4007,22 +4008,34 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { } attr.get_rfc_value(JSON_INT_NAME_VOICE_PACKET_LOSS_THRESHOLD, this->packet_loss_threshold, 0); - if(attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_MODE, this->audio_mode) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_TIMING, this->audio_timing) | - attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_CONFIDENCE_THRESHOLD, this->audio_confidence_threshold, 0.0, 1.0) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_DUCKING_TYPE, this->audio_ducking_type, CTRLM_VOICE_AUDIO_DUCKING_TYPE_ABSOLUTE, CTRLM_VOICE_AUDIO_DUCKING_TYPE_RELATIVE) | - attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_DUCKING_LEVEL, this->audio_ducking_level, 0.0, 1.0) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_AUDIO_DUCKING_BEEP, this->audio_ducking_beep_enabled)) { + + bool audio_mode_updated = false; + audio_mode_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_MODE, this->audio_mode); + audio_mode_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_TIMING, this->audio_timing); + audio_mode_updated |= attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_CONFIDENCE_THRESHOLD, this->audio_confidence_threshold, 0.0, 1.0); + audio_mode_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_DUCKING_TYPE, this->audio_ducking_type, CTRLM_VOICE_AUDIO_DUCKING_TYPE_ABSOLUTE, CTRLM_VOICE_AUDIO_DUCKING_TYPE_RELATIVE); + audio_mode_updated |= attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_DUCKING_LEVEL, this->audio_ducking_level, 0.0, 1.0); + + // JSON_BOOL_NAME_VOICE_AUDIO_DUCKING_BEEP is updated via Voice Control plugin so it cannot be overridden via RFC + + XLOGD_INFO("audio mode updated <%s>", audio_mode_updated ? "YES" : "NO"); + + if(audio_mode_updated) { ctrlm_voice_audio_settings_t audio_settings = {this->audio_mode, this->audio_timing, this->audio_confidence_threshold, this->audio_ducking_type, this->audio_ducking_level, this->audio_ducking_beep_enabled}; this->set_audio_mode(&audio_settings); } // All attributes that need capture configuration to be set - if(attr.get_rfc_value(JSON_ARRAY_NAME_VOICE_SAVE_LAST_UTTERANCE, this->prefs.utterance_save, ctrlm_is_production_build() ? CTRLM_JSON_ARRAY_INDEX_PRD : CTRLM_JSON_ARRAY_INDEX_DEV) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_UTTERANCE_USE_CURTAIL, this->prefs.utterance_use_curtail) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_QTY_MAX, this->prefs.utterance_file_qty_max, 1, 100000) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_SIZE_MAX, this->prefs.utterance_file_size_max, 4 * 1024) | - attr.get_rfc_value(JSON_STR_NAME_VOICE_UTTERANCE_PATH, this->prefs.utterance_path)) { + bool capture_config_updated = false; + capture_config_updated |= attr.get_rfc_value(JSON_ARRAY_NAME_VOICE_SAVE_LAST_UTTERANCE, this->prefs.utterance_save, ctrlm_is_production_build() ? CTRLM_JSON_ARRAY_INDEX_PRD : CTRLM_JSON_ARRAY_INDEX_DEV); + capture_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_UTTERANCE_USE_CURTAIL, this->prefs.utterance_use_curtail); + capture_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_QTY_MAX, this->prefs.utterance_file_qty_max, 1, 100000); + capture_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_SIZE_MAX, this->prefs.utterance_file_size_max, 4 * 1024); + capture_config_updated |= attr.get_rfc_value(JSON_STR_NAME_VOICE_UTTERANCE_PATH, this->prefs.utterance_path); + + XLOGD_INFO("capture config updated <%s>", capture_config_updated ? "YES" : "NO"); + + if(capture_config_updated) { xrsr_capture_config_t capture_config = { .delete_files = !this->prefs.utterance_save, .enable = this->prefs.utterance_save, @@ -4045,22 +4058,21 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { } // All attributes that need a re-route to apply - if(attr.get_rfc_value(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_standby.connect_check_interval) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_CONNECT, this->prefs.dst_params_standby.timeout_connect) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_INACTIVITY, this->prefs.dst_params_standby.timeout_inactivity) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_SESSION, this->prefs.dst_params_standby.timeout_session) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_STANDBY_IPV4_FALLBACK, this->prefs.dst_params_standby.ipv4_fallback) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_BACKOFF_DELAY, this->prefs.dst_params_standby.backoff_delay) | + bool routing_config_updated = false; + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_standby.connect_check_interval); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_CONNECT, this->prefs.dst_params_standby.timeout_connect); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_INACTIVITY, this->prefs.dst_params_standby.timeout_inactivity); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_SESSION, this->prefs.dst_params_standby.timeout_session); + routing_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_STANDBY_IPV4_FALLBACK, this->prefs.dst_params_standby.ipv4_fallback); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_BACKOFF_DELAY, this->prefs.dst_params_standby.backoff_delay); - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_low_latency.connect_check_interval) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_CONNECT, this->prefs.dst_params_low_latency.timeout_connect) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_INACTIVITY, this->prefs.dst_params_low_latency.timeout_inactivity) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_SESSION, this->prefs.dst_params_low_latency.timeout_session) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_LOW_LATENCY_IPV4_FALLBACK, this->prefs.dst_params_low_latency.ipv4_fallback) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_BACKOFF_DELAY, this->prefs.dst_params_low_latency.backoff_delay)) { - reroute = true; - } + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_low_latency.connect_check_interval); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_CONNECT, this->prefs.dst_params_low_latency.timeout_connect); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_INACTIVITY, this->prefs.dst_params_low_latency.timeout_inactivity); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_SESSION, this->prefs.dst_params_low_latency.timeout_session); + routing_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_LOW_LATENCY_IPV4_FALLBACK, this->prefs.dst_params_low_latency.ipv4_fallback); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_BACKOFF_DELAY, this->prefs.dst_params_low_latency.backoff_delay); std::vector obj_server_hosts; if(attr.get_rfc_value(JSON_ARRAY_NAME_VOICE_SERVER_HOSTS, obj_server_hosts)) { @@ -4078,9 +4090,12 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { this->voice_device_disable((ctrlm_voice_device_t)i, true, NULL); } } - reroute = true; + routing_config_updated = true; } - if(reroute) { + + XLOGD_INFO("routing config updated <%s>", routing_config_updated ? "YES" : "NO"); + + if(routing_config_updated) { this->voice_sdk_update_routes(); } } From 5a6dbfbf88cdb7d62128a93b3990bdb595bc0141 Mon Sep 17 00:00:00 2001 From: dwolaver <44593664+dwolaver@users.noreply.github.com> Date: Mon, 18 May 2026 14:26:04 -0400 Subject: [PATCH 03/15] RDKEMW-14906 : VAD detector in Control Manager/Voice SDK (#186) --- include/ctrlm_ipc.h | 3 +- include/ctrlm_ipc_voice.h | 11 ++ src/ctrlm_config_default.json | 4 +- src/telemetry/ctrlm_telemetry_markers.h | 16 ++- src/voice/ctrlm_voice_obj.cpp | 115 ++++++++++++------ src/voice/ctrlm_voice_obj.h | 73 +++++------ src/voice/ctrlm_voice_obj_generic.cpp | 102 +++++++++------- src/voice/ipc/ctrlm_voice_ipc.h | 3 +- src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp | 12 ++ .../ipc/ctrlm_voice_ipc_iarm_thunder.cpp | 16 +++ .../ctrlm_voice_telemetry_events.cpp | 17 ++- .../telemetry/ctrlm_voice_telemetry_events.h | 6 +- 12 files changed, 251 insertions(+), 127 deletions(-) diff --git a/include/ctrlm_ipc.h b/include/ctrlm_ipc.h index 32d0dea1..7d87d12c 100644 --- a/include/ctrlm_ipc.h +++ b/include/ctrlm_ipc.h @@ -302,7 +302,8 @@ typedef enum { CTRLM_RCU_IARM_EVENT_RF4CE_PAIRING_WINDOW_TIMEOUT = 35, ///< Indicates that a battery milestone event occured CTRLM_RCU_IARM_EVENT_FIRMWARE_UPDATE_PROGRESS = 36, ///< Generated when an milestone is reached for remote firmware upgrade CTRLM_RCU_IARM_EVENT_VALIDATION_STATUS = 37, ///< Generated when the validation status changes - CTRLM_MAIN_IARM_EVENT_MAX = 38 ///< Placeholder for the last event (used in registration) + CTRLM_VOICE_IARM_EVENT_SESSION_SILENT = 38, ///< Voice session was silent (no speech detected) + CTRLM_MAIN_IARM_EVENT_MAX = 39 ///< Placeholder for the last event (used in registration) } ctrlm_main_iarm_event_t; /// @brief Remote Control Key Status diff --git a/include/ctrlm_ipc_voice.h b/include/ctrlm_ipc_voice.h index ed96c951..4a912cad 100644 --- a/include/ctrlm_ipc_voice.h +++ b/include/ctrlm_ipc_voice.h @@ -281,6 +281,16 @@ typedef struct { long return_code_internal; ///< Internally generated return code } ctrlm_voice_iarm_event_session_short_t; +typedef struct { + unsigned char api_revision; ///< The revision of this API. + ctrlm_network_id_t network_id; ///< Identifier of network on which the controller is bound + ctrlm_network_type_t network_type; ///< Type of network on which the controller is bound + ctrlm_controller_id_t controller_id; ///< A unique identifier of the remote + unsigned long session_id; ///< A unique id for the voice session. + ctrlm_voice_session_end_reason_t reason; ///< The reason that the voice session was silent + long return_code_internal; ///< Internally generated return code +} ctrlm_voice_iarm_event_session_silent_t; + typedef struct { unsigned char api_revision; ///< The revision of this API char media_service_url[2083]; ///< The url for the media service (null terminated string) @@ -376,6 +386,7 @@ typedef struct { /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_STATS | ctrlm_voice_iarm_event_session_stats_t * | Generated when the statistics of the voice session are available | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_ABORT | ctrlm_voice_iarm_event_session_abort_t * | Generated when a voice session is aborted (denied) | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_SHORT | ctrlm_voice_iarm_event_session_short_t * | Generated when a short voice session is detected | +/// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_SILENT | ctrlm_voice_iarm_event_session_silent_t * | Generated when a silent voice session is detected | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_MEDIA_SERVICE | ctrlm_voice_iarm_event_media_service_t * | Generated when a media service response is received | /// /// IARM events are available on a subscription basis. In order to receive an event, a client must explicitly register to receive the event by calling diff --git a/src/ctrlm_config_default.json b/src/ctrlm_config_default.json index af65c196..21aa49c6 100755 --- a/src/ctrlm_config_default.json +++ b/src/ctrlm_config_default.json @@ -291,7 +291,9 @@ "par_voice_eos_method" : 1, "par_voice_eos_timeout" : 2500, "server_hosts" : [], - "telemetry_session_stats" : false + "telemetry_session_stats" : false, + "voice_activity_detection_mode" : "enabled" + }, "device_update" : { "dir_root" : "/srv/device_update/", diff --git a/src/telemetry/ctrlm_telemetry_markers.h b/src/telemetry/ctrlm_telemetry_markers.h index 23c0f775..cb07ec32 100644 --- a/src/telemetry/ctrlm_telemetry_markers.h +++ b/src/telemetry/ctrlm_telemetry_markers.h @@ -93,7 +93,7 @@ // The Voice Session Statistics Marker reports statistics for the voice session(s). The format of the marker is a json array of arrays with each event in the format below: // // [[event1], [event2], [event3], ...] -// [,,,,,,,,,,,,,,,,,,,,] +// [,,,,,,,,,,,,,,,,,,,,,,,,,] // // - Version of the marker format. // - Name of the device that started the session. @@ -111,13 +111,19 @@ // - lost samples. // - decoder failure count. // - sample buffer high watermark. -// - audio stream success/error code. -// - protocol success/error code. -// - server success/error code. +// - audio stream success/error code. +// - session success/error code. +// - server success/error code. // - server message. // - flag to indicate if session was successful. +// - reason why the stream ended (if available). +// - protocol return code. +// - flag to indicate if voice was detected. +// - peak confidence level. +// - peak RMS level. + #define MARKER_VOICE_SESSION_STATS "ctrlm.voice.session.stats" -#define MARKER_VOICE_SESSION_STATS_VERSION "2" +#define MARKER_VOICE_SESSION_STATS_VERSION "3" // End Voice Session Statistics diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index d8d87159..12402a12 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -186,39 +186,40 @@ ctrlm_voice_t::ctrlm_voice_t() { #ifdef JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_3 this->url_hostname_pattern_add(JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_3); #endif - this->prefs.aspect_ratio = JSON_STR_VALUE_VOICE_ASPECT_RATIO; - this->prefs.guide_language = JSON_STR_VALUE_VOICE_LANGUAGE; - this->prefs.app_id_http = JSON_STR_VALUE_VOICE_APP_ID_HTTP; - this->prefs.app_id_ws = JSON_STR_VALUE_VOICE_APP_ID_WS; - this->prefs.timeout_vrex_connect = JSON_INT_VALUE_VOICE_VREX_REQUEST_TIMEOUT; - this->prefs.timeout_vrex_session = JSON_INT_VALUE_VOICE_VREX_RESPONSE_TIMEOUT; - this->prefs.timeout_stats = JSON_INT_VALUE_VOICE_TIMEOUT_STATS; - this->prefs.timeout_packet_initial = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_INITIAL; - this->prefs.timeout_packet_subsequent = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_SUBSEQUENT; - this->prefs.bitrate_minimum = JSON_INT_VALUE_VOICE_BITRATE_MINIMUM; - this->prefs.time_threshold = JSON_INT_VALUE_VOICE_TIME_THRESHOLD; - this->prefs.utterance_save = ctrlm_is_production_build() ? JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_0 : JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_1; - this->prefs.utterance_use_curtail = JSON_BOOL_VALUE_VOICE_UTTERANCE_USE_CURTAIL; - this->prefs.utterance_file_qty_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_QTY_MAX; - this->prefs.utterance_file_size_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_SIZE_MAX; - this->prefs.utterance_path = JSON_STR_VALUE_VOICE_UTTERANCE_PATH; - this->prefs.utterance_duration_min = JSON_INT_VALUE_VOICE_MINIMUM_DURATION; - this->prefs.ffv_leading_samples = JSON_INT_VALUE_VOICE_FFV_LEADING_SAMPLES; - this->prefs.force_voice_settings = JSON_BOOL_VALUE_VOICE_FORCE_VOICE_SETTINGS; - this->prefs.vrex_test_flag = JSON_BOOL_VALUE_VOICE_VREX_TEST_FLAG; - this->prefs.vrex_wuw_bypass_success_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG; - this->prefs.vrex_wuw_bypass_failure_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG; - this->prefs.force_toggle_fallback = JSON_BOOL_VALUE_VOICE_FORCE_TOGGLE_FALLBACK; - this->prefs.telemetry_session_stats = JSON_BOOL_VALUE_VOICE_TELEMETRY_SESSION_STATS; - this->prefs.par_voice_enabled = false; - this->prefs.par_voice_eos_method = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_METHOD; - this->prefs.par_voice_eos_timeout = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_TIMEOUT; + this->prefs.aspect_ratio = JSON_STR_VALUE_VOICE_ASPECT_RATIO; + this->prefs.guide_language = JSON_STR_VALUE_VOICE_LANGUAGE; + this->prefs.app_id_http = JSON_STR_VALUE_VOICE_APP_ID_HTTP; + this->prefs.app_id_ws = JSON_STR_VALUE_VOICE_APP_ID_WS; + this->prefs.timeout_vrex_connect = JSON_INT_VALUE_VOICE_VREX_REQUEST_TIMEOUT; + this->prefs.timeout_vrex_session = JSON_INT_VALUE_VOICE_VREX_RESPONSE_TIMEOUT; + this->prefs.timeout_stats = JSON_INT_VALUE_VOICE_TIMEOUT_STATS; + this->prefs.timeout_packet_initial = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_INITIAL; + this->prefs.timeout_packet_subsequent = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_SUBSEQUENT; + this->prefs.bitrate_minimum = JSON_INT_VALUE_VOICE_BITRATE_MINIMUM; + this->prefs.time_threshold = JSON_INT_VALUE_VOICE_TIME_THRESHOLD; + this->prefs.utterance_save = ctrlm_is_production_build() ? JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_0 : JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_1; + this->prefs.utterance_use_curtail = JSON_BOOL_VALUE_VOICE_UTTERANCE_USE_CURTAIL; + this->prefs.utterance_file_qty_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_QTY_MAX; + this->prefs.utterance_file_size_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_SIZE_MAX; + this->prefs.utterance_path = JSON_STR_VALUE_VOICE_UTTERANCE_PATH; + this->prefs.utterance_duration_min = JSON_INT_VALUE_VOICE_MINIMUM_DURATION; + this->prefs.ffv_leading_samples = JSON_INT_VALUE_VOICE_FFV_LEADING_SAMPLES; + this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(JSON_STR_VALUE_VOICE_VOICE_ACTIVITY_DETECTION_MODE); + this->prefs.force_voice_settings = JSON_BOOL_VALUE_VOICE_FORCE_VOICE_SETTINGS; + this->prefs.vrex_test_flag = JSON_BOOL_VALUE_VOICE_VREX_TEST_FLAG; + this->prefs.vrex_wuw_bypass_success_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG; + this->prefs.vrex_wuw_bypass_failure_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG; + this->prefs.force_toggle_fallback = JSON_BOOL_VALUE_VOICE_FORCE_TOGGLE_FALLBACK; + this->prefs.telemetry_session_stats = JSON_BOOL_VALUE_VOICE_TELEMETRY_SESSION_STATS; + this->prefs.par_voice_enabled = false; + this->prefs.par_voice_eos_method = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_METHOD; + this->prefs.par_voice_eos_timeout = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_TIMEOUT; this->voice_params_opus_encoder_default(); - this->xrsr_opened = false; - this->voice_ipc = NULL; - this->packet_loss_threshold = JSON_INT_VALUE_VOICE_PACKET_LOSS_THRESHOLD; - this->vsdk_config = NULL; - this->nsm_voice_session = false; + this->xrsr_opened = false; + this->voice_ipc = NULL; + this->packet_loss_threshold = JSON_INT_VALUE_VOICE_PACKET_LOSS_THRESHOLD; + this->vsdk_config = NULL; + this->nsm_voice_session = false; #ifndef TELEMETRY_SUPPORT XLOGD_WARN("telemetry is not enabled"); @@ -261,6 +262,7 @@ ctrlm_voice_t::ctrlm_voice_t() { this->secure_url_required = JSON_BOOL_VALUE_VOICE_REQUIRE_SECURE_URL; XLOGD_TELEMETRY("require i_SAT <%s> i_MTLS <%s> i_secure_url <%s>", this->sat_token_required ? "YES" : "NO", this->mtls_required ? "YES" : "NO", this->secure_url_required ? "YES" : "NO"); + XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); errno_t safec_rc = memset_s(this->sat_token, sizeof(this->sat_token), 0, sizeof(this->sat_token)); ERR_CHK(safec_rc); @@ -425,6 +427,7 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_STR_NAME_VOICE_URL_SRC_MIC_TAP, this->prefs.server_url_src_mic_tap); conf.config_value_get(JSON_STR_NAME_VOICE_LANGUAGE, this->prefs.guide_language); conf.config_value_get(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min); + if(conf.config_value_get(JSON_BOOL_NAME_VOICE_ENABLE_SAT, this->sat_token_required)) { ctrlm_sm_voice_sat_enable_write(this->sat_token_required); XLOGD_TELEMETRY("require c_SAT <%s>", this->sat_token_required ? "YES" : "NO"); @@ -432,6 +435,12 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_STR_NAME_VOICE_ASPECT_RATIO, this->prefs.aspect_ratio); } + std::string voice_activity_detection_mode; + if(conf.config_value_get(JSON_STR_NAME_VOICE_VOICE_ACTIVITY_DETECTION_MODE, voice_activity_detection_mode)) { + this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(voice_activity_detection_mode); + XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); + } + std::vector obj_server_hosts; if(conf.config_value_get(JSON_ARRAY_NAME_VOICE_SERVER_HOSTS, obj_server_hosts)) { this->url_hostname_patterns(obj_server_hosts); @@ -1089,6 +1098,17 @@ void ctrlm_voice_t::voice_params_opus_encoder_default(void) { this->voice_params_opus_samples_per_packet_set(); } +xrsr_stream_voice_activity_mode_t ctrlm_voice_t::voice_activity_detection_mode_to_xrsr(const std::string &mode) { + // Configure voice activity detection parameters based on mode + if(mode == "enabled") { // Voice activity detection will be used but not enforced + return(XRSR_STREAM_VOICE_ACTIVITY_MODE_ENABLED); + } else if(mode == "enforced") { // Voice session will only proceed if voice activity is detected + return(XRSR_STREAM_VOICE_ACTIVITY_MODE_ENFORCED); + } + // Voice activity detection is disabled (or invalid mode) + return(XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED); +} + void ctrlm_voice_t::voice_params_opus_samples_per_packet_set(void) { guchar fr_dur = (this->prefs.opus_encoder_params[3] >> 4) & 0xF; switch(fr_dur) { @@ -2645,6 +2665,12 @@ void ctrlm_voice_t::voice_session_end_callback(ctrlm_voice_session_end_cb_t *ses end.result = SESSION_END_SHORT_UTTERANCE; end.reason = (int)session->end_reason_rcu; this->voice_ipc->session_end(end); + } else if(stats->session_end_reason == XRSR_SESSION_END_REASON_ERROR_AUDIO_SILENT) { + ctrlm_voice_ipc_event_session_end_t end; + end.common = session->ipc_common_data; + end.result = SESSION_END_SILENT_UTTERANCE; + end.reason = (int)session->end_reason_rcu; + this->voice_ipc->session_end(end); } else { ctrlm_voice_ipc_event_session_end_server_stats_t server_stats; ctrlm_voice_ipc_event_session_end_t end; @@ -2929,9 +2955,20 @@ void ctrlm_voice_t::voice_stream_end_callback(ctrlm_voice_stream_end_cb_t *strea } XLOGD_AUTOMATION_TELEMETRY("src <%s> Packets Lost/Total <%u/%u> %.02f%% duration <%u> ms", ctrlm_voice_device_str(session->voice_device), session->packets_lost, session->packets_lost + session->packets_processed, 100.0 * ((double)session->packets_lost / (double)(session->packets_lost + session->packets_processed)), stream_duration); #ifdef TELEMETRY_SUPPORT + uint32_t packets_total = session->packets_lost + session->packets_processed; + int32_t voice_detected = -1; + uint32_t peak_confidence = 0; + int32_t peak_rms_level = 0; + if(stats->audio_stats.vad_frames_processed > 0) { + voice_detected = (stats->audio_stats.vad_voice_detected) ? 1 : 0; + peak_confidence = (stats->audio_stats.vad_confidence_peak * 100); + peak_rms_level = stats->audio_stats.vad_rms_level_peak; + + XLOGD_INFO("VAD frames <%u> voice detected <%s> confidence peak <%u%%> rms level peak <%d dB>", stats->audio_stats.vad_frames_processed, voice_detected == 1 ? "YES" : "NO", peak_confidence, peak_rms_level); + } + if(this->prefs.telemetry_session_stats) { - uint32_t packets_total = session->packets_lost + session->packets_processed; - session->telemetry_session_stats.update_on_stream_end(stream_duration, packets_total, session->packets_lost, packets_total * samples_per_packet, session->packets_lost * samples_per_packet, decoder_failures, 0); + session->telemetry_session_stats.update_on_stream_end(stream_duration, packets_total, session->packets_lost, packets_total * samples_per_packet, session->packets_lost * samples_per_packet, decoder_failures, 0, voice_detected, peak_confidence, peak_rms_level); } #endif } else if(samples_processed > 0) { @@ -2939,7 +2976,7 @@ void ctrlm_voice_t::voice_stream_end_callback(ctrlm_voice_stream_end_cb_t *strea XLOGD_INFO("src <%s> Samples Lost/Total <%u/%u> %.02f%% buffered max <%u> duration <%u> ms", ctrlm_voice_device_str(session->voice_device), samples_lost, samples_lost + samples_processed, 100.0 * ((double)samples_lost / (double)(samples_lost + samples_processed)), samples_buffered_max, stream_duration); #ifdef TELEMETRY_SUPPORT if(this->prefs.telemetry_session_stats) { - session->telemetry_session_stats.update_on_stream_end(stream_duration, 0, 0, samples_lost + samples_processed, samples_lost, decoder_failures, samples_buffered_max); + session->telemetry_session_stats.update_on_stream_end(stream_duration, 0, 0, samples_lost + samples_processed, samples_lost, decoder_failures, samples_buffered_max, -1, 0, 0); } #endif } @@ -4074,6 +4111,14 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { routing_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_LOW_LATENCY_IPV4_FALLBACK, this->prefs.dst_params_low_latency.ipv4_fallback); routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_BACKOFF_DELAY, this->prefs.dst_params_low_latency.backoff_delay); + + std::string voice_activity_detection_mode; + if(attr.get_rfc_value(JSON_STR_NAME_VOICE_VOICE_ACTIVITY_DETECTION_MODE, voice_activity_detection_mode)) { + this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(voice_activity_detection_mode); + XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); + routing_config_updated = true; + } + std::vector obj_server_hosts; if(attr.get_rfc_value(JSON_ARRAY_NAME_VOICE_SERVER_HOSTS, obj_server_hosts)) { this->url_hostname_patterns(obj_server_hosts); diff --git a/src/voice/ctrlm_voice_obj.h b/src/voice/ctrlm_voice_obj.h index 3ec217b2..9c0bc4be 100644 --- a/src/voice/ctrlm_voice_obj.h +++ b/src/voice/ctrlm_voice_obj.h @@ -273,41 +273,42 @@ typedef struct { // End Event Callback Structures typedef struct { - std::string server_url_src_ptt; - std::string server_url_src_ff; - std::string server_url_src_mic_tap; - std::vector server_hosts; - std::string aspect_ratio; - std::string guide_language; - std::string app_id_http; - std::string app_id_ws; - unsigned long timeout_vrex_connect; - unsigned long timeout_vrex_session; - unsigned long timeout_stats; - unsigned long timeout_packet_initial; - unsigned long timeout_packet_subsequent; - guchar bitrate_minimum; - guint16 time_threshold; - bool utterance_save; - bool utterance_use_curtail; - unsigned long utterance_file_qty_max; - unsigned long utterance_file_size_max; - std::string utterance_path; - unsigned long utterance_duration_min; - unsigned long ffv_leading_samples; - bool force_voice_settings; - bool vrex_test_flag; - bool vrex_wuw_bypass_success_flag; - bool vrex_wuw_bypass_failure_flag; - std::string opus_encoder_params_str; - uint8_t opus_encoder_params[CTRLM_RCU_RIB_ATTR_LEN_OPUS_ENCODING_PARAMS]; - bool force_toggle_fallback; - bool telemetry_session_stats; - xrsr_dst_params_t dst_params_standby; - xrsr_dst_params_t dst_params_low_latency; - bool par_voice_enabled; - uint8_t par_voice_eos_method; - uint16_t par_voice_eos_timeout; + std::string server_url_src_ptt; + std::string server_url_src_ff; + std::string server_url_src_mic_tap; + std::vector server_hosts; + std::string aspect_ratio; + std::string guide_language; + std::string app_id_http; + std::string app_id_ws; + unsigned long timeout_vrex_connect; + unsigned long timeout_vrex_session; + unsigned long timeout_stats; + unsigned long timeout_packet_initial; + unsigned long timeout_packet_subsequent; + guchar bitrate_minimum; + guint16 time_threshold; + bool utterance_save; + bool utterance_use_curtail; + unsigned long utterance_file_qty_max; + unsigned long utterance_file_size_max; + std::string utterance_path; + unsigned long utterance_duration_min; + unsigned long ffv_leading_samples; + xrsr_stream_voice_activity_mode_t voice_activity_detection_mode; + bool force_voice_settings; + bool vrex_test_flag; + bool vrex_wuw_bypass_success_flag; + bool vrex_wuw_bypass_failure_flag; + std::string opus_encoder_params_str; + uint8_t opus_encoder_params[CTRLM_RCU_RIB_ATTR_LEN_OPUS_ENCODING_PARAMS]; + bool force_toggle_fallback; + bool telemetry_session_stats; + xrsr_dst_params_t dst_params_standby; + xrsr_dst_params_t dst_params_low_latency; + bool par_voice_enabled; + uint8_t par_voice_eos_method; + uint16_t par_voice_eos_timeout; } voice_session_prefss_t; typedef struct { @@ -637,6 +638,8 @@ class ctrlm_voice_t { bool voice_params_opus_encoder_validate(std::string &opus_encoder_params_str); void voice_params_opus_samples_per_packet_set(void); bool voice_params_hex_str_to_bytes(std::string hex_string, guchar *data, guint32 length); + + xrsr_stream_voice_activity_mode_t voice_activity_detection_mode_to_xrsr(const std::string &mode); bool voice_session_can_request(ctrlm_voice_device_t device); void voice_session_set_active(ctrlm_voice_device_t device); diff --git a/src/voice/ctrlm_voice_obj_generic.cpp b/src/voice/ctrlm_voice_obj_generic.cpp index 4cb35f99..948d6039 100644 --- a/src/voice/ctrlm_voice_obj_generic.cpp +++ b/src/voice/ctrlm_voice_obj_generic.cpp @@ -172,12 +172,17 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { xrsr_src_t src = (xrsr_src_t)j; ctrlm_voice_device_t src_device = xrsr_to_voice_device(src); std::string *url = NULL; + + xrsr_stream_voice_activity_mode_t stream_vad_mode = XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED; sem_wait(&this->device_status_semaphore); if(this->device_status[src_device] != CTRLM_VOICE_DEVICE_STATUS_DISABLED && this->device_status[src_device] != CTRLM_VOICE_DEVICE_STATUS_NOT_SUPPORTED) { switch(src_device) { case CTRLM_VOICE_DEVICE_PTT: { url = &this->prefs.server_url_src_ptt; + + // For PTT source, use the configured VAD mode which may be disabled, enabled, or enforced + stream_vad_mode = this->prefs.voice_activity_detection_mode; break; } case CTRLM_VOICE_DEVICE_MICROPHONE: @@ -232,15 +237,16 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { if(!this->obj_ws_nextgen->get_handlers(&handlers_xrsr)) { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -264,15 +270,16 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { if(!this->obj_ws_nsp->get_handlers(&handlers_xrsr)) { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_PCM_32_BIT | XRSR_AUDIO_FORMAT_PCM_32_BIT_MULTI | XRSR_AUDIO_FORMAT_PCM_RAW; - routes[i].dsts[0].stream_time_min = 0; - routes[i].dsts[0].stream_from = XRSR_STREAM_FROM_LIVE; - routes[i].dsts[0].stream_offset = 0; - routes[i].dsts[0].stream_until = XRSR_STREAM_UNTIL_END_OF_STREAM; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_PCM_32_BIT | XRSR_AUDIO_FORMAT_PCM_32_BIT_MULTI | XRSR_AUDIO_FORMAT_PCM_RAW; + routes[i].dsts[0].stream_time_min = 0; + routes[i].dsts[0].stream_vad_mode = XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED; + routes[i].dsts[0].stream_from = XRSR_STREAM_FROM_LIVE; + routes[i].dsts[0].stream_offset = 0; + routes[i].dsts[0].stream_until = XRSR_STREAM_UNTIL_END_OF_STREAM; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -305,19 +312,20 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { url->append("speech?"); } - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = url->c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = url->c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; #ifdef AUDIO_DECODE - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; #else - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_ADPCM; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_ADPCM; #endif - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -334,15 +342,16 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = url->c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = url->c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -357,15 +366,16 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } diff --git a/src/voice/ipc/ctrlm_voice_ipc.h b/src/voice/ipc/ctrlm_voice_ipc.h index 969a8380..db8dc582 100644 --- a/src/voice/ipc/ctrlm_voice_ipc.h +++ b/src/voice/ipc/ctrlm_voice_ipc.h @@ -92,7 +92,8 @@ typedef enum { SESSION_END_SUCCESS, SESSION_END_FAILURE, SESSION_END_ABORT, - SESSION_END_SHORT_UTTERANCE + SESSION_END_SHORT_UTTERANCE, + SESSION_END_SILENT_UTTERANCE } ctrlm_voice_ipc_event_session_end_result_t; typedef struct { diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp index 6564ef4c..bdcc85d8 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp @@ -158,6 +158,18 @@ bool ctrlm_voice_ipc_iarm_legacy_t::session_end(const ctrlm_voice_ipc_event_sess ret = broadcast_iarm_event_legacy(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_SESSION_SHORT, &event, sizeof(event)); break; } + case SESSION_END_SILENT_UTTERANCE: { + ctrlm_voice_iarm_event_session_silent_t event = {0}; + event.api_revision = CTRLM_VOICE_IARM_BUS_API_REVISION; + event.network_id = session_end.common.network_id; + event.network_type = session_end.common.network_type; + event.controller_id = session_end.common.controller_id; + event.session_id = session_end.common.session_id_ctrlm; + event.reason = (ctrlm_voice_session_end_reason_t)session_end.reason; + event.return_code_internal = session_end.return_code_internal; + ret = broadcast_iarm_event_legacy(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_SESSION_SILENT, &event, sizeof(event)); + break; + } } } // Reset state diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp index 6b407173..b148dda5 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp @@ -62,6 +62,7 @@ #define JSON_SESSION_END_RESULT_ERROR "error" #define JSON_SESSION_END_RESULT_ABORT "abort" #define JSON_SESSION_END_RESULT_SHORT "shortUtterance" +#define JSON_SESSION_END_RESULT_SILENT "silentUtterance" #define JSON_SESSION_END_TRANSCRIPTION "transcription" #define JSON_SESSION_END_PROTOCOL_ERROR "protocolErrorCode" #define JSON_SESSION_END_PROTOCOL_LIBRARY_ERROR "protocolLibraryErrorCode" @@ -71,6 +72,7 @@ #define JSON_SESSION_END_ERROR_REASON "reason" #define JSON_SESSION_END_ABORT_REASON "reason" #define JSON_SESSION_END_SHORT_REASON "reason" +#define JSON_SESSION_END_SILENT_REASON "reason" #define JSON_SESSION_END_STB_STATS "stbStats" #define JSON_SESSION_END_STB_STATS_TYPE "type" #define JSON_SESSION_END_STB_STATS_FIRMWARE "firmware" @@ -291,6 +293,20 @@ bool ctrlm_voice_ipc_iarm_thunder_t::session_end(const ctrlm_voice_ipc_event_ses } break; } + case SESSION_END_SILENT_UTTERANCE: { + int rc_silent; + rc |= json_object_set_new_nocheck(event_data, JSON_SESSION_END_RESULT, json_string(JSON_SESSION_END_RESULT_SILENT)); + + // Add Audio Silent Data to result object + rc_silent = json_object_set_new_nocheck(event_result, JSON_SESSION_END_SILENT_REASON, json_integer(session_end.reason)); + if(0 != rc_silent) { + XLOGD_ERROR("Error creating audio silent JSON subobject"); + JSON_DEREFERENCE(event_result); + } else { + rc |= json_object_set_new_nocheck(event_data, JSON_SESSION_END_RESULT_SILENT, event_result); + } + break; + } } if(session_end.stb_stats) { int stats_rc; diff --git a/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp b/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp index d5f6bba3..041d420a 100644 --- a/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp +++ b/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp @@ -212,7 +212,10 @@ bool ctrlm_voice_telemetry_session_t::event() { ss << "\"" << m_server_message << "\","; ss << m_result << ","; ss << m_end_reason_stream << ","; - ss << m_ret_code_protocol << "]]"; + ss << m_ret_code_protocol << ","; + ss << m_voice_detected << ","; + ss << m_peak_confidence << ","; + ss << m_peak_rms_level << "]]"; if(m_event_list.length() + ss.str().length() > m_event_list_max_size) { // Maximum data size exceeded XLOGD_WARN("telemetry event exceeds max size <%s,%s>", val_marker.c_str(), ss.str().c_str()); @@ -267,7 +270,7 @@ void ctrlm_voice_telemetry_session_t::update_on_key_release(int32_t time_start_l m_has_key_release = true; } -void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_len_act, uint32_t packets_total, uint32_t packets_lost, uint32_t samples_total, uint32_t samples_lost, uint32_t decoder_failures, uint32_t samples_buffered_max) { +void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_len_act, uint32_t packets_total, uint32_t packets_lost, uint32_t samples_total, uint32_t samples_lost, uint32_t decoder_failures, uint32_t samples_buffered_max, int32_t voice_detected, uint32_t peak_confidence, int32_t peak_rms_level) { m_time_stream_len_act = time_stream_len_act; m_packets_total = packets_total; m_packets_lost = packets_lost; @@ -276,6 +279,12 @@ void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_ m_decoder_failures = decoder_failures; m_samples_buffered_max = samples_buffered_max; + if(voice_detected >= 0) { + m_voice_detected = voice_detected; + m_peak_confidence = peak_confidence; + m_peak_rms_level = peak_rms_level; + } + if(m_has_key_release) { m_time_stream_delta = m_time_stream_len_act - m_time_stream_len_exp; } @@ -320,6 +329,10 @@ void ctrlm_voice_telemetry_session_t::reset_stats() { m_result = false; m_ret_code_protocol = 0; + m_voice_detected = -1; + m_peak_confidence = 0; + m_peak_rms_level = 0; + m_server_message.clear(); m_device_type.clear(); m_device_version.clear(); diff --git a/src/voice/telemetry/ctrlm_voice_telemetry_events.h b/src/voice/telemetry/ctrlm_voice_telemetry_events.h index adadfde6..18fcab29 100644 --- a/src/voice/telemetry/ctrlm_voice_telemetry_events.h +++ b/src/voice/telemetry/ctrlm_voice_telemetry_events.h @@ -75,7 +75,7 @@ class ctrlm_voice_telemetry_session_t : public ctrlm_telemetry_event_t Date: Wed, 27 May 2026 08:15:56 -0400 Subject: [PATCH 04/15] RDKEMW-12856: throw early error when IRDB provides empty waveforms (#236) --- src/ble/ctrlm_ble_network.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index 57cdb6da..ab094c0e 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -796,9 +796,10 @@ void ctrlm_obj_network_ble_t::req_process_program_ir_codes(void *data, int size) } if (ble_rcu_interface_) { - if (!ble_rcu_interface_->programIrSignalWaveforms(controllers_[controller_id]->ieee_address_get().get_value(), + if (ir_codes.empty()) { + XLOGD_ERROR("Provided IR waveforms are empty!"); + } else if (!ble_rcu_interface_->programIrSignalWaveforms(controllers_[controller_id]->ieee_address_get().get_value(), std::move(ir_codes), dqm->vendor_info.rcu_support_bitmask)) { - XLOGD_ERROR("failed to program IR signal waveforms on remote"); } else { success = true; From a67f6d974ef655fa3b0e58eef798584717d482a2 Mon Sep 17 00:00:00 2001 From: Gene Gallagher <129112619+egalla204@users.noreply.github.com> Date: Sat, 30 May 2026 08:19:56 -0400 Subject: [PATCH 05/15] RDKEMW-16814: reduce ble status prints and events (#235) --- src/attributes/ctrlm_attr_general.cpp | 12 +- src/attributes/ctrlm_attr_voice.cpp | 4 +- src/ble/ctrlm_ble_controller.cpp | 4 +- src/ble/ctrlm_ble_controller_attr_version.cpp | 8 +- src/ble/ctrlm_ble_network.cpp | 252 ++++++++++++------ src/ble/ctrlm_ble_network.h | 7 + src/ble/ctrlm_ble_rcu_interface.cpp | 6 +- .../gatt/gatt_audioservice_rdk.cpp | 4 +- .../bleservices/gatt/gatt_batteryservice.cpp | 2 +- .../gatt/gatt_deviceinfoservice.cpp | 20 +- .../bleservices/gatt/gatt_infraredservice.cpp | 4 +- .../bleservices/gatt/gatt_infraredsignal.cpp | 12 +- .../gatt/gatt_remotecontrolservice.cpp | 20 +- .../hal/blercu/bluez/blegattnotifypipe.cpp | 2 +- src/ctrlm_controller.cpp | 6 +- src/ctrlm_ir_controller.cpp | 2 +- src/ctrlm_network.cpp | 10 +- src/ctrlm_network.h | 3 + src/database/ctrlm_database.cpp | 2 +- src/database/ctrlm_db_types.cpp | 4 +- src/network/ctrlm_ir_rf_db.h | 2 +- src/rf4ce/ctrlm_rf4ce_controller.cpp | 2 +- src/rf4ce/ctrlm_rf4ce_network.cpp | 2 +- 23 files changed, 251 insertions(+), 139 deletions(-) diff --git a/src/attributes/ctrlm_attr_general.cpp b/src/attributes/ctrlm_attr_general.cpp index a533e6fc..80a6edb9 100644 --- a/src/attributes/ctrlm_attr_general.cpp +++ b/src/attributes/ctrlm_attr_general.cpp @@ -76,7 +76,7 @@ bool ctrlm_string_db_attr_t::read_db(ctrlm_db_ctx_t ctx) { if(blob.read_db(ctx)) { this->value = blob.to_string(); ret = true; - XLOGD_INFO("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to read from db <%s>", this->get_name().c_str()); } @@ -90,7 +90,7 @@ bool ctrlm_string_db_attr_t::write_db(ctrlm_db_ctx_t ctx) { if(blob.from_string(this->value)) { if(blob.write_db(ctx)) { ret = true; - XLOGD_INFO("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to write to db <%s>", this->get_name().c_str()); } @@ -157,7 +157,7 @@ bool ctrlm_uint64_db_attr_t::read_db(ctrlm_db_ctx_t ctx) { if(data.read_db(ctx)) { this->set_value(data.get_uint64()); ret = true; - XLOGD_INFO("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to read from db <%s>", this->get_name().c_str()); } @@ -169,7 +169,7 @@ bool ctrlm_uint64_db_attr_t::write_db(ctrlm_db_ctx_t ctx) { ctrlm_db_uint64_t data(this->get_key(), this->get_table(), this->get_value()); if(data.write_db(ctx)) { ret = true; - XLOGD_INFO("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to write to db <%s>", this->get_name().c_str()); } @@ -237,7 +237,7 @@ bool ctrlm_ieee_db_addr_t::read_db(ctrlm_db_ctx_t ctx) { if(data.read_db(ctx)) { this->set_value(data.get_uint64()); ret = true; - XLOGD_INFO("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to read from db <%s>", this->get_name().c_str()); } @@ -249,7 +249,7 @@ bool ctrlm_ieee_db_addr_t::write_db(ctrlm_db_ctx_t ctx) { ctrlm_db_uint64_t data(this->get_key(), this->get_table(), this->get_value()); if(data.write_db(ctx)) { ret = true; - XLOGD_INFO("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to write to db <%s>", this->get_name().c_str()); } diff --git a/src/attributes/ctrlm_attr_voice.cpp b/src/attributes/ctrlm_attr_voice.cpp index 87f1759d..963b3b09 100644 --- a/src/attributes/ctrlm_attr_voice.cpp +++ b/src/attributes/ctrlm_attr_voice.cpp @@ -256,7 +256,7 @@ bool ctrlm_voice_metrics_t::read_db(ctrlm_db_ctx_t ctx) { this->utterances_exceeding_packet_loss_threshold_today = ((buf[39] << 24) | (buf[38] << 16) | (buf[37] << 8) | buf[36]); this->utterances_exceeding_packet_loss_threshold_yesterday = ((buf[43] << 24) | (buf[42] << 16) | (buf[41] << 8) | buf[40]); ret = true; - XLOGD_INFO("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("data from db is too small <%s>", this->get_name().c_str()); } @@ -322,7 +322,7 @@ bool ctrlm_voice_metrics_t::write_db(ctrlm_db_ctx_t ctx) { if(blob.from_buffer(buf, sizeof(buf))) { if(blob.write_db(ctx)) { ret = true; - XLOGD_INFO("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to write to db <%s>", this->get_name().c_str()); } diff --git a/src/ble/ctrlm_ble_controller.cpp b/src/ble/ctrlm_ble_controller.cpp index f1a87c22..c9e996c9 100644 --- a/src/ble/ctrlm_ble_controller.cpp +++ b/src/ble/ctrlm_ble_controller.cpp @@ -58,7 +58,7 @@ ctrlm_obj_controller_ble_t::ctrlm_obj_controller_ble_t(ctrlm_controller_id_t con wakeup_custom_list_(), irdbs_supported_(0) { - XLOGD_INFO("constructor - controller id <%u>", controller_id); + XLOGD_DEBUG("constructor - controller id <%u>", controller_id); voice_metrics_->read_config(); ieee_address_->set_num_bytes(6); @@ -66,7 +66,7 @@ ctrlm_obj_controller_ble_t::ctrlm_obj_controller_ble_t(ctrlm_controller_id_t con } ctrlm_obj_controller_ble_t::ctrlm_obj_controller_ble_t() { - XLOGD_INFO("default constructor"); + XLOGD_DEBUG("default constructor"); } void ctrlm_obj_controller_ble_t::db_create() { diff --git a/src/ble/ctrlm_ble_controller_attr_version.cpp b/src/ble/ctrlm_ble_controller_attr_version.cpp index 0bfd0a41..9ffb9077 100644 --- a/src/ble/ctrlm_ble_controller_attr_version.cpp +++ b/src/ble/ctrlm_ble_controller_attr_version.cpp @@ -40,7 +40,7 @@ bool ctrlm_ble_sw_version_t::read_db(ctrlm_db_ctx_t ctx) { if(blob.read_db(ctx)) { if (this->from_string(blob.to_string())) { ret = true; - XLOGD_INFO("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("%s read from database failed to parse <%s> ", this->get_name().c_str(), blob.to_string().c_str()); } @@ -58,7 +58,7 @@ bool ctrlm_ble_sw_version_t::write_db(ctrlm_db_ctx_t ctx) { if(blob.from_string(ver_str)) { if(blob.write_db(ctx)) { ret = true; - XLOGD_INFO("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to write to db <%s>", this->get_name().c_str()); } @@ -90,7 +90,7 @@ bool ctrlm_ble_hw_version_t::read_db(ctrlm_db_ctx_t ctx) { if(blob.read_db(ctx)) { if (this->from_string(blob.to_string())) { ret = true; - XLOGD_INFO("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s read from database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("%s read from database failed to parse <%s> ", this->get_name().c_str(), blob.to_string().c_str()); } @@ -108,7 +108,7 @@ bool ctrlm_ble_hw_version_t::write_db(ctrlm_db_ctx_t ctx) { if(blob.from_string(ver_str)) { if(blob.write_db(ctx)) { ret = true; - XLOGD_INFO("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); + XLOGD_DEBUG("%s written to database: %s", this->get_name().c_str(), this->to_string().c_str()); } else { XLOGD_ERROR("failed to write to db <%s>", this->get_name().c_str()); } diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index ab094c0e..c6bdce72 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -56,6 +56,11 @@ using namespace std; #define CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT (MINUTE_IN_MILLISECONDS * 5) // 5 minutes #define CTRLM_BLE_UPGRADE_PAUSE_TIMEOUT (MINUTE_IN_MILLISECONDS * 2) // 2 minutes +#define CTRLM_BLE_PRINT_STATUS_TIMEOUT (1000 * 6) // 6 seconds +#define CTRLM_BLE_EVENT_STATUS_TIMEOUT (1000 * 6) // 6 seconds +#define CTRLM_BLE_PRINT_STATUS_DEFER_MAX (20) +#define CTRLM_BLE_EVENT_STATUS_DEFER_MAX (20) + #define CTRLM_VENDOR_BLE_NETWORK_DISABLE_FILE "/etc/vendor/input/ble_network_disable" #define CTRLM_VENDOR_BLE_REMOTE_WHITELIST_FILE "/etc/vendor/input/ble_remote_whitelist.json" #define CTRLM_VENDOR_BLE_NETWORK_OPTIONS_FILE "/etc/vendor/input/ble_network_options.json" @@ -64,6 +69,8 @@ using namespace std; typedef struct { guint upgrade_controllers_timer_tag; guint upgrade_pause_timer_tag; + guint print_status_timer_tag; + guint event_status_timer_tag; } ctrlm_ble_network_t; static ctrlm_ble_network_t g_ctrlm_ble_network; @@ -77,8 +84,11 @@ static const vector ctrlm_ble_ir_key_names { }; -static gboolean ctrlm_ble_upgrade_controllers(gpointer user_data); -static gboolean ctrlm_ble_upgrade_resume(gpointer user_data); +static gboolean ctrlm_ble_print_status_timer_cb(gpointer user_data); +static gboolean ctrlm_ble_event_status_timer_cb(gpointer user_data); + +static gboolean ctrlm_ble_upgrade_controllers_timer_cb(gpointer user_data); +static gboolean ctrlm_ble_upgrade_resume_timer_cb(gpointer user_data); static bool ctrlm_ble_parse_upgrade_image_info(const std::string &filename, ctrlm_ble_upgrade_image_info_t &image_info); ctrlm_ble_unpair_metrics_t::ctrlm_ble_unpair_metrics_t() @@ -288,6 +298,8 @@ ctrlm_obj_network_ble_t::ctrlm_obj_network_ble_t(ctrlm_network_type_t type, ctrl init_result_ = CTRLM_HAL_RESULT_ERROR; ready_ = false; voice_disabled_ = false; + print_status_defer_count_ = 0; + event_status_defer_count_ = 0; g_ctrlm_ble_network.upgrade_controllers_timer_tag = 0; g_ctrlm_ble_network.upgrade_pause_timer_tag = 0; @@ -325,6 +337,8 @@ ctrlm_obj_network_ble_t::~ctrlm_obj_network_ble_t() { ctrlm_timeout_destroy(&g_ctrlm_ble_network.upgrade_controllers_timer_tag); ctrlm_timeout_destroy(&g_ctrlm_ble_network.upgrade_pause_timer_tag); + ctrlm_timeout_destroy(&g_ctrlm_ble_network.event_status_timer_tag); + ctrlm_timeout_destroy(&g_ctrlm_ble_network.print_status_timer_tag); ctrlm_ble_iarm_terminate(); @@ -414,20 +428,18 @@ void ctrlm_obj_network_ble_t::start(GMainLoop* main_loop) } } - // Print out pairing table - XLOGD_INFO("===================================================================="); - XLOGD_INFO(" %u Bound Controllers", controllers_.size()); - XLOGD_INFO("===================================================================="); + XLOGD_INFO("%u bound BLE controllers", controllers_.size()); for(auto &controller : controllers_) { if (controller.second->is_stale(this->stale_remote_time_threshold_get())) { XLOGD_TELEMETRY("Stale remote suspected: <%s>", controller.second->ieee_address_get().to_string().c_str()); } - controller.second->print_status(); } + schedule_status_print(); + // Read IR RF Database from database ir_rf_database_.load_db(); - XLOGD_INFO("\n%s", this->ir_rf_database_.to_string(true).c_str()); + XLOGD_INFO("\n%s", this->ir_rf_database_.to_string(false).c_str()); } @@ -782,7 +794,8 @@ void ctrlm_obj_network_ble_t::req_process_program_ir_codes(void *data, int size) // First add IR Codes to the IR RF Database (this contains all of the logic for maintaining TV vs AVR codes) ir_rf_database_.add_irdb_codes(dqm->ir_codes); - XLOGD_INFO("\n%s", this->ir_rf_database_.to_string(true).c_str()); + XLOGD_INFO("\n%s", this->ir_rf_database_.to_string(false).c_str()); + XLOGD_DEBUG("\n%s", this->ir_rf_database_.to_string(true).c_str()); // Now get the IR codes for the BLE IR slots for(auto key : ctrlm_ble_ir_key_names) { if(ir_rf_database_.has_entry(key)) { @@ -846,7 +859,7 @@ void ctrlm_obj_network_ble_t::req_process_ir_clear_codes(void *data, int size) { } else { success = true; ir_rf_database_.clear_ir_codes(); - XLOGD_INFO("\n%s", ir_rf_database_.to_string(true).c_str()); + XLOGD_INFO("\n%s", ir_rf_database_.to_string(false).c_str()); controllers_[controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_TV, "0"); controllers_[controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_AVR, "0"); } @@ -1272,7 +1285,7 @@ void ctrlm_obj_network_ble_t::clearUpgradeImages() { upgrade_images_.clear(); } -static gboolean ctrlm_ble_upgrade_controllers(gpointer user_data) { +static gboolean ctrlm_ble_upgrade_controllers_timer_cb(gpointer user_data) { ctrlm_network_id_t* net_id = (ctrlm_network_id_t*) user_data; if (net_id != NULL) { // Allocate a message and send it to Control Manager's queue @@ -1287,7 +1300,7 @@ static gboolean ctrlm_ble_upgrade_controllers(gpointer user_data) { return false; } -static gboolean ctrlm_ble_upgrade_resume(gpointer user_data) { +static gboolean ctrlm_ble_upgrade_resume_timer_cb(gpointer user_data) { ctrlm_network_id_t* net_id = (ctrlm_network_id_t*) user_data; if (net_id != NULL) { // Allocate a message and send it to Control Manager's queue @@ -1371,7 +1384,7 @@ void ctrlm_obj_network_ble_t::req_process_upgrade_controllers(void *data, int si // simply will not take the reboot and the software revision will remain unchanged. XLOGD_DEBUG("Upgrading one remote at a time, setting timer for %d minutes to check if other remotes need upgrades.", CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT / MINUTE_IN_MILLISECONDS); ctrlm_timeout_destroy(&g_ctrlm_ble_network.upgrade_controllers_timer_tag); - g_ctrlm_ble_network.upgrade_controllers_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT, ctrlm_ble_upgrade_controllers, &id_); + g_ctrlm_ble_network.upgrade_controllers_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT, ctrlm_ble_upgrade_controllers_timer_cb, &id_); return; } else { XLOGD_INFO("Software upgrade not required for controller %s.", controller.second->ieee_address_get().to_string().c_str()); @@ -1403,7 +1416,7 @@ void ctrlm_obj_network_ble_t::req_process_upgrade_controllers(void *data, int si } else { XLOGD_INFO("Upgrade currently in progress, setting timer for %d minutes to check if other remotes need upgrades.", CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT / MINUTE_IN_MILLISECONDS); ctrlm_timeout_destroy(&g_ctrlm_ble_network.upgrade_controllers_timer_tag); - g_ctrlm_ble_network.upgrade_controllers_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT, ctrlm_ble_upgrade_controllers, &id_); + g_ctrlm_ble_network.upgrade_controllers_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT, ctrlm_ble_upgrade_controllers_timer_cb, &id_); } } @@ -1667,10 +1680,7 @@ void ctrlm_obj_network_ble_t::ind_rcu_status(ctrlm_hal_ble_RcuStatusData_t *para } void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { - // XLOGD_DEBUG("Enter..."); THREAD_ID_VALIDATE(); - bool report_status = true; - bool print_status = true; ctrlm_hal_ble_RcuStatusData_t *dqm = (ctrlm_hal_ble_RcuStatusData_t *)data; g_assert(dqm); @@ -1687,19 +1697,25 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { // These properties are associated with the network, not a specific RCU case CTRLM_HAL_BLE_PROPERTY_IS_PAIRING: // don't send up status event for this since CTRLM_HAL_BLE_PROPERTY_STATE handles all pairing states. - report_status = false; - print_status = false; break; case CTRLM_HAL_BLE_PROPERTY_PAIRING_CODE: - report_status = false; // don't send up status event for this. break; case CTRLM_HAL_BLE_PROPERTY_STATE: XLOGD_TELEMETRY("BLE remote RF pairing state changed to <%s>", ctrlm_rf_pair_state_str(dqm->state)); state_ = dqm->state; + if (state_ == CTRLM_RF_PAIR_STATE_COMPLETE || state_ == CTRLM_RF_PAIR_STATE_IDLE) { + // allow some time for all remote data to be received before sending the event to avoid multiple events + schedule_status_event(); + } else { + // for any other states, send event immediately. + schedule_status_event(true); + } break; case CTRLM_HAL_BLE_PROPERTY_IR_STATE: XLOGD_TELEMETRY("BLE remote IR programming state changed to <%s>", ctrlm_ir_state_str(dqm->ir_state)); ir_state_ = dqm->ir_state; + // send event immediately. + schedule_status_event(true); break; default: { @@ -1708,35 +1724,58 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { if (false == getControllerId(dqm->rcu_data.ieee_address, &controller_id)) { XLOGD_ERROR("Controller <%s> NOT found in the network!!", ctrlm_convert_mac_long_to_string(dqm->rcu_data.ieee_address).c_str()); - report_status = false; - print_status = false; } else { auto controller = controllers_[controller_id]; switch (dqm->property_updated) { case CTRLM_HAL_BLE_PROPERTY_DEVICE_ID: controller->set_device_minor_id(dqm->rcu_data.device_minor_id); + XLOGD_INFO("Controller <%s> device minor ID changed to <%d>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.device_minor_id); + //report this immediately since listeners use this to find the input device path for the remote + schedule_status_event(true); break; case CTRLM_HAL_BLE_PROPERTY_NAME: controller->setName(string(dqm->rcu_data.name), true); + XLOGD_INFO("Controller <%s> name changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.name); + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_MANUFACTURER: controller->setManufacturer(string(dqm->rcu_data.manufacturer), true); + XLOGD_INFO("Controller <%s> manufacturer changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.manufacturer); + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_MODEL: controller->setModel(string(dqm->rcu_data.model), true); + XLOGD_INFO("Controller <%s> model changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.model); + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_SERIAL_NUMBER: controller->setSerialNumber(string(dqm->rcu_data.serial_number), true); + XLOGD_INFO("Controller <%s> serial number changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.serial_number); + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_HW_REVISION: controller->setHwRevision(string(dqm->rcu_data.hw_revision), true); + XLOGD_INFO("Controller <%s> hardware revision changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.hw_revision); + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_FW_REVISION: controller->setFwRevision(string(dqm->rcu_data.fw_revision), true); + XLOGD_INFO("Controller <%s> firmware revision changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.fw_revision); + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_SW_REVISION: { controller->setSwRevision(string(dqm->rcu_data.sw_revision), true); + XLOGD_INFO("Controller <%s> software revision changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.sw_revision); + schedule_status_print(); + schedule_status_event(); + // SW Rev updated, make sure xconf config file is updated ctrlm_main_queue_msg_header_t *msg = (ctrlm_main_queue_msg_header_t *)g_malloc(sizeof(ctrlm_main_queue_msg_header_t)); if(msg == NULL) { @@ -1749,26 +1788,33 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { } case CTRLM_HAL_BLE_PROPERTY_IR_CODE: controller->setIrCode(dqm->rcu_data.ir_code); + XLOGD_INFO("Controller <%s> IR code changed to <%d>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.ir_code); break; case CTRLM_HAL_BLE_PROPERTY_TOUCH_MODE: controller->setTouchMode(dqm->rcu_data.touch_mode); - print_status = false; - report_status = false; + XLOGD_INFO("Controller <%s> touch mode changed to <%u>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.touch_mode); break; case CTRLM_HAL_BLE_PROPERTY_TOUCH_MODE_SETTABLE: controller->setTouchModeSettable(dqm->rcu_data.touch_mode_settable); - print_status = false; - report_status = false; + XLOGD_INFO("Controller <%s> touch mode settable changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.touch_mode_settable ? "TRUE" : "FALSE"); break; case CTRLM_HAL_BLE_PROPERTY_BATTERY_LEVEL: controller->setBatteryPercent(dqm->rcu_data.battery_level, true); - print_status = false; + XLOGD_INFO("Controller <%s> battery level changed to <%u%%>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.battery_level); + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_CONNECTED: controller->setConnected(dqm->rcu_data.connected); + XLOGD_INFO("Controller <%s> connected changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.connected ? "TRUE" : "FALSE"); + + schedule_status_print(); + // send event immediately. + schedule_status_event(true); break; case CTRLM_HAL_BLE_PROPERTY_AUDIO_STREAMING: controller->setAudioStreaming(dqm->rcu_data.audio_streaming); + XLOGD_INFO("Controller <%s> audio streaming changed to <%s>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.audio_streaming ? "TRUE" : "FALSE"); if(!dqm->rcu_data.audio_streaming) { // report end of stream stats if (ble_rcu_interface_) { @@ -1797,19 +1843,14 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { } } } - - report_status = false; - print_status = false; break; case CTRLM_HAL_BLE_PROPERTY_AUDIO_GAIN_LEVEL: controller->setAudioGainLevel(dqm->rcu_data.audio_gain_level); - report_status = false; - print_status = false; + XLOGD_INFO("Controller <%s> audio gain level changed to <%u>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.audio_gain_level); break; case CTRLM_HAL_BLE_PROPERTY_AUDIO_CODECS: controller->setAudioCodecs(dqm->rcu_data.audio_codecs); - report_status = false; - print_status = false; + XLOGD_INFO("Controller <%s> audio codecs changed to <0x%X>", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.audio_codecs); break; case CTRLM_HAL_BLE_PROPERTY_IS_UPGRADING: XLOGD_AUTOMATION_INFO("Controller <%s> firmware upgrading = %s", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.is_upgrading ? "TRUE" : "FALSE"); @@ -1819,8 +1860,6 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { // flag to TRUE based on this property. Controller upgrade progress flag should only be set to true if packets are actively being sent controller->setUpgradeInProgress(false); } - report_status = false; - print_status = false; break; case CTRLM_HAL_BLE_PROPERTY_UPGRADE_PROGRESS: XLOGD_AUTOMATION_INFO("Controller <%s> firmware upgrade %d%% complete...", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.upgrade_progress); @@ -1830,21 +1869,17 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { controller->set_upgrade_progress(dqm->rcu_data.upgrade_progress); if (controller->is_upgrade_progress_at_increment()) { - iarm_event_rcu_firmware_status(*controller); + iarm_event_rcu_firmware_status(*controller); } - report_status = false; - print_status = false; break; case CTRLM_HAL_BLE_PROPERTY_UPGRADE_ERROR: XLOGD_AUTOMATION_ERROR("Controller <%s> firmware upgrade FAILED with error <%s>.", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.upgrade_error); - report_status = false; - print_status = false; controller->set_upgrade_error(dqm->rcu_data.upgrade_error); if (controller->retry_ota()) { controller->setUpgradeAttempted(false); XLOGD_WARN("Upgrade failed, setting timer for %d minutes to retry.", CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT / MINUTE_IN_MILLISECONDS); ctrlm_timeout_destroy(&g_ctrlm_ble_network.upgrade_controllers_timer_tag); - g_ctrlm_ble_network.upgrade_controllers_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT, ctrlm_ble_upgrade_controllers, &id_); + g_ctrlm_ble_network.upgrade_controllers_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_CONTINUE_TIMEOUT, ctrlm_ble_upgrade_controllers_timer_cb, &id_); controller->set_upgrade_state(CTRLM_RCU_UPGRADE_STATE_RETRYING); } else { controller->check_upgrade_error(); @@ -1871,8 +1906,6 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { case CTRLM_HAL_BLE_PROPERTY_UNPAIR_REASON: XLOGD_AUTOMATION_INFO("Controller <%s> notified reason for unpairing = <%s>", controller->ieee_address_get().to_string().c_str(), ctrlm_ble_unpair_reason_str(dqm->rcu_data.unpair_reason)); last_rcu_unpair_metrics_.write_rcu_unpair_event(controller->ieee_address_get().get_value(), string(ctrlm_ble_unpair_reason_str(dqm->rcu_data.unpair_reason))); - report_status = false; - print_status = false; if (this->unpair_on_remote_request_ && (dqm->rcu_data.unpair_reason == CTRLM_BLE_RCU_UNPAIR_REASON_SFM || dqm->rcu_data.unpair_reason == CTRLM_BLE_RCU_UNPAIR_REASON_FACTORY_RESET || @@ -1891,8 +1924,6 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { dqm->rcu_data.reboot_reason == CTRLM_BLE_RCU_REBOOT_REASON_ASSERT ? dqm->rcu_data.assert_report : "" , dqm->rcu_data.reboot_reason == CTRLM_BLE_RCU_REBOOT_REASON_ASSERT ? "\"" : ""); - report_status = false; - print_status = false; if (dqm->rcu_data.reboot_reason == CTRLM_BLE_RCU_REBOOT_REASON_FW_UPDATE) { controller->ota_clear_all_failure_counters(); @@ -1910,48 +1941,114 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { XLOGD_INFO("Controller <%s> notified last wakeup key = %u (%s key)", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.last_wakeup_key, ctrlm_linux_key_code_str(dqm->rcu_data.last_wakeup_key, false)); controller->setLastWakeupKey(dqm->rcu_data.last_wakeup_key); - print_status = false; + + // send event immediately. + schedule_status_event(true); break; case CTRLM_HAL_BLE_PROPERTY_WAKEUP_CONFIG: controller->setWakeupConfig(dqm->rcu_data.wakeup_config); XLOGD_INFO("Controller <%s> notified wakeup config = <%s>", controller->ieee_address_get().to_string().c_str(), ctrlm_rcu_wakeup_config_str(controller->get_wakeup_config())); - if (controller->get_wakeup_config() == CTRLM_RCU_WAKEUP_CONFIG_CUSTOM) { - // Don't report status yet if its a custom config. Do that after we receive the custom list - report_status = false; - print_status = false; - } + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_WAKEUP_CUSTOM_LIST: controller->setWakeupCustomList(dqm->rcu_data.wakeup_custom_list, dqm->rcu_data.wakeup_custom_list_size); XLOGD_INFO("Controller <%s> notified wakeup custom list = <%s>", controller->ieee_address_get().to_string().c_str(), controller->wakeupCustomListToString().c_str()); - if (controller->get_wakeup_config() != CTRLM_RCU_WAKEUP_CONFIG_CUSTOM) { - // Only report status if the config is set to custom - report_status = false; - print_status = false; - } + schedule_status_print(); + schedule_status_event(); break; case CTRLM_HAL_BLE_PROPERTY_IRDBS_SUPPORTED: controller->setSupportedIrdbs(dqm->rcu_data.irdbs_supported); - report_status = false; - print_status = false; + schedule_status_print(); break; default: XLOGD_WARN("Unhandled Property: %d !!!!!!!!!!!!!!!!!!!!!!!!", dqm->property_updated); - report_status = false; - print_status = false; break; } } break; } } +} + +void ctrlm_obj_network_ble_t::schedule_status_print(bool immediately) { + XLOGD_DEBUG("immediately = <%s>, print_status_defer_count_ = <%d>", immediately ? "TRUE" : "FALSE", print_status_defer_count_); + THREAD_ID_VALIDATE(); + ctrlm_timeout_destroy(&g_ctrlm_ble_network.print_status_timer_tag); + if (immediately || print_status_defer_count_ > CTRLM_BLE_PRINT_STATUS_DEFER_MAX) { + print_status_defer_count_ = 0; + ctrlm_main_queue_msg_header_t msg; + errno_t safec_rc = memset_s(&msg, sizeof(msg), 0, sizeof(msg)); + ERR_CHK(safec_rc); + ctrlm_main_queue_handler_push(CTRLM_HANDLER_NETWORK, (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_print_status, &msg, sizeof(msg), NULL, id_); + } else { + print_status_defer_count_++; + g_ctrlm_ble_network.print_status_timer_tag = ctrlm_timeout_create(CTRLM_BLE_PRINT_STATUS_TIMEOUT, ctrlm_ble_print_status_timer_cb, &id_); + } +} + +void ctrlm_obj_network_ble_t::schedule_status_event(bool immediately) { + XLOGD_DEBUG("immediately = <%s>, event_status_defer_count_ = <%d>", immediately ? "TRUE" : "FALSE", event_status_defer_count_); + THREAD_ID_VALIDATE(); + ctrlm_timeout_destroy(&g_ctrlm_ble_network.event_status_timer_tag); + if (immediately || event_status_defer_count_ > CTRLM_BLE_EVENT_STATUS_DEFER_MAX) { + event_status_defer_count_ = 0; + ctrlm_main_queue_msg_header_t msg; + errno_t safec_rc = memset_s(&msg, sizeof(msg), 0, sizeof(msg)); + ERR_CHK(safec_rc); + ctrlm_main_queue_handler_push(CTRLM_HANDLER_NETWORK, (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_event_status, &msg, sizeof(msg), NULL, id_); + } else { + event_status_defer_count_++; + g_ctrlm_ble_network.event_status_timer_tag = ctrlm_timeout_create(CTRLM_BLE_EVENT_STATUS_TIMEOUT, ctrlm_ble_event_status_timer_cb, &id_); + } +} + + +void ctrlm_obj_network_ble_t::req_process_print_status(void *data, int size) { + XLOGD_DEBUG("Enter..."); + THREAD_ID_VALIDATE(); + + print_status_defer_count_ = 0; + printStatus(); +} - if (true == print_status) { - printStatus(); +void ctrlm_obj_network_ble_t::req_process_event_status(void *data, int size) { + XLOGD_DEBUG("Enter..."); + THREAD_ID_VALIDATE(); + + event_status_defer_count_ = 0; + iarm_event_rcu_status(); +} + + +static gboolean ctrlm_ble_print_status_timer_cb(gpointer user_data) { + XLOGD_DEBUG("Enter..."); + ctrlm_network_id_t* net_id = (ctrlm_network_id_t*) user_data; + if (net_id != NULL) { + // Allocate a message and send it to Control Manager's queue + ctrlm_main_queue_msg_header_t msg; + errno_t safec_rc = memset_s(&msg, sizeof(msg), 0, sizeof(msg)); + ERR_CHK(safec_rc); + + ctrlm_main_queue_handler_push(CTRLM_HANDLER_NETWORK, (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_print_status, &msg, sizeof(msg), NULL, *net_id); } - if (true == report_status) { - iarm_event_rcu_status(); + g_ctrlm_ble_network.print_status_timer_tag = 0; + return false; +} + +static gboolean ctrlm_ble_event_status_timer_cb(gpointer user_data) { + XLOGD_DEBUG("Enter..."); + ctrlm_network_id_t* net_id = (ctrlm_network_id_t*) user_data; + if (net_id != NULL) { + // Allocate a message and send it to Control Manager's queue + ctrlm_main_queue_msg_header_t msg; + errno_t safec_rc = memset_s(&msg, sizeof(msg), 0, sizeof(msg)); + ERR_CHK(safec_rc); + + ctrlm_main_queue_handler_push(CTRLM_HANDLER_NETWORK, (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_event_status, &msg, sizeof(msg), NULL, *net_id); } + g_ctrlm_ble_network.event_status_timer_tag = 0; + return false; } void ctrlm_obj_network_ble_t::populate_rcu_status_message(ctrlm_iarm_RcuStatus_params_t *msg) { @@ -2033,6 +2130,10 @@ ctrlm_controller_id_t ctrlm_obj_network_ble_t::controller_add(ctrlm_hal_ble_rcu_ controller->db_store(); } + + schedule_status_print(); + schedule_status_event(); + return id; } @@ -2060,13 +2161,9 @@ void ctrlm_obj_network_ble_t::ind_process_paired(void *data, int size) { return; } - ctrlm_controller_id_t id = controller_add(dqm->rcu_data); - if (controller_exists(id)) { - controllers_[id]->print_status(); - } + controller_add(dqm->rcu_data); // Sync currently connected devices with the HAL - if (ble_rcu_interface_) { auto devices = ble_rcu_interface_->getManagedDevices(); @@ -2130,9 +2227,9 @@ void ctrlm_obj_network_ble_t::ind_process_unpaired(void *data, int size) { controller_remove(id); controllers_.erase(id); - // report updated controller status to the plugin - printStatus(); - iarm_event_rcu_status(); + + schedule_status_print(); + schedule_status_event(); // Update xconf config file ctrlm_main_queue_msg_header_t *msg = (ctrlm_main_queue_msg_header_t *)g_malloc(sizeof(ctrlm_main_queue_msg_header_t)); @@ -2227,7 +2324,7 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { XLOGD_INFO("Upgrade cancelled since remote is being used. Setting timer to resume upgrade in %d minutes.", CTRLM_BLE_UPGRADE_PAUSE_TIMEOUT / MINUTE_IN_MILLISECONDS); //delete existing timer and start again ctrlm_timeout_destroy(&g_ctrlm_ble_network.upgrade_pause_timer_tag); - g_ctrlm_ble_network.upgrade_pause_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_PAUSE_TIMEOUT, ctrlm_ble_upgrade_resume, &id_); + g_ctrlm_ble_network.upgrade_pause_timer_tag = ctrlm_timeout_create(CTRLM_BLE_UPGRADE_PAUSE_TIMEOUT, ctrlm_ble_upgrade_resume_timer_cb, &id_); } else if (controller->getUpgradeStuck() && !upgrade_in_progress_) { @@ -2235,7 +2332,7 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { controller->ieee_address_get().to_string().c_str()); //delete existing timer and immediately kick off another upgrade ctrlm_timeout_destroy(&g_ctrlm_ble_network.upgrade_pause_timer_tag); - g_ctrlm_ble_network.upgrade_pause_timer_tag = ctrlm_timeout_create(500, ctrlm_ble_upgrade_resume, &id_); + g_ctrlm_ble_network.upgrade_pause_timer_tag = ctrlm_timeout_create(500, ctrlm_ble_upgrade_resume_timer_cb, &id_); } } else if (key_status == CTRLM_KEY_STATUS_UP) { @@ -2380,8 +2477,7 @@ bool ctrlm_obj_network_ble_t::getControllerId(unsigned long long ieee_address, c } } if ( !found ) { - XLOGD_WARN("Controller matching ieee_address (%s) NOT FOUND.", - ctrlm_convert_mac_long_to_string(ieee_address).c_str()); + XLOGD_DEBUG("Controller matching ieee_address (%s) NOT FOUND.", ctrlm_convert_mac_long_to_string(ieee_address).c_str()); } return found; } @@ -2430,7 +2526,7 @@ ctrlm_controller_id_t ctrlm_obj_network_ble_t::controller_id_assign() { // Get the next available controller id for(ctrlm_controller_id_t index = BLE_RCU_ID_RANGE_MIN; index < BLE_RCU_ID_RANGE_MAX; index++) { if(!controller_exists(index)) { - XLOGD_INFO("controller id %u", index); + XLOGD_DEBUG("controller id %u", index); return(index); } } diff --git a/src/ble/ctrlm_ble_network.h b/src/ble/ctrlm_ble_network.h index 1182889a..90724d94 100644 --- a/src/ble/ctrlm_ble_network.h +++ b/src/ble/ctrlm_ble_network.h @@ -168,6 +168,11 @@ class ctrlm_obj_network_ble_t : public ctrlm_obj_network_t { virtual void req_process_cancel_controller_upgrade(void *data, int size); virtual void req_process_status_controller_upgrade(void *data, int size); + void schedule_status_print(bool immediately = false); + void schedule_status_event(bool immediately = false); + virtual void req_process_print_status(void *data, int size); + virtual void req_process_event_status(void *data, int size); + virtual json_t * xconf_export_controllers(); void addUpgradeImage(const ctrlm_ble_upgrade_image_info_t &image_info); void clearUpgradeImages(); @@ -215,6 +220,8 @@ class ctrlm_obj_network_ble_t : public ctrlm_obj_network_t { bool upgrade_in_progress_ = false; bool unpair_on_remote_request_ = true; ctrlm_ble_unpair_metrics_t last_rcu_unpair_metrics_; + int print_status_defer_count_ = 0; + int event_status_defer_count_ = 0; std::map controllers_; std::map upgrade_images_; diff --git a/src/ble/ctrlm_ble_rcu_interface.cpp b/src/ble/ctrlm_ble_rcu_interface.cpp index f90cdc67..4f3e1635 100644 --- a/src/ble/ctrlm_ble_rcu_interface.cpp +++ b/src/ble/ctrlm_ble_rcu_interface.cpp @@ -1397,7 +1397,7 @@ std::vector ctrlm_ble_rcu_interface_t::getManagedDevices() return ret; } - XLOGD_INFO("Get list of currently managed devices"); + XLOGD_DEBUG("Get list of currently managed devices"); auto devices = m_controller->managedDevices(); for (auto const &device : devices) { @@ -1482,8 +1482,8 @@ static int OpenKeyInputDevice(uint64_t ieee_address) uint64_t evdev_macaddr = ctrlm_convert_mac_string_to_long(libevdev_get_uniq(evdev)); if (evdev_macaddr == ieee_address) { - XLOGD_INFO("Input Dev Node (%s) for device (0x%llX) FOUND, returning file descriptor: <%d>", - keyInputFilename.c_str(), ieee_address, input_fd); + XLOGD_INFO("Input Dev Node (%s) for device: <%s> (0x%llX) FOUND, returning file descriptor: <%d>", + keyInputFilename.c_str(), libevdev_get_name(evdev), ieee_address, input_fd); libevdev_free(evdev); evdev = NULL; diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp index 265a5d83..80897f10 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp @@ -468,7 +468,7 @@ void GattAudioServiceRdk::requestGainLevel() if (value.size() == 1) { m_gainLevel = value[0]; - XLOGD_INFO("Successfully read from RCU gain level = %u", m_gainLevel); + XLOGD_DEBUG("Successfully read from RCU gain level = %u", m_gainLevel); m_gainLevelChangedSlots.invoke(m_gainLevel); } else { XLOGD_ERROR("gain value received has invalid length (%d bytes)", value.size()); @@ -502,7 +502,7 @@ void GattAudioServiceRdk::requestAudioCodecs() if (value.size() == sizeof(m_audioCodecs)) { memcpy(&m_audioCodecs, value.data(), sizeof(m_audioCodecs)); - XLOGD_INFO("Successfully read from RCU audio codecs bit mask = 0x%X", m_audioCodecs); + XLOGD_DEBUG("Successfully read from RCU audio codecs bit mask = 0x%X", m_audioCodecs); m_audioCodecsChangedSlots.invoke(m_audioCodecs); } else { XLOGD_ERROR("audio codec received has invalid length (%d bytes)", value.size()); diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_batteryservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_batteryservice.cpp index 0dd84fcf..951674e7 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_batteryservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_batteryservice.cpp @@ -275,7 +275,7 @@ void GattBatteryService::onBatteryLevelChanged(const std::vector &newVa return; } - XLOGD_INFO("battery level changed to %u%%", newValue[0]); + XLOGD_DEBUG("battery level changed to %u%%", newValue[0]); // clamp the reply between 0 and 100 unsigned int level = sanitiseBatteryLevel(newValue[0]); diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_deviceinfoservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_deviceinfoservice.cpp index f4955dfa..441dda76 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_deviceinfoservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_deviceinfoservice.cpp @@ -239,11 +239,9 @@ void GattDeviceInfoService::onEnteredState(int state) */ void GattDeviceInfoService::onExitedState(int state) { - // on exiting the initialising state we should have all the required - // device info fields, so at this point log a milestone message with all - // the details + // on exiting the initialising state we should have all the required device info fields if (state == InitialisingState) { - XLOGD_INFO("bluetooth rcu device info [ %s / %s / hw:%s / fw:%s / sw:%s ]", + XLOGD_DEBUG("bluetooth rcu device info [ %s / %s / hw:%s / fw:%s / sw:%s ]", m_manufacturerName.c_str(), m_modelNumber.c_str(), m_hardwareRevision.c_str(), m_firmwareVersion.c_str(), m_softwareVersion.c_str()); @@ -380,7 +378,7 @@ void GattDeviceInfoService::setManufacturerName(const std::vector &valu if (name != m_manufacturerName) { m_manufacturerName = name; - XLOGD_INFO("manufacturer name: %s", m_manufacturerName.c_str()); + XLOGD_DEBUG("manufacturer name: %s", m_manufacturerName.c_str()); m_manufacturerNameChangedSlots.invoke(m_manufacturerName); } @@ -401,7 +399,7 @@ void GattDeviceInfoService::setModelNumber(const std::vector &value) if (model != m_modelNumber) { m_modelNumber = model; - XLOGD_INFO("model number: %s", m_modelNumber.c_str()); + XLOGD_DEBUG("model number: %s", m_modelNumber.c_str()); m_modelNumberChangedSlots.invoke(m_modelNumber); } @@ -422,7 +420,7 @@ void GattDeviceInfoService::setSerialNumber(const std::vector &value) if (serial != m_serialNumber) { m_serialNumber = serial; - XLOGD_INFO("serial number: %s", m_serialNumber.c_str()); + XLOGD_DEBUG("serial number: %s", m_serialNumber.c_str()); m_serialNumberChangedSlots.invoke(m_serialNumber); } @@ -443,7 +441,7 @@ void GattDeviceInfoService::setHardwareRevision(const std::vector &valu if (hwVersion != m_hardwareRevision) { m_hardwareRevision = hwVersion; - XLOGD_INFO("hardware revision: %s", m_hardwareRevision.c_str()); + XLOGD_DEBUG("hardware revision: %s", m_hardwareRevision.c_str()); m_hardwareRevisionChangedSlots.invoke(m_hardwareRevision); } @@ -465,7 +463,7 @@ void GattDeviceInfoService::setFirmwareVersion(const std::vector &value if (fwVersion != m_firmwareVersion) { m_firmwareVersion = fwVersion; - XLOGD_INFO("firmware version: %s", m_firmwareVersion.c_str()); + XLOGD_DEBUG("firmware version: %s", m_firmwareVersion.c_str()); m_firmwareVersionChangedSlots.invoke(m_firmwareVersion); } @@ -487,7 +485,7 @@ void GattDeviceInfoService::setSoftwareVersion(const std::vector &value if (swVersion != m_softwareVersion) { m_softwareVersion = swVersion; - XLOGD_INFO("software version: %s", m_softwareVersion.c_str()); + XLOGD_DEBUG("software version: %s", m_softwareVersion.c_str()); m_softwareVersionChangedSlots.invoke(m_softwareVersion); } @@ -518,7 +516,7 @@ void GattDeviceInfoService::setSystemId(const std::vector &value) (((uint64_t)value[6]) << 8) | (((uint64_t)value[7]) << 0); - XLOGD_INFO("system id: 0x%016llx", m_systemId); + XLOGD_DEBUG("system id: 0x%016llx", m_systemId); } // ----------------------------------------------------------------------------- diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp index 40dff3c6..d7be62e4 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp @@ -449,7 +449,7 @@ void GattInfraredService::requestIrSupport() if (value.size() == 1) { uint8_t irSupport_ = value[0]; - XLOGD_INFO("IR support value = 0x%x", irSupport_); + XLOGD_DEBUG("IR support value = 0x%x", irSupport_); if (irSupport_ != m_irSupport) { m_irSupport = irSupport_; m_irSupportChangedSlots.invoke(m_irSupport); @@ -500,7 +500,7 @@ void GattInfraredService::requestCodeId() (int32_t(value[2]) << 16) | (int32_t(value[3]) << 24); - XLOGD_INFO("IR code ID = %d", codeId_); + XLOGD_DEBUG("IR code ID = %d", codeId_); if (codeId_ != m_codeId) { m_codeId = codeId_; diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_infraredsignal.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_infraredsignal.cpp index b5ecc8fb..351e5cb9 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_infraredsignal.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_infraredsignal.cpp @@ -365,27 +365,27 @@ void GattInfraredSignal::onEnteredInitialisingState() switch (value[0]) { case 0x0C: m_keyCode = Key_PowerPrimary; - XLOGD_INFO("found characteristic for 0x%02hhx (Key_PowerPrimary)", value[0]); + XLOGD_DEBUG("found characteristic for 0x%02hhx (Key_PowerPrimary)", value[0]); break; case 0x0B: m_keyCode = Key_PowerSecondary; - XLOGD_INFO("found characteristic for 0x%02hhx (Key_PowerSecondary)", value[0]); + XLOGD_DEBUG("found characteristic for 0x%02hhx (Key_PowerSecondary)", value[0]); break; case 0x29: m_keyCode = Key_Input; - XLOGD_INFO("found characteristic for 0x%02hhx (Key_Input)", value[0]); + XLOGD_DEBUG("found characteristic for 0x%02hhx (Key_Input)", value[0]); break; case 0x10: m_keyCode = Key_VolUp; - XLOGD_INFO("found characteristic for 0x%02hhx (Key_VolUp)", value[0]); + XLOGD_DEBUG("found characteristic for 0x%02hhx (Key_VolUp)", value[0]); break; case 0x11: m_keyCode = Key_VolDown; - XLOGD_INFO("found characteristic for 0x%02hhx (Key_VolDown)", value[0]); + XLOGD_DEBUG("found characteristic for 0x%02hhx (Key_VolDown)", value[0]); break; case 0x0D: m_keyCode = Key_Mute; - XLOGD_INFO("found characteristic for 0x%02hhx (Key_Mute)", value[0]); + XLOGD_DEBUG("found characteristic for 0x%02hhx (Key_Mute)", value[0]); break; /* diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp index a6c89198..2ec23b0a 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp @@ -279,7 +279,7 @@ void GattRemoteControlService::requestStartUnpairNotify() m_stateMachine.cancelDelayedEvents(RetryStartNotifyEvent); m_stateMachine.postDelayedEvent(RetryStartNotifyEvent, 2000); } else { - XLOGD_INFO("request to start notifications on Unpair characteristic succeeded"); + XLOGD_DEBUG("request to start notifications on Unpair characteristic succeeded"); } }; @@ -303,7 +303,7 @@ void GattRemoteControlService::requestStartRebootNotify() m_stateMachine.cancelDelayedEvents(RetryStartNotifyEvent); m_stateMachine.postDelayedEvent(RetryStartNotifyEvent, 2000); } else { - XLOGD_INFO("request to start notifications on Reboot Reason characteristic succeeded"); + XLOGD_DEBUG("request to start notifications on Reboot Reason characteristic succeeded"); } }; @@ -332,7 +332,7 @@ void GattRemoteControlService::requestRawBatteryVoltageChangedNotify() m_stateMachine.cancelDelayedEvents(RetryStartNotifyEvent); m_stateMachine.postDelayedEvent(RetryStartNotifyEvent, 2000); } else { - XLOGD_INFO("request to start notifications on Raw Battery Voltage characteristic succeeded"); + XLOGD_DEBUG("request to start notifications on Raw Battery Voltage characteristic succeeded"); } }; @@ -433,7 +433,7 @@ void GattRemoteControlService::requestUnpairReason() if (value.size() == 1) { m_unpairReason = value[0]; - XLOGD_INFO("Initial unpair reason is %u (%s)", m_unpairReason, + XLOGD_DEBUG("Initial unpair reason is %u (%s)", m_unpairReason, ctrlm_ble_unpair_reason_str((ctrlm_ble_RcuUnpairReason_t)m_unpairReason)); } else { XLOGD_ERROR("Unpair reason received has invalid length (%d bytes)", value.size()); @@ -468,7 +468,7 @@ void GattRemoteControlService::requestRebootReason() if (value.size() == 1) { m_rebootReason = value[0]; - XLOGD_INFO("Initial reboot reason is %u (%s)", m_rebootReason, + XLOGD_DEBUG("Initial reboot reason is %u (%s)", m_rebootReason, ctrlm_ble_reboot_reason_str((ctrlm_ble_RcuRebootReason_t)m_rebootReason)); if (m_rebootReason == CTRLM_BLE_RCU_REBOOT_REASON_ASSERT) { @@ -509,7 +509,7 @@ void GattRemoteControlService::requestAssertReport() if (value.size() == CTRLM_RCU_ASSERT_REPORT_MAX_SIZE) { string assertStr(value.begin(), value.end()); - XLOGD_INFO("Initial RCU assert report is <%s>", assertStr.c_str()); + XLOGD_DEBUG("Initial RCU assert report is <%s>", assertStr.c_str()); } else { XLOGD_ERROR("RCU assert report has invalid length (%d bytes)", value.size()); } @@ -545,7 +545,7 @@ void GattRemoteControlService::requestLastKeypress() if (value.size() == 1) { m_lastKeypress = value[0]; - XLOGD_INFO("Successfully read last key press characteristic, value = <0x%X>, emitting signal...", m_lastKeypress); + XLOGD_DEBUG("Successfully read last key press characteristic, value = <0x%X>, emitting signal...", m_lastKeypress); m_lastKeypressChangedSlots.invoke(m_lastKeypress); } else { XLOGD_ERROR("Last key press received has invalid length (%d bytes)", value.size()); @@ -625,7 +625,7 @@ void GattRemoteControlService::requestAdvConfig() if (value.size() == 1) { m_advConfig = value[0]; - XLOGD_INFO("Successfully read advertising config characteristic, value = <0x%X>, emitting signal...", m_advConfig); + XLOGD_DEBUG("Successfully read advertising config characteristic, value = <0x%X>, emitting signal...", m_advConfig); m_advConfigChangedSlots.invoke(m_advConfig); } else { XLOGD_ERROR("Advertising config received has invalid length (%d bytes)", value.size()); @@ -662,7 +662,7 @@ void GattRemoteControlService::requestAdvConfigCustomList() XLOGD_ERROR("Failed to read custom advertising config due to <%s>", reply->errorMessage().c_str()); } else { m_advConfigCustomList = reply->result(); - XLOGD_INFO("Successfully read advertising config custom list characteristic"); + XLOGD_DEBUG("Successfully read advertising config custom list characteristic"); m_advConfigCustomListChangedSlots.invoke(m_advConfigCustomList); } @@ -882,7 +882,7 @@ void GattRemoteControlService::onRebootReasonChanged(const vector &newV XLOGD_ERROR("Reboot reason received has invalid length (%d bytes)", newValue.size()); } else { m_rebootReason = newValue[0]; - XLOGD_INFO("reboot reason changed to %u (%s)", m_rebootReason, + XLOGD_DEBUG("reboot reason changed to %u (%s)", m_rebootReason, ctrlm_ble_reboot_reason_str((ctrlm_ble_RcuRebootReason_t)m_rebootReason)); if (m_rebootReason == CTRLM_BLE_RCU_REBOOT_REASON_ASSERT && m_assertReportCharacteristic) { diff --git a/src/ble/hal/blercu/bluez/blegattnotifypipe.cpp b/src/ble/hal/blercu/bluez/blegattnotifypipe.cpp index 6f54af37..1c9c0afc 100644 --- a/src/ble/hal/blercu/bluez/blegattnotifypipe.cpp +++ b/src/ble/hal/blercu/bluez/blegattnotifypipe.cpp @@ -183,7 +183,7 @@ BleGattNotifyPipe::BleGattNotifyPipe(int notifyPipeFd, uint16_t mtu, BleUuid uui m_notifyThread.name = "ble_notify"; sem_init(&m_notifyThreadSem, 0, 0); - XLOGD_INFO("Launching thread <%s> for %s...", m_notifyThread.name, m_uuid.toString().c_str()); + XLOGD_DEBUG("Launching thread <%s> for %s...", m_notifyThread.name, m_uuid.toString().c_str()); if (false == ThreadCreate(&m_notifyThread, NotifyThread, this)) { sem_destroy(&m_notifyThreadSem); } else { diff --git a/src/ctrlm_controller.cpp b/src/ctrlm_controller.cpp index 60173d47..0e7b175e 100644 --- a/src/ctrlm_controller.cpp +++ b/src/ctrlm_controller.cpp @@ -43,15 +43,15 @@ ctrlm_obj_controller_t::ctrlm_obj_controller_t(ctrlm_controller_id_t controller_ voice_metrics_(std::make_shared(&network, controller_id)), ota_failure_cnt_from_last_success_(std::make_shared("OTA Failure Count From Last Success", 0, &network, controller_id, "ota_failure_cnt_last_success")) { - XLOGD_INFO("constructor - %u", controller_id_); + XLOGD_DEBUG("constructor - %u", controller_id_); } ctrlm_obj_controller_t::ctrlm_obj_controller_t() { - XLOGD_INFO("constructor - default"); + XLOGD_DEBUG("constructor - default"); } ctrlm_obj_controller_t::~ctrlm_obj_controller_t() { - XLOGD_INFO("deconstructor - %u", controller_id_); + XLOGD_DEBUG("destructor - %u", controller_id_); } void ctrlm_obj_controller_t::db_load() { diff --git a/src/ctrlm_ir_controller.cpp b/src/ctrlm_ir_controller.cpp index c42b8eae..d2b16855 100644 --- a/src/ctrlm_ir_controller.cpp +++ b/src/ctrlm_ir_controller.cpp @@ -143,7 +143,7 @@ bool ctrlm_ir_controller_t::read_config() { string substr; getline( ss, substr, ',' ); if (!substr.empty()) { - XLOGD_INFO("Adding IR input device name: <%s>", substr.c_str()); + XLOGD_DEBUG("Adding IR input device name: <%s>", substr.c_str()); input_device_names_.push_back(substr); } } diff --git a/src/ctrlm_network.cpp b/src/ctrlm_network.cpp index 26201231..8715c038 100644 --- a/src/ctrlm_network.cpp +++ b/src/ctrlm_network.cpp @@ -66,7 +66,7 @@ ctrlm_obj_network_t::ctrlm_obj_network_t() { } ctrlm_obj_network_t::~ctrlm_obj_network_t() { - XLOGD_INFO("deconstructor - Type (%u) Id (%u) Name (%s)", type_, id_, name_.c_str()); + XLOGD_INFO("destructor - Type (%u) Id (%u) Name (%s)", type_, id_, name_.c_str()); if(NULL != hal_api_term_) { ctrlm_network_term_hal_t *term_data; @@ -882,6 +882,14 @@ void ctrlm_obj_network_t::req_process_upgrade_controllers(void *data, int size) XLOGD_WARN("not implemented for %s network", name_get()); } +void ctrlm_obj_network_t::req_process_print_status(void *data, int size) { + XLOGD_WARN("not implemented for %s network", name_get()); +} + +void ctrlm_obj_network_t::req_process_event_status(void *data, int size) { + XLOGD_WARN("not implemented for %s network", name_get()); +} + void ctrlm_obj_network_t::req_process_start_controller_upgrade(void *data, int size) { ctrlm_main_queue_msg_start_controller_upgrade_t *dqm = (ctrlm_main_queue_msg_start_controller_upgrade_t *)data; diff --git a/src/ctrlm_network.h b/src/ctrlm_network.h index ca6ad547..592e6e84 100644 --- a/src/ctrlm_network.h +++ b/src/ctrlm_network.h @@ -288,6 +288,9 @@ class ctrlm_obj_network_t virtual void power_state_change(gboolean waking_up); time_t stale_remote_time_threshold_get(); + virtual void req_process_print_status(void *data, int size); + virtual void req_process_event_status(void *data, int size); + virtual void iarm_event_rcu_status(void); virtual void iarm_event_rcu_validation_status(void); virtual void iarm_event_rcu_firmware_status(const ctrlm_obj_controller_t &rcu); diff --git a/src/database/ctrlm_database.cpp b/src/database/ctrlm_database.cpp index dfe2bb21..e28e4432 100644 --- a/src/database/ctrlm_database.cpp +++ b/src/database/ctrlm_database.cpp @@ -2183,7 +2183,7 @@ void ctrlm_db_controller_create(ctrlm_network_id_t network_id, ctrlm_controller_ char table_name_controller_entry[CONTROLLER_TABLE_NAME_MAX_LEN]; char key[3]; - XLOGD_INFO("network id %u controller id %u", network_id, controller_id); + XLOGD_DEBUG("network id %u controller id %u", network_id, controller_id); switch(ctrlm_network_type_get(network_id)) { case CTRLM_NETWORK_TYPE_IP: diff --git a/src/database/ctrlm_db_types.cpp b/src/database/ctrlm_db_types.cpp index 0a464b06..362c9f3c 100644 --- a/src/database/ctrlm_db_types.cpp +++ b/src/database/ctrlm_db_types.cpp @@ -35,7 +35,7 @@ ctrlm_db_obj_t::~ctrlm_db_obj_t() { bool ctrlm_db_obj_t::read_db(ctrlm_db_ctx_t ctx) { bool ret = false; sqlite3 *handle = (sqlite3 *)ctx; - XLOGD_INFO("reading blob %s from table %s", this->key.c_str(), this->table.c_str()); + XLOGD_DEBUG("reading blob %s from table %s", this->key.c_str(), this->table.c_str()); if(handle) { sqlite3_stmt *stmt = NULL; std::string query = "SELECT value FROM " + this->table + " WHERE key='" + this->key + "';"; @@ -63,7 +63,7 @@ bool ctrlm_db_obj_t::read_db(ctrlm_db_ctx_t ctx) { bool ctrlm_db_obj_t::write_db(ctrlm_db_ctx_t ctx) { bool ret = false; sqlite3 *handle = (sqlite3 *)ctx; - XLOGD_INFO("writing blob %s to table %s", this->key.c_str(), this->table.c_str()); + XLOGD_DEBUG("writing blob %s to table %s", this->key.c_str(), this->table.c_str()); if(handle) { sqlite3_stmt *stmt = NULL; std::string query = "INSERT OR REPLACE INTO " + this->table + "(key,value) VALUES (?,?);"; diff --git a/src/network/ctrlm_ir_rf_db.h b/src/network/ctrlm_ir_rf_db.h index c9f489a2..439bf1e0 100644 --- a/src/network/ctrlm_ir_rf_db.h +++ b/src/network/ctrlm_ir_rf_db.h @@ -43,7 +43,7 @@ class ctrlm_ir_rf_db_t { ctrlm_ir_rf_db_t(bool power_toggle_favor_tv = true, bool power_discrete_favor_tv = false); /** - * Default Deconstructor + * Default destructor */ virtual ~ctrlm_ir_rf_db_t(); diff --git a/src/rf4ce/ctrlm_rf4ce_controller.cpp b/src/rf4ce/ctrlm_rf4ce_controller.cpp index 960b3fd2..c298ab9e 100644 --- a/src/rf4ce/ctrlm_rf4ce_controller.cpp +++ b/src/rf4ce/ctrlm_rf4ce_controller.cpp @@ -212,7 +212,7 @@ ctrlm_obj_controller_rf4ce_t::ctrlm_obj_controller_rf4ce_t() { } ctrlm_obj_controller_rf4ce_t::~ctrlm_obj_controller_rf4ce_t() { - XLOGD_INFO("deconstructor"); + XLOGD_INFO("destructor"); if (metrics_tag_ != 0) { XLOGD_WARN("metrics timer destroyed"); diff --git a/src/rf4ce/ctrlm_rf4ce_network.cpp b/src/rf4ce/ctrlm_rf4ce_network.cpp index 2caa75c2..85a3caea 100644 --- a/src/rf4ce/ctrlm_rf4ce_network.cpp +++ b/src/rf4ce/ctrlm_rf4ce_network.cpp @@ -304,7 +304,7 @@ ctrlm_obj_network_rf4ce_t::ctrlm_obj_network_rf4ce_t() { } ctrlm_obj_network_rf4ce_t::~ctrlm_obj_network_rf4ce_t() { - XLOGD_INFO("deconstructor"); + XLOGD_INFO("destructor"); for (auto timeout : bind_validation_failed_timeout_) { ctrlm_timeout_destroy(&timeout->timer_id); if (controller_exists(timeout->controller_id)){ From 6e4a8b551fd1a1d76e685e71bee6f47e97e12046 Mon Sep 17 00:00:00 2001 From: Kelvin Lu <119349872+klu339@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:13:24 -0400 Subject: [PATCH 06/15] RDKEMW-15135: T2 RCU pairing (#189) --- src/ble/ctrlm_ble_network.cpp | 60 ++++++++++ src/ble/ctrlm_ble_network.h | 2 + src/ble/ctrlm_ble_rcu_interface.cpp | 6 + src/ble/ctrlm_ble_rcu_interface.h | 5 + src/ble/hal/blercu/blercuadapter.h | 6 +- src/ble/hal/blercu/blercucontroller.cpp | 23 +++- src/ble/hal/blercu/blercucontroller.h | 18 +++ src/ble/hal/blercu/blercudevice.h | 5 +- .../hal/blercu/blercupairingstatemachine.cpp | 110 +++++++++++++++++- .../hal/blercu/blercupairingstatemachine.h | 42 ++++++- src/ble/hal/blercu/bluez/blercuadapter.cpp | 18 +-- src/ble/hal/blercu/bluez/blercuadapter_p.h | 4 +- src/ble/hal/blercu/bluez/blercudevice.cpp | 10 +- src/ble/hal/blercu/bluez/blercudevice_p.h | 2 +- src/telemetry/ctrlm_telemetry_event.cpp | 12 +- src/telemetry/ctrlm_telemetry_event.h | 2 + src/telemetry/ctrlm_telemetry_markers.h | 24 ++++ .../telemetry/ctrlm_voice_telemetry_events.h | 2 +- 18 files changed, 321 insertions(+), 30 deletions(-) diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index c6bdce72..94a9022c 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -47,6 +47,8 @@ #include #include #include "ctrlm_rcp_ipc_iarm_thunder.h" +#include "ctrlm_telemetry.h" +#include using namespace std; @@ -384,6 +386,9 @@ ctrlm_hal_result_t ctrlm_obj_network_ble_t::hal_init_request(GThread *ctrlm_main ble_rcu_interface_->addRcuKeypressHandler(Slot(is_alive_, std::bind(&ctrlm_obj_network_ble_t::ind_keypress, this, std::placeholders::_1))); + ble_rcu_interface_->addRcuPairingOutcomeHandler(Slot(is_alive_, + std::bind(&ctrlm_obj_network_ble_t::ind_rcu_pairing_outcome, this, std::placeholders::_1))); + ble_rcu_interface_->startKeyMonitorThread(); @@ -1671,6 +1676,61 @@ void ctrlm_obj_network_ble_t::req_process_unpair(void *data, int size) { // BEGIN - Process Indications from HAL to the network in CTRLM Main thread context // ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +void ctrlm_obj_network_ble_t::ind_rcu_pairing_outcome(const BleRcuPairingOutcome &outcome) { + // Queue to the ctrlm main thread for duration computation and telemetry emission + auto copy = std::make_shared(outcome); + ctrlm_main_queue_handler_push_new( + CTRLM_HANDLER_NETWORK, + (ctrlm_msg_handler_network_t)&ctrlm_obj_network_ble_t::ind_process_rcu_pairing_outcome, + copy, + NULL, + id_); +} + +void ctrlm_obj_network_ble_t::ind_process_rcu_pairing_outcome(void *data, int size) { + THREAD_ID_VALIDATE(); + const BleRcuPairingOutcome *outcome = static_cast(data); + if (sizeof(BleRcuPairingOutcome) != size) { + XLOGD_ERROR("Invalid size!"); + return; + } + if (!outcome) { + XLOGD_ERROR("pairing outcome data is NULL"); + return; + } + +#ifdef TELEMETRY_SUPPORT + // Serialize to array for ctrlm.rcu.pairing.attempt_accum T2 marker + std::stringstream ss; + ss << "["; + ss << MARKER_RCU_PAIRING_ATTEMPT_VERSION << ","; + ss << "\"" << name_get() << "\","; + ss << outcome->method << ","; + ss << outcome->result << ","; + ss << outcome->discovered << ","; + ss << "\"" << outcome->name << "\","; + ss << outcome->bluezRetries << ","; + + for (uint8_t i = 0; i < outcome->maxBluezRetries; i++) { + if (i >= outcome->bluezRetries || i >= outcome->bluezError.size()) { + ss << "\"null\""; + } else { + std::string msg = outcome->bluezError.at(i); + auto pos = msg.rfind(": "); + msg = (pos != std::string::npos) ? msg.substr(pos + 2) : msg; + ss << "\"" << msg << "\""; + } + if (i != (outcome->maxBluezRetries-1)) { + ss << ","; + } + } + ss << "]"; + + ctrlm_telemetry_event_t ev(MARKER_RCU_PAIRING_ATTEMPT, ss.str()); + ev.event(); +#endif // TELEMETRY_SUPPORT +} + void ctrlm_obj_network_ble_t::ind_rcu_status(ctrlm_hal_ble_RcuStatusData_t *params) { // push to the main queue and process it synchronously there diff --git a/src/ble/ctrlm_ble_network.h b/src/ble/ctrlm_ble_network.h index 90724d94..73f2c8b9 100644 --- a/src/ble/ctrlm_ble_network.h +++ b/src/ble/ctrlm_ble_network.h @@ -134,11 +134,13 @@ class ctrlm_obj_network_ble_t : public ctrlm_obj_network_t { void ind_rcu_paired(ctrlm_hal_ble_IndPaired_params_t *params); void ind_rcu_unpaired(ctrlm_hal_ble_IndUnPaired_params_t *params); void ind_keypress(ctrlm_hal_ble_IndKeypress_params_t *params); + void ind_rcu_pairing_outcome(const BleRcuPairingOutcome &outcome); void ind_process_rcu_status(void *data, int size); void ind_process_paired(void *data, int size); void ind_process_unpaired(void *data, int size); void ind_process_keypress(void *data, int size); + void ind_process_rcu_pairing_outcome(void *data, int size); virtual void req_process_network_status(void *data, int size); virtual void req_process_controller_status(void *data, int size); diff --git a/src/ble/ctrlm_ble_rcu_interface.cpp b/src/ble/ctrlm_ble_rcu_interface.cpp index 4f3e1635..2fd194b7 100644 --- a/src/ble/ctrlm_ble_rcu_interface.cpp +++ b/src/ble/ctrlm_ble_rcu_interface.cpp @@ -185,6 +185,12 @@ void ctrlm_ble_rcu_interface_t::initialize() m_rcuUnpairedSlots.invoke(¶ms); }; m_controller->addManagedDeviceRemovedSlot(Slot(m_isAlive, deviceRemovedSlot)); + + auto pairingOutcomeSlot = [this](const BleRcuPairingOutcome &outcome) + { + m_rcuPairingOutcomeSlots.invoke(outcome); + }; + m_controller->addPairingOutcomeSlot(Slot(m_isAlive, pairingOutcomeSlot)); } voice_params_par_t params; diff --git a/src/ble/ctrlm_ble_rcu_interface.h b/src/ble/ctrlm_ble_rcu_interface.h index c9209c6f..e29c7fc8 100644 --- a/src/ble/ctrlm_ble_rcu_interface.h +++ b/src/ble/ctrlm_ble_rcu_interface.h @@ -145,12 +145,17 @@ class ctrlm_ble_rcu_interface_t { m_rcuKeypressSlots.addSlot(func); } + inline void addRcuPairingOutcomeHandler(const Slot &func) + { + m_rcuPairingOutcomeSlots.addSlot(func); + } Slots m_rcuStatusChangedSlots; Slots m_rcuPairedSlots; Slots m_rcuUnpairedSlots; Slots m_rcuKeypressSlots; + Slots m_rcuPairingOutcomeSlots; private: std::shared_ptr m_isAlive; diff --git a/src/ble/hal/blercu/blercuadapter.h b/src/ble/hal/blercu/blercuadapter.h index cd9fc9e8..3407c25e 100644 --- a/src/ble/hal/blercu/blercuadapter.h +++ b/src/ble/hal/blercu/blercuadapter.h @@ -72,7 +72,7 @@ class BleRcuAdapter virtual bool isDevicePaired(const BleAddress &address) const = 0; virtual bool isDeviceConnected(const BleAddress &address) const = 0; - virtual bool addDevice(const BleAddress &address) = 0; + virtual bool addDevice(const BleAddress &address, int retries) = 0; virtual bool removeDevice(const BleAddress &address) = 0; virtual bool setConnectionParams(BleAddress address, double minInterval, double maxInterval, @@ -107,7 +107,7 @@ class BleRcuAdapter { m_deviceNameChangedSlots.addSlot(func); } - inline void addDevicePairingErrorSlot(const Slot &func) + inline void addDevicePairingErrorSlot(const Slot &func) { m_devicePairingErrorSlots.addSlot(func); } @@ -135,7 +135,7 @@ class BleRcuAdapter Slots m_deviceFoundSlots; Slots m_deviceRemovedSlots; Slots m_deviceNameChangedSlots; - Slots m_devicePairingErrorSlots; + Slots m_devicePairingErrorSlots; Slots m_devicePairingChangedSlots; Slots m_deviceReadyChangedSlots; diff --git a/src/ble/hal/blercu/blercucontroller.cpp b/src/ble/hal/blercu/blercucontroller.cpp index 9924c9df..fd077d1f 100644 --- a/src/ble/hal/blercu/blercucontroller.cpp +++ b/src/ble/hal/blercu/blercucontroller.cpp @@ -41,7 +41,6 @@ using namespace std; - class BleRcuController_userdata { public: BleRcuController_userdata(shared_ptr isAlive_, BleRcuControllerImpl* controller_) @@ -607,6 +606,17 @@ void BleRcuControllerImpl::onFinishedPairing() m_state = Complete; m_stateChangedSlots.invoke(m_state); + + // emit pairing outcome telemetry + BleRcuPairingOutcome outcome; + outcome.method = m_pairingStateMachine.pairingMethod(); + outcome.result = BleRcuPairingStateMachine::SUCCESS; + outcome.bluezRetries = m_pairingStateMachine.bluezRetries(); + outcome.maxBluezRetries = MAX_PAIRING_RETRIES; + outcome.bluezError = m_pairingStateMachine.bluezError(); + outcome.name = m_pairingStateMachine.pairedName(); + outcome.discovered = m_pairingStateMachine.discoveredDevices(); + m_pairingOutcomeSlots.invoke(outcome); } // ----------------------------------------------------------------------------- @@ -646,6 +656,17 @@ void BleRcuControllerImpl::onFailedPairing() m_state = Failed; m_stateChangedSlots.invoke(m_state); + + // emit pairing outcome telemetry + BleRcuPairingOutcome outcome; + outcome.method = m_pairingStateMachine.pairingMethod(); + outcome.result = m_pairingStateMachine.failureReason(); + outcome.bluezRetries = m_pairingStateMachine.bluezRetries(); + outcome.maxBluezRetries = MAX_PAIRING_RETRIES; + outcome.bluezError = m_pairingStateMachine.bluezError(); + outcome.discovered = m_pairingStateMachine.discoveredDevices(); + outcome.name = ""; + m_pairingOutcomeSlots.invoke(outcome); } diff --git a/src/ble/hal/blercu/blercucontroller.h b/src/ble/hal/blercu/blercucontroller.h index 770d4d07..b4eab730 100644 --- a/src/ble/hal/blercu/blercucontroller.h +++ b/src/ble/hal/blercu/blercucontroller.h @@ -36,10 +36,23 @@ #include #include +#include +#include +#include class BleRcuDevice; +struct BleRcuPairingOutcome +{ + int method; // numeric enum value for PairingMethod + int result; // numeric enum value for FailureReason + int discovered; // number of discovered devices + int bluezRetries; // number of bluez retries attempted + int maxBluezRetries; // max number of retries + std::string name; // empty on failure + std::vector bluezError; // bluez error messages +}; class BleRcuController { @@ -101,12 +114,17 @@ class BleRcuController { m_stateChangedSlots.addSlot(func); } + inline void addPairingOutcomeSlot(const Slot &func) + { + m_pairingOutcomeSlots.addSlot(func); + } protected: Slots m_managedDeviceAddedSlots; Slots m_managedDeviceRemovedSlots; Slots m_pairingStateChangedSlots; Slots m_stateChangedSlots; + Slots m_pairingOutcomeSlots; }; #endif // !defined(BLERCUCONTROLLER_H) diff --git a/src/ble/hal/blercu/blercudevice.h b/src/ble/hal/blercu/blercudevice.h index 8ab7cc44..ef297db7 100644 --- a/src/ble/hal/blercu/blercudevice.h +++ b/src/ble/hal/blercu/blercudevice.h @@ -187,17 +187,18 @@ class BleRcuDevice { m_readyChangedSlots.addSlot(func); } - inline void addPairingErrorSlot(const Slot &func) + inline void addPairingErrorSlot(const Slot &func) { m_pairingErrorSlots.addSlot(func); } + protected: Slots m_connectedChangedSlots; Slots m_pairedChangedSlots; Slots m_nameChangedSlots; Slots m_readyChangedSlots; - Slots m_pairingErrorSlots; + Slots m_pairingErrorSlots; }; diff --git a/src/ble/hal/blercu/blercupairingstatemachine.cpp b/src/ble/hal/blercu/blercupairingstatemachine.cpp index e23c6939..0779fb69 100644 --- a/src/ble/hal/blercu/blercupairingstatemachine.cpp +++ b/src/ble/hal/blercu/blercupairingstatemachine.cpp @@ -52,6 +52,9 @@ BleRcuPairingStateMachine::BleRcuPairingStateMachine(const shared_ptraddPoweredChangedSlot(Slot(m_isAlive, std::bind(&BleRcuPairingStateMachine::onAdapterPoweredChanged, this, std::placeholders::_1))); - m_adapter->addDevicePairingErrorSlot(Slot(m_isAlive, - std::bind(&BleRcuPairingStateMachine::onDevicePairingError, this, std::placeholders::_1, std::placeholders::_2))); + m_adapter->addDevicePairingErrorSlot(Slot(m_isAlive, + std::bind(&BleRcuPairingStateMachine::onDevicePairingError, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4))); } BleRcuPairingStateMachine::~BleRcuPairingStateMachine() @@ -206,6 +209,37 @@ bool BleRcuPairingStateMachine::isScanningForAutoPair() const return isRunning() && m_isAutoPairing && m_stateMachine.inState(DiscoverySuperState); } +BleRcuPairingStateMachine::PairingMethod BleRcuPairingStateMachine::pairingMethod() const +{ + return m_pairingMethod; +} + +BleRcuPairingStateMachine::FailureReason BleRcuPairingStateMachine::failureReason() const +{ + return m_failureReason; +} + +int BleRcuPairingStateMachine::discoveredDevices() const +{ + return m_discoveredDevices.size(); +} + +int BleRcuPairingStateMachine::bluezRetries() const +{ + return m_bluezRetries; +} + +std::string BleRcuPairingStateMachine::pairedName() const +{ + auto it = m_discoveredDevices.find(m_pairedMac); + return (it != m_discoveredDevices.end()) ? it->second : ""; +} + +std::vector BleRcuPairingStateMachine::bluezError() const +{ + return m_bluezErrorMsg; +} + // ----------------------------------------------------------------------------- /*! @@ -239,6 +273,14 @@ void BleRcuPairingStateMachine::startAutoWithTimeout(int timeoutMs) m_targetedPairingNames.push_back(name); } + // reset telemetry tracking state + m_pairingMethod = AUTO_TIMEOUT; + m_failureReason = SUCCESS; + m_discoveredDevices.clear(); + m_bluezRetries = 0; + m_pairedMac.clear(); + m_bluezErrorMsg.clear(); + // start the state machine m_stateMachine.start(); @@ -289,6 +331,14 @@ void BleRcuPairingStateMachine::startWithCode(uint8_t pairingCode) m_targetedPairingNames.push_back(std::regex(nameWithCode, std::regex_constants::ECMAScript)); } + // reset telemetry tracking state + m_pairingMethod = IR_CODE; + m_failureReason = SUCCESS; + m_discoveredDevices.clear(); + m_bluezRetries = 0; + m_pairedMac.clear(); + m_bluezErrorMsg.clear(); + // start the state machine m_stateMachine.start(); @@ -321,6 +371,14 @@ void BleRcuPairingStateMachine::startWithMacList(const std::vector & // set the list of addresses to filter for m_pairingMacList = macList; + // reset telemetry tracking state + m_pairingMethod = MAC_LIST; + m_failureReason = SUCCESS; + m_discoveredDevices.clear(); + m_bluezRetries = 0; + m_pairedMac.clear(); + m_bluezErrorMsg.clear(); + // start the state machine m_stateMachine.start(); @@ -353,6 +411,9 @@ void BleRcuPairingStateMachine::stop() XLOGD_INFO("cancelling pairing"); + // record the cancellation reason before posting the event + m_failureReason = FAIL_CANCELLED; + // post a cancel event and let the state-machine clean up m_stateMachine.postEvent(CancelRequestEvent); } @@ -432,15 +493,21 @@ void BleRcuPairingStateMachine::onStateExit(int state) */ void BleRcuPairingStateMachine::onStateTransition(int oldState, int newState) { + FailureReason newFailureReason = SUCCESS; + if (newState == FinishedState) { if (oldState == UnpairingState) { XLOGD_AUTOMATION_WARN("timed-out in un-pairing phase (failed rcu may be left paired)"); + newFailureReason = FAIL_PAIRING_TIMEOUT; } else if (oldState == StartingDiscoveryState) { XLOGD_AUTOMATION_ERROR("timed-out waiting for discovery started signal"); + newFailureReason = FAIL_DISCOVERY_TIMEOUT; } else if (oldState == DiscoveringState) { XLOGD_AUTOMATION_ERROR("timed-out in discovery phase (didn't find target rcu device to pair to)"); + newFailureReason = FAIL_TARGET_RCU_NOT_FOUND; } else if (oldState == StoppingDiscoveryState) { XLOGD_AUTOMATION_ERROR("timed-out waiting for discovery to stop (suggesting something has gone wrong inside bluez)"); + newFailureReason = FAIL_DISCOVERY_STOP_TIMEOUT; } } else if (newState == UnpairingState) { if (oldState == EnablePairableState || oldState == PairingState) { @@ -449,6 +516,11 @@ void BleRcuPairingStateMachine::onStateTransition(int oldState, int newState) XLOGD_AUTOMATION_WARN("timed-out in setup phase (rcu didn't response to all requests within %dms)", m_setupTimeout); } } + + // Change the failure reason only if it hasn't already been set elsewhere + if (newFailureReason != SUCCESS && m_failureReason == SUCCESS) { + m_failureReason = newFailureReason; + } } void BleRcuPairingStateMachine::onEnteredStoppingDiscoveryStartedExternally() @@ -521,6 +593,9 @@ void BleRcuPairingStateMachine::onDiscoveryChanged(bool discovering) if (discovering) { m_stateMachine.postEvent(DiscoveryStartedEvent); } else { + if (m_stateMachine.inState(DiscoverySuperState)) { + m_failureReason = FAIL_DISCOVERY_STOPPED; + } m_stateMachine.postEvent(DiscoveryStoppedEvent); } } @@ -543,6 +618,7 @@ void BleRcuPairingStateMachine::onEnteredDiscoveringState() map::const_iterator it = deviceNames.begin(); for (; it != deviceNames.end(); ++it) { processDevice(it->first, it->second); + m_discoveredDevices.emplace(it->first, it->second); } } @@ -660,7 +736,7 @@ void BleRcuPairingStateMachine::onEnteredPairingState() m_pairingSlots.invoke(); // request the manager to pair with the device - m_adapter->addDevice(m_targetAddress); + m_adapter->addDevice(m_targetAddress, MAX_PAIRING_RETRIES); } // ----------------------------------------------------------------------------- @@ -714,6 +790,7 @@ void BleRcuPairingStateMachine::onEnteredUnpairingState() // remove (unpair) the target device because we've failed :-( if (m_adapter->removeDevice(m_targetAddress) == false) { + m_failureReason = FAIL_PAIRING_TIMEOUT; m_stateMachine.postEvent(DeviceUnpairedEvent); } } @@ -883,6 +960,7 @@ void BleRcuPairingStateMachine::onDeviceFound(const BleAddress &address, XLOGD_DEBUG("device added %s %s (target %s)", address.toString().c_str(), name.c_str(), m_targetAddress.toString().c_str()); + m_discoveredDevices.emplace(address, name); processDevice(address, name); } @@ -905,6 +983,11 @@ void BleRcuPairingStateMachine::onDeviceRemoved(const BleAddress &address) // check if the device removed is the one we're targeting if (!m_targetAddress.isNull() && (m_targetAddress == address)) { + if (m_stateMachine.inState(UnpairingState)) { + m_failureReason = FAIL_PAIRING_TIMEOUT; + } else { + m_failureReason = FAIL_DEVICE_REMOVED; + } m_stateMachine.postEvent(DeviceRemovedEvent); } } @@ -927,6 +1010,7 @@ void BleRcuPairingStateMachine::onDeviceNameChanged(const BleAddress &address, XLOGD_DEBUG("device name changed %s %s (target %s)", address.toString().c_str(), name.c_str(), m_targetAddress.toString().c_str()); + m_discoveredDevices[address] = name; processDevice(address, name); } @@ -941,12 +1025,23 @@ void BleRcuPairingStateMachine::onDeviceNameChanged(const BleAddress &address, */ void BleRcuPairingStateMachine::onDevicePairingError(const BleAddress &address, - const string &error) + const string &error, + int retryCnt, + bool finalRetry) { if (!m_stateMachine.isRunning()) { return; } + m_failureReason = FAIL_BLUEZ_ERROR; + m_bluezRetries = retryCnt; + m_bluezErrorMsg.push_back(error); + + if (!finalRetry) { + // Still retrying so don't stop pairing and timers yet + return; + } + XLOGD_ERROR("Device (%s) pairing failed, shutting down state machine...", address.toString().c_str()); // stop the pairing and setup timeout timers @@ -979,6 +1074,11 @@ void BleRcuPairingStateMachine::onDevicePairingChanged(const BleAddress &address if (paired) { m_stateMachine.postEvent(DevicePairedEvent); } else { + if (m_stateMachine.inState(UnpairingState)) { + m_failureReason = FAIL_PAIRING_TIMEOUT; + } else { + m_failureReason = FAIL_DEVICE_UNPAIRED; + } m_stateMachine.postEvent(DeviceUnpairedEvent); } } @@ -1006,6 +1106,7 @@ void BleRcuPairingStateMachine::onDeviceReadyChanged(const BleAddress &address, if (ready) { m_pairingSuccesses++; m_pairingSucceeded = true; + m_pairedMac = address; m_stateMachine.postEvent(DeviceReadyEvent); } } @@ -1027,6 +1128,7 @@ void BleRcuPairingStateMachine::onAdapterPoweredChanged(bool powered) } if (!powered) { + m_failureReason = FAIL_ADAPTER_OFF; m_stateMachine.postEvent(AdapterPoweredOffEvent); } } diff --git a/src/ble/hal/blercu/blercupairingstatemachine.h b/src/ble/hal/blercu/blercupairingstatemachine.h index cfb15c31..f3d18178 100644 --- a/src/ble/hal/blercu/blercupairingstatemachine.h +++ b/src/ble/hal/blercu/blercupairingstatemachine.h @@ -39,14 +39,36 @@ #include #include #include - +#include class BleRcuAdapter; class ConfigSettings; +#define MAX_PAIRING_RETRIES 3 class BleRcuPairingStateMachine { +public: + enum PairingMethod { + AUTO_TIMEOUT, + MAC_LIST, + IR_CODE, + }; + + enum FailureReason { + SUCCESS = 0, + FAIL_DISCOVERY_TIMEOUT, + FAIL_DISCOVERY_STOPPED, + FAIL_DISCOVERY_STOP_TIMEOUT, + FAIL_PAIRING_TIMEOUT, + FAIL_TARGET_RCU_NOT_FOUND, + FAIL_BLUEZ_ERROR, + FAIL_ADAPTER_OFF, + FAIL_DEVICE_UNPAIRED, + FAIL_DEVICE_REMOVED, + FAIL_CANCELLED + }; + public: enum State { RunningSuperState, @@ -73,6 +95,14 @@ class BleRcuPairingStateMachine bool isScanningForAutoPair() const; int pairingCode() const; +// Pairing outcome getters (valid after the state machine stops) + PairingMethod pairingMethod() const; + FailureReason failureReason() const; + int discoveredDevices() const; + int bluezRetries() const; + std::string pairedName() const; + std::vector bluezError() const; + // public slots: void startAutoWithTimeout(int timeoutMs); void startWithCode(uint8_t pairingCode); @@ -114,7 +144,7 @@ class BleRcuPairingStateMachine void onDeviceNameChanged(const BleAddress &address, const std::string &name); void onDevicePairingChanged(const BleAddress &address, bool paired); void onDeviceReadyChanged(const BleAddress &address, bool ready); - void onDevicePairingError(const BleAddress &address, const std::string &error); + void onDevicePairingError(const BleAddress &address, const std::string &error, int retryCnt, bool finalRetry); void onAdapterPoweredChanged(bool powered); @@ -172,6 +202,14 @@ class BleRcuPairingStateMachine int m_pairingSuccesses; bool m_pairingSucceeded; + // Pairing outcome tracking + PairingMethod m_pairingMethod; + FailureReason m_failureReason; + std::map m_discoveredDevices; + int m_bluezRetries; + BleAddress m_pairedMac; + std::vector m_bluezErrorMsg; + BtrMgrAdapter m_btrMgrAdapter; bool discoveryStartedExternally = false; BtrMgrAdapter::OperationType lastOperationType = BtrMgrAdapter::unknownOperation; diff --git a/src/ble/hal/blercu/bluez/blercuadapter.cpp b/src/ble/hal/blercu/bluez/blercuadapter.cpp index 56949f40..ea05fbc4 100644 --- a/src/ble/hal/blercu/bluez/blercuadapter.cpp +++ b/src/ble/hal/blercu/bluez/blercuadapter.cpp @@ -1216,10 +1216,10 @@ bool BleRcuAdapterBluez::isDeviceConnected(const BleAddress &address) const // ----------------------------------------------------------------------------- /*! - \fn void BleRcuManager::addDevice(const BleAddress &address) + \fn void BleRcuManager::addDevice(const BleAddress &address, int retries) Sends a request to the bluez daemon to pair the device with the given - \a address. The request is sent even if the device is already paired, + \a address and \a retries. The request is sent even if the device is already paired, this is to handle the case where a pending unpair notification is sitting in the dbus queue but not yet processed. @@ -1227,7 +1227,7 @@ bool BleRcuAdapterBluez::isDeviceConnected(const BleAddress &address) const \sa isDevicePaired(), removeDevice() */ -bool BleRcuAdapterBluez::addDevice(const BleAddress &address) +bool BleRcuAdapterBluez::addDevice(const BleAddress &address, int retries) { if (!m_stateMachine.inState(AdapterPoweredOnState)) { return false; @@ -1245,10 +1245,10 @@ bool BleRcuAdapterBluez::addDevice(const BleAddress &address) XLOGD_INFO("requesting bluez pair %s", device->address().toString().c_str()); - device->addPairingErrorSlot(Slot(m_isAlive, - std::bind(&BleRcuAdapterBluez::onDevicePairingError, this, address, std::placeholders::_1))); + device->addPairingErrorSlot(Slot(m_isAlive, + std::bind(&BleRcuAdapterBluez::onDevicePairingError, this, address, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))); - device->pair(0); + device->pair(0, retries); return true; } @@ -1688,9 +1688,11 @@ void BleRcuAdapterBluez::onDeviceNameChanged(const BleAddress &address, */ void BleRcuAdapterBluez::onDevicePairingError(const BleAddress &address, - const std::string &error) + const std::string &error, + int retryCnt, + bool finalRetry) { - m_devicePairingErrorSlots.invoke(address, error); + m_devicePairingErrorSlots.invoke(address, error, retryCnt, finalRetry); } // ----------------------------------------------------------------------------- diff --git a/src/ble/hal/blercu/bluez/blercuadapter_p.h b/src/ble/hal/blercu/bluez/blercuadapter_p.h index aeb35f5c..33a2e48d 100644 --- a/src/ble/hal/blercu/bluez/blercuadapter_p.h +++ b/src/ble/hal/blercu/bluez/blercuadapter_p.h @@ -94,7 +94,7 @@ class BleRcuAdapterBluez : public BleRcuAdapter bool isDevicePaired(const BleAddress &address) const override; bool isDeviceConnected(const BleAddress &address) const override; - bool addDevice(const BleAddress &address) override; + bool addDevice(const BleAddress &address, int retries) override; bool removeDevice(const BleAddress &address) override; bool setConnectionParams(BleAddress address, double minInterval, double maxInterval, @@ -113,7 +113,7 @@ class BleRcuAdapterBluez : public BleRcuAdapter const std::vector &interfaces); void onDeviceNameChanged(const BleAddress &address, const std::string &name); - void onDevicePairingError(const BleAddress &address, const std::string &error); + void onDevicePairingError(const BleAddress &address, const std::string &error, int retryCnt, bool finalRetry); void onDevicePairedChanged(const BleAddress &address, bool paired); void onDeviceReadyChanged(const BleAddress &address, bool ready); diff --git a/src/ble/hal/blercu/bluez/blercudevice.cpp b/src/ble/hal/blercu/bluez/blercudevice.cpp index 08aa0287..575c8a5f 100644 --- a/src/ble/hal/blercu/bluez/blercudevice.cpp +++ b/src/ble/hal/blercu/bluez/blercudevice.cpp @@ -98,7 +98,7 @@ BleRcuDeviceBluez::BleRcuDeviceBluez(const BleAddress &bdaddr, , m_lastServicesResolvedState(false) , m_isPairing(false) , m_pairingRetryCnt(0) - , m_maxPairingRetries(3) + , m_maxPairingRetries(0) , m_timeSinceReady(0) , m_recoveryAttempts(0) , m_maxRecoveryAttempts(100) @@ -242,8 +242,9 @@ static gboolean getInitialDeviceProperties(gpointer user_data) request then the \a pairingError signal will be emitted. */ -void BleRcuDeviceBluez::pair(int timeout) +void BleRcuDeviceBluez::pair(int timeout, int retries) { + m_maxPairingRetries = retries; m_deviceProxy->Pair( PendingReply<>(m_isAlive, std::bind(&BleRcuDeviceBluez::onPairRequestReply, this, std::placeholders::_1))); @@ -269,10 +270,11 @@ void BleRcuDeviceBluez::onPairRequestReply(PendingReply<> *reply) if (m_pairingRetryCnt < m_maxPairingRetries) { m_pairingRetryCnt++; XLOGD_INFO("Retrying pairing, attempt %d out of %d ", m_pairingRetryCnt, m_maxPairingRetries); - pair(0); + m_pairingErrorSlots.invoke(reply->errorMessage(), m_pairingRetryCnt, false); + pair(0, m_maxPairingRetries); } else { + m_pairingErrorSlots.invoke(reply->errorMessage(), m_pairingRetryCnt, true); m_pairingRetryCnt = 0; - m_pairingErrorSlots.invoke(reply->errorMessage()); } } else { XLOGD_DEBUG("%s pairing request successful", m_address.toString().c_str()); diff --git a/src/ble/hal/blercu/bluez/blercudevice_p.h b/src/ble/hal/blercu/bluez/blercudevice_p.h index 39ecd86c..0700b227 100644 --- a/src/ble/hal/blercu/bluez/blercudevice_p.h +++ b/src/ble/hal/blercu/bluez/blercudevice_p.h @@ -92,7 +92,7 @@ class BleRcuDeviceBluez : public BleRcuDevice public: std::string bluezObjectPath() const; - void pair(int timeout); + void pair(int timeout, int retries); void cancelPairing(); void connect(); diff --git a/src/telemetry/ctrlm_telemetry_event.cpp b/src/telemetry/ctrlm_telemetry_event.cpp index 6ca82571..f6bda2d1 100644 --- a/src/telemetry/ctrlm_telemetry_event.cpp +++ b/src/telemetry/ctrlm_telemetry_event.cpp @@ -34,6 +34,14 @@ bool ctrlm_telemetry_event_t::event() const { template <> bool ctrlm_telemetry_event_t::event() const { - XLOGD_TELEMETRY("telemetry event <%s, %s>", this->marker.c_str(), this->value.c_str()); - return(t2_event_s((char *)this->marker.c_str(), (char *)this->value.c_str()) == T2ERROR_SUCCESS); + if (value.length() > CTRLM_TELEMETRY_MAX_EVENT_SIZE_BYTES) { + XLOGD_ERROR("telemetry event <%s> dropped: value length <%zu> exceeds maximum <%d> bytes", + this->marker.c_str(), + this->value.length(), + CTRLM_TELEMETRY_MAX_EVENT_SIZE_BYTES); + return false; + } else { + XLOGD_TELEMETRY("telemetry event <%s, %s>", this->marker.c_str(), this->value.c_str()); + return(t2_event_s((char *)this->marker.c_str(), (char *)this->value.c_str()) == T2ERROR_SUCCESS); + } } diff --git a/src/telemetry/ctrlm_telemetry_event.h b/src/telemetry/ctrlm_telemetry_event.h index 1628afb1..b83ca853 100644 --- a/src/telemetry/ctrlm_telemetry_event.h +++ b/src/telemetry/ctrlm_telemetry_event.h @@ -26,6 +26,8 @@ #include #endif +#define CTRLM_TELEMETRY_MAX_EVENT_SIZE_BYTES 4536 // Maximum size of event list in bytes (there are limitations in the T2 implementation that restrict the maximum size of an event string. This value should be within the limits of the T2 implementation) + template class ctrlm_telemetry_event_t { static_assert(std::is_same::value || std::is_same::value || std::is_same::value, "Invalid telemetry event type"); diff --git a/src/telemetry/ctrlm_telemetry_markers.h b/src/telemetry/ctrlm_telemetry_markers.h index cb07ec32..446bec55 100644 --- a/src/telemetry/ctrlm_telemetry_markers.h +++ b/src/telemetry/ctrlm_telemetry_markers.h @@ -131,4 +131,28 @@ // End Voice Markers // +// +// RCU Markers +// + +// BLE RCU Pairing Attempt Marker +// Value format: Array of comma separated integers or string with the fields below. +// [,,,,,,,,,] +// - version of the marker format. +// - type of network the marker is coming from +// - enum representing pairing method (see PairingMethod) +// - enum that represents "success" or failure reason (see FailureReason) +// - number of discovered devices from pairing attempt +// - name of paired remote, empty string on failure +// - number of bluez pair() retries made before success or final error +// - error message 1 from bluez layer or null if no error +// - error message 2 from bluez layer or null if no error +// - error message 3 from bluez layer or null if no error +#define MARKER_RCU_PAIRING_ATTEMPT "ctrlm.rcu.pairing.attempt_accum" +#define MARKER_RCU_PAIRING_ATTEMPT_VERSION 1 + +// +// End RCU Markers +// + #endif diff --git a/src/voice/telemetry/ctrlm_voice_telemetry_events.h b/src/voice/telemetry/ctrlm_voice_telemetry_events.h index 18fcab29..f6f10127 100644 --- a/src/voice/telemetry/ctrlm_voice_telemetry_events.h +++ b/src/voice/telemetry/ctrlm_voice_telemetry_events.h @@ -79,7 +79,7 @@ class ctrlm_voice_telemetry_session_t : public ctrlm_telemetry_event_t Date: Fri, 5 Jun 2026 10:51:24 -0400 Subject: [PATCH 07/15] RDKEMW-4433: local server build option (#57) --- CMakeLists.txt | 1 + src/CMakeLists.txt | 4 + .../bleservices/gatt/gatt_audioservice.cpp | 2 +- src/server/CMakeLists.txt | 54 ++ src/server/ctrlm_server_app.h | 49 ++ src/server/ctrlms_main.c | 135 ++++ src/server/ctrlms_version.c | 57 ++ src/server/ctrlms_version.h | 40 ++ src/server/ctrlms_ws.cpp | 586 ++++++++++++++++++ src/server/ctrlms_ws.h | 37 ++ src/voice/ctrlm_voice_obj.cpp | 14 +- src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp | 1 - 12 files changed, 973 insertions(+), 7 deletions(-) create mode 100644 src/server/CMakeLists.txt create mode 100644 src/server/ctrlm_server_app.h create mode 100644 src/server/ctrlms_main.c create mode 100644 src/server/ctrlms_version.c create mode 100644 src/server/ctrlms_version.h create mode 100644 src/server/ctrlms_ws.cpp create mode 100644 src/server/ctrlms_ws.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 112b3c00..b9cdf593 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ option(BLE_ENABLED "Enable BLE" ON) option(BLE_SERVICES "Enable BLE Services" OFF) option(BREAKPAD "Enable BREAKPAD" OFF) option(BUILD_CTRLM_FACTORY "Build Control Factory Test" OFF) +option(BUILD_CTRLM_SERVER "Build Control Server Daemon" OFF) option(FDC_ENABLED "Enable FDC" OFF) option(IP_ENABLED "Enable IP" OFF) option(RF4CE_ENABLED "Enable RF4CE" ON) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ab20447c..93f3f745 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -140,6 +140,10 @@ if(BUILD_FACTORY_TEST) add_subdirectory(factory) endif() +if(BUILD_CTRLM_SERVER) + add_subdirectory(server) +endif() + if(USE_IARM_POWER_MANAGER) target_sources(controlMgr PRIVATE ipc/ctrlm_ipc_iarm_powermanager.cpp diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp index 4a3e3596..ec9819d1 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp @@ -620,7 +620,7 @@ void GattAudioService::stopStreaming(uint32_t audioDuration, PendingReply<> &&re } if(m_missedSequences >= frameCountMax) { - XLOGD_ERROR("missed frames greater than frame count max"); + XLOGD_ERROR("missed frames <%u> greater than frame count max <%u>", m_missedSequences, frameCountMax); } else { frameCountMax -= m_missedSequences; // compensate for missed frames diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt new file mode 100644 index 00000000..714a3fdd --- /dev/null +++ b/src/server/CMakeLists.txt @@ -0,0 +1,54 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# 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. +########################################################################## + +add_executable(controlServer ctrlms_main.c) + +include_directories( + . + ${CMAKE_SYSROOT}/usr/include/safeclib + ${CMAKE_SYSROOT}/usr/include/libsafec + ${CMAKE_SYSROOT}/usr/include/nopoll + ${CMAKE_SYSROOT}/usr/include +) + +target_sources(controlServer PRIVATE + ctrlms_version.c + ctrlms_ws.cpp +) + +target_compile_options(controlServer PUBLIC -fPIC -rdynamic -Wall -Werror) + +target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper systemd ${CMAKE_DL_LIBS}) + +if(AUTH_ENABLED) + target_compile_definitions(controlServer PRIVATE CTRLMS_WSS_ENABLED) + target_link_libraries(controlServer ssl crypto RdkCertSelector rdkconfig secure_wrapper) +endif() + +if(USE_SAFEC) + find_package(PkgConfig) + pkg_check_modules(SAFEC REQUIRED libsafec) + if(SAFEC_FOUND) + target_link_libraries(controlServer ${SAFEC_LIBRARIES}) + endif() +else() + target_compile_definitions(controlServer PUBLIC SAFEC_DUMMY_API) +endif() + +install(TARGETS controlServer DESTINATION bin) diff --git a/src/server/ctrlm_server_app.h b/src/server/ctrlm_server_app.h new file mode 100644 index 00000000..98c308f7 --- /dev/null +++ b/src/server/ctrlm_server_app.h @@ -0,0 +1,49 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * 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 +#include +#include + +class ctrlms_app_interface_t +{ + public: + virtual ~ctrlms_app_interface_t() {}; + + virtual void ws_connected(void); + virtual void ws_disconnected(void); + virtual bool ws_receive_audio(const unsigned char *payload, int payload_size); + virtual bool ws_receive_json(const json_t *json_obj); + void ws_send_json(const json_t *json_obj); + void ws_handle_set(void *handle); + + private: + void *ws_handle = NULL; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +ctrlms_app_interface_t *ctrlms_app_interface_create(void); + +#ifdef __cplusplus +} +#endif diff --git a/src/server/ctrlms_main.c b/src/server/ctrlms_main.c new file mode 100644 index 00000000..91cb89d6 --- /dev/null +++ b/src/server/ctrlms_main.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CTRLMS_VERSION "1.0" + +#define CTRLMS_WS_PORT_INT (9881) + +typedef struct { + bool silent; + bool verbose; +} ctrlms_options_t; + +static bool ctrlms_cmdline_args(int argc, char *argv[]); +static error_t ctrlms_parse_opt(int key, char *arg, struct argp_state *state); +static void ctrlms_signal_handler(int signum); + +const char *argp_program_version = "controlServer " CTRLMS_VERSION; +const char *argp_program_bug_address = ""; + +static char doc[] = "controlServer -- a server application"; + +static char args_doc[] = ""; + +static struct argp_option options[] = { + {"verbose", 'v', 0, 0, "Produce verbose output" }, + {"quiet", 'q', 0, 0, "Don't produce any output" }, + { 0 } +}; + +static struct argp argp = { options, ctrlms_parse_opt, args_doc, doc }; + +static ctrlms_options_t g_ctrlms_opts = { .silent = false, + .verbose = false + }; + +void ctrlms_signal_handler(int signum) { + if(signum == SIGTERM) { + ctrlms_ws_term(); + } +} + +int main(int argc, char* argv[]) { + int result = -1; + if(signal(SIGTERM, ctrlms_signal_handler) == SIG_ERR) { + XLOGD_ERROR("ctrlms_main: failed to set SIGTERM handler <%s>", strerror(errno)); + } + + // Parse command line arguments + if(!ctrlms_cmdline_args(argc, argv)) { + return(result); + } + + xlog_level_t level = XLOG_LEVEL_WARN; + if(g_ctrlms_opts.silent) { + level = XLOG_LEVEL_ERROR; + } else if(g_ctrlms_opts.verbose) { + level = XLOG_LEVEL_INFO; + // TODO Add option to allow debug level logging + //} else if() { + // level = XLOG_LEVEL_DEBUG; + } + + if(!ctrlms_init(level)) { + XLOGD_ERROR("ctrlms_main: init failed"); + } else { + // Start listening for connections + if(!ctrlms_ws_init(CTRLMS_WS_PORT_INT, true)) { + XLOGD_ERROR("ctrlms_main: ws init failed"); + } else { + result = 0; + XLOGD_INFO("Notifying systemd of successful initialization"); + sd_notifyf(0, "READY=1\nSTATUS=ctrlm-server has successfully initialized\nMAINPID=%lu", (unsigned long)getpid()); + ctrlms_ws_listen(); + } + XLOGD_INFO("ctrlms_main: main loop ended"); + } + ctrlms_term(); + XLOGD_INFO("ctrlms_main: return"); + + return(result); +} + +error_t ctrlms_parse_opt(int key, char *arg, struct argp_state *state) { + // Get the input argument from argp_parse, which we know is a pointer to our arguments structure. + ctrlms_options_t *arguments = state->input; + + switch(key) { + case 'q': { + arguments->silent = true; + break; + } + case 'v': { + arguments->verbose = true; + break; + } + case ARGP_KEY_ARG: { + argp_usage(state); + return(ARGP_ERR_UNKNOWN); + } + case ARGP_KEY_END: { + break; + } + default: { + return(ARGP_ERR_UNKNOWN); + } + } + + return(0); +} + +bool ctrlms_cmdline_args(int argc, char *argv[]) { + argp_parse(&argp, argc, argv, 0, 0, &g_ctrlms_opts); + + #if 0 + if() { // Nothing to do + printf("Invalid options specified. Try 'controlServer --help' or 'controlServer --usage' for more information.\n"); + return(false); + } + #endif + + XLOGD_INFO("verbose <%s>", g_ctrlms_opts.verbose ? "YES" : "NO"); + XLOGD_INFO("silent <%s>", g_ctrlms_opts.silent ? "YES" : "NO"); + + return(true); +} diff --git a/src/server/ctrlms_version.c b/src/server/ctrlms_version.c new file mode 100644 index 00000000..8077e816 --- /dev/null +++ b/src/server/ctrlms_version.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + bool initialized; + bool is_production; +} ctrlms_global_t; + +ctrlms_global_t g_ctrlms = { + .initialized = false, + .is_production = true +}; + +bool ctrlms_init(xlog_level_t level) { + rdk_version_info_t info; + int ret_val = rdk_version_parse_version(&info); + + if(ret_val != 0) { + XLOGD_ERROR("parse error <%s>\n", info.parse_error == NULL ? "" : info.parse_error); + rdk_version_object_free(&info); + return(false); + } + + g_ctrlms.is_production = info.production_build; + + rdk_version_object_free(&info); + + int rc = xlog_init(XLOG_MODULE_ID, NULL, 0, true, false); + xlog_level_set_all(level); + + if(rc != 0) { + XLOGD_ERROR("failed to init xlog"); + return(false); + } + + g_ctrlms.initialized = true; + return(true); +} + +void ctrlms_term(void) { + g_ctrlms.initialized = false; + xlog_term(); +} + +bool ctrlms_is_initialized(void) { + return(g_ctrlms.initialized); +} + +bool ctrlms_is_production(void) { + return(g_ctrlms.is_production); +} + diff --git a/src/server/ctrlms_version.h b/src/server/ctrlms_version.h new file mode 100644 index 00000000..cede9db4 --- /dev/null +++ b/src/server/ctrlms_version.h @@ -0,0 +1,40 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * 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. +*/ +#ifndef _CTRLMS_VERSION_H_ +#define _CTRLMS_VERSION_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool ctrlms_init(xlog_level_t level); +void ctrlms_term(void); + +bool ctrlms_is_initialized(void); +bool ctrlms_is_production(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/server/ctrlms_ws.cpp b/src/server/ctrlms_ws.cpp new file mode 100644 index 00000000..28b179fd --- /dev/null +++ b/src/server/ctrlms_ws.cpp @@ -0,0 +1,586 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CTRLMS_WSS_ENABLED +#include "rdkcertselector.h" +#include +#include +#define CTRLMS_WS_TLS_CERT_KEY_FILE "/tmp/serverXXXXXX" +#define CTRLMS_WS_CERT_FILENAME_PREFIX "file://" +#endif + +typedef struct { + volatile sig_atomic_t term_requested; + noPollCtx * nopoll_ctx; + noPollConnOpts * opts; + char tmp_cert[32]; + bool tmp_cert_created; + bool cert_valid; + noPollConn * nopoll_conn; + void * app_handle; + ctrlms_app_interface_t *app_interface; +} ctrlms_ws_global_t; + +static bool ctrlms_ws_load_app(ctrlms_ws_global_t *state, bool use_stub, void **handle); + +static nopoll_bool ctrlms_ws_on_accept(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); +static nopoll_bool ctrlms_ws_on_ready(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); +static void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data); +static void ctrlms_ws_on_ping(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data); +static void ctrlms_ws_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); +static void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * log_msg, noPollPtr user_data); +#ifdef CTRLMS_WSS_ENABLED +static bool ctrlms_ws_cert_config(FILE *cert_key_fp); +static bool ctrlms_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additional_certs); +#endif + +ctrlms_ws_global_t g_ctrlms_ws; + +bool ctrlms_ws_init(uint16_t port, bool log_enable) { + errno_t safec_rc = -1; + + g_ctrlms_ws.term_requested = 0; + g_ctrlms_ws.nopoll_ctx = NULL; + g_ctrlms_ws.opts = NULL; + g_ctrlms_ws.cert_valid = false; + g_ctrlms_ws.tmp_cert_created = false; + memset(g_ctrlms_ws.tmp_cert, 0, sizeof(g_ctrlms_ws.tmp_cert)); + g_ctrlms_ws.app_interface = NULL; + g_ctrlms_ws.app_handle = NULL; + g_ctrlms_ws.nopoll_conn = NULL; + + bool result = false; + do { + if(!ctrlms_ws_load_app(&g_ctrlms_ws, false, &g_ctrlms_ws.app_handle)) { + XLOGD_INFO("exiting due to app load failure"); + break; + } + + g_ctrlms_ws.nopoll_ctx = nopoll_ctx_new(); + if(g_ctrlms_ws.nopoll_ctx == NULL) { + XLOGD_ERROR("nopoll context create"); + break; + } + + #ifdef CTRLMS_WSS_ENABLED + do { + int cert_key_fd = -1; + FILE *cert_key_fp = NULL; + int errsv; + + safec_rc = sprintf_s(g_ctrlms_ws.tmp_cert, sizeof(g_ctrlms_ws.tmp_cert), "%s", CTRLMS_WS_TLS_CERT_KEY_FILE); + if(safec_rc < EOK) { + ERR_CHK(safec_rc); + } + + cert_key_fd = mkstemp(g_ctrlms_ws.tmp_cert); + if(cert_key_fd == -1) { + errsv = errno; + XLOGD_ERROR("mkstemp failed: <%s>", strerror(errsv)); + break; + } + g_ctrlms_ws.tmp_cert_created = true; + + cert_key_fp = fdopen(cert_key_fd, "w"); + if(cert_key_fp == NULL) { + errsv = errno; + XLOGD_ERROR("fdopen failed: <%s>", strerror(errsv)); + if(0 != close(cert_key_fd)) { + errsv = errno; + XLOGD_ERROR("failed to close cert/key file descriptor <%s>", strerror(errsv)); + } + break; + } + + if(!ctrlms_ws_cert_config(cert_key_fp)) { + XLOGD_ERROR("failed to set cert or key"); + fclose(cert_key_fp); + break; + } + fclose(cert_key_fp); + + // Init OpenSSL + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + g_ctrlms_ws.cert_valid = true; + } while(0); + + if(!g_ctrlms_ws.cert_valid) { + XLOGD_ERROR("no valid cert/key available"); + break; + } + #endif + + if(log_enable) { + nopoll_log_enable(g_ctrlms_ws.nopoll_ctx, nopoll_true); + nopoll_log_set_handler(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_nopoll_log, NULL); + } + g_ctrlms_ws.opts = nopoll_conn_opts_new(); + if(g_ctrlms_ws.opts == NULL) { + XLOGD_ERROR("nopoll connection options create"); + nopoll_ctx_unref(g_ctrlms_ws.nopoll_ctx); + g_ctrlms_ws.nopoll_ctx = NULL; + break; + } + + nopoll_ctx_set_on_accept(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_on_accept, &g_ctrlms_ws); + nopoll_ctx_set_on_ready(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_on_ready, &g_ctrlms_ws); + nopoll_ctx_set_on_msg(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_on_message, &g_ctrlms_ws); + + if(g_ctrlms_ws.cert_valid) { + nopoll_conn_opts_set_ssl_protocol(g_ctrlms_ws.opts, NOPOLL_METHOD_TLSV1_2); + nopoll_conn_opts_ssl_host_verify(g_ctrlms_ws.opts, nopoll_false); //localhost will not match host specified in certificate + + if(!nopoll_conn_opts_set_ssl_certs(g_ctrlms_ws.opts, g_ctrlms_ws.tmp_cert, g_ctrlms_ws.tmp_cert, NULL, NULL)) { + XLOGD_ERROR("Failed to add cert/key files to nopoll_conn"); + nopoll_ctx_unref(g_ctrlms_ws.nopoll_ctx); + g_ctrlms_ws.nopoll_ctx = NULL; + nopoll_conn_opts_free(g_ctrlms_ws.opts); + g_ctrlms_ws.opts = NULL; + break; + } + } + + char port_str[6]; + safec_rc = sprintf_s(port_str, sizeof(port_str), "%u", port); + if(safec_rc < EOK) { + ERR_CHK(safec_rc); + } + + // Start loopback-only IPv6 listener + if(g_ctrlms_ws.cert_valid) { + g_ctrlms_ws.nopoll_conn = nopoll_listener_tls_new_opts6(g_ctrlms_ws.nopoll_ctx, g_ctrlms_ws.opts, "::1", port_str); + } else { + g_ctrlms_ws.nopoll_conn = nopoll_listener_new_opts6(g_ctrlms_ws.nopoll_ctx, g_ctrlms_ws.opts, "::1", port_str); + } + if(!nopoll_conn_is_ok(g_ctrlms_ws.nopoll_conn)) { + XLOGD_ERROR("Listener connection IPv6 NOT ok"); + nopoll_ctx_unref(g_ctrlms_ws.nopoll_ctx); + g_ctrlms_ws.nopoll_ctx = NULL; + nopoll_conn_opts_free(g_ctrlms_ws.opts); + g_ctrlms_ws.opts = NULL; + break; + } + + result = true; + } while(0); + + if(!result) { + if(g_ctrlms_ws.tmp_cert_created) { + if(0 != unlink(g_ctrlms_ws.tmp_cert)) { + int errsv = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(errsv)); + } + } + if(g_ctrlms_ws.app_interface != NULL) { + delete g_ctrlms_ws.app_interface; + g_ctrlms_ws.app_interface = NULL; + } + if(g_ctrlms_ws.app_handle != NULL) { + dlclose(g_ctrlms_ws.app_handle); + g_ctrlms_ws.app_handle = NULL; + } + } + + return(result); +} + +bool ctrlms_ws_listen(void) { + XLOGD_INFO("Enter main loop"); + // Poll with a 100 ms timeout so the loop can observe term_requested, which + // is set exclusively by ctrlms_ws_term() — an async-signal-safe operation. + while(!g_ctrlms_ws.term_requested) { + nopoll_loop_wait(g_ctrlms_ws.nopoll_ctx, 100000); + } + + nopoll_conn_opts_unref(g_ctrlms_ws.opts); + nopoll_conn_unref(g_ctrlms_ws.nopoll_conn); + nopoll_ctx_unref(g_ctrlms_ws.nopoll_ctx); + g_ctrlms_ws.nopoll_ctx = NULL; + + if(g_ctrlms_ws.cert_valid) { + if(0 != unlink(g_ctrlms_ws.tmp_cert)) { + int err_store = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + } + } + + if(g_ctrlms_ws.app_interface != NULL) { + delete g_ctrlms_ws.app_interface; + g_ctrlms_ws.app_interface = NULL; + } + if(g_ctrlms_ws.app_handle != NULL) { + dlclose(g_ctrlms_ws.app_handle); + g_ctrlms_ws.app_handle = NULL; + } + return(true); +} + +void ctrlms_ws_term(void) { + g_ctrlms_ws.term_requested = 1; +} + +static nopoll_bool ctrlms_ws_on_accept(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { + ctrlms_ws_global_t *state = (ctrlms_ws_global_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return(nopoll_false); + } + + // Set ping handler + nopoll_conn_set_on_ping_msg(conn, ctrlms_ws_on_ping, user_data); + + return(nopoll_true); +} + +static nopoll_bool ctrlms_ws_on_ready(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { + ctrlms_ws_global_t *state = (ctrlms_ws_global_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return(nopoll_false); + } + + XLOGD_INFO("Connection established"); + state->app_interface->ws_handle_set((void *)conn); + state->app_interface->ws_connected(); + + nopoll_conn_set_on_close(conn, ctrlms_ws_on_close, user_data); + + return(nopoll_true); +} + +static void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data) { + ctrlms_ws_global_t *state = (ctrlms_ws_global_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return; + } + + bool close_conn = false; + int payload_size = nopoll_msg_get_payload_size(msg); + const unsigned char *payload = nopoll_msg_get_payload(msg); + + switch(nopoll_msg_opcode(msg)) { + case NOPOLL_TEXT_FRAME: { + XLOGD_DEBUG("NOPOLL_TEXT_FRAME size <%d>", payload_size); + + json_t *json_obj = json_loadb((const char *)payload, payload_size, 0, NULL); + + if(json_obj == NULL) { + XLOGD_ERROR("Failed to parse JSON object"); + break; + } else { + // Pass the incoming payload to the application as a borrowed reference. + // The json_obj pointer is only valid for the duration of this call and + // must not be stored by the callee unless it takes its own reference. + close_conn = state->app_interface->ws_receive_json(json_obj); + json_decref(json_obj); + } + break; + } + case NOPOLL_BINARY_FRAME: { + XLOGD_DEBUG("NOPOLL_BINARY_FRAME size <%d>", payload_size); + + // Pass the incoming payload to the application + close_conn = state->app_interface->ws_receive_audio(payload, payload_size); + break; + } + case NOPOLL_CONTINUATION_FRAME: { + XLOGD_INFO("NOPOLL_CONTINUATION_FRAME"); + break; + } + default: { + XLOGD_INFO("NOPOLL_UNKNOWN"); + break; + } + } + + if(close_conn) { + const char *reason = "app closed"; + nopoll_conn_close_ext(conn, 1000, reason, strlen(reason)); + } +} + +static void ctrlms_ws_on_ping(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data) { + ctrlms_ws_global_t *state = (ctrlms_ws_global_t *)user_data; + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return; + } + + XLOGD_INFO("Ping received"); + // Do nothing, we don't care about this event +} + +static void ctrlms_ws_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { + ctrlms_ws_global_t *state = (ctrlms_ws_global_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return; + } + XLOGD_INFO(""); + + state->app_interface->ws_disconnected(); + state->app_interface->ws_handle_set(NULL); +} + +#ifdef CTRLMS_WSS_ENABLED +static bool ctrlms_ws_cert_config(FILE* cert_key_fp) { + bool ret = false; + + rdkcertselector_h cert_selector = NULL; + PKCS12 *p12_cert = NULL; + EVP_PKEY *pkey = NULL; + X509 *x509_cert = NULL; + STACK_OF(X509) *additional_certs = NULL; + do { + FILE *device_cert_fp = NULL; + + char *cert_path = NULL; + char *cert_password = NULL; + cert_selector = rdkcertselector_new(NULL, NULL, "FBK_MTLS"); + + if(cert_selector == NULL){ + XLOGD_TELEMETRY("cert selector init failed"); + break; + } + + rdkcertselectorStatus_t cert_status = rdkcertselector_getCert(cert_selector, &cert_path, &cert_password); + + if(cert_status != certselectorOk) { + XLOGD_TELEMETRY("cert selector retrieval failed"); + break; + } + + if(cert_path == NULL || cert_password == NULL) { + XLOGD_TELEMETRY("cert selector get failed"); + break; + } + + char *local_path = cert_path; + if(strncmp(local_path, CTRLMS_WS_CERT_FILENAME_PREFIX, strlen(CTRLMS_WS_CERT_FILENAME_PREFIX)) == 0) { + local_path += strlen(CTRLMS_WS_CERT_FILENAME_PREFIX); + } + + device_cert_fp = fopen(local_path, "rb"); + if(device_cert_fp == NULL) { + XLOGD_ERROR("unable to open P12 certificate"); + break; + } + + d2i_PKCS12_fp(device_cert_fp, &p12_cert); + fclose(device_cert_fp); + device_cert_fp = NULL; + + if(p12_cert == NULL) { + XLOGD_ERROR("unable to read P12 certificate"); + break; + } + + if(1 != PKCS12_parse(p12_cert, cert_password, &pkey, &x509_cert, &additional_certs)) { + XLOGD_ERROR("unable to parse P12 certificate"); + break; + } + + if(1 != PEM_write_X509(cert_key_fp, x509_cert)) { + XLOGD_ERROR("failed to write temp cert"); + break; + } + + if(!ctrlms_ws_add_chain(cert_key_fp, additional_certs)) { + XLOGD_ERROR("failed to add chain certs"); + break; + } + + if(1 != PEM_write_PrivateKey(cert_key_fp, pkey, NULL, (unsigned char*)cert_password, strlen(cert_password), NULL, NULL)) { + XLOGD_ERROR("failed to write temp key"); + break; + } + + ret = true; + }while(0); + + if(cert_selector != NULL) { + rdkcertselector_free(&cert_selector); + } + + if(p12_cert != NULL) { + PKCS12_free(p12_cert); + } + + if(pkey != NULL) { + EVP_PKEY_free(pkey); + } + + if(x509_cert != NULL) { + X509_free(x509_cert); + } + + if(additional_certs != NULL) { + sk_X509_pop_free(additional_certs, X509_free); + } + + return ret; +} + +static bool ctrlms_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additional_certs) { + if(cert_key_fp == NULL) { + XLOGD_ERROR("null file pointer"); + return false; + } + if(additional_certs == NULL) { + XLOGD_WARN("no additional certs"); + return true; + } + + for(int32_t index = 0; index < sk_X509_num(additional_certs); index++) { + X509 *cert = sk_X509_value(additional_certs, index); + if(1 != PEM_write_X509(cert_key_fp, cert)) { + XLOGD_ERROR("failed to write temp cert index %d", index); + return false; + } + } + + return true; +} +#endif + +static void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * log_msg, noPollPtr user_data) { + xlog_args_t args; + args.options = XLOG_OPTS_DEFAULT; + args.color = XLOG_COLOR_NONE; + args.function = XLOG_FUNCTION_NONE; + args.line = XLOG_LINE_NONE; + args.id = XLOG_MODULE_ID; + args.size_max = XLOG_BUF_SIZE_DEFAULT; + switch(level) { + case NOPOLL_LEVEL_DEBUG: { args.level = XLOG_LEVEL_DEBUG; break; } + case NOPOLL_LEVEL_INFO: { args.level = XLOG_LEVEL_INFO; break; } + case NOPOLL_LEVEL_WARNING: { args.level = XLOG_LEVEL_WARN; break; } + case NOPOLL_LEVEL_CRITICAL: { args.level = XLOG_LEVEL_ERROR; break; } + default: { args.level = XLOG_LEVEL_INFO; break; } + } + int errsv = errno; + xlog_printf(&args, "%s", log_msg); + errno = errsv; +} + +typedef ctrlms_app_interface_t *(*ctrlms_app_interface_create_t)(void); + +static bool ctrlms_ws_load_app(ctrlms_ws_global_t *state, bool use_stub, void **handle) { + if(handle == NULL) { + XLOGD_ERROR("invalid params"); + return(false); + } + *handle = dlopen("libctrlm-server-app.so", RTLD_NOW); + if(NULL == *handle) { + XLOGD_WARN("failed to load server app plugin <%s>", dlerror()); + + if(use_stub) { + XLOGD_INFO("Using stub implementation of app interface"); + state->app_interface = new ctrlms_app_interface_t(); + return(true); + } + return(false); + } + + dlerror(); // Clear any existing error + ctrlms_app_interface_create_t app_interface = (ctrlms_app_interface_create_t)dlsym(*handle, "ctrlms_app_interface_create"); + char *error = dlerror(); + + if(error != NULL) { + XLOGD_ERROR("failed to find plugin interface, error <%s>", error); + dlclose(*handle); + *handle = NULL; + + if(use_stub) { + XLOGD_INFO("Using stub implementation of app interface"); + state->app_interface = new ctrlms_app_interface_t(); + return(true); + } + return(false); + } + + XLOGD_INFO("successfully loaded plugin interface"); + state->app_interface = (*app_interface)(); + + if(NULL == state->app_interface) { + XLOGD_ERROR("failed to create plugin app interface"); + dlclose(*handle); + *handle = NULL; + if(use_stub) { + XLOGD_INFO("Using stub implementation of app interface"); + state->app_interface = new ctrlms_app_interface_t(); + return(true); + } + return(false); + } + + return(true); +} + +void ctrlms_app_interface_t::ws_connected(void) { + XLOGD_INFO("STUB: implement ws_connected"); +} + +void ctrlms_app_interface_t::ws_disconnected(void) { + XLOGD_INFO("STUB: implement ws_disconnected"); +} + +// Return true to request that the WebSocket connection be closed. +// Return false to keep the connection open after handling/ignoring the frame. +bool ctrlms_app_interface_t::ws_receive_audio(const unsigned char *payload, int payload_size) { + XLOGD_INFO("STUB: audio received size <%d>", payload_size); + return(false); +}; +bool ctrlms_app_interface_t::ws_receive_json(const json_t *json_obj) { + XLOGD_INFO("STUB: json object received"); + return(false); +} + +void ctrlms_app_interface_t::ws_send_json(const json_t *json_obj) { + if(json_obj == NULL) { + XLOGD_ERROR("json object is NULL"); + return; + } + if(ws_handle == NULL) { + XLOGD_ERROR("ws connection is not established"); + return; + } + char *payload = json_dumps(json_obj, JSON_COMPACT | JSON_ENSURE_ASCII); + if(payload == NULL) { + XLOGD_ERROR("failed to dump JSON object"); + return; + } + + XLOGD_INFO("Sending <%s>", payload); + int rc = nopoll_conn_send_text((noPollConn *)ws_handle, payload, strlen(payload)); + if(rc <= 0) { + XLOGD_ERROR("failed to send message"); + } + free(payload); +} + +void ctrlms_app_interface_t::ws_handle_set(void *handle) { + ws_handle = handle; +} diff --git a/src/server/ctrlms_ws.h b/src/server/ctrlms_ws.h new file mode 100644 index 00000000..68a16a45 --- /dev/null +++ b/src/server/ctrlms_ws.h @@ -0,0 +1,37 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * 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. +*/ +#ifndef _CTRLMS_WS_H_ +#define _CTRLMS_WS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool ctrlms_ws_init(uint16_t port, bool log_enable); +bool ctrlms_ws_listen(void); +void ctrlms_ws_term(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index 12402a12..7ddf515f 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -1391,7 +1391,9 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net xrsr_session_request_t request_params; request_params.type = XRSR_SESSION_REQUEST_TYPE_INVALID; - + + uint8_t dst_index = 0; + if(is_session_by_text) { XLOGD_INFO("Requesting the speech router start a text-only session with transcription = <%s>", l_transcription_in); if(session->state_src == CTRLM_VOICE_STATE_SRC_STREAMING || session->state_dst != CTRLM_VOICE_STATE_DST_READY) { @@ -1402,7 +1404,9 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_params.value.text.text = l_transcription_in; xrsr_audio_format_t xrsr_format = { .type = XRSR_AUDIO_FORMAT_NONE}; - if (false == xrsr_session_request(voice_device_to_xrsr(device_type), xrsr_format, request_params, uuid, false, false)) { + + + if (false == xrsr_session_request(voice_device_to_xrsr(device_type), dst_index, xrsr_format, request_params, uuid, false, false)) { XLOGD_ERROR("Failed to acquire the text-only session from the speech router."); return VOICE_SESSION_RESPONSE_BUSY; } @@ -1422,7 +1426,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net xrsr_format.type = XRSR_AUDIO_FORMAT_PCM; } - if (false == xrsr_session_request(voice_device_to_xrsr(device_type), xrsr_format, request_params, uuid, false, false)) { + if (false == xrsr_session_request(voice_device_to_xrsr(device_type), dst_index, xrsr_format, request_params, uuid, false, false)) { XLOGD_ERROR("Failed to acquire the audio file session from the speech router."); return VOICE_SESSION_RESPONSE_BUSY; } @@ -1439,7 +1443,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_params.type = XRSR_SESSION_REQUEST_TYPE_AUDIO_MIC; request_params.value.audio_mic.stream_params_required = this->nsm_voice_session; - if(false == xrsr_session_request(voice_device_to_xrsr(device_type), xrsr_format, request_params, uuid, low_latency, low_cpu_util)) { + if(false == xrsr_session_request(voice_device_to_xrsr(device_type), dst_index, xrsr_format, request_params, uuid, low_latency, low_cpu_util)) { XLOGD_ERROR("Failed to acquire the microphone session from the speech router."); return VOICE_SESSION_RESPONSE_BUSY; } @@ -1468,7 +1472,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_params.value.audio_fd.callback = (create_pipe) ? NULL : ctrlm_voice_data_post_processing_cb; // RF4CE does not use pipe read callback request_params.value.audio_fd.user_data = (create_pipe) ? NULL : (void *)this; - if(false == xrsr_session_request(voice_device_to_xrsr(device_type), voice_format_to_xrsr(format), request_params, uuid, false, false)) { + if(false == xrsr_session_request(voice_device_to_xrsr(device_type), dst_index, voice_format_to_xrsr(format), request_params, uuid, false, false)) { XLOGD_TELEMETRY("Failed to acquire voice session"); this->voice_session_notify_abort(network_id, controller_id, 0, CTRLM_VOICE_SESSION_ABORT_REASON_BUSY); if(create_pipe) { diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp index bdcc85d8..25b2c8d6 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp @@ -178,7 +178,6 @@ bool ctrlm_voice_ipc_iarm_legacy_t::session_end(const ctrlm_voice_ipc_event_sess } bool ctrlm_voice_ipc_iarm_legacy_t::server_message(const char *message, unsigned long size) { - XLOGD_INFO("Not supported"); return(true); } From 3fa9bf79d45327633e90ccbe018f25226788e339 Mon Sep 17 00:00:00 2001 From: Gene Gallagher <129112619+egalla204@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:10:43 -0400 Subject: [PATCH 08/15] RDKEMW-13988: add new buttons for interim remote 2.0 support (#234) --- src/ctrlm_utils.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/ctrlm_utils.cpp b/src/ctrlm_utils.cpp index b77595ac..eb8c7b7e 100644 --- a/src/ctrlm_utils.cpp +++ b/src/ctrlm_utils.cpp @@ -1324,10 +1324,10 @@ static const map> ctrlm_linux_key_name {KEY_BATTERY, {"Battery Low", "Battery Low"}}, {KEY_F16, {"Plus", "Plus"}}, {KEY_F13, {"Option", "Option"}}, - {KEY_F4, {"Netflix App", "Netflix App"}}, // APP key for PLATCO - {KEY_INSERT, {"Peacock App", "Peakcock App"}}, // APP key for PLATCO - {KEY_END, {"Disney+ App", "Disney+ App"}}, // APP key for PLATCO - {KEY_DELETE, {"Prime Video App", "Prime Video App"}}, // APP key for PLATCO + {KEY_F4, {"Netflix App", "Netflix App"}}, + {KEY_INSERT, {"Peacock App", "Peacock App"}}, + {KEY_END, {"Disney+ App", "Disney+ App"}}, + {KEY_DELETE, {"Prime Video App", "Prime Video App"}}, {KEY_PAGEUP, {"Channel+", "Channel+"}}, {KEY_PAGEDOWN, {"Channel-", "Channel-"}}, {KEY_SCROLLUP, {"Page+", "Page+"}}, @@ -1342,9 +1342,9 @@ static const map> ctrlm_linux_key_name {KEY_F6, {"Provider", "Provider"}}, {KEY_KPDOT, {"App A", "App A"}}, // A key for RF4CE remotes (also asterisk key) {KEY_F14, {"Quick Access", "Quick Access"}}, // B key for RF4CE remotes (C and D are not defined yet) - {KEY_KPRIGHTPAREN, {"App 1", "App 1"}}, // APP key for LLAMA - {KEY_KPLEFTPAREN, {"App 2", "App 2"}}, // APP key for LLAMA - {KEY_KPCOMMA, {"App 3", "App 3"}}, // APP key for LLAMA + {KEY_KPRIGHTPAREN, {"App 1", "App 1"}}, + {KEY_KPLEFTPAREN, {"App 2", "App 2"}}, + {KEY_KPCOMMA, {"App 3", "App 3"}}, {KEY_CONNECT, {"US BLE Pairing", "US BLE Pairing"}}, {KEY_BLUETOOTH, {"EU BLE Pairing", "EU BLE Pairing"}}, {KEY_VOLUMEDOWN, {"Vol Down", "Vol Down"}}, @@ -1361,7 +1361,11 @@ static const map> ctrlm_linux_key_name {KEY_PLAYPAUSE, {"Play/Pause", "Play/Pause"}}, {KEY_CONFIG, {"Config", "Config"}}, {KEY_POWER, {"Power", "Power"}}, - {KEY_MUTE, {"Mute", "Mute"}} + {KEY_MUTE, {"Mute", "Mute"}}, + {KEY_EQUAL, {"Sports", "Sports"}}, + {KEY_RIGHTBRACE, {"Playlist/Library", "Playlist/Library"}}, + {KEY_LEFTBRACE, {"Live TV", "Live TV"}}, + {KEY_MINUS, {"Best Of", "Best Of"}} }; From e8a2e89140cf7b044a6934f27ad15bd13a1e11e6 Mon Sep 17 00:00:00 2001 From: Gene Gallagher <129112619+egalla204@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:44:34 -0400 Subject: [PATCH 09/15] RDKEMW-18979: onStatus event to contain RCU data from all networks (#238) --- src/ctrlm.h | 3 + src/ctrlm_main.cpp | 34 ++++ src/ipc/ctrlm_rcp_ipc_event.cpp | 6 +- src/ipc/ctrlm_rcp_ipc_event.h | 6 +- src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp | 247 +++++++++++++++---------- src/ipc/ctrlm_rcp_ipc_iarm_thunder.h | 7 + 6 files changed, 196 insertions(+), 107 deletions(-) diff --git a/src/ctrlm.h b/src/ctrlm.h index 376608b3..b5c73f0a 100644 --- a/src/ctrlm.h +++ b/src/ctrlm.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "ctrlm_hal.h" #include "ctrlm_hal_rf4ce.h" #include "ctrlm_hal_ble.h" @@ -53,6 +54,7 @@ class ctrlm_voice_endpoint_t; class ctrlm_irdb_interface_t; class ctrlm_auth_t; class ctrlm_telemetry_t; +class ctrlm_rcp_ipc_net_status_t; typedef enum { CTRLM_THREAD_MONITOR_RESPONSE_DEAD = 0, CTRLM_THREAD_MONITOR_RESPONSE_ALIVE = 1 @@ -508,6 +510,7 @@ void ctrlm_update_last_key_info(int controller_id, ctrlm_key_source_t sou ctrlm_irdb_interface_t* ctrlm_main_irdb_get(); ctrlm_auth_t* ctrlm_main_auth_get(); void ctrlm_main_auth_start_poll(); +std::shared_ptr ctrlm_main_all_network_rcu_status_get(); std::string ctrlm_device_id_get(); std::string ctrlm_stb_name_get(); std::string ctrlm_device_mac_get(); diff --git a/src/ctrlm_main.cpp b/src/ctrlm_main.cpp index 902b4331..a4b38fac 100644 --- a/src/ctrlm_main.cpp +++ b/src/ctrlm_main.cpp @@ -85,6 +85,8 @@ #include "ctrlm_voice_obj_generic.h" #include "ctrlm_voice_endpoint.h" #include "ctrlm_irdb_interface.h" +#include "ctrlm_rcp_ipc_event.h" + #ifdef FDC_ENABLED #include "xr_fdc.h" #endif @@ -877,6 +879,38 @@ void ctrlm_main_ir_last_keypress_get(ctrlm_ir_last_keypress_t *last_key_info) { } } +static void ctrlm_main_network_handler_execute_for_all(ctrlm_msg_handler_network_t handler, void *data, int size) { + for (auto const &network_it : g_ctrlm.networks) { + if (network_it.second && network_it.second->is_ready()) { + (network_it.second->*handler)(data, size); + } + } +} + +std::shared_ptr ctrlm_main_all_network_rcu_status_get() { + if (g_ctrlm.main_thread != g_thread_self ()) { + XLOGD_ERROR("not called from ctrlm_main_thread!!!!!"); + if(!ctrlm_is_production_build()) { + g_assert(0); + } + return nullptr; + } + + std::shared_ptr> params = + std::make_shared>(); + + params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); + + ctrlm_main_queue_msg_get_rcu_status_t msg = {}; + msg.params = params; + + ctrlm_main_network_handler_execute_for_all((ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_get_rcu_status, + &msg, + sizeof(msg)); + + return params; +} + void ctrlm_utils_sem_wait(){ sem_wait(&g_ctrlm.ctrlm_utils_sem); } diff --git a/src/ipc/ctrlm_rcp_ipc_event.cpp b/src/ipc/ctrlm_rcp_ipc_event.cpp index 510be650..7ed3d43d 100644 --- a/src/ipc/ctrlm_rcp_ipc_event.cpp +++ b/src/ipc/ctrlm_rcp_ipc_event.cpp @@ -135,17 +135,17 @@ void ctrlm_rcp_ipc_net_status_t::populate_status(const ctrlm_obj_network_t &netw } } -ctrlm_ir_state_t ctrlm_rcp_ipc_net_status_t::get_ir_prog_state(void) +ctrlm_ir_state_t ctrlm_rcp_ipc_net_status_t::get_ir_prog_state(void) const { return irdb_state_; } -ctrlm_rf_pair_state_t ctrlm_rcp_ipc_net_status_t::get_rf_pair_state(void) +ctrlm_rf_pair_state_t ctrlm_rcp_ipc_net_status_t::get_rf_pair_state(void) const { return pair_state_; } -void ctrlm_rcp_ipc_net_status_t::get_controller_status_list(std::vector &list) +void ctrlm_rcp_ipc_net_status_t::get_controller_status_list(std::vector &list) const { for (const auto &status : controller_status_list_) { list.push_back(status); diff --git a/src/ipc/ctrlm_rcp_ipc_event.h b/src/ipc/ctrlm_rcp_ipc_event.h index e2c36e7c..03b29da4 100644 --- a/src/ipc/ctrlm_rcp_ipc_event.h +++ b/src/ipc/ctrlm_rcp_ipc_event.h @@ -120,9 +120,9 @@ class ctrlm_rcp_ipc_net_status_t : public ctrlm_virtual_json_t ctrlm_network_type_t get_type() const { return net_type_; } void populate_status(const ctrlm_obj_network_t &network); - ctrlm_ir_state_t get_ir_prog_state(void); - ctrlm_rf_pair_state_t get_rf_pair_state(void); - void get_controller_status_list(std::vector &list); + ctrlm_ir_state_t get_ir_prog_state(void) const; + ctrlm_rf_pair_state_t get_rf_pair_state(void) const; + void get_controller_status_list(std::vector &list) const; private: uint8_t api_revision_ = 0; diff --git a/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp b/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp index 5ed63e70..15b267d7 100644 --- a/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp +++ b/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp @@ -19,6 +19,7 @@ #include "algorithm" #include "ctrlm_rcp_ipc_iarm_thunder.h" +#include "ctrlm.h" #include "ctrlm_network.h" #include "ctrlm_log.h" #include @@ -103,6 +104,42 @@ bool ctrlm_rcp_ipc_iarm_thunder_t::is_thunder_device_update_enabled() const return (thunder_device_update_enabled_); } +json_t *ctrlm_rcp_ipc_iarm_thunder_t::build_rcu_status_json( + const std::map &status_map, + ctrlm_ir_state_t ir_prog_state, + ctrlm_rf_pair_state_t rf_pair_state, + ctrlm_network_type_t type) +{ + json_t *status = json_object(); + json_t *net_type_supported = json_array(); + json_t *remote_array = json_array(); + std::vector remotes; + int err = 0; + + for (auto const &it : ctrlm_network_types_get()) { + err |= json_array_append_new(net_type_supported, json_integer(it)); + } + for (auto &it : status_map) { + it.second.get_controller_status_list(remotes); + } + for (const auto &remote : remotes) { + err |= json_array_append_new(remote_array, remote.to_json()); + } + + err |= json_object_set_new_nocheck(status, REMOTE_DATA, remote_array); + err |= json_object_set_new_nocheck(status, NET_TYPES_SUPPORTED, net_type_supported); + err |= json_object_set_new_nocheck(status, NET_TYPE, json_integer(type)); + err |= json_object_set_new_nocheck(status, IR_PROG_STATE, json_string(ctrlm_ir_state_str(ir_prog_state))); + err |= json_object_set_new_nocheck(status, PAIRING_STATE, json_string(ctrlm_rf_pair_state_str(rf_pair_state))); + + if (err) { + XLOGD_ERROR("JSON object set error"); + json_decref(status); + return nullptr; + } + return status; +} + bool ctrlm_rcp_ipc_iarm_thunder_t::on_status(const ctrlm_rcp_ipc_net_status_t &net_status) const { if (!is_running(atomic_running_)) { @@ -115,11 +152,23 @@ bool ctrlm_rcp_ipc_iarm_thunder_t::on_status(const ctrlm_rcp_ipc_net_status_t &n return(false); } - json_t *ret = json_object(); - int err = 0; + std::shared_ptr ptr = ctrlm_main_all_network_rcu_status_get(); + if (ptr == nullptr) { + XLOGD_ERROR("Failed to get RCU status from main thread"); + return false; + } + std::shared_ptr> params = + std::static_pointer_cast>(ptr); + + const std::map status_map = params->get_reply(); - err |= json_object_set_new_nocheck(ret, STATUS, net_status.to_json()); + json_t *status = build_rcu_status_json(status_map, net_status.get_ir_prog_state(), net_status.get_rf_pair_state(), net_status.get_type()); + if (status == nullptr) { + return(false); + } + json_t *ret = json_object(); + int err = json_object_set_new_nocheck(ret, STATUS, status); if (err) { XLOGD_ERROR("JSON object set error"); json_decref(ret); @@ -129,6 +178,70 @@ bool ctrlm_rcp_ipc_iarm_thunder_t::on_status(const ctrlm_rcp_ipc_net_status_t &n return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_RCU_IARM_EVENT_RCU_STATUS, ret); } + +IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::get_net_status(void *arg) +{ + XLOGD_INFO(""); + + if (!is_running(atomic_running_)) { + XLOGD_ERROR("IARM Call received when IARM component in stopped/terminated state"); + return(IARM_RESULT_INVALID_STATE); + } + + ctrlm_main_iarm_call_json_t *call_data = static_cast(arg); + + if (!call_data || call_data->api_revision != CTRLM_MAIN_IARM_BUS_API_REVISION) { + XLOGD_ERROR("NULL parameter"); + return(IARM_RESULT_INVALID_PARAM); + } + + std::shared_ptr> params = std::make_shared>(); + params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); + + sync_send_netw_handler_to_main_queue_new, + ctrlm_main_queue_msg_get_rcu_status_t> + (params, + (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_get_rcu_status); + + + std::map status_map = params->get_reply(); + + + ctrlm_network_type_t type = CTRLM_NETWORK_TYPE_INVALID; + ctrlm_ir_state_t ir_prog_state = CTRLM_IR_STATE_UNKNOWN; + ctrlm_rf_pair_state_t rf_pair_state = CTRLM_RF_PAIR_STATE_UNKNOWN; + + // For now default to RF4CE network reporting if available + for (auto &it : status_map) { + ir_prog_state = it.second.get_ir_prog_state(); + rf_pair_state = it.second.get_rf_pair_state(); + type = it.second.get_type(); + + if (type == CTRLM_NETWORK_TYPE_RF4CE) { + break; + } + } + + json_t *status = build_rcu_status_json(status_map, ir_prog_state, rf_pair_state, type); + if (status == nullptr) { + return(IARM_RESULT_INVALID_STATE); + } + + json_t *ret = json_object(); + + int err = 0; + err |= json_object_set_new_nocheck(ret, STATUS, status); + err |= json_object_set_new_nocheck(ret, SUCCESS, json_boolean(params->get_result())); + + if (err || !ctrlm_json_to_iarm_call_data_result(ret, call_data)) { + XLOGD_ERROR("JSON object set error"); + json_decref(ret); + return(IARM_RESULT_INVALID_STATE); + } + + return(IARM_RESULT_SUCCESS); +} + bool ctrlm_rcp_ipc_iarm_thunder_t::on_validation_status(const ctrlm_rcp_ipc_validation_status_t &validation_status) const { if (!is_running(atomic_running_)) { @@ -223,6 +336,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::start_pairing(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } @@ -250,17 +364,20 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::start_pairing(void *arg) json_array_foreach(mac_addr_array, index, value) { if (!json_is_string(value)) { XLOGD_ERROR("An element of the %s array is not a string", MAC_ADDRESS_LIST); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } uint64_t mac_addr = ctrlm_convert_mac_string_to_long(json_string_value(value)); if (mac_addr == 0) { XLOGD_ERROR("An invalid mac address was provided <%s>", json_string_value(value)); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } mac_addr_list.push_back(mac_addr); } } + json_decref(payload); if(!scanEnable && mac_addr_list.size() > 0) { XLOGD_WARN("scanEnable is false but macAddressList is not empty. Ignoring macAddressList."); @@ -325,6 +442,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::stop_pairing(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } @@ -337,6 +455,8 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::stop_pairing(void *arg) if (!config.config_value_get(SCAN_DISABLE, scanDisable)) { XLOGD_INFO("Missing %s parameter - defaulting to %s", SCAN_DISABLE, scanDisable ? "true" : "false"); } + json_decref(payload); + bool result = true; if(!screenBindDisable && !scanDisable) { @@ -373,94 +493,6 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::stop_pairing(void *arg) return(IARM_RESULT_SUCCESS); } -IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::get_net_status(void *arg) -{ - XLOGD_INFO(""); - - if (!is_running(atomic_running_)) { - XLOGD_ERROR("IARM Call received when IARM component in stopped/terminated state"); - return(IARM_RESULT_INVALID_STATE); - } - - ctrlm_main_iarm_call_json_t *call_data = static_cast(arg); - - if (!call_data || call_data->api_revision != CTRLM_MAIN_IARM_BUS_API_REVISION) { - XLOGD_ERROR("NULL parameter"); - return(IARM_RESULT_INVALID_PARAM); - } - - json_t *payload = json_loads(call_data->payload, JSON_DECODE_ANY, NULL); - json_config config(payload); - - if (!payload || !config.current_object_get()) { - XLOGD_ERROR("Bad payload from call data"); - return(IARM_RESULT_INVALID_PARAM); - } - - - std::shared_ptr> params = std::make_shared>(); - params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); - - sync_send_netw_handler_to_main_queue_new, - ctrlm_main_queue_msg_get_rcu_status_t> - (params, - (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_get_rcu_status); - - json_t *ret = json_object(); - json_t *status = json_object(); - json_t *net_type_supported = json_array(); - json_t *remote_array = json_array(); - - std::map status_map = params->get_reply(); - std::vector remotes; - - ctrlm_network_type_t type = CTRLM_NETWORK_TYPE_INVALID; - ctrlm_ir_state_t ir_prog_state = CTRLM_IR_STATE_UNKNOWN; - ctrlm_rf_pair_state_t rf_pair_state = CTRLM_RF_PAIR_STATE_UNKNOWN; - int err = 0; - - for (auto const &it : ctrlm_network_types_get()) { - err |= json_array_append_new(net_type_supported, json_integer(it)); - } - for (auto &it : status_map) { - it.second.get_controller_status_list(remotes); - } - for (const auto &remote : remotes) { - err |= json_array_append_new(remote_array, remote.to_json()); - } - // For now default to RF4CE network reporting if available - for (auto &it : status_map) { - ir_prog_state = it.second.get_ir_prog_state(); - rf_pair_state = it.second.get_rf_pair_state(); - type = it.second.get_type(); - - if (type == CTRLM_NETWORK_TYPE_RF4CE) { - break; - } - } - - err |= json_object_set_new_nocheck(status, REMOTE_DATA, remote_array); - err |= json_object_set_new_nocheck(status, NET_TYPES_SUPPORTED, net_type_supported); - err |= json_object_set_new_nocheck(status, NET_TYPE, json_integer(type)); - err |= json_object_set_new_nocheck(status, IR_PROG_STATE, json_string(ctrlm_ir_state_str(ir_prog_state))); - err |= json_object_set_new_nocheck(status, PAIRING_STATE, json_string(ctrlm_rf_pair_state_str(rf_pair_state))); - - err |= json_object_set_new_nocheck(ret, STATUS, status); - err |= json_object_set_new_nocheck(ret, SUCCESS, json_boolean(params->get_result())); - - if (err) { - XLOGD_ERROR("JSON object set error"); - json_decref(ret); - return(IARM_RESULT_INVALID_STATE); - } - - if (!ctrlm_json_to_iarm_call_data_result(ret, call_data)) { - json_decref(ret); - return(IARM_RESULT_INVALID_STATE); - } - - return(IARM_RESULT_SUCCESS); -} IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::get_last_keypress(void *arg) { @@ -478,14 +510,6 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::get_last_keypress(void *arg) return(IARM_RESULT_INVALID_PARAM); } - json_t *payload = json_loads(call_data->payload, JSON_DECODE_ANY, NULL); - json_config config(payload); - - if (!payload || !config.current_object_get()) { - XLOGD_ERROR("Bad payload from call data"); - return(IARM_RESULT_INVALID_PARAM); - } - std::shared_ptr> params = std::make_shared>(); params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); @@ -554,14 +578,17 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::find_my_remote(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } std::string level; if(!config.config_value_get(LEVEL, level)) { XLOGD_ERROR("Missing %s parameter", LEVEL); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } + json_decref(payload); std::shared_ptr params = std::make_shared(); params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); @@ -650,6 +677,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::write_rcu_wakeup_config(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } @@ -657,6 +685,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::write_rcu_wakeup_config(void *arg) std::string wakeup_config; if(!config.config_value_get(WAKEUP_CONFIG, wakeup_config)) { XLOGD_ERROR("Missing %s parameter", WAKEUP_CONFIG); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } @@ -664,9 +693,11 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::write_rcu_wakeup_config(void *arg) if (ctrlm_utils_str_to_wakeup_config(wakeup_config) == CTRLM_RCU_WAKEUP_CONFIG_CUSTOM) { if(!config.config_value_get(CUSTOM_KEYS, custom_keys)) { XLOGD_ERROR("Missing %s parameter", CUSTOM_KEYS); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } } + json_decref(payload); ctrlm_iarm_call_WriteRcuWakeupConfig_params_t params = {}; params.network_id = CTRLM_MAIN_NETWORK_ID_ALL; @@ -716,6 +747,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::unpair(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } @@ -728,17 +760,20 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::unpair(void *arg) json_array_foreach(mac_addr_array, index, value) { if (!json_is_string(value)) { XLOGD_ERROR("An element of the %s array is not a string", MAC_ADDRESS_LIST); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } uint64_t mac_addr = ctrlm_convert_mac_string_to_long(json_string_value(value)); if (mac_addr == 0) { XLOGD_ERROR("An invalid mac address was provided <%s>", json_string_value(value)); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } mac_addr_list.push_back(mac_addr); } } + json_decref(payload); std::shared_ptr params = std::make_shared(); params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); @@ -794,6 +829,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::start_fw_update(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } @@ -808,6 +844,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::start_fw_update(void *arg) std::string filename; if (!config.config_value_get(FILENAME, filename)) { XLOGD_ERROR("Missing %s parameter", FILENAME); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } @@ -816,20 +853,22 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::start_fw_update(void *arg) XLOGD_INFO("%s parameter was omitted", FILETYPE); } - uint8_t percent_increment = 0; + int percent_increment = 0; if (!config.config_value_get(PERCENT_INCREMENT, percent_increment)) { XLOGD_INFO("%s parameter was omitted", PERCENT_INCREMENT); } else if (percent_increment < 0 || percent_increment > 100) { XLOGD_ERROR("%s parameter out of bounds", PERCENT_INCREMENT); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } + json_decref(payload); std::shared_ptr params = std::make_shared(); std::shared_ptr> upgrade_sessions = std::make_shared>(); params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); params->ieee_address = ieee_address; - params->percent_increment = percent_increment; + params->percent_increment = (uint8_t)percent_increment; params->filetype = filetype; params->filename = filename; params->upgrade_sessions = upgrade_sessions; @@ -890,14 +929,17 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::cancel_fw_update(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } std::string session_id; if (!config.config_value_get(SESSION_ID, session_id) || !ctrlm_utils_is_valid_uuid(session_id)) { XLOGD_ERROR("Missing or bad %s parameter", SESSION_ID); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } + json_decref(payload); std::shared_ptr params = std::make_shared(); params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); @@ -953,14 +995,17 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::status_fw_update(void *arg) if (!payload || !config.current_object_get()) { XLOGD_ERROR("Bad payload from call data"); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } std::string session_id; if (!config.config_value_get(SESSION_ID, session_id) || !ctrlm_utils_is_valid_uuid(session_id)) { XLOGD_ERROR("Missing or bad %s parameter", SESSION_ID); + json_decref(payload); return(IARM_RESULT_INVALID_PARAM); } + json_decref(payload); std::shared_ptr params = std::make_shared(); params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); diff --git a/src/ipc/ctrlm_rcp_ipc_iarm_thunder.h b/src/ipc/ctrlm_rcp_ipc_iarm_thunder.h index 384df5ae..3cda825d 100644 --- a/src/ipc/ctrlm_rcp_ipc_iarm_thunder.h +++ b/src/ipc/ctrlm_rcp_ipc_iarm_thunder.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "ctrlm_ipc_iarm.h" #include "ctrlm_ipc.h" #include "ctrlm_utils.h" @@ -94,6 +95,12 @@ class ctrlm_rcp_ipc_iarm_thunder_t : public ctrlm_ipc_iarm_t static IARM_Result_t status_fw_update(void *arg); static IARM_Result_t unpair(void *arg); + + static json_t *build_rcu_status_json(const std::map &status_map, + ctrlm_ir_state_t ir_prog_state, + ctrlm_rf_pair_state_t rf_pair_state, + ctrlm_network_type_t type); + template static void sync_send_netw_handler_to_main_queue(T1 params, ctrlm_msg_handler_network_t handler) { From f0af08001e9e35b432380ec198f9b2561dd15fef Mon Sep 17 00:00:00 2001 From: Kelvin Lu <119349872+klu339@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:22:21 -0400 Subject: [PATCH 10/15] RDKEMW-16966: T2 Events for IRDB Programming (#239) --- include/ctrlm_hal_ble.h | 1 + src/ble/ctrlm_ble_controller.cpp | 25 +++++- src/ble/ctrlm_ble_network.cpp | 42 ++++++++++ src/ble/ctrlm_ble_rcu_interface.cpp | 22 +++-- src/irdb/ctrlm_irdb_interface.cpp | 84 ++++++++++++++++++- src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp | 34 ++++++++ src/telemetry/ctrlm_telemetry_markers.h | 12 +++ .../ctrlm_thunder_plugin_display_settings.cpp | 2 +- 8 files changed, 212 insertions(+), 10 deletions(-) diff --git a/include/ctrlm_hal_ble.h b/include/ctrlm_hal_ble.h index f9cb6a14..0f84fc64 100644 --- a/include/ctrlm_hal_ble.h +++ b/include/ctrlm_hal_ble.h @@ -132,6 +132,7 @@ typedef struct { int pairing_code; ctrlm_hal_ble_RcuProperty_t property_updated; ctrlm_hal_ble_rcu_data_t rcu_data; + char ir_fail_reason[CTRLM_MAX_PARAM_STR_LEN]; } ctrlm_hal_ble_RcuStatusData_t; typedef struct { diff --git a/src/ble/ctrlm_ble_controller.cpp b/src/ble/ctrlm_ble_controller.cpp index c9e996c9..388fabb8 100644 --- a/src/ble/ctrlm_ble_controller.cpp +++ b/src/ble/ctrlm_ble_controller.cpp @@ -31,6 +31,7 @@ #include "ctrlm_controller.h" #include "ctrlm_hal_ip.h" #include "blercu/bleservices/blercuupgradeservice.h" +#include "ctrlm_telemetry_event.h" #include #include @@ -534,19 +535,30 @@ void ctrlm_obj_controller_ble_t::setSupportedIrdbs(uint8_t vendor_support_bitmas if (irdb == NULL) { XLOGD_ERROR("IRDB interface is NULL!!!"); +#ifdef TELEMETRY_SUPPORT + char t2_buf[256]; + snprintf(t2_buf, sizeof(t2_buf), "[0,0x%02X,\"unknown\",0x00,0]", vendor_support_bitmask); + ctrlm_telemetry_event_t ev(MARKER_IRDB_VENDOR_SET, t2_buf); + ev.event(); +#endif return; } ctrlm_irdb_vendor_info_t rcu_vendor_info{}; rcu_vendor_info.rcu_support_bitmask = vendor_support_bitmask; - if (!irdb->set_vendor(rcu_vendor_info)) { + bool set_result = irdb->set_vendor(rcu_vendor_info); + if (!set_result) { XLOGD_ERROR("Failed to set IRDB vendor info for controller <%s> with bitmask <0x%X>.", ieee_address_get().to_string().c_str(), vendor_support_bitmask); } ctrlm_irdb_vendor_info_t vendor_info{}; + vendor_info.name = "unknown"; + vendor_info.rcu_support_bitmask = 0xFF; + bool supported = false; if (irdb->get_vendor_info(vendor_info)) { - if (isSupportedIrdb(vendor_info)) { + supported = isSupportedIrdb(vendor_info); + if (supported) { XLOGD_INFO("Controller <%s> IRDBs supported bitmask = <0x%X>, which DOES support the loaded IRDB plugin vendor <%s>", ieee_address_get().to_string().c_str(), vendor_support_bitmask, vendor_info.name.c_str()); } else { @@ -557,6 +569,15 @@ void ctrlm_obj_controller_ble_t::setSupportedIrdbs(uint8_t vendor_support_bitmas XLOGD_WARN("Controller <%s> IRDBs supported bitmask = <0x%X>, couldn't retrieve IRDB plugin vendor info.", ieee_address_get().to_string().c_str(), vendor_support_bitmask); } +#ifdef TELEMETRY_SUPPORT + char t2_buf[256]; + snprintf(t2_buf, sizeof(t2_buf), "[%d,0x%02X,\"%s\",0x%02X,%d]", + (int)set_result,vendor_support_bitmask, + vendor_info.name.c_str(), vendor_info.rcu_support_bitmask, + (int)supported); + ctrlm_telemetry_event_t ev(MARKER_IRDB_VENDOR_SET, t2_buf); + ev.event(); +#endif } uint8_t ctrlm_obj_controller_ble_t::getSupportedIrdbs() const { diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index 94a9022c..480d900a 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -49,6 +49,7 @@ #include "ctrlm_rcp_ipc_iarm_thunder.h" #include "ctrlm_telemetry.h" #include +#include "ctrlm_telemetry_event.h" using namespace std; @@ -792,6 +793,16 @@ void ctrlm_obj_network_ble_t::req_process_program_ir_codes(void *data, int size) XLOGD_ERROR("Controller doesn't exist!"); } else if (!controllers_[controller_id]->isSupportedIrdb(dqm->vendor_info)) { XLOGD_ERROR("Unsupported IRDB - not continuing with ir code download!"); +#ifdef TELEMETRY_SUPPORT + char t2_buf[256]; + snprintf(t2_buf, sizeof(t2_buf), "[%d,\"unsupported\",0x%02X,\"%s\",0x%02X]", + (int)CTRLM_IR_STATE_FAILED, + controllers_[controller_id]->getSupportedIrdbs(), + dqm->vendor_info.name.c_str(), + dqm->vendor_info.rcu_support_bitmask); + ctrlm_telemetry_event_t ev(MARKER_IRDB_PROGRAM_RESULT, t2_buf); + ev.event(); +#endif } else { if(dqm->ir_codes) { @@ -1776,6 +1787,37 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { ir_state_ = dqm->ir_state; // send event immediately. schedule_status_event(true); +#ifdef TELEMETRY_SUPPORT + { + ctrlm_irdb_vendor_info_t vendor_info{}; + vendor_info.name = "unknown"; + vendor_info.rcu_support_bitmask = 0xFF; + + ctrlm_irdb_interface_t *irdb = ctrlm_main_irdb_get(); + if (irdb) { irdb->get_vendor_info(vendor_info); } + char t2_buf[256]; + if (dqm->ir_state == CTRLM_IR_STATE_COMPLETE || dqm->ir_state == CTRLM_IR_STATE_FAILED) { + ctrlm_controller_id_t controller_id; + uint8_t rcu_bitmask = 0xFF; + if (false == getControllerId(dqm->rcu_data.ieee_address, &controller_id)) { + XLOGD_ERROR("Controller <%s> NOT found in the network!!", + ctrlm_convert_mac_long_to_string(dqm->rcu_data.ieee_address).c_str()); + } else { + auto controller = controllers_[controller_id]; + rcu_bitmask = controller->getSupportedIrdbs(); + } + + snprintf(t2_buf, sizeof(t2_buf), "[%d,\"%s\",0x%02X,\"%s\",0x%02X]", + (int)dqm->ir_state, + dqm->ir_fail_reason, + rcu_bitmask, + vendor_info.name.c_str(), + vendor_info.rcu_support_bitmask); + ctrlm_telemetry_event_t ev(MARKER_IRDB_PROGRAM_RESULT, t2_buf); + ev.event(); + } + } +#endif break; default: { diff --git a/src/ble/ctrlm_ble_rcu_interface.cpp b/src/ble/ctrlm_ble_rcu_interface.cpp index 2fd194b7..36c9dbaf 100644 --- a/src/ble/ctrlm_ble_rcu_interface.cpp +++ b/src/ble/ctrlm_ble_rcu_interface.cpp @@ -1166,7 +1166,7 @@ bool ctrlm_ble_rcu_interface_t::programIrSignalWaveforms(uint64_t ieee_address, uint8_t vendor) { // lambda invoked when the request returns - auto replyHandler = [this](PendingReply<> *reply) mutable + auto replyHandler = [this, ieee_address](PendingReply<> *reply) mutable { bool success = false; @@ -1179,9 +1179,13 @@ bool ctrlm_ble_rcu_interface_t::programIrSignalWaveforms(uint64_t ieee_address, success = true; } - ctrlm_hal_ble_RcuStatusData_t params; + ctrlm_hal_ble_RcuStatusData_t params = {}; params.property_updated = CTRLM_HAL_BLE_PROPERTY_IR_STATE; params.ir_state = success ? CTRLM_IR_STATE_COMPLETE : CTRLM_IR_STATE_FAILED; + params.rcu_data.ieee_address = ieee_address; + if (!success) { + snprintf(params.ir_fail_reason, CTRLM_MAX_PARAM_STR_LEN, "%s", reply->errorMessage().c_str()); + } m_rcuStatusChangedSlots.invoke(¶ms); }; @@ -1247,9 +1251,10 @@ bool ctrlm_ble_rcu_interface_t::programIrSignalWaveforms(uint64_t ieee_address, success = false; } - ctrlm_hal_ble_RcuStatusData_t params; + ctrlm_hal_ble_RcuStatusData_t params = {}; params.property_updated = CTRLM_HAL_BLE_PROPERTY_IR_STATE; params.ir_state = success ? CTRLM_IR_STATE_WAITING : CTRLM_IR_STATE_FAILED; + params.rcu_data.ieee_address = ieee_address; m_rcuStatusChangedSlots.invoke(¶ms); return success; @@ -1258,7 +1263,7 @@ bool ctrlm_ble_rcu_interface_t::programIrSignalWaveforms(uint64_t ieee_address, bool ctrlm_ble_rcu_interface_t::eraseIrSignals(uint64_t ieee_address) { // lambda invoked when the request returns - auto replyHandler = [this](PendingReply<> *reply) mutable + auto replyHandler = [this, ieee_address](PendingReply<> *reply) mutable { bool success = false; @@ -1271,9 +1276,13 @@ bool ctrlm_ble_rcu_interface_t::eraseIrSignals(uint64_t ieee_address) success = true; } - ctrlm_hal_ble_RcuStatusData_t params; + ctrlm_hal_ble_RcuStatusData_t params = {}; params.property_updated = CTRLM_HAL_BLE_PROPERTY_IR_STATE; params.ir_state = success ? CTRLM_IR_STATE_COMPLETE : CTRLM_IR_STATE_FAILED; + params.rcu_data.ieee_address = ieee_address; + if (!success) { + snprintf(params.ir_fail_reason, CTRLM_MAX_PARAM_STR_LEN, "%s", reply->errorMessage().c_str()); + } m_rcuStatusChangedSlots.invoke(¶ms); }; @@ -1295,9 +1304,10 @@ bool ctrlm_ble_rcu_interface_t::eraseIrSignals(uint64_t ieee_address) success = false; } - ctrlm_hal_ble_RcuStatusData_t params; + ctrlm_hal_ble_RcuStatusData_t params = {}; params.property_updated = CTRLM_HAL_BLE_PROPERTY_IR_STATE; params.ir_state = success ? CTRLM_IR_STATE_WAITING : CTRLM_IR_STATE_FAILED; + params.rcu_data.ieee_address = ieee_address; m_rcuStatusChangedSlots.invoke(¶ms); return success; diff --git a/src/irdb/ctrlm_irdb_interface.cpp b/src/irdb/ctrlm_irdb_interface.cpp index 01fa6588..bab8f9a6 100644 --- a/src/irdb/ctrlm_irdb_interface.cpp +++ b/src/irdb/ctrlm_irdb_interface.cpp @@ -21,6 +21,7 @@ #include "ctrlm_network.h" #include "ctrlm_log.h" #include "ctrlm_irdb_stub.h" +#include "ctrlm_telemetry_event.h" #include "ctrlm_ipc_iarm.h" #include "ctrlm_irdb_ipc_iarm_thunder.h" @@ -41,10 +42,36 @@ #include #include +#include #include +#include using namespace std; +typedef enum { + CTRLM_IRDB_AUTOLOOKUP_SOURCE_EDID = 0, + CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, + CTRLM_IRDB_AUTOLOOKUP_SOURCE_INFOFRAME, + CTRLM_IRDB_AUTOLOOKUP_SOURCE_UNKNOWN +} ctrlm_irdb_autolookup_source_t; + +typedef enum { + CTRLM_IRDB_AUTOLOOKUP_RESULT_SUCCESS = 0, + CTRLM_IRDB_AUTOLOOKUP_RESULT_NULL, + CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_SOURCE_DATA, + CTRLM_IRDB_AUTOLOOKUP_RESULT_FAILED, + CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_CODES_FOR_SOURCE, + CTRLM_IRDB_AUTOLOOKUP_RESULT_DEV_TYPE_INVALID, + CTRLM_IRDB_AUTOLOOKUP_RESULT_UNKNOWN, +} ctrlm_irdb_autolookup_result_t; + +class ctrlm_irdb_t2_autolookup_entry_t { +public: + ctrlm_irdb_t2_autolookup_entry_t(int p, ctrlm_irdb_autolookup_source_t s, ctrlm_irdb_autolookup_result_t r): port(p), source(s), result(r) {} + int port = -1; + ctrlm_irdb_autolookup_source_t source = CTRLM_IRDB_AUTOLOOKUP_SOURCE_UNKNOWN; + ctrlm_irdb_autolookup_result_t result = CTRLM_IRDB_AUTOLOOKUP_RESULT_UNKNOWN; +}; static ctrlm_irdb_interface_t *_instance = NULL; @@ -420,6 +447,13 @@ bool comp_autolookup_ranked_list (ctrlm_irdb_autolookup_entry_ranked_t i, ctrlm_ bool ctrlm_irdb_interface_t::get_ir_codes_by_autolookup(ctrlm_autolookup_ranked_list_by_type_t &codes) { std::unique_lock guard(m_mutex); bool ret = false; + std::vector t2_info; + + // Retrieve vendor info now while mutex is held (avoids recursive lock via get_vendor_info()) + ctrlm_irdb_vendor_info_t t2_vendor_info{}; + if (g_irdb.pluginGetVendorInfo) { + (*g_irdb.pluginGetVendorInfo)(t2_vendor_info); + } #if defined(CTRLM_THUNDER) if(m_platform_tv == false) { @@ -436,20 +470,26 @@ bool ctrlm_irdb_interface_t::get_ir_codes_by_autolookup(ctrlm_autolookup_ranked_ if(type != CTRLM_IRDB_DEV_TYPE_INVALID) { codes[type].insert(codes[type].end(), ir_codes.begin(), ir_codes.end()); ret = true; + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_EDID, CTRLM_IRDB_AUTOLOOKUP_RESULT_SUCCESS); } else { XLOGD_ERROR("edid dev type invalid"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_EDID, CTRLM_IRDB_AUTOLOOKUP_RESULT_DEV_TYPE_INVALID); } } else { XLOGD_ERROR("no codes for edid data"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_EDID, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_CODES_FOR_SOURCE); } } else { XLOGD_ERROR("Failed getting codes by edid"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_EDID, CTRLM_IRDB_AUTOLOOKUP_RESULT_FAILED); } } else { XLOGD_ERROR("No EDID data"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_EDID, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_SOURCE_DATA); } } else { XLOGD_ERROR("display_settings is NULL"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_EDID, CTRLM_IRDB_AUTOLOOKUP_RESULT_NULL); } if(g_irdb.cec_source) { // Check CEC data @@ -465,21 +505,27 @@ bool ctrlm_irdb_interface_t::get_ir_codes_by_autolookup(ctrlm_autolookup_ranked_ if(type != CTRLM_IRDB_DEV_TYPE_INVALID) { codes[type].insert(codes[type].end(), ir_codes.begin(), ir_codes.end()); ret = true; + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_SUCCESS); } else { XLOGD_ERROR("cec dev type invalid"); + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_DEV_TYPE_INVALID); } } else { XLOGD_WARN("no code for cec device <%s>", itr.osd.c_str()); + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_CODES_FOR_SOURCE); } } else { XLOGD_WARN("Failed to get codes for cec device <%s>", itr.osd.c_str()); + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_FAILED); } } } else { XLOGD_ERROR("No CEC device data"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_SOURCE_DATA); } } else { XLOGD_ERROR("cec is NULL"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_NULL); } } else { if(g_irdb.av_input) { @@ -495,21 +541,27 @@ bool ctrlm_irdb_interface_t::get_ir_codes_by_autolookup(ctrlm_autolookup_ranked_ if(type != CTRLM_IRDB_DEV_TYPE_INVALID) { codes[type].insert(codes[type].end(), ir_codes.begin(), ir_codes.end()); ret = true; + t2_info.emplace_back(itr.first, CTRLM_IRDB_AUTOLOOKUP_SOURCE_INFOFRAME, CTRLM_IRDB_AUTOLOOKUP_RESULT_SUCCESS); } else { XLOGD_ERROR("port %d infoframe dev type invalid", itr.first); + t2_info.emplace_back(itr.first, CTRLM_IRDB_AUTOLOOKUP_SOURCE_INFOFRAME, CTRLM_IRDB_AUTOLOOKUP_RESULT_DEV_TYPE_INVALID); } } else { XLOGD_WARN("no code for port %d infoframe", itr.first); + t2_info.emplace_back(itr.first, CTRLM_IRDB_AUTOLOOKUP_SOURCE_INFOFRAME, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_CODES_FOR_SOURCE); } } else { XLOGD_WARN("Failed to get codes for port %d infoframe", itr.first); + t2_info.emplace_back(itr.first, CTRLM_IRDB_AUTOLOOKUP_SOURCE_INFOFRAME, CTRLM_IRDB_AUTOLOOKUP_RESULT_FAILED); } } else { XLOGD_WARN("no infoframe for port %d", itr.first); + t2_info.emplace_back(itr.first, CTRLM_IRDB_AUTOLOOKUP_SOURCE_INFOFRAME, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_SOURCE_DATA); } } } else { XLOGD_ERROR("hdmi is NULL"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_INFOFRAME, CTRLM_IRDB_AUTOLOOKUP_RESULT_NULL); } if(g_irdb.cec_sink) { // Check CEC data @@ -524,21 +576,27 @@ bool ctrlm_irdb_interface_t::get_ir_codes_by_autolookup(ctrlm_autolookup_ranked_ if(type != CTRLM_IRDB_DEV_TYPE_INVALID) { codes[type].insert(codes[type].end(), ir_codes.begin(), ir_codes.end()); ret = true; + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_SUCCESS); } else { XLOGD_ERROR("cec dev type invalid"); + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_DEV_TYPE_INVALID); } } else { XLOGD_WARN("no code for cec device <%s>", itr.osd.c_str()); + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_CODES_FOR_SOURCE); } } else { XLOGD_WARN("Failed to get codes for cec device <%s>", itr.osd.c_str()); + t2_info.emplace_back(itr.port, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_FAILED); } } } else { XLOGD_ERROR("No CEC device data"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_NO_SOURCE_DATA); } } else { - XLOGD_ERROR("cec_sink is NULL"); + XLOGD_ERROR("cec is NULL"); + t2_info.emplace_back(-1, CTRLM_IRDB_AUTOLOOKUP_SOURCE_CEC, CTRLM_IRDB_AUTOLOOKUP_RESULT_NULL); } } #endif @@ -557,6 +615,30 @@ bool ctrlm_irdb_interface_t::get_ir_codes_by_autolookup(ctrlm_autolookup_ranked_ codes[CTRLM_IRDB_DEV_TYPE_AVR].erase( unique( codes[CTRLM_IRDB_DEV_TYPE_AVR].begin(), codes[CTRLM_IRDB_DEV_TYPE_AVR].end() ), codes[CTRLM_IRDB_DEV_TYPE_AVR].end() ); } +#ifdef TELEMETRY_SUPPORT + size_t tv_count = codes.count(CTRLM_IRDB_DEV_TYPE_TV) ? codes.at(CTRLM_IRDB_DEV_TYPE_TV).size() : 0; + size_t avr_count = codes.count(CTRLM_IRDB_DEV_TYPE_AVR) ? codes.at(CTRLM_IRDB_DEV_TYPE_AVR).size() : 0; + std::stringstream ss; + bool first = true; + for (const auto &entry : t2_info) { + if (!first) { + ss << ","; + } + ss << entry.port << "," << static_cast(entry.source) << "," << static_cast(entry.result); + first = false; + } + std::string results = ss.str(); + ss.str(""); + ss << "[\"" << t2_vendor_info.name << "\"" + << ",0x" << std::hex << std::setfill('0') << std::setw(2) << (int)t2_vendor_info.rcu_support_bitmask + << "," << std::dec << (int)m_platform_tv + << "," << tv_count + << "," << avr_count + << "," << (results.empty() ? "\"\"" : results) << "]"; + ctrlm_telemetry_event_t ev(MARKER_IRDB_AUTOLOOKUP_RESULT, ss.str()); + ev.event(); +#endif + return(ret); } diff --git a/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp b/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp index 48b7c29a..e58f7a64 100644 --- a/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp +++ b/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp @@ -23,6 +23,7 @@ #include "ctrlm_log.h" #include "json_config.h" #include "ctrlm_utils.h" +#include "ctrlm_telemetry_event.h" namespace { constexpr char const* NET_TYPE = "netType"; @@ -152,6 +153,17 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::get_manufacturers(void *arg) { } } +#ifdef TELEMETRY_SUPPORT + ctrlm_irdb_vendor_info_t vendor_info{}; + if (irdb) { + irdb->get_vendor_info(vendor_info); + } + char t2_buf[128]; + snprintf(t2_buf, sizeof(t2_buf), "[\"%s\",0x%02X,\"%s\",%d]", vendor_info.name.c_str(), vendor_info.rcu_support_bitmask, CTRLM_MAIN_IARM_CALL_IR_MANUFACTURERS, (int)success); + ctrlm_telemetry_event_t ev(MARKER_IRDB_MANUAL_RESULT, t2_buf); + ev.event(); +#endif + // Assemble the return json_object_set_new(ret, SUCCESS, json_boolean(success)); if(success) { @@ -226,6 +238,17 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::get_models(void *arg) { } } +#ifdef TELEMETRY_SUPPORT + ctrlm_irdb_vendor_info_t vendor_info{}; + if(irdb) { + irdb->get_vendor_info(vendor_info); + } + char t2_buf[128]; + snprintf(t2_buf, sizeof(t2_buf), "[\"%s\",0x%02X,\"%s\",%d]", vendor_info.name.c_str(), vendor_info.rcu_support_bitmask, CTRLM_MAIN_IARM_CALL_IR_MODELS, (int)success); + ctrlm_telemetry_event_t ev(MARKER_IRDB_MANUAL_RESULT, t2_buf); + ev.event(); +#endif + // Assemble the return json_object_set_new(ret, SUCCESS, json_boolean(success)); if(success) { @@ -366,6 +389,17 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::get_irdb_entry_ids(void *arg) { } } +#ifdef TELEMETRY_SUPPORT + ctrlm_irdb_vendor_info_t vendor_info{}; + if (irdb) { + irdb->get_vendor_info(vendor_info); + } + char t2_buf[128]; + snprintf(t2_buf, sizeof(t2_buf), "[\"%s\",0x%02X,\"%s\",%d]", vendor_info.name.c_str(), vendor_info.rcu_support_bitmask, CTRLM_MAIN_IARM_CALL_IR_CODES, (int)success); + ctrlm_telemetry_event_t ev(MARKER_IRDB_MANUAL_RESULT, t2_buf); + ev.event(); +#endif + // Assemble the return json_object_set_new(ret, SUCCESS, json_boolean(success)); if(success) { diff --git a/src/telemetry/ctrlm_telemetry_markers.h b/src/telemetry/ctrlm_telemetry_markers.h index 446bec55..e9ba968b 100644 --- a/src/telemetry/ctrlm_telemetry_markers.h +++ b/src/telemetry/ctrlm_telemetry_markers.h @@ -153,6 +153,18 @@ // // End RCU Markers + +// IRDB Markers +// + +// All _accum markers are aggregated by the T2 platform within each reporting window. +#define MARKER_IRDB_AUTOLOOKUP_RESULT "ctrlm.irdb.autolookup.result_accum" // format: ,,,,, +#define MARKER_IRDB_MANUAL_RESULT "ctrlm.irdb.manual.result_accum" // format: ,,, +#define MARKER_IRDB_PROGRAM_RESULT "ctrlm.irdb.program.result_accum" // format: ,,,, +#define MARKER_IRDB_VENDOR_SET "ctrlm.irdb.vendor_set_accum" // format: ,,,, + +// +// End IRDB Markers // #endif diff --git a/src/thunder/plugins/ctrlm_thunder_plugin_display_settings.cpp b/src/thunder/plugins/ctrlm_thunder_plugin_display_settings.cpp index 662af928..b11001e6 100644 --- a/src/thunder/plugins/ctrlm_thunder_plugin_display_settings.cpp +++ b/src/thunder/plugins/ctrlm_thunder_plugin_display_settings.cpp @@ -118,7 +118,7 @@ bool ctrlm_thunder_plugin_display_settings_t::_get_edid() { } } ret = true; - XLOGD_DEBUG("EDID data <%d> bytes: \n<%s>", edid_size, ss.str().c_str()); + XLOGD_INFO("EDID data <%d> bytes: \n<%s>", edid_size, ss.str().c_str()); } else { XLOGD_ERROR("Failed to decode EDID base64!"); } From a449407540eec21e4da8895afeba464c2b40abc2 Mon Sep 17 00:00:00 2001 From: Gene Gallagher <129112619+egalla204@users.noreply.github.com> Date: Mon, 8 Jun 2026 19:31:56 -0400 Subject: [PATCH 11/15] RDKEMW-13898: prevent simultaneous irdb codes from different vendors (#220) --- src/ble/ctrlm_ble_network.cpp | 21 ++++++++++- src/database/ctrlm_database.cpp | 48 +++++++++++++++++++++---- src/database/ctrlm_database.h | 8 ++--- src/network/ctrlm_ir_rf_db.cpp | 62 ++++++++++++++++++++++++++++++--- src/network/ctrlm_ir_rf_db.h | 31 ++++++++++++++++- 5 files changed, 153 insertions(+), 17 deletions(-) diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index 480d900a..f5c316b4 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -806,10 +806,29 @@ void ctrlm_obj_network_ble_t::req_process_program_ir_codes(void *data, int size) } else { if(dqm->ir_codes) { + if (dqm->ir_codes->type == CTRLM_IRDB_DEV_TYPE_TV && (ir_rf_database_.get_avr_ir_vendor_id() != 0 && ir_rf_database_.get_avr_ir_vendor_id() != dqm->vendor_info.rcu_support_bitmask) ) { + // if we are programming TV codes but the previous AVR codes are from a different IRDB vendor, then clear out the AVR codes. + // the remote cannot send different codes from different IRDB vendors at the same time. + XLOGD_INFO("Programming TV codes from vendor %s(0x%X), but currently have AVR codes from %s(0x%X). Clearing AVR codes.", + dqm->vendor_info.name.c_str(), (unsigned int)dqm->vendor_info.rcu_support_bitmask, + ir_rf_database_.get_avr_ir_vendor_name().c_str(), (unsigned int)ir_rf_database_.get_avr_ir_vendor_id()); + + ir_rf_database_.clear_avr_ir_codes(); + + } else if (dqm->ir_codes->type == CTRLM_IRDB_DEV_TYPE_AVR && (ir_rf_database_.get_tv_ir_vendor_id() != 0 && ir_rf_database_.get_tv_ir_vendor_id() != dqm->vendor_info.rcu_support_bitmask) ) { + // if we are programming AVR codes but the previous TV codes are from a different IRDB vendor, then clear out the TV codes. + // the remote cannot send different codes from different IRDB vendors at the same time. + XLOGD_INFO("Programming AVR codes from vendor %s(0x%X), but currently have TV codes from %s(0x%X). Clearing TV codes.", + dqm->vendor_info.name.c_str(), (unsigned int)dqm->vendor_info.rcu_support_bitmask, + ir_rf_database_.get_tv_ir_vendor_name().c_str(), (unsigned int)ir_rf_database_.get_tv_ir_vendor_id()); + + ir_rf_database_.clear_tv_ir_codes(); + } + std::map> ir_codes; // First add IR Codes to the IR RF Database (this contains all of the logic for maintaining TV vs AVR codes) - ir_rf_database_.add_irdb_codes(dqm->ir_codes); + ir_rf_database_.add_irdb_codes(dqm->ir_codes, dqm->vendor_info.rcu_support_bitmask, dqm->vendor_info.name); XLOGD_INFO("\n%s", this->ir_rf_database_.to_string(false).c_str()); XLOGD_DEBUG("\n%s", this->ir_rf_database_.to_string(true).c_str()); // Now get the IR codes for the BLE IR slots diff --git a/src/database/ctrlm_database.cpp b/src/database/ctrlm_database.cpp index e28e4432..2e9e6e5c 100644 --- a/src/database/ctrlm_database.cpp +++ b/src/database/ctrlm_database.cpp @@ -71,7 +71,11 @@ using namespace std; #define CTRLM_DB_IR_COMMAND_REPEATS "ir_command_repeats" #define CTRLM_DB_DEVICE_UPDATE_SESSION_STATE "du_session_state" #define CTRLM_DB_TV_IR_CODE_ID "tv_ir_code_id" +#define CTRLM_DB_TV_IR_VENDOR_ID "tv_ir_vendor_id" +#define CTRLM_DB_TV_IR_VENDOR_NAME "tv_ir_vendor_name" #define CTRLM_DB_AVR_IR_CODE_ID "avr_ir_code_id" +#define CTRLM_DB_AVR_IR_VENDOR_ID "avr_ir_vendor_id" +#define CTRLM_DB_AVR_IR_VENDOR_NAME "avr_ir_vendor_name" #define CTRLM_DB_TABLE_VOICE "ctrlm_voice" @@ -706,11 +710,13 @@ void ctrlm_db_ir_command_repeats_read(guchar **data, guint32 *length) { ctrlm_db_read_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_IR_COMMAND_REPEATS, data, length); } -void ctrlm_db_tv_ir_code_id_write(const std::string id) { +void ctrlm_db_tv_ir_code_id_write(const std::string id, unsigned char vendor_id, const std::string vendor_name) { ctrlm_db_write_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_TV_IR_CODE_ID, (const guchar*) id.c_str(), id.length()); + ctrlm_db_write_uint64(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_TV_IR_VENDOR_ID, vendor_id); + ctrlm_db_write_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_TV_IR_VENDOR_NAME, (const guchar*) vendor_name.c_str(), vendor_name.length()); } -void ctrlm_db_tv_ir_code_id_read(std::string &id) { +void ctrlm_db_tv_ir_code_id_read(std::string &id, unsigned char &vendor_id, std::string &vendor_name) { guchar *data = NULL; guint32 length = 0; ctrlm_db_read_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_TV_IR_CODE_ID, &data, &length); @@ -718,15 +724,31 @@ void ctrlm_db_tv_ir_code_id_read(std::string &id) { id.assign((char *)data, length); ctrlm_db_free(data); } else { - XLOGD_WARN("Failed to load tv_ir_code_id from db"); + XLOGD_WARN("Failed to load %s from db", CTRLM_DB_TV_IR_CODE_ID); + } + + guint64 vendor_id_64 = 0; + ctrlm_db_read_uint64(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_TV_IR_VENDOR_ID, (sqlite_uint64*)&vendor_id_64); + vendor_id = vendor_id_64; + + data = NULL; + length = 0; + ctrlm_db_read_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_TV_IR_VENDOR_NAME, &data, &length); + if(NULL != data) { + vendor_name.assign((char *)data, length); + ctrlm_db_free(data); + } else { + XLOGD_WARN("Failed to load %s from db", CTRLM_DB_TV_IR_VENDOR_NAME); } } -void ctrlm_db_avr_ir_code_id_write(const std::string id) { +void ctrlm_db_avr_ir_code_id_write(const std::string id, unsigned char vendor_id, const std::string vendor_name) { ctrlm_db_write_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_AVR_IR_CODE_ID, (const guchar*) id.c_str(), id.length()); + ctrlm_db_write_uint64(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_AVR_IR_VENDOR_ID, vendor_id); + ctrlm_db_write_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_AVR_IR_VENDOR_NAME, (const guchar*) vendor_name.c_str(), vendor_name.length()); } -void ctrlm_db_avr_ir_code_id_read(std::string &id) { +void ctrlm_db_avr_ir_code_id_read(std::string &id, unsigned char &vendor_id, std::string &vendor_name) { guchar *data = NULL; guint32 length = 0; ctrlm_db_read_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_AVR_IR_CODE_ID, &data, &length); @@ -734,7 +756,21 @@ void ctrlm_db_avr_ir_code_id_read(std::string &id) { id.assign((char *)data, length); ctrlm_db_free(data); } else { - XLOGD_WARN("Failed to load avr_ir_code_id from db"); + XLOGD_WARN("Failed to load %s from db", CTRLM_DB_AVR_IR_CODE_ID); + } + + guint64 vendor_id_64 = 0; + ctrlm_db_read_uint64(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_AVR_IR_VENDOR_ID, (sqlite_uint64*)&vendor_id_64); + vendor_id = vendor_id_64; + + data = NULL; + length = 0; + ctrlm_db_read_blob(CTRLM_DB_TABLE_CTRLMGR, CTRLM_DB_AVR_IR_VENDOR_NAME, &data, &length); + if(NULL != data) { + vendor_name.assign((char *)data, length); + ctrlm_db_free(data); + } else { + XLOGD_WARN("Failed to load %s from db", CTRLM_DB_AVR_IR_VENDOR_NAME); } } diff --git a/src/database/ctrlm_database.h b/src/database/ctrlm_database.h index 4716b075..f842c71f 100644 --- a/src/database/ctrlm_database.h +++ b/src/database/ctrlm_database.h @@ -77,10 +77,10 @@ void ctrlm_db_chime_volume_write(guchar *data, guint32 length); void ctrlm_db_chime_volume_read(guchar **data, guint32 *length); void ctrlm_db_ir_command_repeats_write(guchar *data, guint32 length); void ctrlm_db_ir_command_repeats_read(guchar **data, guint32 *length); -void ctrlm_db_tv_ir_code_id_write(const std::string id); -void ctrlm_db_tv_ir_code_id_read(std::string &id); -void ctrlm_db_avr_ir_code_id_write(const std::string id); -void ctrlm_db_avr_ir_code_id_read(std::string &id); +void ctrlm_db_tv_ir_code_id_write(const std::string id, unsigned char vendor_id, const std::string vendor_name); +void ctrlm_db_tv_ir_code_id_read(std::string &id, unsigned char &vendor_id, std::string &vendor_name); +void ctrlm_db_avr_ir_code_id_write(const std::string id, unsigned char vendor_id, const std::string vendor_name); +void ctrlm_db_avr_ir_code_id_read(std::string &id, unsigned char &vendor_id, std::string &vendor_name); void ctrlm_db_rf4ce_networks_list(std::vector *network_ids); void ctrlm_db_rf4ce_controllers_list(ctrlm_network_id_t network_id, std::vector *controller_ids); diff --git a/src/network/ctrlm_ir_rf_db.cpp b/src/network/ctrlm_ir_rf_db.cpp index 126e57cb..6c821f5a 100644 --- a/src/network/ctrlm_ir_rf_db.cpp +++ b/src/network/ctrlm_ir_rf_db.cpp @@ -42,7 +42,11 @@ ctrlm_ir_rf_db_t::ctrlm_ir_rf_db_t(bool power_toggle_favor_tv, bool power_discre this->power_toggle_favor_tv = power_toggle_favor_tv; this->power_discrete_favor_tv = power_discrete_favor_tv; this->tv_ir_code_id_ = "0"; + this->tv_ir_vendor_id_ = 0; + this->tv_ir_vendor_name_ = "INVALID"; this->avr_ir_code_id_ = "0"; + this->avr_ir_vendor_id_ = 0; + this->avr_ir_vendor_name_ = "INVALID"; } ctrlm_ir_rf_db_t::~ctrlm_ir_rf_db_t() { @@ -174,17 +178,21 @@ ctrlm_key_code_t to_ctrlm_keycode(ctrlm_irdb_key_code_t irdb_code) { } } -bool ctrlm_ir_rf_db_t::add_irdb_codes(ctrlm_irdb_ir_code_set_t *ir_codes) { +bool ctrlm_ir_rf_db_t::add_irdb_codes(ctrlm_irdb_ir_code_set_t *ir_codes, unsigned char ir_vendor_id, const std::string &ir_vendor_name) { bool ret = false; if(ir_codes) { ctrlm_ir_rf_db_dev_type_t type = ctrlm_ir_rf_db_entry_t::type_from_irdb(ir_codes->type); switch(type) { case CTRLM_IR_RF_DB_DEV_TV: { this->tv_ir_code_id_ = ir_codes->id; + this->tv_ir_vendor_id_ = ir_vendor_id; + this->tv_ir_vendor_name_ = ir_vendor_name; break; } case CTRLM_IR_RF_DB_DEV_AVR: { this->avr_ir_code_id_ = ir_codes->id; + this->avr_ir_vendor_id_ = ir_vendor_id; + this->avr_ir_vendor_name_ = ir_vendor_name; break; } default: { @@ -205,52 +213,75 @@ bool ctrlm_ir_rf_db_t::add_irdb_codes(ctrlm_irdb_ir_code_set_t *ir_codes) { } void ctrlm_ir_rf_db_t::clear_tv_ir_codes() { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_TV_POWER_ON); this->remove_entry(CTRLM_KEY_CODE_TV_POWER_ON); + + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_TV_POWER_OFF); this->remove_entry(CTRLM_KEY_CODE_TV_POWER_OFF); + + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_TV_POWER); this->remove_entry(CTRLM_KEY_CODE_TV_POWER); + if(this->has_entry(CTRLM_KEY_CODE_VOL_UP)) { if(this->ir_rf_db[CTRLM_KEY_CODE_VOL_UP]->get_type() == CTRLM_IR_RF_DB_DEV_TV) { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_VOL_UP); this->remove_entry(CTRLM_KEY_CODE_VOL_UP); } } if(this->has_entry(CTRLM_KEY_CODE_VOL_DOWN)) { if(this->ir_rf_db[CTRLM_KEY_CODE_VOL_DOWN]->get_type() == CTRLM_IR_RF_DB_DEV_TV) { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_VOL_DOWN); this->remove_entry(CTRLM_KEY_CODE_VOL_DOWN); } } if(this->has_entry(CTRLM_KEY_CODE_MUTE)) { if(this->ir_rf_db[CTRLM_KEY_CODE_MUTE]->get_type() == CTRLM_IR_RF_DB_DEV_TV) { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_MUTE); this->remove_entry(CTRLM_KEY_CODE_MUTE); } } + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_INPUT_SELECT); this->remove_entry(CTRLM_KEY_CODE_INPUT_SELECT); this->fix_common_slots_and_ir_flags(); this->tv_ir_code_id_ = "0"; + this->tv_ir_vendor_id_ = 0; + this->tv_ir_vendor_name_ = "INVALID"; } void ctrlm_ir_rf_db_t::clear_avr_ir_codes() { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_AVR_POWER_ON); this->remove_entry(CTRLM_KEY_CODE_AVR_POWER_ON); + + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_AVR_POWER_OFF); this->remove_entry(CTRLM_KEY_CODE_AVR_POWER_OFF); + + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_AVR_POWER_TOGGLE); this->remove_entry(CTRLM_KEY_CODE_AVR_POWER_TOGGLE); + if(this->has_entry(CTRLM_KEY_CODE_VOL_UP)) { if(this->ir_rf_db[CTRLM_KEY_CODE_VOL_UP]->get_type() == CTRLM_IR_RF_DB_DEV_AVR) { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_VOL_UP); this->remove_entry(CTRLM_KEY_CODE_VOL_UP); } } if(this->has_entry(CTRLM_KEY_CODE_VOL_DOWN)) { if(this->ir_rf_db[CTRLM_KEY_CODE_VOL_DOWN]->get_type() == CTRLM_IR_RF_DB_DEV_AVR) { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_VOL_DOWN); this->remove_entry(CTRLM_KEY_CODE_VOL_DOWN); } } if(this->has_entry(CTRLM_KEY_CODE_MUTE)) { if(this->ir_rf_db[CTRLM_KEY_CODE_MUTE]->get_type() == CTRLM_IR_RF_DB_DEV_AVR) { + ctrlm_db_ir_rf_database_delete(CTRLM_KEY_CODE_MUTE); this->remove_entry(CTRLM_KEY_CODE_MUTE); } } this->fix_common_slots_and_ir_flags(); this->avr_ir_code_id_ = "0"; + this->avr_ir_vendor_id_ = 0; + this->avr_ir_vendor_name_ = "INVALID"; } void ctrlm_ir_rf_db_t::clear_ir_codes() { @@ -259,7 +290,11 @@ void ctrlm_ir_rf_db_t::clear_ir_codes() { this->remove_entry(itr->first); } this->tv_ir_code_id_ = "0"; + this->tv_ir_vendor_id_ = 0; + this->tv_ir_vendor_name_ = "INVALID"; this->avr_ir_code_id_ = "0"; + this->avr_ir_vendor_id_ = 0; + this->avr_ir_vendor_name_ = "INVALID"; } ctrlm_ir_rf_db_entry_t *ctrlm_ir_rf_db_t::get_ir_code(ctrlm_key_code_t key) { @@ -274,7 +309,9 @@ std::string ctrlm_ir_rf_db_t::to_string(bool debug) const { std::stringstream ss; ss << "IR RF Database: "<< std::endl; ss << "\tTV IR Code ID <" << tv_ir_code_id_ << ">" << std::endl; + ss << "\tTV IR Vendor Info <" << tv_ir_vendor_name_ << ": " << (unsigned int)tv_ir_vendor_id_ << ">" << std::endl; ss << "\tAVR IR Code ID <" << avr_ir_code_id_ << ">" << std::endl; + ss << "\tAVR IR Vendor Info <" << avr_ir_vendor_name_ << ": " << (unsigned int)avr_ir_vendor_id_ << ">" << std::endl; for(auto itr = this->ir_rf_db.begin(); itr != this->ir_rf_db.end(); itr++) { if(itr->second != NULL) { ss << "\tKeySlot <" << ctrlm_key_code_str(itr->first) << ">, " << itr->second->to_string(debug) << std::endl; @@ -373,16 +410,16 @@ void ctrlm_ir_rf_db_t::load_db() { this->replace_entry(itr->first, entry); } } - ctrlm_db_tv_ir_code_id_read(tv_ir_code_id_); - ctrlm_db_avr_ir_code_id_read(avr_ir_code_id_); + ctrlm_db_tv_ir_code_id_read(tv_ir_code_id_, tv_ir_vendor_id_, tv_ir_vendor_name_); + ctrlm_db_avr_ir_code_id_read(avr_ir_code_id_, avr_ir_vendor_id_, avr_ir_vendor_name_); } bool ctrlm_ir_rf_db_t::store_db() { for(auto itr = this->ir_rf_db.begin(); itr != this->ir_rf_db.end(); itr++) { this->store_entry(itr->first); } - ctrlm_db_tv_ir_code_id_write(tv_ir_code_id_); - ctrlm_db_avr_ir_code_id_write(avr_ir_code_id_); + ctrlm_db_tv_ir_code_id_write(tv_ir_code_id_, tv_ir_vendor_id_, tv_ir_vendor_name_); + ctrlm_db_avr_ir_code_id_write(avr_ir_code_id_, avr_ir_vendor_id_, avr_ir_vendor_name_); return(true); // TODO, maybe change to void } @@ -449,7 +486,22 @@ std::string ctrlm_ir_rf_db_t::get_tv_ir_code_id() { return tv_ir_code_id_; } +unsigned char ctrlm_ir_rf_db_t::get_tv_ir_vendor_id() { + return tv_ir_vendor_id_; +} + +std::string ctrlm_ir_rf_db_t::get_tv_ir_vendor_name() { + return tv_ir_vendor_name_; +} + std::string ctrlm_ir_rf_db_t::get_avr_ir_code_id() { return avr_ir_code_id_; } +unsigned char ctrlm_ir_rf_db_t::get_avr_ir_vendor_id() { + return avr_ir_vendor_id_; +} + +std::string ctrlm_ir_rf_db_t::get_avr_ir_vendor_name() { + return avr_ir_vendor_name_; +} diff --git a/src/network/ctrlm_ir_rf_db.h b/src/network/ctrlm_ir_rf_db.h index 439bf1e0..d383e3d5 100644 --- a/src/network/ctrlm_ir_rf_db.h +++ b/src/network/ctrlm_ir_rf_db.h @@ -60,7 +60,7 @@ class ctrlm_ir_rf_db_t { * @param ir_codes A keymap supplied from the CTRLM IRDB component * @reutrn True if the entries were added to the IRRF Database, False otherwise. */ - bool add_irdb_codes(ctrlm_irdb_ir_code_set_t *ir_codes); + bool add_irdb_codes(ctrlm_irdb_ir_code_set_t *ir_codes, unsigned char ir_vendor_id = 0, const std::string &ir_vendor_name = ""); /** * Function to clear all TV IR codes stored in the IR RF Database @@ -120,11 +120,36 @@ class ctrlm_ir_rf_db_t { */ std::string get_tv_ir_code_id(); + /** + * Function used to get the TV IR vendor ID in the ControlMgr Database. + * @return the TV IR vendor ID as unsigned char + */ + unsigned char get_tv_ir_vendor_id(); + + /** + * Function used to get the TV IR vendor name in the ControlMgr Database. + * @return the TV IR vendor name in string form. + */ + std::string get_tv_ir_vendor_name(); + /** * Function used to get the AVR IR code ID in the ControlMgr Database. * @return the AVR IR code ID in string form. */ std::string get_avr_ir_code_id(); + + /** + * Function used to get the AVR IR vendor ID in the ControlMgr Database. + * @return the AVR IR vendor ID as unsigned char + */ + unsigned char get_avr_ir_vendor_id(); + + /** + * Function used to get the AVR IR vendor name in the ControlMgr Database. + * @return the AVR IR vendor name in string form. + */ + std::string get_avr_ir_vendor_name(); + private: /** @@ -157,7 +182,11 @@ class ctrlm_ir_rf_db_t { bool power_toggle_favor_tv; bool power_discrete_favor_tv; std::string tv_ir_code_id_; + unsigned char tv_ir_vendor_id_; + std::string tv_ir_vendor_name_; std::string avr_ir_code_id_; + unsigned char avr_ir_vendor_id_; + std::string avr_ir_vendor_name_; }; From 9667bf74d230cf610f5fa453820aef45ec894d38 Mon Sep 17 00:00:00 2001 From: Gene Gallagher Date: Mon, 8 Jun 2026 20:02:33 -0400 Subject: [PATCH 12/15] 1.1.17 release changelog updates --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27ed797d..7b2b696b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,23 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.1.17](https://github.com/rdkcentral/control/compare/1.1.16...1.1.17) + +> 8 June 2026 + +- RDKEMW-13898: prevent simultaneous irdb codes from different vendors [`#220`](https://github.com/rdkcentral/control/pull/220) +- RDKEMW-16966: T2 Events for IRDB Programming [`#239`](https://github.com/rdkcentral/control/pull/239) +- RDKEMW-18979: onStatus event to contain RCU data from all networks [`#238`](https://github.com/rdkcentral/control/pull/238) +- RDKEMW-13988: add new buttons for interim remote 2.0 support [`#234`](https://github.com/rdkcentral/control/pull/234) +- RDKEMW-4433: local server build option [`#57`](https://github.com/rdkcentral/control/pull/57) +- RDKEMW-15135: T2 RCU pairing [`#189`](https://github.com/rdkcentral/control/pull/189) +- RDKEMW-16814: reduce ble status prints and events [`#235`](https://github.com/rdkcentral/control/pull/235) +- RDKEMW-12856: throw early error when IRDB provides empty waveforms [`#236`](https://github.com/rdkcentral/control/pull/236) +- RDKEMW-14906 : VAD detector in Control Manager/Voice SDK [`#186`](https://github.com/rdkcentral/control/pull/186) +- RDKEMW-17620 : ctrlm rfc value groups not updating [`#219`](https://github.com/rdkcentral/control/pull/219) +- RDKEMW-18299 : only register SIGQUIT when process is interactive [`#229`](https://github.com/rdkcentral/control/pull/229) + + #### [1.1.16](https://github.com/rdkcentral/control/compare/1.1.15...1.1.16) > 8 May 2026 From 577091351e263fd8fcc67aeaf096714cbb6078bd Mon Sep 17 00:00:00 2001 From: Gene Gallagher <129112619+egalla204@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:03:52 -0400 Subject: [PATCH 13/15] Revert "RDKEMW-14906 : VAD detector in Control Manager/Voice SDK (#186)" (#245) This reverts commit 5a6dbfbf88cdb7d62128a93b3990bdb595bc0141. --- include/ctrlm_ipc.h | 3 +- include/ctrlm_ipc_voice.h | 11 -- src/ctrlm_config_default.json | 4 +- src/telemetry/ctrlm_telemetry_markers.h | 16 +-- src/voice/ctrlm_voice_obj.cpp | 115 ++++++------------ src/voice/ctrlm_voice_obj.h | 73 ++++++----- src/voice/ctrlm_voice_obj_generic.cpp | 102 +++++++--------- src/voice/ipc/ctrlm_voice_ipc.h | 3 +- src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp | 12 -- .../ipc/ctrlm_voice_ipc_iarm_thunder.cpp | 16 --- .../ctrlm_voice_telemetry_events.cpp | 17 +-- .../telemetry/ctrlm_voice_telemetry_events.h | 6 +- 12 files changed, 127 insertions(+), 251 deletions(-) diff --git a/include/ctrlm_ipc.h b/include/ctrlm_ipc.h index 7d87d12c..32d0dea1 100644 --- a/include/ctrlm_ipc.h +++ b/include/ctrlm_ipc.h @@ -302,8 +302,7 @@ typedef enum { CTRLM_RCU_IARM_EVENT_RF4CE_PAIRING_WINDOW_TIMEOUT = 35, ///< Indicates that a battery milestone event occured CTRLM_RCU_IARM_EVENT_FIRMWARE_UPDATE_PROGRESS = 36, ///< Generated when an milestone is reached for remote firmware upgrade CTRLM_RCU_IARM_EVENT_VALIDATION_STATUS = 37, ///< Generated when the validation status changes - CTRLM_VOICE_IARM_EVENT_SESSION_SILENT = 38, ///< Voice session was silent (no speech detected) - CTRLM_MAIN_IARM_EVENT_MAX = 39 ///< Placeholder for the last event (used in registration) + CTRLM_MAIN_IARM_EVENT_MAX = 38 ///< Placeholder for the last event (used in registration) } ctrlm_main_iarm_event_t; /// @brief Remote Control Key Status diff --git a/include/ctrlm_ipc_voice.h b/include/ctrlm_ipc_voice.h index 4a912cad..ed96c951 100644 --- a/include/ctrlm_ipc_voice.h +++ b/include/ctrlm_ipc_voice.h @@ -281,16 +281,6 @@ typedef struct { long return_code_internal; ///< Internally generated return code } ctrlm_voice_iarm_event_session_short_t; -typedef struct { - unsigned char api_revision; ///< The revision of this API. - ctrlm_network_id_t network_id; ///< Identifier of network on which the controller is bound - ctrlm_network_type_t network_type; ///< Type of network on which the controller is bound - ctrlm_controller_id_t controller_id; ///< A unique identifier of the remote - unsigned long session_id; ///< A unique id for the voice session. - ctrlm_voice_session_end_reason_t reason; ///< The reason that the voice session was silent - long return_code_internal; ///< Internally generated return code -} ctrlm_voice_iarm_event_session_silent_t; - typedef struct { unsigned char api_revision; ///< The revision of this API char media_service_url[2083]; ///< The url for the media service (null terminated string) @@ -386,7 +376,6 @@ typedef struct { /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_STATS | ctrlm_voice_iarm_event_session_stats_t * | Generated when the statistics of the voice session are available | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_ABORT | ctrlm_voice_iarm_event_session_abort_t * | Generated when a voice session is aborted (denied) | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_SHORT | ctrlm_voice_iarm_event_session_short_t * | Generated when a short voice session is detected | -/// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_SILENT | ctrlm_voice_iarm_event_session_silent_t * | Generated when a silent voice session is detected | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_MEDIA_SERVICE | ctrlm_voice_iarm_event_media_service_t * | Generated when a media service response is received | /// /// IARM events are available on a subscription basis. In order to receive an event, a client must explicitly register to receive the event by calling diff --git a/src/ctrlm_config_default.json b/src/ctrlm_config_default.json index 21aa49c6..af65c196 100755 --- a/src/ctrlm_config_default.json +++ b/src/ctrlm_config_default.json @@ -291,9 +291,7 @@ "par_voice_eos_method" : 1, "par_voice_eos_timeout" : 2500, "server_hosts" : [], - "telemetry_session_stats" : false, - "voice_activity_detection_mode" : "enabled" - + "telemetry_session_stats" : false }, "device_update" : { "dir_root" : "/srv/device_update/", diff --git a/src/telemetry/ctrlm_telemetry_markers.h b/src/telemetry/ctrlm_telemetry_markers.h index e9ba968b..f1314e0f 100644 --- a/src/telemetry/ctrlm_telemetry_markers.h +++ b/src/telemetry/ctrlm_telemetry_markers.h @@ -93,7 +93,7 @@ // The Voice Session Statistics Marker reports statistics for the voice session(s). The format of the marker is a json array of arrays with each event in the format below: // // [[event1], [event2], [event3], ...] -// [,,,,,,,,,,,,,,,,,,,,,,,,,] +// [,,,,,,,,,,,,,,,,,,,,] // // - Version of the marker format. // - Name of the device that started the session. @@ -111,19 +111,13 @@ // - lost samples. // - decoder failure count. // - sample buffer high watermark. -// - audio stream success/error code. -// - session success/error code. -// - server success/error code. +// - audio stream success/error code. +// - protocol success/error code. +// - server success/error code. // - server message. // - flag to indicate if session was successful. -// - reason why the stream ended (if available). -// - protocol return code. -// - flag to indicate if voice was detected. -// - peak confidence level. -// - peak RMS level. - #define MARKER_VOICE_SESSION_STATS "ctrlm.voice.session.stats" -#define MARKER_VOICE_SESSION_STATS_VERSION "3" +#define MARKER_VOICE_SESSION_STATS_VERSION "2" // End Voice Session Statistics diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index 7ddf515f..c81a629a 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -186,40 +186,39 @@ ctrlm_voice_t::ctrlm_voice_t() { #ifdef JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_3 this->url_hostname_pattern_add(JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_3); #endif - this->prefs.aspect_ratio = JSON_STR_VALUE_VOICE_ASPECT_RATIO; - this->prefs.guide_language = JSON_STR_VALUE_VOICE_LANGUAGE; - this->prefs.app_id_http = JSON_STR_VALUE_VOICE_APP_ID_HTTP; - this->prefs.app_id_ws = JSON_STR_VALUE_VOICE_APP_ID_WS; - this->prefs.timeout_vrex_connect = JSON_INT_VALUE_VOICE_VREX_REQUEST_TIMEOUT; - this->prefs.timeout_vrex_session = JSON_INT_VALUE_VOICE_VREX_RESPONSE_TIMEOUT; - this->prefs.timeout_stats = JSON_INT_VALUE_VOICE_TIMEOUT_STATS; - this->prefs.timeout_packet_initial = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_INITIAL; - this->prefs.timeout_packet_subsequent = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_SUBSEQUENT; - this->prefs.bitrate_minimum = JSON_INT_VALUE_VOICE_BITRATE_MINIMUM; - this->prefs.time_threshold = JSON_INT_VALUE_VOICE_TIME_THRESHOLD; - this->prefs.utterance_save = ctrlm_is_production_build() ? JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_0 : JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_1; - this->prefs.utterance_use_curtail = JSON_BOOL_VALUE_VOICE_UTTERANCE_USE_CURTAIL; - this->prefs.utterance_file_qty_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_QTY_MAX; - this->prefs.utterance_file_size_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_SIZE_MAX; - this->prefs.utterance_path = JSON_STR_VALUE_VOICE_UTTERANCE_PATH; - this->prefs.utterance_duration_min = JSON_INT_VALUE_VOICE_MINIMUM_DURATION; - this->prefs.ffv_leading_samples = JSON_INT_VALUE_VOICE_FFV_LEADING_SAMPLES; - this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(JSON_STR_VALUE_VOICE_VOICE_ACTIVITY_DETECTION_MODE); - this->prefs.force_voice_settings = JSON_BOOL_VALUE_VOICE_FORCE_VOICE_SETTINGS; - this->prefs.vrex_test_flag = JSON_BOOL_VALUE_VOICE_VREX_TEST_FLAG; - this->prefs.vrex_wuw_bypass_success_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG; - this->prefs.vrex_wuw_bypass_failure_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG; - this->prefs.force_toggle_fallback = JSON_BOOL_VALUE_VOICE_FORCE_TOGGLE_FALLBACK; - this->prefs.telemetry_session_stats = JSON_BOOL_VALUE_VOICE_TELEMETRY_SESSION_STATS; - this->prefs.par_voice_enabled = false; - this->prefs.par_voice_eos_method = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_METHOD; - this->prefs.par_voice_eos_timeout = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_TIMEOUT; + this->prefs.aspect_ratio = JSON_STR_VALUE_VOICE_ASPECT_RATIO; + this->prefs.guide_language = JSON_STR_VALUE_VOICE_LANGUAGE; + this->prefs.app_id_http = JSON_STR_VALUE_VOICE_APP_ID_HTTP; + this->prefs.app_id_ws = JSON_STR_VALUE_VOICE_APP_ID_WS; + this->prefs.timeout_vrex_connect = JSON_INT_VALUE_VOICE_VREX_REQUEST_TIMEOUT; + this->prefs.timeout_vrex_session = JSON_INT_VALUE_VOICE_VREX_RESPONSE_TIMEOUT; + this->prefs.timeout_stats = JSON_INT_VALUE_VOICE_TIMEOUT_STATS; + this->prefs.timeout_packet_initial = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_INITIAL; + this->prefs.timeout_packet_subsequent = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_SUBSEQUENT; + this->prefs.bitrate_minimum = JSON_INT_VALUE_VOICE_BITRATE_MINIMUM; + this->prefs.time_threshold = JSON_INT_VALUE_VOICE_TIME_THRESHOLD; + this->prefs.utterance_save = ctrlm_is_production_build() ? JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_0 : JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_1; + this->prefs.utterance_use_curtail = JSON_BOOL_VALUE_VOICE_UTTERANCE_USE_CURTAIL; + this->prefs.utterance_file_qty_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_QTY_MAX; + this->prefs.utterance_file_size_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_SIZE_MAX; + this->prefs.utterance_path = JSON_STR_VALUE_VOICE_UTTERANCE_PATH; + this->prefs.utterance_duration_min = JSON_INT_VALUE_VOICE_MINIMUM_DURATION; + this->prefs.ffv_leading_samples = JSON_INT_VALUE_VOICE_FFV_LEADING_SAMPLES; + this->prefs.force_voice_settings = JSON_BOOL_VALUE_VOICE_FORCE_VOICE_SETTINGS; + this->prefs.vrex_test_flag = JSON_BOOL_VALUE_VOICE_VREX_TEST_FLAG; + this->prefs.vrex_wuw_bypass_success_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG; + this->prefs.vrex_wuw_bypass_failure_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG; + this->prefs.force_toggle_fallback = JSON_BOOL_VALUE_VOICE_FORCE_TOGGLE_FALLBACK; + this->prefs.telemetry_session_stats = JSON_BOOL_VALUE_VOICE_TELEMETRY_SESSION_STATS; + this->prefs.par_voice_enabled = false; + this->prefs.par_voice_eos_method = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_METHOD; + this->prefs.par_voice_eos_timeout = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_TIMEOUT; this->voice_params_opus_encoder_default(); - this->xrsr_opened = false; - this->voice_ipc = NULL; - this->packet_loss_threshold = JSON_INT_VALUE_VOICE_PACKET_LOSS_THRESHOLD; - this->vsdk_config = NULL; - this->nsm_voice_session = false; + this->xrsr_opened = false; + this->voice_ipc = NULL; + this->packet_loss_threshold = JSON_INT_VALUE_VOICE_PACKET_LOSS_THRESHOLD; + this->vsdk_config = NULL; + this->nsm_voice_session = false; #ifndef TELEMETRY_SUPPORT XLOGD_WARN("telemetry is not enabled"); @@ -262,7 +261,6 @@ ctrlm_voice_t::ctrlm_voice_t() { this->secure_url_required = JSON_BOOL_VALUE_VOICE_REQUIRE_SECURE_URL; XLOGD_TELEMETRY("require i_SAT <%s> i_MTLS <%s> i_secure_url <%s>", this->sat_token_required ? "YES" : "NO", this->mtls_required ? "YES" : "NO", this->secure_url_required ? "YES" : "NO"); - XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); errno_t safec_rc = memset_s(this->sat_token, sizeof(this->sat_token), 0, sizeof(this->sat_token)); ERR_CHK(safec_rc); @@ -427,7 +425,6 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_STR_NAME_VOICE_URL_SRC_MIC_TAP, this->prefs.server_url_src_mic_tap); conf.config_value_get(JSON_STR_NAME_VOICE_LANGUAGE, this->prefs.guide_language); conf.config_value_get(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min); - if(conf.config_value_get(JSON_BOOL_NAME_VOICE_ENABLE_SAT, this->sat_token_required)) { ctrlm_sm_voice_sat_enable_write(this->sat_token_required); XLOGD_TELEMETRY("require c_SAT <%s>", this->sat_token_required ? "YES" : "NO"); @@ -435,12 +432,6 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_STR_NAME_VOICE_ASPECT_RATIO, this->prefs.aspect_ratio); } - std::string voice_activity_detection_mode; - if(conf.config_value_get(JSON_STR_NAME_VOICE_VOICE_ACTIVITY_DETECTION_MODE, voice_activity_detection_mode)) { - this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(voice_activity_detection_mode); - XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); - } - std::vector obj_server_hosts; if(conf.config_value_get(JSON_ARRAY_NAME_VOICE_SERVER_HOSTS, obj_server_hosts)) { this->url_hostname_patterns(obj_server_hosts); @@ -1098,17 +1089,6 @@ void ctrlm_voice_t::voice_params_opus_encoder_default(void) { this->voice_params_opus_samples_per_packet_set(); } -xrsr_stream_voice_activity_mode_t ctrlm_voice_t::voice_activity_detection_mode_to_xrsr(const std::string &mode) { - // Configure voice activity detection parameters based on mode - if(mode == "enabled") { // Voice activity detection will be used but not enforced - return(XRSR_STREAM_VOICE_ACTIVITY_MODE_ENABLED); - } else if(mode == "enforced") { // Voice session will only proceed if voice activity is detected - return(XRSR_STREAM_VOICE_ACTIVITY_MODE_ENFORCED); - } - // Voice activity detection is disabled (or invalid mode) - return(XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED); -} - void ctrlm_voice_t::voice_params_opus_samples_per_packet_set(void) { guchar fr_dur = (this->prefs.opus_encoder_params[3] >> 4) & 0xF; switch(fr_dur) { @@ -2669,12 +2649,6 @@ void ctrlm_voice_t::voice_session_end_callback(ctrlm_voice_session_end_cb_t *ses end.result = SESSION_END_SHORT_UTTERANCE; end.reason = (int)session->end_reason_rcu; this->voice_ipc->session_end(end); - } else if(stats->session_end_reason == XRSR_SESSION_END_REASON_ERROR_AUDIO_SILENT) { - ctrlm_voice_ipc_event_session_end_t end; - end.common = session->ipc_common_data; - end.result = SESSION_END_SILENT_UTTERANCE; - end.reason = (int)session->end_reason_rcu; - this->voice_ipc->session_end(end); } else { ctrlm_voice_ipc_event_session_end_server_stats_t server_stats; ctrlm_voice_ipc_event_session_end_t end; @@ -2959,20 +2933,9 @@ void ctrlm_voice_t::voice_stream_end_callback(ctrlm_voice_stream_end_cb_t *strea } XLOGD_AUTOMATION_TELEMETRY("src <%s> Packets Lost/Total <%u/%u> %.02f%% duration <%u> ms", ctrlm_voice_device_str(session->voice_device), session->packets_lost, session->packets_lost + session->packets_processed, 100.0 * ((double)session->packets_lost / (double)(session->packets_lost + session->packets_processed)), stream_duration); #ifdef TELEMETRY_SUPPORT - uint32_t packets_total = session->packets_lost + session->packets_processed; - int32_t voice_detected = -1; - uint32_t peak_confidence = 0; - int32_t peak_rms_level = 0; - if(stats->audio_stats.vad_frames_processed > 0) { - voice_detected = (stats->audio_stats.vad_voice_detected) ? 1 : 0; - peak_confidence = (stats->audio_stats.vad_confidence_peak * 100); - peak_rms_level = stats->audio_stats.vad_rms_level_peak; - - XLOGD_INFO("VAD frames <%u> voice detected <%s> confidence peak <%u%%> rms level peak <%d dB>", stats->audio_stats.vad_frames_processed, voice_detected == 1 ? "YES" : "NO", peak_confidence, peak_rms_level); - } - if(this->prefs.telemetry_session_stats) { - session->telemetry_session_stats.update_on_stream_end(stream_duration, packets_total, session->packets_lost, packets_total * samples_per_packet, session->packets_lost * samples_per_packet, decoder_failures, 0, voice_detected, peak_confidence, peak_rms_level); + uint32_t packets_total = session->packets_lost + session->packets_processed; + session->telemetry_session_stats.update_on_stream_end(stream_duration, packets_total, session->packets_lost, packets_total * samples_per_packet, session->packets_lost * samples_per_packet, decoder_failures, 0); } #endif } else if(samples_processed > 0) { @@ -2980,7 +2943,7 @@ void ctrlm_voice_t::voice_stream_end_callback(ctrlm_voice_stream_end_cb_t *strea XLOGD_INFO("src <%s> Samples Lost/Total <%u/%u> %.02f%% buffered max <%u> duration <%u> ms", ctrlm_voice_device_str(session->voice_device), samples_lost, samples_lost + samples_processed, 100.0 * ((double)samples_lost / (double)(samples_lost + samples_processed)), samples_buffered_max, stream_duration); #ifdef TELEMETRY_SUPPORT if(this->prefs.telemetry_session_stats) { - session->telemetry_session_stats.update_on_stream_end(stream_duration, 0, 0, samples_lost + samples_processed, samples_lost, decoder_failures, samples_buffered_max, -1, 0, 0); + session->telemetry_session_stats.update_on_stream_end(stream_duration, 0, 0, samples_lost + samples_processed, samples_lost, decoder_failures, samples_buffered_max); } #endif } @@ -4115,14 +4078,6 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { routing_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_LOW_LATENCY_IPV4_FALLBACK, this->prefs.dst_params_low_latency.ipv4_fallback); routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_BACKOFF_DELAY, this->prefs.dst_params_low_latency.backoff_delay); - - std::string voice_activity_detection_mode; - if(attr.get_rfc_value(JSON_STR_NAME_VOICE_VOICE_ACTIVITY_DETECTION_MODE, voice_activity_detection_mode)) { - this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(voice_activity_detection_mode); - XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); - routing_config_updated = true; - } - std::vector obj_server_hosts; if(attr.get_rfc_value(JSON_ARRAY_NAME_VOICE_SERVER_HOSTS, obj_server_hosts)) { this->url_hostname_patterns(obj_server_hosts); diff --git a/src/voice/ctrlm_voice_obj.h b/src/voice/ctrlm_voice_obj.h index 9c0bc4be..3ec217b2 100644 --- a/src/voice/ctrlm_voice_obj.h +++ b/src/voice/ctrlm_voice_obj.h @@ -273,42 +273,41 @@ typedef struct { // End Event Callback Structures typedef struct { - std::string server_url_src_ptt; - std::string server_url_src_ff; - std::string server_url_src_mic_tap; - std::vector server_hosts; - std::string aspect_ratio; - std::string guide_language; - std::string app_id_http; - std::string app_id_ws; - unsigned long timeout_vrex_connect; - unsigned long timeout_vrex_session; - unsigned long timeout_stats; - unsigned long timeout_packet_initial; - unsigned long timeout_packet_subsequent; - guchar bitrate_minimum; - guint16 time_threshold; - bool utterance_save; - bool utterance_use_curtail; - unsigned long utterance_file_qty_max; - unsigned long utterance_file_size_max; - std::string utterance_path; - unsigned long utterance_duration_min; - unsigned long ffv_leading_samples; - xrsr_stream_voice_activity_mode_t voice_activity_detection_mode; - bool force_voice_settings; - bool vrex_test_flag; - bool vrex_wuw_bypass_success_flag; - bool vrex_wuw_bypass_failure_flag; - std::string opus_encoder_params_str; - uint8_t opus_encoder_params[CTRLM_RCU_RIB_ATTR_LEN_OPUS_ENCODING_PARAMS]; - bool force_toggle_fallback; - bool telemetry_session_stats; - xrsr_dst_params_t dst_params_standby; - xrsr_dst_params_t dst_params_low_latency; - bool par_voice_enabled; - uint8_t par_voice_eos_method; - uint16_t par_voice_eos_timeout; + std::string server_url_src_ptt; + std::string server_url_src_ff; + std::string server_url_src_mic_tap; + std::vector server_hosts; + std::string aspect_ratio; + std::string guide_language; + std::string app_id_http; + std::string app_id_ws; + unsigned long timeout_vrex_connect; + unsigned long timeout_vrex_session; + unsigned long timeout_stats; + unsigned long timeout_packet_initial; + unsigned long timeout_packet_subsequent; + guchar bitrate_minimum; + guint16 time_threshold; + bool utterance_save; + bool utterance_use_curtail; + unsigned long utterance_file_qty_max; + unsigned long utterance_file_size_max; + std::string utterance_path; + unsigned long utterance_duration_min; + unsigned long ffv_leading_samples; + bool force_voice_settings; + bool vrex_test_flag; + bool vrex_wuw_bypass_success_flag; + bool vrex_wuw_bypass_failure_flag; + std::string opus_encoder_params_str; + uint8_t opus_encoder_params[CTRLM_RCU_RIB_ATTR_LEN_OPUS_ENCODING_PARAMS]; + bool force_toggle_fallback; + bool telemetry_session_stats; + xrsr_dst_params_t dst_params_standby; + xrsr_dst_params_t dst_params_low_latency; + bool par_voice_enabled; + uint8_t par_voice_eos_method; + uint16_t par_voice_eos_timeout; } voice_session_prefss_t; typedef struct { @@ -638,8 +637,6 @@ class ctrlm_voice_t { bool voice_params_opus_encoder_validate(std::string &opus_encoder_params_str); void voice_params_opus_samples_per_packet_set(void); bool voice_params_hex_str_to_bytes(std::string hex_string, guchar *data, guint32 length); - - xrsr_stream_voice_activity_mode_t voice_activity_detection_mode_to_xrsr(const std::string &mode); bool voice_session_can_request(ctrlm_voice_device_t device); void voice_session_set_active(ctrlm_voice_device_t device); diff --git a/src/voice/ctrlm_voice_obj_generic.cpp b/src/voice/ctrlm_voice_obj_generic.cpp index 948d6039..4cb35f99 100644 --- a/src/voice/ctrlm_voice_obj_generic.cpp +++ b/src/voice/ctrlm_voice_obj_generic.cpp @@ -172,17 +172,12 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { xrsr_src_t src = (xrsr_src_t)j; ctrlm_voice_device_t src_device = xrsr_to_voice_device(src); std::string *url = NULL; - - xrsr_stream_voice_activity_mode_t stream_vad_mode = XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED; sem_wait(&this->device_status_semaphore); if(this->device_status[src_device] != CTRLM_VOICE_DEVICE_STATUS_DISABLED && this->device_status[src_device] != CTRLM_VOICE_DEVICE_STATUS_NOT_SUPPORTED) { switch(src_device) { case CTRLM_VOICE_DEVICE_PTT: { url = &this->prefs.server_url_src_ptt; - - // For PTT source, use the configured VAD mode which may be disabled, enabled, or enforced - stream_vad_mode = this->prefs.voice_activity_detection_mode; break; } case CTRLM_VOICE_DEVICE_MICROPHONE: @@ -237,16 +232,15 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { if(!this->obj_ws_nextgen->get_handlers(&handlers_xrsr)) { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_vad_mode = stream_vad_mode; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -270,16 +264,15 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { if(!this->obj_ws_nsp->get_handlers(&handlers_xrsr)) { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_PCM_32_BIT | XRSR_AUDIO_FORMAT_PCM_32_BIT_MULTI | XRSR_AUDIO_FORMAT_PCM_RAW; - routes[i].dsts[0].stream_time_min = 0; - routes[i].dsts[0].stream_vad_mode = XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED; - routes[i].dsts[0].stream_from = XRSR_STREAM_FROM_LIVE; - routes[i].dsts[0].stream_offset = 0; - routes[i].dsts[0].stream_until = XRSR_STREAM_UNTIL_END_OF_STREAM; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_PCM_32_BIT | XRSR_AUDIO_FORMAT_PCM_32_BIT_MULTI | XRSR_AUDIO_FORMAT_PCM_RAW; + routes[i].dsts[0].stream_time_min = 0; + routes[i].dsts[0].stream_from = XRSR_STREAM_FROM_LIVE; + routes[i].dsts[0].stream_offset = 0; + routes[i].dsts[0].stream_until = XRSR_STREAM_UNTIL_END_OF_STREAM; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -312,20 +305,19 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { url->append("speech?"); } - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = url->c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = url->c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; #ifdef AUDIO_DECODE - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; #else - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_ADPCM; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_ADPCM; #endif - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_vad_mode = stream_vad_mode; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -342,16 +334,15 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = url->c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_vad_mode = stream_vad_mode; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = url->c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } @@ -366,16 +357,15 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_vad_mode = stream_vad_mode; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } diff --git a/src/voice/ipc/ctrlm_voice_ipc.h b/src/voice/ipc/ctrlm_voice_ipc.h index db8dc582..969a8380 100644 --- a/src/voice/ipc/ctrlm_voice_ipc.h +++ b/src/voice/ipc/ctrlm_voice_ipc.h @@ -92,8 +92,7 @@ typedef enum { SESSION_END_SUCCESS, SESSION_END_FAILURE, SESSION_END_ABORT, - SESSION_END_SHORT_UTTERANCE, - SESSION_END_SILENT_UTTERANCE + SESSION_END_SHORT_UTTERANCE } ctrlm_voice_ipc_event_session_end_result_t; typedef struct { diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp index 25b2c8d6..88fbd3b0 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp @@ -158,18 +158,6 @@ bool ctrlm_voice_ipc_iarm_legacy_t::session_end(const ctrlm_voice_ipc_event_sess ret = broadcast_iarm_event_legacy(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_SESSION_SHORT, &event, sizeof(event)); break; } - case SESSION_END_SILENT_UTTERANCE: { - ctrlm_voice_iarm_event_session_silent_t event = {0}; - event.api_revision = CTRLM_VOICE_IARM_BUS_API_REVISION; - event.network_id = session_end.common.network_id; - event.network_type = session_end.common.network_type; - event.controller_id = session_end.common.controller_id; - event.session_id = session_end.common.session_id_ctrlm; - event.reason = (ctrlm_voice_session_end_reason_t)session_end.reason; - event.return_code_internal = session_end.return_code_internal; - ret = broadcast_iarm_event_legacy(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_SESSION_SILENT, &event, sizeof(event)); - break; - } } } // Reset state diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp index b148dda5..6b407173 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp @@ -62,7 +62,6 @@ #define JSON_SESSION_END_RESULT_ERROR "error" #define JSON_SESSION_END_RESULT_ABORT "abort" #define JSON_SESSION_END_RESULT_SHORT "shortUtterance" -#define JSON_SESSION_END_RESULT_SILENT "silentUtterance" #define JSON_SESSION_END_TRANSCRIPTION "transcription" #define JSON_SESSION_END_PROTOCOL_ERROR "protocolErrorCode" #define JSON_SESSION_END_PROTOCOL_LIBRARY_ERROR "protocolLibraryErrorCode" @@ -72,7 +71,6 @@ #define JSON_SESSION_END_ERROR_REASON "reason" #define JSON_SESSION_END_ABORT_REASON "reason" #define JSON_SESSION_END_SHORT_REASON "reason" -#define JSON_SESSION_END_SILENT_REASON "reason" #define JSON_SESSION_END_STB_STATS "stbStats" #define JSON_SESSION_END_STB_STATS_TYPE "type" #define JSON_SESSION_END_STB_STATS_FIRMWARE "firmware" @@ -293,20 +291,6 @@ bool ctrlm_voice_ipc_iarm_thunder_t::session_end(const ctrlm_voice_ipc_event_ses } break; } - case SESSION_END_SILENT_UTTERANCE: { - int rc_silent; - rc |= json_object_set_new_nocheck(event_data, JSON_SESSION_END_RESULT, json_string(JSON_SESSION_END_RESULT_SILENT)); - - // Add Audio Silent Data to result object - rc_silent = json_object_set_new_nocheck(event_result, JSON_SESSION_END_SILENT_REASON, json_integer(session_end.reason)); - if(0 != rc_silent) { - XLOGD_ERROR("Error creating audio silent JSON subobject"); - JSON_DEREFERENCE(event_result); - } else { - rc |= json_object_set_new_nocheck(event_data, JSON_SESSION_END_RESULT_SILENT, event_result); - } - break; - } } if(session_end.stb_stats) { int stats_rc; diff --git a/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp b/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp index 041d420a..d5f6bba3 100644 --- a/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp +++ b/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp @@ -212,10 +212,7 @@ bool ctrlm_voice_telemetry_session_t::event() { ss << "\"" << m_server_message << "\","; ss << m_result << ","; ss << m_end_reason_stream << ","; - ss << m_ret_code_protocol << ","; - ss << m_voice_detected << ","; - ss << m_peak_confidence << ","; - ss << m_peak_rms_level << "]]"; + ss << m_ret_code_protocol << "]]"; if(m_event_list.length() + ss.str().length() > m_event_list_max_size) { // Maximum data size exceeded XLOGD_WARN("telemetry event exceeds max size <%s,%s>", val_marker.c_str(), ss.str().c_str()); @@ -270,7 +267,7 @@ void ctrlm_voice_telemetry_session_t::update_on_key_release(int32_t time_start_l m_has_key_release = true; } -void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_len_act, uint32_t packets_total, uint32_t packets_lost, uint32_t samples_total, uint32_t samples_lost, uint32_t decoder_failures, uint32_t samples_buffered_max, int32_t voice_detected, uint32_t peak_confidence, int32_t peak_rms_level) { +void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_len_act, uint32_t packets_total, uint32_t packets_lost, uint32_t samples_total, uint32_t samples_lost, uint32_t decoder_failures, uint32_t samples_buffered_max) { m_time_stream_len_act = time_stream_len_act; m_packets_total = packets_total; m_packets_lost = packets_lost; @@ -279,12 +276,6 @@ void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_ m_decoder_failures = decoder_failures; m_samples_buffered_max = samples_buffered_max; - if(voice_detected >= 0) { - m_voice_detected = voice_detected; - m_peak_confidence = peak_confidence; - m_peak_rms_level = peak_rms_level; - } - if(m_has_key_release) { m_time_stream_delta = m_time_stream_len_act - m_time_stream_len_exp; } @@ -329,10 +320,6 @@ void ctrlm_voice_telemetry_session_t::reset_stats() { m_result = false; m_ret_code_protocol = 0; - m_voice_detected = -1; - m_peak_confidence = 0; - m_peak_rms_level = 0; - m_server_message.clear(); m_device_type.clear(); m_device_version.clear(); diff --git a/src/voice/telemetry/ctrlm_voice_telemetry_events.h b/src/voice/telemetry/ctrlm_voice_telemetry_events.h index f6f10127..93badb20 100644 --- a/src/voice/telemetry/ctrlm_voice_telemetry_events.h +++ b/src/voice/telemetry/ctrlm_voice_telemetry_events.h @@ -75,7 +75,7 @@ class ctrlm_voice_telemetry_session_t : public ctrlm_telemetry_event_t Date: Wed, 17 Jun 2026 09:27:52 -0400 Subject: [PATCH 14/15] 1.1.18 release changelog updates --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b2b696b..b1103259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.1.18](https://github.com/rdkcentral/control/compare/1.1.17...1.1.18) + +> 17 June 2026 + +- Revert "RDKEMW-14906 : VAD detector in Control Manager/Voice SDK (#186)" [`#245`](https://github.com/rdkcentral/control/pull/245) + + #### [1.1.17](https://github.com/rdkcentral/control/compare/1.1.16...1.1.17) > 8 June 2026 From 61a6d570ac661084114864676294e07afb40ded4 Mon Sep 17 00:00:00 2001 From: dwolaver <44593664+dwolaver@users.noreply.github.com> Date: Thu, 25 Jun 2026 10:59:00 -0400 Subject: [PATCH 15/15] RDKEMW-20361 : ctrlm branch name print in logs not correct (#248) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9cdf593..00266a6f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,7 +264,7 @@ install(FILES ${CMAKE_BINARY_DIR}/ctrlm_config.json DESTINATION ${CMAKE_INSTALL_ # GENERATED FILES add_custom_command( OUTPUT ctrlm_version_build.h COMMAND echo -n "#define CTRLM_MAIN_BRANCH \"" >> ctrlm_version_build.h - COMMAND git -C ${CMAKE_CURRENT_SOURCE_DIR} branch --all --contains | sed -n -e "s/^\\s*remotes\\/origin\\///" -e "2p" | tr -d "\\n" >> ctrlm_version_build.h + COMMAND sh -c "src=\"${CMAKE_CURRENT_SOURCE_DIR}\"; ref=$(git -C \"$src\" describe --tags --exact-match 2>/dev/null || git -C \"$src\" branch -a --format='%(refname:short)' --contains HEAD 2>/dev/null | sed -n '2{p;q}'); echo -n \"$ref\" >> ctrlm_version_build.h" COMMAND echo "\"" >> ctrlm_version_build.h COMMAND echo -n "#define CTRLM_MAIN_COMMIT_ID " >> ctrlm_version_build.h COMMAND git -C ${CMAKE_CURRENT_SOURCE_DIR} log --format=\"%H\" -n 1 >> ctrlm_version_build.h