From 6f4e0ebf3fad9f70b862ca6eb4c85ce0e3fb3c51 Mon Sep 17 00:00:00 2001 From: "Nagulapally, Arun Kumar" Date: Wed, 9 Sep 2026 11:08:07 -0700 Subject: [PATCH 01/19] Reason for change: Test Procedure: Risks: Low Signed-off-by: arunkumar_nagulapally@comcast.com --- scripts/start_adv_security.sh | 98 +++++++++++++++++++ .../cosa_adv_security_internal.c | 59 +++++++++++ .../CcspAdvSecurityInternalTest.cpp | 73 ++++++++++++++ 3 files changed, 230 insertions(+) diff --git a/scripts/start_adv_security.sh b/scripts/start_adv_security.sh index 53a7b99..413236b 100755 --- a/scripts/start_adv_security.sh +++ b/scripts/start_adv_security.sh @@ -550,6 +550,96 @@ disable_cujotelemetry() fi } +SPEEDTEST_NI_TIMER_PID_FILE="/tmp/cujo_ni_speedtest_timer.pid" + +speedtest_cancel_networkintelligence_timer() +{ + local timer_pid + + if [ -r "${SPEEDTEST_NI_TIMER_PID_FILE}" ]; then + read -r timer_pid < "${SPEEDTEST_NI_TIMER_PID_FILE}" + if [[ "${timer_pid}" =~ ^[0-9]+$ ]] && kill -0 "${timer_pid}" 2>/dev/null; then + kill "${timer_pid}" + fi + rm -f "${SPEEDTEST_NI_TIMER_PID_FILE}" + fi +} + +speedtest_set_qosd_enable() +{ + # $1 = 0 to disable cujo-qosd, 1 to enable cujo-qosd + local enable_val="$1" + local cli_out + local cli_rc + + cli_out=$(cujo-ni-cli "{\"method\":\"set_configs\", \"configs\": {\"cujoniqos.daemon.enable\": ${enable_val}}}" 2>&1) + cli_rc=$? + echo_t "ARUN: cujo-ni-cli set_configs cujoniqos.daemon.enable=${enable_val} rc=${cli_rc} out=${cli_out}" >> ${ADVSEC_AGENT_LOG_PATH} + if [ ${cli_rc} -ne 0 ] || echo "${cli_out}" | grep -q '"ipc_method_status"[[:space:]]*:[[:space:]]*"NOK"'; then + return 1 + fi + return 0 +} + +speedtest_pause_networkintelligence() +{ + local unpause_timeout + + echo_t "ARUN: speedtest_pause_networkintelligence invoked (SpeedTest status=1)" >> ${ADVSEC_AGENT_LOG_PATH} + + if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then + echo_t "cujo-qosd pause skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} + return 0 + fi + + unpause_timeout=$(dmcli eRT retv "Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut" 2>/dev/null) + echo_t "ARUN: SubscriberUnPauseTimeOut read as '${unpause_timeout}'" >> ${ADVSEC_AGENT_LOG_PATH} + if ! [[ "${unpause_timeout}" =~ ^[0-9]+$ ]] || [ "${unpause_timeout}" -eq 0 ]; then + echo_t "Disabling cujo-qosd for speedtest failed: invalid SubscriberUnPauseTimeOut=${unpause_timeout}" >> ${ADVSEC_AGENT_LOG_PATH} + return 1 + fi + + speedtest_cancel_networkintelligence_timer + echo_t "Disabling cujo-qosd for speedtest (unpause timeout=${unpause_timeout}s)" >> ${ADVSEC_AGENT_LOG_PATH} + if ! speedtest_set_qosd_enable 0; then + echo_t "Disabling cujo-qosd for speedtest failed" >> ${ADVSEC_AGENT_LOG_PATH} + return 1 + fi + + ( + sleep "${unpause_timeout}" + echo_t "ARUN: unpause timer expired after ${unpause_timeout}s" >> ${ADVSEC_AGENT_LOG_PATH} + echo_t "IMP_CUJO_NI_SubscriberUnPauseTimeOut" >> ${ADVSEC_AGENT_LOG_PATH} + if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then + echo_t "cujo-qosd resume skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} + else + echo_t "Enabling cujo-qosd (unpause timeout expired)" >> ${ADVSEC_AGENT_LOG_PATH} + if ! speedtest_set_qosd_enable 1; then + echo_t "Enabling cujo-qosd failed" >> ${ADVSEC_AGENT_LOG_PATH} + fi + fi + ) & + echo $! > "${SPEEDTEST_NI_TIMER_PID_FILE}" +} + +speedtest_resume_networkintelligence() +{ + echo_t "ARUN: speedtest_resume_networkintelligence invoked (SpeedTest status=5)" >> ${ADVSEC_AGENT_LOG_PATH} + + if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then + echo_t "cujo-qosd resume skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} + speedtest_cancel_networkintelligence_timer + return 0 + fi + + speedtest_cancel_networkintelligence_timer + echo_t "Enabling cujo-qosd (speedtest complete)" >> ${ADVSEC_AGENT_LOG_PATH} + if ! speedtest_set_qosd_enable 1; then + echo_t "Enabling cujo-qosd failed" >> ${ADVSEC_AGENT_LOG_PATH} + return 1 + fi +} + enable_networkintelligence() { touch $ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH @@ -873,6 +963,14 @@ if [ "$1" = "-disableNI" ]; then disable_networkintelligence "RR" "FR" fi +if [ "$1" = "-speedtestNIStart" ]; then + speedtest_pause_networkintelligence +fi + +if [ "$1" = "-speedtestNIComplete" ]; then + speedtest_resume_networkintelligence +fi + if [ "$1" = "-enableWifiDCL" ]; then enable_wifidatacollection "RR" fi diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 14eaf6b..0947720 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -66,6 +66,7 @@ #define ADVSEC_SYSEVENT_CURRENT_WAN_IFNAME_EVENT "current_wan_ifname" #define LEVL_DML "Device.WiFi.Levl" +#define SPEEDTEST_STATUS_DML "Device.IP.Diagnostics.X_RDKCENTRAL-COM_SpeedTest.Status" #define ADVSEC_WAIT_FOR_TIMEOUT (60 * 60) #define MAX_VALUE 32 @@ -429,6 +430,56 @@ static void eventReceiveHandler( } #endif +#ifdef NETWORK_INTELLIGENCE +STATIC void speedtestEventReceiveHandler( + rbusHandle_t handle, + rbusEvent_t const* event, + rbusEventSubscription_t* subscription) +{ + rbusValue_t value; + const char *status; + errno_t rc; + + (void)handle; + (void)subscription; + + value = rbusObject_GetValue(event->data, NULL); + if (value == NULL) + { + CcspTraceError(("SpeedTest status event has no value\n")); + return; + } + + status = rbusValue_GetString(value, NULL); + if (status == NULL) + { + CcspTraceError(("SpeedTest status event value is invalid\n")); + return; + } + + CcspTraceInfo(("ARUN: SpeedTest status event received, status=%s\n", status)); + + if (strcmp(status, "1") == 0) + { + CcspTraceInfo(("ARUN: SpeedTest status=1, disabling cujo-qosd for speedtest\n")); + rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIStart &"); + if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) + { + CcspTraceError(("%s: failed to disable cujo-qosd for SpeedTest, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); + } + } + else if (strcmp(status, "5") == 0) + { + CcspTraceInfo(("ARUN: SpeedTest status=5, enabling cujo-qosd after speedtest\n")); + rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIComplete &"); + if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) + { + CcspTraceError(("%s: failed to enable cujo-qosd for SpeedTest, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); + } + } +} +#endif + #ifdef WIFI_DATA_COLLECTION static int touch_file(const char *filepath) { @@ -1564,6 +1615,14 @@ CosaSecurityInitialize CcspTraceError(("AdvSecurityEventConsumer: rbusEvent_Subscribe %s failed: %d\n", LEVL_DML, ret)); return ANSC_STATUS_FAILURE; } +#endif +#ifdef NETWORK_INTELLIGENCE + ret = rbusEvent_Subscribe(rbus_handle, SPEEDTEST_STATUS_DML, speedtestEventReceiveHandler, NULL, 0); + if(ret != RBUS_ERROR_SUCCESS) + { + CcspTraceError(("AdvSecurityEventConsumer: rbusEvent_Subscribe %s failed: %d\n", SPEEDTEST_STATUS_DML, ret)); + return ANSC_STATUS_FAILURE; + } #endif return returnStatus; } diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index bc4d4ee..0a1bbd0 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1441,6 +1441,79 @@ TEST_F(CcspAdvSecurityInternalTestFixture, CosaRabidSetDNSCacheSize) free(g_pAdvSecAgent); } +#ifdef NETWORK_INTELLIGENCE +extern "C" void speedtestEventReceiveHandler(rbusHandle_t handle, rbusEvent_t const* event, rbusEventSubscription_t* subscription); + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_StopsNI) +{ + int marker = 0; + rbusValue_t value = (rbusValue_t)▮ + rbusEvent_t event = {}; + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return(value)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetString(value, _)) + .Times(1) + .WillOnce(Return("1")); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("/usr/ccsp/advsec/start_adv_security.sh -speedtestNIStart &"), _)) + .Times(1) + .WillOnce(Return(0)); + + speedtestEventReceiveHandler(NULL, &event, NULL); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_StartsNI) +{ + int marker = 0; + rbusValue_t value = (rbusValue_t)▮ + rbusEvent_t event = {}; + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return(value)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetString(value, _)) + .Times(1) + .WillOnce(Return("5")); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("/usr/ccsp/advsec/start_adv_security.sh -speedtestNIComplete &"), _)) + .Times(1) + .WillOnce(Return(0)); + + speedtestEventReceiveHandler(NULL, &event, NULL); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_NullValue_NoAction) +{ + rbusEvent_t event = {}; + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return((rbusValue_t)NULL)); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); + + speedtestEventReceiveHandler(NULL, &event, NULL); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Other_NoAction) +{ + int marker = 0; + rbusValue_t value = (rbusValue_t)▮ + rbusEvent_t event = {}; + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return(value)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetString(value, _)) + .Times(1) + .WillOnce(Return("2")); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); + + speedtestEventReceiveHandler(NULL, &event, NULL); +} +#endif + TEST_F(CcspAdvSecurityInternalTestFixture, CosaAdvPCInit) { const char *AdvParentalControlRFCEnabled = "Adv_PCRFCEnable"; From 08833153b1fec63000ef97f06e833b060826b2b9 Mon Sep 17 00:00:00 2001 From: "Nagulapally, Arun Kumar" Date: Tue, 15 Sep 2026 11:58:10 -0700 Subject: [PATCH 02/19] adv sec read the dml as iinteger not as string --- .../AdvSecurityDml/cosa_adv_security_internal.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 0947720..717341f 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -437,7 +437,7 @@ STATIC void speedtestEventReceiveHandler( rbusEventSubscription_t* subscription) { rbusValue_t value; - const char *status; + uint32_t status; errno_t rc; (void)handle; @@ -450,16 +450,10 @@ STATIC void speedtestEventReceiveHandler( return; } - status = rbusValue_GetString(value, NULL); - if (status == NULL) - { - CcspTraceError(("SpeedTest status event value is invalid\n")); - return; - } - - CcspTraceInfo(("ARUN: SpeedTest status event received, status=%s\n", status)); + status = rbusValue_GetUInt32(value); + CcspTraceInfo(("ARUN: SpeedTest status event received, status=%u\n", status)); - if (strcmp(status, "1") == 0) + if (status == 1) { CcspTraceInfo(("ARUN: SpeedTest status=1, disabling cujo-qosd for speedtest\n")); rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIStart &"); @@ -468,7 +462,7 @@ STATIC void speedtestEventReceiveHandler( CcspTraceError(("%s: failed to disable cujo-qosd for SpeedTest, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); } } - else if (strcmp(status, "5") == 0) + else if (status == 5) { CcspTraceInfo(("ARUN: SpeedTest status=5, enabling cujo-qosd after speedtest\n")); rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIComplete &"); From d031f238ee28b71ad6140b69821d12f9381b2d71 Mon Sep 17 00:00:00 2001 From: "Nagulapally, Arun Kumar" Date: Wed, 16 Sep 2026 08:53:12 -0700 Subject: [PATCH 03/19] use pthread_cond_timedwait instead of timeout logic in .sh --- scripts/start_adv_security.sh | 64 +++----- .../cosa_adv_security_internal.c | 153 ++++++++++++++++++ .../CcspAdvSecurityInternalTest.cpp | 50 +++++- 3 files changed, 218 insertions(+), 49 deletions(-) diff --git a/scripts/start_adv_security.sh b/scripts/start_adv_security.sh index 413236b..ed227aa 100755 --- a/scripts/start_adv_security.sh +++ b/scripts/start_adv_security.sh @@ -550,21 +550,6 @@ disable_cujotelemetry() fi } -SPEEDTEST_NI_TIMER_PID_FILE="/tmp/cujo_ni_speedtest_timer.pid" - -speedtest_cancel_networkintelligence_timer() -{ - local timer_pid - - if [ -r "${SPEEDTEST_NI_TIMER_PID_FILE}" ]; then - read -r timer_pid < "${SPEEDTEST_NI_TIMER_PID_FILE}" - if [[ "${timer_pid}" =~ ^[0-9]+$ ]] && kill -0 "${timer_pid}" 2>/dev/null; then - kill "${timer_pid}" - fi - rm -f "${SPEEDTEST_NI_TIMER_PID_FILE}" - fi -} - speedtest_set_qosd_enable() { # $1 = 0 to disable cujo-qosd, 1 to enable cujo-qosd @@ -583,8 +568,6 @@ speedtest_set_qosd_enable() speedtest_pause_networkintelligence() { - local unpause_timeout - echo_t "ARUN: speedtest_pause_networkintelligence invoked (SpeedTest status=1)" >> ${ADVSEC_AGENT_LOG_PATH} if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then @@ -592,34 +575,11 @@ speedtest_pause_networkintelligence() return 0 fi - unpause_timeout=$(dmcli eRT retv "Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut" 2>/dev/null) - echo_t "ARUN: SubscriberUnPauseTimeOut read as '${unpause_timeout}'" >> ${ADVSEC_AGENT_LOG_PATH} - if ! [[ "${unpause_timeout}" =~ ^[0-9]+$ ]] || [ "${unpause_timeout}" -eq 0 ]; then - echo_t "Disabling cujo-qosd for speedtest failed: invalid SubscriberUnPauseTimeOut=${unpause_timeout}" >> ${ADVSEC_AGENT_LOG_PATH} - return 1 - fi - - speedtest_cancel_networkintelligence_timer - echo_t "Disabling cujo-qosd for speedtest (unpause timeout=${unpause_timeout}s)" >> ${ADVSEC_AGENT_LOG_PATH} + echo_t "Disabling cujo-qosd for speedtest" >> ${ADVSEC_AGENT_LOG_PATH} if ! speedtest_set_qosd_enable 0; then echo_t "Disabling cujo-qosd for speedtest failed" >> ${ADVSEC_AGENT_LOG_PATH} return 1 fi - - ( - sleep "${unpause_timeout}" - echo_t "ARUN: unpause timer expired after ${unpause_timeout}s" >> ${ADVSEC_AGENT_LOG_PATH} - echo_t "IMP_CUJO_NI_SubscriberUnPauseTimeOut" >> ${ADVSEC_AGENT_LOG_PATH} - if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then - echo_t "cujo-qosd resume skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} - else - echo_t "Enabling cujo-qosd (unpause timeout expired)" >> ${ADVSEC_AGENT_LOG_PATH} - if ! speedtest_set_qosd_enable 1; then - echo_t "Enabling cujo-qosd failed" >> ${ADVSEC_AGENT_LOG_PATH} - fi - fi - ) & - echo $! > "${SPEEDTEST_NI_TIMER_PID_FILE}" } speedtest_resume_networkintelligence() @@ -628,11 +588,9 @@ speedtest_resume_networkintelligence() if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then echo_t "cujo-qosd resume skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} - speedtest_cancel_networkintelligence_timer return 0 fi - speedtest_cancel_networkintelligence_timer echo_t "Enabling cujo-qosd (speedtest complete)" >> ${ADVSEC_AGENT_LOG_PATH} if ! speedtest_set_qosd_enable 1; then echo_t "Enabling cujo-qosd failed" >> ${ADVSEC_AGENT_LOG_PATH} @@ -640,6 +598,22 @@ speedtest_resume_networkintelligence() fi } +speedtest_timeout_networkintelligence() +{ + echo_t "IMP_CUJO_NI_SubscriberUnPauseTimeOut" >> ${ADVSEC_AGENT_LOG_PATH} + + if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then + echo_t "cujo-qosd resume skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} + return 0 + fi + + echo_t "Enabling cujo-qosd (unpause timeout expired)" >> ${ADVSEC_AGENT_LOG_PATH} + if ! speedtest_set_qosd_enable 1; then + echo_t "Enabling cujo-qosd failed" >> ${ADVSEC_AGENT_LOG_PATH} + return 1 + fi +} + enable_networkintelligence() { touch $ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH @@ -971,6 +945,10 @@ if [ "$1" = "-speedtestNIComplete" ]; then speedtest_resume_networkintelligence fi +if [ "$1" = "-speedtestNITimeout" ]; then + speedtest_timeout_networkintelligence +fi + if [ "$1" = "-enableWifiDCL" ]; then enable_wifidatacollection "RR" fi diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 717341f..153946d 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -67,6 +67,7 @@ #define LEVL_DML "Device.WiFi.Levl" #define SPEEDTEST_STATUS_DML "Device.IP.Diagnostics.X_RDKCENTRAL-COM_SpeedTest.Status" +#define SPEEDTEST_TIMEOUT_DML "Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut" #define ADVSEC_WAIT_FOR_TIMEOUT (60 * 60) #define MAX_VALUE 32 @@ -152,6 +153,13 @@ static char *g_RaptrEnabled = "Adv_RaptrRFCEnable"; #ifdef NETWORK_INTELLIGENCE static char *g_AdvSecNetworkIntelligenceEnabled = "Adv_AdvSecNetworkIntelligenceRFCEnable"; static char *g_NetworkIntelligenceMemoryLimit = "Advsecurity_NetworkIntelligenceMemoryLimit"; +static pthread_mutex_t speedtestMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t speedtestCond = PTHREAD_COND_INITIALIZER; +static pthread_t speedtestTimerThread; +static struct timespec speedtestDeadline; +static BOOL speedtestTimerStarted = FALSE; +static BOOL speedtestTimerActive = FALSE; +static BOOL speedtestTimerShutdown = FALSE; #endif #ifdef WIFI_DATA_COLLECTION static char *g_AdvWifiDataCollection = "Adv_WifiDataCollectionRFCEnable"; @@ -431,6 +439,119 @@ static void eventReceiveHandler( #endif #ifdef NETWORK_INTELLIGENCE +static void *speedtestTimerHandler(void *arg) +{ + int waitStatus; + errno_t rc; + + (void)arg; + pthread_mutex_lock(&speedtestMutex); + + while (!speedtestTimerShutdown) + { + while (!speedtestTimerActive && !speedtestTimerShutdown) + { + pthread_cond_wait(&speedtestCond, &speedtestMutex); + } + + waitStatus = 0; + while (speedtestTimerActive && !speedtestTimerShutdown && waitStatus != ETIMEDOUT) + { + waitStatus = pthread_cond_timedwait(&speedtestCond, &speedtestMutex, &speedtestDeadline); + if (waitStatus != 0 && waitStatus != ETIMEDOUT) + { + CcspTraceError(("%s: pthread_cond_timedwait failed, error=%d\n", __FUNCTION__, waitStatus)); + speedtestTimerActive = FALSE; + } + } + + if (speedtestTimerActive && !speedtestTimerShutdown && waitStatus == ETIMEDOUT) + { + speedtestTimerActive = FALSE; + pthread_mutex_unlock(&speedtestMutex); + CcspTraceWarning(("SpeedTest timeout expired, enabling cujo-qosd\n")); + rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNITimeout &"); + if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) + { + CcspTraceError(("%s: failed to enable cujo-qosd after SpeedTest timeout, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); + } + pthread_mutex_lock(&speedtestMutex); + } + } + + pthread_mutex_unlock(&speedtestMutex); + return NULL; +} + +static BOOL speedtestArmTimer(uint32_t timeout) +{ + struct timespec deadline; + int err; + + if (clock_gettime(CLOCK_REALTIME, &deadline) != 0) + { + CcspTraceError(("%s: clock_gettime failed, errno=%d\n", __FUNCTION__, errno)); + return FALSE; + } + deadline.tv_sec += timeout; + + pthread_mutex_lock(&speedtestMutex); + if (speedtestTimerShutdown) + { + pthread_mutex_unlock(&speedtestMutex); + return FALSE; + } + if (!speedtestTimerStarted) + { + speedtestTimerShutdown = FALSE; + err = pthread_create(&speedtestTimerThread, NULL, speedtestTimerHandler, NULL); + if (err != 0) + { + pthread_mutex_unlock(&speedtestMutex); + CcspTraceError(("%s: failed to create SpeedTest timer thread, error=%d\n", __FUNCTION__, err)); + return FALSE; + } + speedtestTimerStarted = TRUE; + } + + speedtestDeadline = deadline; + speedtestTimerActive = TRUE; + pthread_cond_signal(&speedtestCond); + pthread_mutex_unlock(&speedtestMutex); + return TRUE; +} + +static void speedtestCancelTimer(void) +{ + pthread_mutex_lock(&speedtestMutex); + speedtestTimerActive = FALSE; + pthread_cond_signal(&speedtestCond); + pthread_mutex_unlock(&speedtestMutex); +} + +static BOOL speedtestGetTimeout(uint32_t *timeout) +{ + rbusValue_t value = NULL; + int ret; + + ret = rbus_get(rbus_handle, SPEEDTEST_TIMEOUT_DML, &value); + if (ret != RBUS_ERROR_SUCCESS || value == NULL) + { + CcspTraceError(("%s: rbus_get failed for %s, error=%d\n", __FUNCTION__, SPEEDTEST_TIMEOUT_DML, ret)); + return FALSE; + } + + *timeout = rbusValue_GetUInt32(value); + rbusValue_Release(value); + if (*timeout == 0) + { + CcspTraceError(("%s: invalid SpeedTest timeout=0\n", __FUNCTION__)); + return FALSE; + } + + return TRUE; +} + STATIC void speedtestEventReceiveHandler( rbusHandle_t handle, rbusEvent_t const* event, @@ -438,6 +559,7 @@ STATIC void speedtestEventReceiveHandler( { rbusValue_t value; uint32_t status; + uint32_t timeout; errno_t rc; (void)handle; @@ -455,15 +577,22 @@ STATIC void speedtestEventReceiveHandler( if (status == 1) { + if (!speedtestGetTimeout(&timeout) || !speedtestArmTimer(timeout)) + { + CcspTraceError(("%s: failed to arm SpeedTest timeout; cujo-qosd will not be disabled\n", __FUNCTION__)); + return; + } CcspTraceInfo(("ARUN: SpeedTest status=1, disabling cujo-qosd for speedtest\n")); rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIStart &"); if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) { + speedtestCancelTimer(); CcspTraceError(("%s: failed to disable cujo-qosd for SpeedTest, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); } } else if (status == 5) { + speedtestCancelTimer(); CcspTraceInfo(("ARUN: SpeedTest status=5, enabling cujo-qosd after speedtest\n")); rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIComplete &"); if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) @@ -1611,6 +1740,9 @@ CosaSecurityInitialize } #endif #ifdef NETWORK_INTELLIGENCE + pthread_mutex_lock(&speedtestMutex); + speedtestTimerShutdown = FALSE; + pthread_mutex_unlock(&speedtestMutex); ret = rbusEvent_Subscribe(rbus_handle, SPEEDTEST_STATUS_DML, speedtestEventReceiveHandler, NULL, 0); if(ret != RBUS_ERROR_SUCCESS) { @@ -1631,6 +1763,27 @@ CosaSecurityRemove ANSC_STATUS returnStatus = ANSC_STATUS_SUCCESS; PCOSA_DATAMODEL_AGENT pMyObject = (PCOSA_DATAMODEL_AGENT)hThisObject; +#ifdef NETWORK_INTELLIGENCE + BOOL joinSpeedtestTimer; + + pthread_mutex_lock(&speedtestMutex); + joinSpeedtestTimer = speedtestTimerStarted; + speedtestTimerShutdown = TRUE; + speedtestTimerActive = FALSE; + if (joinSpeedtestTimer) + { + pthread_cond_signal(&speedtestCond); + } + pthread_mutex_unlock(&speedtestMutex); + if (joinSpeedtestTimer) + { + pthread_join(speedtestTimerThread, NULL); + pthread_mutex_lock(&speedtestMutex); + speedtestTimerStarted = FALSE; + pthread_mutex_unlock(&speedtestMutex); + } +#endif + /* Remove self */ FreeCosaDmAgent(pMyObject); rbus_close(rbus_handle); diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 0a1bbd0..983c13c 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1447,15 +1447,25 @@ extern "C" void speedtestEventReceiveHandler(rbusHandle_t handle, rbusEvent_t co TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_StopsNI) { int marker = 0; + int timeoutMarker = 0; rbusValue_t value = (rbusValue_t)▮ + rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; rbusEvent_t event = {}; EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) .Times(1) .WillOnce(Return(value)); - EXPECT_CALL(*g_rbusMock, rbusValue_GetString(value, _)) + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return("1")); + .WillOnce(Return(1)); + EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) + .Times(1) + .WillOnce(Return(86400)); + EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) + .Times(1); EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("/usr/ccsp/advsec/start_adv_security.sh -speedtestNIStart &"), _)) .Times(1) .WillOnce(Return(0)); @@ -1463,6 +1473,34 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_StopsNI) speedtestEventReceiveHandler(NULL, &event, NULL); } +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_StartingWithZeroTimeout_NoAction) +{ + int marker = 0; + int timeoutMarker = 0; + rbusValue_t value = (rbusValue_t)▮ + rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; + rbusEvent_t event = {}; + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return(value)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) + .Times(1) + .WillOnce(Return(1)); + EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) + .Times(1) + .WillOnce(Return(0)); + EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) + .Times(1); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); + + speedtestEventReceiveHandler(NULL, &event, NULL); +} + TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_StartsNI) { int marker = 0; @@ -1472,9 +1510,9 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_StartsNI) EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) .Times(1) .WillOnce(Return(value)); - EXPECT_CALL(*g_rbusMock, rbusValue_GetString(value, _)) + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return("5")); + .WillOnce(Return(5)); EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("/usr/ccsp/advsec/start_adv_security.sh -speedtestNIComplete &"), _)) .Times(1) .WillOnce(Return(0)); @@ -1504,9 +1542,9 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Other_NoAction) EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) .Times(1) .WillOnce(Return(value)); - EXPECT_CALL(*g_rbusMock, rbusValue_GetString(value, _)) + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return("2")); + .WillOnce(Return(2)); EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) .Times(0); From 3bc3cdf6d19215ee07fea2996f2a21bf9d9cd384 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Tue, 22 Sep 2026 14:56:47 +0530 Subject: [PATCH 04/19] Move NI pause resume logic from shell script to C --- scripts/start_adv_security.sh | 76 ----- .../cosa_adv_security_internal.c | 263 +++++++++++------- .../CcspAdvSecurityInternalTest.cpp | 218 ++++++++++++++- 3 files changed, 373 insertions(+), 184 deletions(-) diff --git a/scripts/start_adv_security.sh b/scripts/start_adv_security.sh index 547a799..0651391 100755 --- a/scripts/start_adv_security.sh +++ b/scripts/start_adv_security.sh @@ -567,70 +567,6 @@ disable_cujotelemetry() fi } -speedtest_set_qosd_enable() -{ - # $1 = 0 to disable cujo-qosd, 1 to enable cujo-qosd - local enable_val="$1" - local cli_out - local cli_rc - - cli_out=$(cujo-ni-cli "{\"method\":\"set_configs\", \"configs\": {\"cujoniqos.daemon.enable\": ${enable_val}}}" 2>&1) - cli_rc=$? - echo_t "ARUN: cujo-ni-cli set_configs cujoniqos.daemon.enable=${enable_val} rc=${cli_rc} out=${cli_out}" >> ${ADVSEC_AGENT_LOG_PATH} - if [ ${cli_rc} -ne 0 ] || echo "${cli_out}" | grep -q '"ipc_method_status"[[:space:]]*:[[:space:]]*"NOK"'; then - return 1 - fi - return 0 -} - -speedtest_pause_networkintelligence() -{ - echo_t "ARUN: speedtest_pause_networkintelligence invoked (SpeedTest status=1)" >> ${ADVSEC_AGENT_LOG_PATH} - - if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then - echo_t "cujo-qosd pause skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} - return 0 - fi - - echo_t "Disabling cujo-qosd for speedtest" >> ${ADVSEC_AGENT_LOG_PATH} - if ! speedtest_set_qosd_enable 0; then - echo_t "Disabling cujo-qosd for speedtest failed" >> ${ADVSEC_AGENT_LOG_PATH} - return 1 - fi -} - -speedtest_resume_networkintelligence() -{ - echo_t "ARUN: speedtest_resume_networkintelligence invoked (SpeedTest status=5)" >> ${ADVSEC_AGENT_LOG_PATH} - - if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then - echo_t "cujo-qosd resume skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} - return 0 - fi - - echo_t "Enabling cujo-qosd (speedtest complete)" >> ${ADVSEC_AGENT_LOG_PATH} - if ! speedtest_set_qosd_enable 1; then - echo_t "Enabling cujo-qosd failed" >> ${ADVSEC_AGENT_LOG_PATH} - return 1 - fi -} - -speedtest_timeout_networkintelligence() -{ - echo_t "IMP_CUJO_NI_SubscriberUnPauseTimeOut" >> ${ADVSEC_AGENT_LOG_PATH} - - if [ ! -e ${ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH} ]; then - echo_t "cujo-qosd resume skipped for speedtest: Network Intelligence is not enabled" >> ${ADVSEC_AGENT_LOG_PATH} - return 0 - fi - - echo_t "Enabling cujo-qosd (unpause timeout expired)" >> ${ADVSEC_AGENT_LOG_PATH} - if ! speedtest_set_qosd_enable 1; then - echo_t "Enabling cujo-qosd failed" >> ${ADVSEC_AGENT_LOG_PATH} - return 1 - fi -} - enable_networkintelligence() { touch $ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH @@ -1004,18 +940,6 @@ if [ "$1" = "-deactivateNI_R" ]; then deactivate_networkintelligence "RR" fi -if [ "$1" = "-speedtestNIStart" ]; then - speedtest_pause_networkintelligence -fi - -if [ "$1" = "-speedtestNIComplete" ]; then - speedtest_resume_networkintelligence -fi - -if [ "$1" = "-speedtestNITimeout" ]; then - speedtest_timeout_networkintelligence -fi - if [ "$1" = "-enableWifiDCL" ]; then enable_wifidatacollection "RR" fi diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index af9bf8f..198812e 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -71,6 +71,8 @@ #define LEVL_DML "Device.WiFi.Levl" #define SPEEDTEST_STATUS_DML "Device.IP.Diagnostics.X_RDKCENTRAL-COM_SpeedTest.Status" #define SPEEDTEST_TIMEOUT_DML "Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut" +#define ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH "/tmp/advsec_networkintelligence_enabled" +#define CUJONICLI_SET_QOSD_ENABLE_CMD "/usr/bin/cujo-ni-cli \"{\\\"method\\\":\\\"set_configs\\\", \\\"configs\\\": {\\\"cujoniqos.daemon.enable\\\": %d}}\" &" #define ADVSEC_WAIT_FOR_TIMEOUT (60 * 60) #define MAX_VALUE 32 @@ -157,13 +159,12 @@ static char *g_RaptrEnabled = "Adv_RaptrRFCEnable"; #ifdef NETWORK_INTELLIGENCE static char *g_AdvSecNetworkIntelligenceEnabled = "Adv_AdvSecNetworkIntelligenceRFCEnable"; static char *g_NetworkIntelligenceMemoryLimit = "Advsecurity_NetworkIntelligenceMemoryLimit"; -static pthread_mutex_t speedtestMutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t speedtestCond = PTHREAD_COND_INITIALIZER; -static pthread_t speedtestTimerThread; -static struct timespec speedtestDeadline; -static BOOL speedtestTimerStarted = FALSE; -static BOOL speedtestTimerActive = FALSE; -static BOOL speedtestTimerShutdown = FALSE; +STATIC pthread_mutex_t ni_speedtest_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t ni_speedtest_cond = PTHREAD_COND_INITIALIZER; +static struct timespec ni_speedtest_deadline; +STATIC BOOL ni_speedtest_thread_running = FALSE; +static BOOL ni_speedtest_wake_early = FALSE; +static BOOL ni_speedtest_shutdown = FALSE; static char *g_NetworkIntelligenceActivate = "Adv_AdvSecNetworkIntelligenceActivate"; #endif #ifdef WIFI_DATA_COLLECTION @@ -444,54 +445,110 @@ static void eventReceiveHandler( #endif #ifdef NETWORK_INTELLIGENCE -static void *speedtestTimerHandler(void *arg) +static BOOL is_ni_enabled_and_activated(void) +{ + struct stat st; + + if (stat(ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH, &st) != 0) + { + return FALSE; + } + if (stat(ADVSEC_NETWORKINTELLIGENCE_ACTIVATED_PATH, &st) != 0) + { + return FALSE; + } + return TRUE; +} + +static BOOL ni_qosd_enable(BOOL enable) { - int waitStatus; errno_t rc; + rc = v_secure_system(CUJONICLI_SET_QOSD_ENABLE_CMD, enable ? 1 : 0); + if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) + { + CcspTraceError(("%s: cujo-ni-cli set_configs cujoniqos.daemon.enable=%d failed, rc=%d\n", + __FUNCTION__, enable ? 1 : 0, WEXITSTATUS(rc))); + return FALSE; + } + return TRUE; +} + +static BOOL ni_qosd_pause(void) +{ + if (!is_ni_enabled_and_activated()) + { + CcspTraceInfo(("%s: cujo-qosd pause skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); + return TRUE; + } + + CcspTraceInfo(("%s: pausing cujo-qosd\n", __FUNCTION__)); + return ni_qosd_enable(FALSE); +} + +static BOOL ni_qosd_resume(void) +{ + if (!is_ni_enabled_and_activated()) + { + CcspTraceInfo(("%s: cujo-qosd resume skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); + return TRUE; + } + + CcspTraceInfo(("%s: resuming cujo-qosd\n", __FUNCTION__)); + return ni_qosd_enable(TRUE); +} + +static void *ni_speedtest_handler(void *arg) +{ + int waitStatus = 0; + BOOL timedOut; + (void)arg; - pthread_mutex_lock(&speedtestMutex); - while (!speedtestTimerShutdown) + pthread_detach(pthread_self()); + + if (!ni_qosd_pause()) { - while (!speedtestTimerActive && !speedtestTimerShutdown) - { - pthread_cond_wait(&speedtestCond, &speedtestMutex); - } + CcspTraceError(("%s: failed to pause Network Intelligence for SpeedTest\n", __FUNCTION__)); + } - waitStatus = 0; - while (speedtestTimerActive && !speedtestTimerShutdown && waitStatus != ETIMEDOUT) + pthread_mutex_lock(&ni_speedtest_mutex); + while (!ni_speedtest_wake_early && !ni_speedtest_shutdown && waitStatus != ETIMEDOUT) + { + waitStatus = pthread_cond_timedwait(&ni_speedtest_cond, &ni_speedtest_mutex, &ni_speedtest_deadline); + if (waitStatus != 0 && waitStatus != ETIMEDOUT) { - waitStatus = pthread_cond_timedwait(&speedtestCond, &speedtestMutex, &speedtestDeadline); - if (waitStatus != 0 && waitStatus != ETIMEDOUT) - { - CcspTraceError(("%s: pthread_cond_timedwait failed, error=%d\n", __FUNCTION__, waitStatus)); - speedtestTimerActive = FALSE; - } + CcspTraceError(("%s: pthread_cond_timedwait failed, error=%d\n", __FUNCTION__, waitStatus)); + break; } + } + timedOut = (waitStatus == ETIMEDOUT); + ni_speedtest_wake_early = FALSE; + pthread_mutex_unlock(&ni_speedtest_mutex); - if (speedtestTimerActive && !speedtestTimerShutdown && waitStatus == ETIMEDOUT) - { - speedtestTimerActive = FALSE; - pthread_mutex_unlock(&speedtestMutex); - CcspTraceWarning(("SpeedTest timeout expired, enabling cujo-qosd\n")); - rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNITimeout &"); - if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) - { - CcspTraceError(("%s: failed to enable cujo-qosd after SpeedTest timeout, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); - } - pthread_mutex_lock(&speedtestMutex); - } + if (timedOut) + { + CcspTraceWarning(("IMP_CUJO_NI_SubscriberUnPauseTimeOut: SpeedTest timeout expired, enabling cujo-qosd\n")); + } + if (!ni_qosd_resume()) + { + CcspTraceError(("%s: failed to resume Network Intelligence after SpeedTest\n", __FUNCTION__)); } - pthread_mutex_unlock(&speedtestMutex); + pthread_mutex_lock(&ni_speedtest_mutex); + ni_speedtest_thread_running = FALSE; + pthread_cond_broadcast(&ni_speedtest_cond); + pthread_mutex_unlock(&ni_speedtest_mutex); + return NULL; } -static BOOL speedtestArmTimer(uint32_t timeout) +static BOOL ni_speedtest_trigger(uint32_t timeout) { struct timespec deadline; + pthread_t tid; int err; + BOOL alreadyRunning; if (clock_gettime(CLOCK_REALTIME, &deadline) != 0) { @@ -500,38 +557,53 @@ static BOOL speedtestArmTimer(uint32_t timeout) } deadline.tv_sec += timeout; - pthread_mutex_lock(&speedtestMutex); - if (speedtestTimerShutdown) + pthread_mutex_lock(&ni_speedtest_mutex); + if (ni_speedtest_shutdown) { - pthread_mutex_unlock(&speedtestMutex); + pthread_mutex_unlock(&ni_speedtest_mutex); return FALSE; } - if (!speedtestTimerStarted) + + alreadyRunning = ni_speedtest_thread_running; + ni_speedtest_deadline = deadline; + + if (alreadyRunning) { - speedtestTimerShutdown = FALSE; - err = pthread_create(&speedtestTimerThread, NULL, speedtestTimerHandler, NULL); - if (err != 0) - { - pthread_mutex_unlock(&speedtestMutex); - CcspTraceError(("%s: failed to create SpeedTest timer thread, error=%d\n", __FUNCTION__, err)); - return FALSE; - } - speedtestTimerStarted = TRUE; + pthread_cond_signal(&ni_speedtest_cond); + pthread_mutex_unlock(&ni_speedtest_mutex); + CcspTraceInfo(("%s: SpeedTest timer already running, refreshed deadline\n", __FUNCTION__)); + return TRUE; } - speedtestDeadline = deadline; - speedtestTimerActive = TRUE; - pthread_cond_signal(&speedtestCond); - pthread_mutex_unlock(&speedtestMutex); + ni_speedtest_wake_early = FALSE; + ni_speedtest_thread_running = TRUE; + pthread_mutex_unlock(&ni_speedtest_mutex); + + err = pthread_create(&tid, NULL, ni_speedtest_handler, NULL); + if (err != 0) + { + pthread_mutex_lock(&ni_speedtest_mutex); + ni_speedtest_thread_running = FALSE; + /* Wake any concurrent waiter (e.g. CosaSecurityRemove blocked in + * pthread_cond_wait expecting this thread to finish) since no + * thread was actually created to signal it later. */ + pthread_cond_broadcast(&ni_speedtest_cond); + pthread_mutex_unlock(&ni_speedtest_mutex); + CcspTraceError(("%s: failed to create SpeedTest timer thread, error=%d\n", __FUNCTION__, err)); + return FALSE; + } return TRUE; } -static void speedtestCancelTimer(void) +static void ni_speedtest_complete(void) { - pthread_mutex_lock(&speedtestMutex); - speedtestTimerActive = FALSE; - pthread_cond_signal(&speedtestCond); - pthread_mutex_unlock(&speedtestMutex); + pthread_mutex_lock(&ni_speedtest_mutex); + if (ni_speedtest_thread_running) + { + ni_speedtest_wake_early = TRUE; + pthread_cond_signal(&ni_speedtest_cond); + } + pthread_mutex_unlock(&ni_speedtest_mutex); } static BOOL speedtestGetTimeout(uint32_t *timeout) @@ -548,11 +620,6 @@ static BOOL speedtestGetTimeout(uint32_t *timeout) *timeout = rbusValue_GetUInt32(value); rbusValue_Release(value); - if (*timeout == 0) - { - CcspTraceError(("%s: invalid SpeedTest timeout=0\n", __FUNCTION__)); - return FALSE; - } return TRUE; } @@ -565,7 +632,6 @@ STATIC void speedtestEventReceiveHandler( rbusValue_t value; uint32_t status; uint32_t timeout; - errno_t rc; (void)handle; (void)subscription; @@ -578,32 +644,31 @@ STATIC void speedtestEventReceiveHandler( } status = rbusValue_GetUInt32(value); - CcspTraceInfo(("ARUN: SpeedTest status event received, status=%u\n", status)); + CcspTraceInfo(("%s: SpeedTest status event received, status=%u\n", __FUNCTION__, status)); if (status == 1) { - if (!speedtestGetTimeout(&timeout) || !speedtestArmTimer(timeout)) + if (!speedtestGetTimeout(&timeout)) { - CcspTraceError(("%s: failed to arm SpeedTest timeout; cujo-qosd will not be disabled\n", __FUNCTION__)); + CcspTraceError(("%s: failed to get SpeedTest timeout; cujo-qosd will not be paused\n", __FUNCTION__)); return; } - CcspTraceInfo(("ARUN: SpeedTest status=1, disabling cujo-qosd for speedtest\n")); - rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIStart &"); - if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) + if (timeout == 0) + { + /* SubscriberUnPauseTimeOut of 0 means the pause/unpause + * feature is disabled for this cycle: do not pause or, later, + * resume Network Intelligence. */ + CcspTraceInfo(("%s: SpeedTest timeout is 0, skipping cujo-qosd pause\n", __FUNCTION__)); + return; + } + if (!ni_speedtest_trigger(timeout)) { - speedtestCancelTimer(); - CcspTraceError(("%s: failed to disable cujo-qosd for SpeedTest, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); + CcspTraceError(("%s: failed to start SpeedTest timer; cujo-qosd will not be paused\n", __FUNCTION__)); } } else if (status == 5) { - speedtestCancelTimer(); - CcspTraceInfo(("ARUN: SpeedTest status=5, enabling cujo-qosd after speedtest\n")); - rc = v_secure_system(TEMP_DOWNLOAD_LOCATION"/usr/ccsp/advsec/start_adv_security.sh -speedtestNIComplete &"); - if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) - { - CcspTraceError(("%s: failed to enable cujo-qosd for SpeedTest, rc=%d\n", __FUNCTION__, WEXITSTATUS(rc))); - } + ni_speedtest_complete(); } } #endif @@ -1763,9 +1828,9 @@ CosaSecurityInitialize } #endif #ifdef NETWORK_INTELLIGENCE - pthread_mutex_lock(&speedtestMutex); - speedtestTimerShutdown = FALSE; - pthread_mutex_unlock(&speedtestMutex); + pthread_mutex_lock(&ni_speedtest_mutex); + ni_speedtest_shutdown = FALSE; + pthread_mutex_unlock(&ni_speedtest_mutex); ret = rbusEvent_Subscribe(rbus_handle, SPEEDTEST_STATUS_DML, speedtestEventReceiveHandler, NULL, 0); if(ret != RBUS_ERROR_SUCCESS) { @@ -1787,24 +1852,22 @@ CosaSecurityRemove PCOSA_DATAMODEL_AGENT pMyObject = (PCOSA_DATAMODEL_AGENT)hThisObject; #ifdef NETWORK_INTELLIGENCE - BOOL joinSpeedtestTimer; - - pthread_mutex_lock(&speedtestMutex); - joinSpeedtestTimer = speedtestTimerStarted; - speedtestTimerShutdown = TRUE; - speedtestTimerActive = FALSE; - if (joinSpeedtestTimer) - { - pthread_cond_signal(&speedtestCond); - } - pthread_mutex_unlock(&speedtestMutex); - if (joinSpeedtestTimer) - { - pthread_join(speedtestTimerThread, NULL); - pthread_mutex_lock(&speedtestMutex); - speedtestTimerStarted = FALSE; - pthread_mutex_unlock(&speedtestMutex); + /* Signal shutdown and wake any in-flight (detached) SpeedTest timer + * thread so it unpauses Network Intelligence and exits before we tear + * down. Wait for it to finish since it is detached and cannot be + * joined. */ + pthread_mutex_lock(&ni_speedtest_mutex); + ni_speedtest_shutdown = TRUE; + if (ni_speedtest_thread_running) + { + ni_speedtest_wake_early = TRUE; + pthread_cond_broadcast(&ni_speedtest_cond); + while (ni_speedtest_thread_running) + { + pthread_cond_wait(&ni_speedtest_cond, &ni_speedtest_mutex); + } } + pthread_mutex_unlock(&ni_speedtest_mutex); #endif /* Remove self */ diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 2b94338..66e8b37 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1442,22 +1442,175 @@ TEST_F(CcspAdvSecurityInternalTestFixture, CosaRabidSetDNSCacheSize) } #ifdef NETWORK_INTELLIGENCE +#include + extern "C" void speedtestEventReceiveHandler(rbusHandle_t handle, rbusEvent_t const* event, rbusEventSubscription_t* subscription); +/* Internal SpeedTest timer state exposed (non-static in unit-test builds + * via the STATIC macro) purely so tests can synchronize with the + * asynchronous timer thread. No test-only function is defined in + * production source for this. */ +extern "C" pthread_mutex_t ni_speedtest_mutex; +extern "C" BOOL ni_speedtest_thread_running; + +static const char *g_speedtestNIEnabledPath = "/tmp/advsec_networkintelligence_enabled"; +static const char *g_speedtestNIActivatedPath = "/tmp/advsec_networkintelligence_activated"; + +static void CreateSpeedtestNIEnabledAndActivated(void) +{ + FILE *file = fopen(g_speedtestNIEnabledPath, "w"); + if (file) fclose(file); + file = fopen(g_speedtestNIActivatedPath, "w"); + if (file) fclose(file); +} + +static void RemoveSpeedtestNIEnabledAndActivated(void) +{ + remove(g_speedtestNIEnabledPath); + remove(g_speedtestNIActivatedPath); +} + +/* Test-only helper: reads the SpeedTest timer thread's running state + * under its production mutex. */ +static bool IsSpeedtestThreadRunning(void) +{ + BOOL running; + + pthread_mutex_lock(&ni_speedtest_mutex); + running = ni_speedtest_thread_running; + pthread_mutex_unlock(&ni_speedtest_mutex); + return running ? true : false; +} + +/* Polls IsSpeedtestThreadRunning() until the SpeedTest timer thread + * reaches the desired state or maxWaitMs elapses. The thread's work + * (mocked v_secure_system calls) completes in microseconds, so this + * bounds test time while avoiding a fixed, potentially-flaky sleep. */ +static void WaitForSpeedtestThreadState(bool running, int maxWaitMs) +{ + int waited = 0; + while (IsSpeedtestThreadRunning() != running && waited < maxWaitMs) + { + usleep(2000); + waited += 2; + } +} -TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_StopsNI) +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_PausesAndCompletes_ResumesNI) { int marker = 0; int timeoutMarker = 0; + int completeMarker = 5; rbusValue_t value = (rbusValue_t)▮ rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; + rbusValue_t completeValue = (rbusValue_t)&completeMarker; rbusEvent_t event = {}; + CreateSpeedtestNIEnabledAndActivated(); EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(2) + .WillOnce(Return(value)) + .WillOnce(Return(completeValue)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return(value)); + .WillOnce(Return(1)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) + .Times(1) + .WillOnce(Return(5)); + EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) + .Times(1) + .WillOnce(Return(86400)); + EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) + .Times(1); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("cujo-ni-cli"), _)) + .Times(2) + .WillRepeatedly(Return(0)); + + /* status=1: starts the timer thread, which pauses NI immediately. */ + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(true, 200); + EXPECT_TRUE(IsSpeedtestThreadRunning()); + + /* status=5: wakes the thread early so it resumes NI and exits. */ + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(false, 500); + EXPECT_FALSE(IsSpeedtestThreadRunning()); + + RemoveSpeedtestNIEnabledAndActivated(); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateStart_RefreshesDeadlineOnly) +{ + int marker = 0; + int timeoutMarker = 0; + int completeMarker = 5; + rbusValue_t value = (rbusValue_t)▮ + rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; + rbusValue_t completeValue = (rbusValue_t)&completeMarker; + rbusEvent_t event = {}; + CreateSpeedtestNIEnabledAndActivated(); + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(3) + .WillOnce(Return(value)) + .WillOnce(Return(value)) + .WillOnce(Return(completeValue)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) + .Times(2) + .WillRepeatedly(Return(1)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) + .Times(1) + .WillOnce(Return(5)); + EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) + .Times(2) + .WillRepeatedly(Return(86400)); + EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) + .Times(2); + /* Only ONE pause + ONE resume, even though status=1 fires twice. */ + EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("cujo-ni-cli"), _)) + .Times(2) + .WillRepeatedly(Return(0)); + + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(true, 200); + + /* Duplicate status=1 while already running: refresh deadline only. */ + speedtestEventReceiveHandler(NULL, &event, NULL); + EXPECT_TRUE(IsSpeedtestThreadRunning()); + + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(false, 500); + EXPECT_FALSE(IsSpeedtestThreadRunning()); + + RemoveSpeedtestNIEnabledAndActivated(); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIDisabled_NoAction) +{ + int marker = 0; + int timeoutMarker = 0; + int completeMarker = 5; + rbusValue_t value = (rbusValue_t)▮ + rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; + rbusValue_t completeValue = (rbusValue_t)&completeMarker; + rbusEvent_t event = {}; + RemoveSpeedtestNIEnabledAndActivated(); + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(2) + .WillOnce(Return(value)) + .WillOnce(Return(completeValue)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) .WillOnce(Return(1)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) + .Times(1) + .WillOnce(Return(5)); EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) .Times(1) .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); @@ -1466,11 +1619,59 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_StopsNI) .WillOnce(Return(86400)); EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) .Times(1); - EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("/usr/ccsp/advsec/start_adv_security.sh -speedtestNIStart &"), _)) + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); + + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(true, 200); + + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(false, 500); + EXPECT_FALSE(IsSpeedtestThreadRunning()); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIEnabledButNotActivated_NoAction) +{ + int marker = 0; + int timeoutMarker = 0; + int completeMarker = 5; + rbusValue_t value = (rbusValue_t)▮ + rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; + rbusValue_t completeValue = (rbusValue_t)&completeMarker; + rbusEvent_t event = {}; + remove(g_speedtestNIActivatedPath); + FILE *file = fopen(g_speedtestNIEnabledPath, "w"); + if (file) fclose(file); + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(2) + .WillOnce(Return(value)) + .WillOnce(Return(completeValue)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return(0)); + .WillOnce(Return(1)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) + .Times(1) + .WillOnce(Return(5)); + EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) + .Times(1) + .WillOnce(Return(86400)); + EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) + .Times(1); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); + + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(true, 200); speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(false, 500); + EXPECT_FALSE(IsSpeedtestThreadRunning()); + + remove(g_speedtestNIEnabledPath); } TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_StartingWithZeroTimeout_NoAction) @@ -1499,9 +1700,11 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_StartingWithZeroTime .Times(0); speedtestEventReceiveHandler(NULL, &event, NULL); + + EXPECT_FALSE(IsSpeedtestThreadRunning()); } -TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_StartsNI) +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_NoThreadRunning_NoAction) { int marker = 0; rbusValue_t value = (rbusValue_t)▮ @@ -1513,9 +1716,8 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_StartsNI) EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) .WillOnce(Return(5)); - EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("/usr/ccsp/advsec/start_adv_security.sh -speedtestNIComplete &"), _)) - .Times(1) - .WillOnce(Return(0)); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); speedtestEventReceiveHandler(NULL, &event, NULL); } From ae204c909cac505d22ceef3378b30492036cb96c Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Tue, 22 Sep 2026 16:45:59 +0530 Subject: [PATCH 05/19] Update cujo-ni-cli cmd to avoid parsing error --- .../cosa_adv_security_internal.c | 6 +++--- .../cosa_adv_security_internal.h | 2 ++ .../CcspAdvSecurityInternalTest.cpp | 20 +++++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 198812e..8e4ac33 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -72,7 +72,7 @@ #define SPEEDTEST_STATUS_DML "Device.IP.Diagnostics.X_RDKCENTRAL-COM_SpeedTest.Status" #define SPEEDTEST_TIMEOUT_DML "Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut" #define ADVSEC_NETWORKINTELLIGENCE_ENABLED_PATH "/tmp/advsec_networkintelligence_enabled" -#define CUJONICLI_SET_QOSD_ENABLE_CMD "/usr/bin/cujo-ni-cli \"{\\\"method\\\":\\\"set_configs\\\", \\\"configs\\\": {\\\"cujoniqos.daemon.enable\\\": %d}}\" &" +#define CUJONICLI_SET_QOSD_ENABLE_CMD "/usr/bin/cujo-ni-cli '{\"method\":\"set_configs\", \"configs\": {\"cujoniqos.daemon.enable\": %d}}' &" #define ADVSEC_WAIT_FOR_TIMEOUT (60 * 60) #define MAX_VALUE 32 @@ -646,7 +646,7 @@ STATIC void speedtestEventReceiveHandler( status = rbusValue_GetUInt32(value); CcspTraceInfo(("%s: SpeedTest status event received, status=%u\n", __FUNCTION__, status)); - if (status == 1) + if (status == ST_TR181_STATUS_STARTING) { if (!speedtestGetTimeout(&timeout)) { @@ -666,7 +666,7 @@ STATIC void speedtestEventReceiveHandler( CcspTraceError(("%s: failed to start SpeedTest timer; cujo-qosd will not be paused\n", __FUNCTION__)); } } - else if (status == 5) + else if (status == ST_TR181_STATUS_COMPLETE) { ni_speedtest_complete(); } diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.h b/source/AdvSecurityDml/cosa_adv_security_internal.h index d8cacd4..e3e9a8f 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.h +++ b/source/AdvSecurityDml/cosa_adv_security_internal.h @@ -43,6 +43,8 @@ #define ADVSEC_LogLevel_VERBOSE 4 #define BUFLEN_1024 1024 #define PARTNER_REDIRECTORURL_PARAMNAME "Device.DeviceInfo.X_RDKCENTRAL-COM_Syndication.AdvsecRedirectorURL" +#define ST_TR181_STATUS_STARTING 1 +#define ST_TR181_STATUS_COMPLETE 5 typedef enum { ADVSEC_SAFEBROWSING=0, diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 66e8b37..2aed89e 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1512,10 +1512,10 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_PausesAndCo .WillOnce(Return(completeValue)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return(1)); + .WillOnce(Return(ST_TR181_STATUS_STARTING)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) .Times(1) - .WillOnce(Return(5)); + .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) .Times(1) .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); @@ -1559,10 +1559,10 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateSt .WillOnce(Return(completeValue)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(2) - .WillRepeatedly(Return(1)); + .WillRepeatedly(Return(ST_TR181_STATUS_STARTING)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) .Times(1) - .WillOnce(Return(5)); + .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) .Times(2) .WillRepeatedly(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); @@ -1607,10 +1607,10 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIDisabled_ .WillOnce(Return(completeValue)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return(1)); + .WillOnce(Return(ST_TR181_STATUS_STARTING)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) .Times(1) - .WillOnce(Return(5)); + .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) .Times(1) .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); @@ -1649,10 +1649,10 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIEnabledBu .WillOnce(Return(completeValue)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return(1)); + .WillOnce(Return(ST_TR181_STATUS_STARTING)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) .Times(1) - .WillOnce(Return(5)); + .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) .Times(1) .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); @@ -1687,7 +1687,7 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_StartingWithZeroTime .WillOnce(Return(value)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return(1)); + .WillOnce(Return(ST_TR181_STATUS_STARTING)); EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) .Times(1) .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); @@ -1715,7 +1715,7 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_NoThreadRun .WillOnce(Return(value)); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) - .WillOnce(Return(5)); + .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) .Times(0); From b48d6473f7eaf0b6ddaa29393e84abb548db5525 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Tue, 22 Sep 2026 21:56:53 +0530 Subject: [PATCH 06/19] Update logging and add T2 markers --- source/AdvSecurityDml/Makefile.am | 2 +- .../cosa_adv_security_internal.c | 39 ++++++++++++------- .../cosa_adv_security_webconfig.c | 4 +- .../cosa_network_intelligence_webconfig.c | 4 +- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/source/AdvSecurityDml/Makefile.am b/source/AdvSecurityDml/Makefile.am index 92a273c..da1e1c3 100644 --- a/source/AdvSecurityDml/Makefile.am +++ b/source/AdvSecurityDml/Makefile.am @@ -26,7 +26,7 @@ hardware_platform = i686-linux-gnu lib_LTLIBRARIES = libdmlasecurity.la libdmlasecurity_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/source/AdvSecurityDml -I$(top_srcdir)/source/AdvSecuritySsp $(CPPFLAGS) -I$(top_srcdir)/../Utopia/source/include/sysevent -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/rbus libdmlasecurity_la_SOURCES = plugin_main.c cosa_adv_security_internal.c cosa_adv_security_dml.c cosa_adv_security_webconfig.c advsecurity_helpers.c advsecurity_param.c -libdmlasecurity_la_LDFLAGS = -lccsp_common -lsyscfg -lsysevent -lwebconfig_framework -lmsgpackc -ltrower-base64 -lsecure_wrapper -lrbus -lev -lpthread $(SSP_LDFLAGS) +libdmlasecurity_la_LDFLAGS = -lccsp_common -lsyscfg -lsysevent -lwebconfig_framework -lmsgpackc -ltrower-base64 -lsecure_wrapper -lrbus -lev -lpthread -ltelemetry_msgsender $(SSP_LDFLAGS) if WIFI_DATA_COLLECTION libdmlasecurity_la_CPPFLAGS += -DDML_SUPPORT -DWIFI_DATA_COLLECTION diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 8e4ac33..42a9492 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -161,7 +161,7 @@ static char *g_AdvSecNetworkIntelligenceEnabled = "Adv_AdvSecNetworkIntelligence static char *g_NetworkIntelligenceMemoryLimit = "Advsecurity_NetworkIntelligenceMemoryLimit"; STATIC pthread_mutex_t ni_speedtest_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t ni_speedtest_cond = PTHREAD_COND_INITIALIZER; -static struct timespec ni_speedtest_deadline; +static struct timespec ni_speedtest_timeout; STATIC BOOL ni_speedtest_thread_running = FALSE; static BOOL ni_speedtest_wake_early = FALSE; static BOOL ni_speedtest_shutdown = FALSE; @@ -471,6 +471,14 @@ static BOOL ni_qosd_enable(BOOL enable) __FUNCTION__, enable ? 1 : 0, WEXITSTATUS(rc))); return FALSE; } + if (enable) + { + t2_event_d("SYS_INFO_CUJO_NI_resume", 1); + } + else + { + t2_event_d("SYS_INFO_CUJO_NI_pause", 1); + } return TRUE; } @@ -478,11 +486,11 @@ static BOOL ni_qosd_pause(void) { if (!is_ni_enabled_and_activated()) { - CcspTraceInfo(("%s: cujo-qosd pause skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); + CcspTraceInfo(("%s: Network Intelligence pause skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); return TRUE; } - CcspTraceInfo(("%s: pausing cujo-qosd\n", __FUNCTION__)); + CcspTraceInfo(("%s: pausing Network Intelligence\n", __FUNCTION__)); return ni_qosd_enable(FALSE); } @@ -490,11 +498,11 @@ static BOOL ni_qosd_resume(void) { if (!is_ni_enabled_and_activated()) { - CcspTraceInfo(("%s: cujo-qosd resume skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); + CcspTraceInfo(("%s: Network Intelligence resume skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); return TRUE; } - CcspTraceInfo(("%s: resuming cujo-qosd\n", __FUNCTION__)); + CcspTraceInfo(("%s: resuming Network Intelligence\n", __FUNCTION__)); return ni_qosd_enable(TRUE); } @@ -515,7 +523,7 @@ static void *ni_speedtest_handler(void *arg) pthread_mutex_lock(&ni_speedtest_mutex); while (!ni_speedtest_wake_early && !ni_speedtest_shutdown && waitStatus != ETIMEDOUT) { - waitStatus = pthread_cond_timedwait(&ni_speedtest_cond, &ni_speedtest_mutex, &ni_speedtest_deadline); + waitStatus = pthread_cond_timedwait(&ni_speedtest_cond, &ni_speedtest_mutex, &ni_speedtest_timeout); if (waitStatus != 0 && waitStatus != ETIMEDOUT) { CcspTraceError(("%s: pthread_cond_timedwait failed, error=%d\n", __FUNCTION__, waitStatus)); @@ -528,7 +536,8 @@ static void *ni_speedtest_handler(void *arg) if (timedOut) { - CcspTraceWarning(("IMP_CUJO_NI_SubscriberUnPauseTimeOut: SpeedTest timeout expired, enabling cujo-qosd\n")); + CcspTraceWarning(("%s: SpeedTest timeout expired, resuming Network Intelligence\n", __FUNCTION__)); + t2_event_d("IMP_CUJO_NI_SubscriberUnPauseTimeOut", 1); } if (!ni_qosd_resume()) { @@ -545,17 +554,17 @@ static void *ni_speedtest_handler(void *arg) static BOOL ni_speedtest_trigger(uint32_t timeout) { - struct timespec deadline; + struct timespec ni_resume_timeout; pthread_t tid; int err; BOOL alreadyRunning; - if (clock_gettime(CLOCK_REALTIME, &deadline) != 0) + if (clock_gettime(CLOCK_REALTIME, &ni_resume_timeout) != 0) { CcspTraceError(("%s: clock_gettime failed, errno=%d\n", __FUNCTION__, errno)); return FALSE; } - deadline.tv_sec += timeout; + ni_resume_timeout.tv_sec += timeout; pthread_mutex_lock(&ni_speedtest_mutex); if (ni_speedtest_shutdown) @@ -565,13 +574,13 @@ static BOOL ni_speedtest_trigger(uint32_t timeout) } alreadyRunning = ni_speedtest_thread_running; - ni_speedtest_deadline = deadline; + ni_speedtest_timeout = ni_resume_timeout; if (alreadyRunning) { pthread_cond_signal(&ni_speedtest_cond); pthread_mutex_unlock(&ni_speedtest_mutex); - CcspTraceInfo(("%s: SpeedTest timer already running, refreshed deadline\n", __FUNCTION__)); + CcspTraceInfo(("%s: SpeedTest triggered again, refreshed timeout\n", __FUNCTION__)); return TRUE; } @@ -650,7 +659,7 @@ STATIC void speedtestEventReceiveHandler( { if (!speedtestGetTimeout(&timeout)) { - CcspTraceError(("%s: failed to get SpeedTest timeout; cujo-qosd will not be paused\n", __FUNCTION__)); + CcspTraceError(("%s: failed to get SpeedTest timeout, Network Intelligence will not be paused\n", __FUNCTION__)); return; } if (timeout == 0) @@ -658,12 +667,12 @@ STATIC void speedtestEventReceiveHandler( /* SubscriberUnPauseTimeOut of 0 means the pause/unpause * feature is disabled for this cycle: do not pause or, later, * resume Network Intelligence. */ - CcspTraceInfo(("%s: SpeedTest timeout is 0, skipping cujo-qosd pause\n", __FUNCTION__)); + CcspTraceInfo(("%s: SpeedTest SubscriberUnPauseTimeOut is 0, skipping Network Intelligence pause\n", __FUNCTION__)); return; } if (!ni_speedtest_trigger(timeout)) { - CcspTraceError(("%s: failed to start SpeedTest timer; cujo-qosd will not be paused\n", __FUNCTION__)); + CcspTraceError(("%s: Network Intelligence SpeedTest trigger failed, Network Intelligence will not be paused\n", __FUNCTION__)); } } else if (status == ST_TR181_STATUS_COMPLETE) diff --git a/source/AdvSecurityDml/cosa_adv_security_webconfig.c b/source/AdvSecurityDml/cosa_adv_security_webconfig.c index 7e4a8fb..38d4625 100644 --- a/source/AdvSecurityDml/cosa_adv_security_webconfig.c +++ b/source/AdvSecurityDml/cosa_adv_security_webconfig.c @@ -202,7 +202,7 @@ pErr advsec_webconfig_process_request(void *Data) int advsec_webconfig_rollback() { // return 0 to notify framework when rollback is success - CcspTraceInfo((" Entering %s \n",__FUNCTION__)); + CcspTraceInfo(("Entering %s\n", __FUNCTION__)); int ret = 0; @@ -215,7 +215,7 @@ int advsec_webconfig_rollback() void advsec_webconfig_free_resources(void *arg) { - CcspTraceInfo((" Entering %s \n",__FUNCTION__)); + CcspTraceInfo(("Entering %s\n", __FUNCTION__)); execData *blob_exec_data = (execData*) arg; /* CID 158652: Dereference before null check */ diff --git a/source/AdvSecurityDml/cosa_network_intelligence_webconfig.c b/source/AdvSecurityDml/cosa_network_intelligence_webconfig.c index 63713ca..936a7fb 100644 --- a/source/AdvSecurityDml/cosa_network_intelligence_webconfig.c +++ b/source/AdvSecurityDml/cosa_network_intelligence_webconfig.c @@ -93,7 +93,7 @@ pErr ni_webconfig_process_request(void *Data) int ni_webconfig_rollback() { // return 0 to notify framework when rollback is success - CcspTraceInfo((" Entering %s \n",__FUNCTION__)); + CcspTraceInfo(("Entering %s\n", __FUNCTION__)); int ret = 0; @@ -105,7 +105,7 @@ int ni_webconfig_rollback() /* Callback function to free webconfig resources */ void ni_webconfig_free_resources(void *arg) { - CcspTraceInfo((" Entering %s \n",__FUNCTION__)); + CcspTraceInfo(("Entering %s\n", __FUNCTION__)); execData *blob_exec_data = (execData*) arg; if ( blob_exec_data != NULL ) From 377f47c07a3223361dd55579d85aa0de72eaef52 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Tue, 22 Sep 2026 22:12:21 +0530 Subject: [PATCH 07/19] Add header for T2 event marker --- source/AdvSecurityDml/cosa_adv_security_internal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.h b/source/AdvSecurityDml/cosa_adv_security_internal.h index e3e9a8f..2389bc1 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.h +++ b/source/AdvSecurityDml/cosa_adv_security_internal.h @@ -31,6 +31,7 @@ #include #include "ansc_platform.h" #include "ansc_string_util.h" +#include #define ADVSEC_MIN_LOG_TIMEOUT (60 * 1) #define ADVSEC_MAX_LOG_TIMEOUT (60 * 48) From f803ff6e20492b13f02fb4728b512a57f1036ae5 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Tue, 22 Sep 2026 22:20:13 +0530 Subject: [PATCH 08/19] Fix unit test binary compilation error --- source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h index f73fbf9..0b84b74 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include From f5a8e7313aae8b3de1f4ee790d43920131b111fd Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Tue, 22 Sep 2026 22:32:43 +0530 Subject: [PATCH 09/19] Add mock telemetry library for unit test --- source/test/CcspAdvSecurityDmlTest/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/source/test/CcspAdvSecurityDmlTest/Makefile.am b/source/test/CcspAdvSecurityDmlTest/Makefile.am index 0fcbcb0..49db1db 100644 --- a/source/test/CcspAdvSecurityDmlTest/Makefile.am +++ b/source/test/CcspAdvSecurityDmlTest/Makefile.am @@ -57,6 +57,7 @@ CcspAdvSecurityDmlTest_gtest_bin_LDADD = \ $(HOME)/usr/local/lib/libmock_secure_wrapper.la \ $(HOME)/usr/local/lib/libmock_msgpack.la \ $(HOME)/usr/local/lib/libmock_safec_lib.la \ + $(HOME)/usr/local/lib/libmock_telemetry.la \ $(HOME)/usr/local/lib/libmock_webconfigframework.la \ $(HOME)/usr/local/lib/libmock_base64.la \ $(HOME)/usr/local/lib/libmock_rbus.la \ From 9e133cb8f8bcb9c2d84a31747da7b03db7e47315 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Tue, 22 Sep 2026 23:04:06 +0530 Subject: [PATCH 10/19] Fix unit test compilation issue --- source/AdvSecurityDml/cosa_adv_security_internal.c | 7 +++++-- .../test/CcspAdvSecurityDmlTest/CcspAdvSecurityDmlTest.cpp | 3 +++ .../CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp | 3 +++ source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.cpp | 1 + source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h | 3 ++- .../CcspAdvSecurityWebconfigTest.cpp | 3 +++ 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 42a9492..1710d2b 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -586,12 +586,14 @@ static BOOL ni_speedtest_trigger(uint32_t timeout) ni_speedtest_wake_early = FALSE; ni_speedtest_thread_running = TRUE; - pthread_mutex_unlock(&ni_speedtest_mutex); + /* Hold the mutex across pthread_create() itself so a concurrent + * CosaSecurityRemove() can never observe ni_speedtest_thread_running + * == TRUE while thread creation is still in progress (and thus no + * thread exists yet to eventually broadcast completion). */ err = pthread_create(&tid, NULL, ni_speedtest_handler, NULL); if (err != 0) { - pthread_mutex_lock(&ni_speedtest_mutex); ni_speedtest_thread_running = FALSE; /* Wake any concurrent waiter (e.g. CosaSecurityRemove blocked in * pthread_cond_wait expecting this thread to finish) since no @@ -601,6 +603,7 @@ static BOOL ni_speedtest_trigger(uint32_t timeout) CcspTraceError(("%s: failed to create SpeedTest timer thread, error=%d\n", __FUNCTION__, err)); return FALSE; } + pthread_mutex_unlock(&ni_speedtest_mutex); return TRUE; } diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityDmlTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityDmlTest.cpp index 3525b14..791fe8c 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityDmlTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityDmlTest.cpp @@ -41,6 +41,7 @@ class CcspAdvSecurityDmlTestFixture : public ::testing::Test { g_syseventMock = new SyseventMock(); g_webconfigFwMock = new webconfigFwMock(); g_anscWrapperApiMock = new AnscWrapperApiMock(); + g_telemetryMock = new telemetryMock(); } void TearDown() override { @@ -60,6 +61,7 @@ class CcspAdvSecurityDmlTestFixture : public ::testing::Test { delete g_syseventMock; delete g_webconfigFwMock; delete g_anscWrapperApiMock; + delete g_telemetryMock; g_syscfgMock = nullptr; g_securewrapperMock = nullptr; g_msgpackMock = nullptr; @@ -76,6 +78,7 @@ class CcspAdvSecurityDmlTestFixture : public ::testing::Test { g_syseventMock = nullptr; g_webconfigFwMock = nullptr; g_anscWrapperApiMock = nullptr; + g_telemetryMock = nullptr; } }; diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 2aed89e..506d5cc 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -43,6 +43,7 @@ class CcspAdvSecurityInternalTestFixture : public ::testing::Test { g_syseventMock = new SyseventMock(); g_webconfigFwMock = new webconfigFwMock(); g_anscWrapperApiMock = new AnscWrapperApiMock(); + g_telemetryMock = new telemetryMock(); } void TearDown() override { @@ -62,6 +63,7 @@ class CcspAdvSecurityInternalTestFixture : public ::testing::Test { delete g_syseventMock; delete g_webconfigFwMock; delete g_anscWrapperApiMock; + delete g_telemetryMock; g_syscfgMock = nullptr; g_securewrapperMock = nullptr; g_msgpackMock = nullptr; @@ -78,6 +80,7 @@ class CcspAdvSecurityInternalTestFixture : public ::testing::Test { g_syseventMock = nullptr; g_webconfigFwMock = nullptr; g_anscWrapperApiMock = nullptr; + g_telemetryMock = nullptr; } }; diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.cpp index a690f88..342b12e 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.cpp @@ -34,3 +34,4 @@ cjsonMock *g_cjsonMock = NULL; SyseventMock *g_syseventMock = NULL; webconfigFwMock *g_webconfigFwMock = NULL; AnscWrapperApiMock * g_anscWrapperApiMock = NULL; +telemetryMock *g_telemetryMock = NULL; diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h index 0b84b74..1223968 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityMock.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -44,6 +43,7 @@ #include #include #include +#include extern SyscfgMock *g_syscfgMock; extern SecureWrapperMock *g_securewrapperMock; @@ -61,6 +61,7 @@ extern cjsonMock *g_cjsonMock; extern SyseventMock *g_syseventMock; extern webconfigFwMock *g_webconfigFwMock; extern AnscWrapperApiMock *g_anscWrapperApiMock; +extern telemetryMock *g_telemetryMock; using namespace std; using std::experimental::filesystem::exists; diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityWebconfigTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityWebconfigTest.cpp index 2c24502..a1e000c 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityWebconfigTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityWebconfigTest.cpp @@ -37,6 +37,7 @@ class CcspAdvSecurityWebconfigTestFixture : public ::testing::Test { g_syseventMock = new SyseventMock(); g_webconfigFwMock = new webconfigFwMock(); g_anscWrapperApiMock = new AnscWrapperApiMock(); + g_telemetryMock = new telemetryMock(); } void TearDown() override { @@ -56,6 +57,7 @@ class CcspAdvSecurityWebconfigTestFixture : public ::testing::Test { delete g_syseventMock; delete g_webconfigFwMock; delete g_anscWrapperApiMock; + delete g_telemetryMock; g_syscfgMock = nullptr; g_securewrapperMock = nullptr; g_msgpackMock = nullptr; @@ -72,6 +74,7 @@ class CcspAdvSecurityWebconfigTestFixture : public ::testing::Test { g_syseventMock = nullptr; g_webconfigFwMock = nullptr; g_anscWrapperApiMock = nullptr; + g_telemetryMock = nullptr; } }; From ac365ababb70ebc151c6544cb37af8762f391857 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Wed, 23 Sep 2026 10:40:22 +0530 Subject: [PATCH 11/19] remove refresh timeout logic on multiple ST_TR181_STATUS_STARTING events --- .../cosa_adv_security_internal.c | 48 +++++++---------- .../CcspAdvSecurityInternalTest.cpp | 52 ++++++++++++++++--- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 1710d2b..1b6fa65 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -518,6 +518,10 @@ static void *ni_speedtest_handler(void *arg) if (!ni_qosd_pause()) { CcspTraceError(("%s: failed to pause Network Intelligence for SpeedTest\n", __FUNCTION__)); + pthread_mutex_lock(&ni_speedtest_mutex); + ni_speedtest_thread_running = FALSE; + pthread_mutex_unlock(&ni_speedtest_mutex); + return NULL; } pthread_mutex_lock(&ni_speedtest_mutex); @@ -546,18 +550,26 @@ static void *ni_speedtest_handler(void *arg) pthread_mutex_lock(&ni_speedtest_mutex); ni_speedtest_thread_running = FALSE; - pthread_cond_broadcast(&ni_speedtest_cond); pthread_mutex_unlock(&ni_speedtest_mutex); return NULL; } +static BOOL is_ni_speedtest_running(void) +{ + BOOL running; + + pthread_mutex_lock(&ni_speedtest_mutex); + running = ni_speedtest_thread_running; + pthread_mutex_unlock(&ni_speedtest_mutex); + return running; +} + static BOOL ni_speedtest_trigger(uint32_t timeout) { struct timespec ni_resume_timeout; pthread_t tid; int err; - BOOL alreadyRunning; if (clock_gettime(CLOCK_REALTIME, &ni_resume_timeout) != 0) { @@ -573,32 +585,15 @@ static BOOL ni_speedtest_trigger(uint32_t timeout) return FALSE; } - alreadyRunning = ni_speedtest_thread_running; ni_speedtest_timeout = ni_resume_timeout; - if (alreadyRunning) - { - pthread_cond_signal(&ni_speedtest_cond); - pthread_mutex_unlock(&ni_speedtest_mutex); - CcspTraceInfo(("%s: SpeedTest triggered again, refreshed timeout\n", __FUNCTION__)); - return TRUE; - } - ni_speedtest_wake_early = FALSE; ni_speedtest_thread_running = TRUE; - /* Hold the mutex across pthread_create() itself so a concurrent - * CosaSecurityRemove() can never observe ni_speedtest_thread_running - * == TRUE while thread creation is still in progress (and thus no - * thread exists yet to eventually broadcast completion). */ err = pthread_create(&tid, NULL, ni_speedtest_handler, NULL); if (err != 0) { ni_speedtest_thread_running = FALSE; - /* Wake any concurrent waiter (e.g. CosaSecurityRemove blocked in - * pthread_cond_wait expecting this thread to finish) since no - * thread was actually created to signal it later. */ - pthread_cond_broadcast(&ni_speedtest_cond); pthread_mutex_unlock(&ni_speedtest_mutex); CcspTraceError(("%s: failed to create SpeedTest timer thread, error=%d\n", __FUNCTION__, err)); return FALSE; @@ -660,6 +655,11 @@ STATIC void speedtestEventReceiveHandler( if (status == ST_TR181_STATUS_STARTING) { + if (is_ni_speedtest_running()) + { + CcspTraceInfo(("%s: Network Intelligence SpeedTest handler already in progress, ignoring ST_TR181_STATUS_STARTING event\n", __FUNCTION__)); + return; + } if (!speedtestGetTimeout(&timeout)) { CcspTraceError(("%s: failed to get SpeedTest timeout, Network Intelligence will not be paused\n", __FUNCTION__)); @@ -1864,20 +1864,12 @@ CosaSecurityRemove PCOSA_DATAMODEL_AGENT pMyObject = (PCOSA_DATAMODEL_AGENT)hThisObject; #ifdef NETWORK_INTELLIGENCE - /* Signal shutdown and wake any in-flight (detached) SpeedTest timer - * thread so it unpauses Network Intelligence and exits before we tear - * down. Wait for it to finish since it is detached and cannot be - * joined. */ pthread_mutex_lock(&ni_speedtest_mutex); ni_speedtest_shutdown = TRUE; if (ni_speedtest_thread_running) { ni_speedtest_wake_early = TRUE; - pthread_cond_broadcast(&ni_speedtest_cond); - while (ni_speedtest_thread_running) - { - pthread_cond_wait(&ni_speedtest_cond, &ni_speedtest_mutex); - } + pthread_cond_signal(&ni_speedtest_cond); } pthread_mutex_unlock(&ni_speedtest_mutex); #endif diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 506d5cc..d5e4e94 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1544,7 +1544,44 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_PausesAndCo RemoveSpeedtestNIEnabledAndActivated(); } -TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateStart_RefreshesDeadlineOnly) +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_PauseFails_ClearsRunningState) +{ + int marker = 0; + int timeoutMarker = 0; + rbusValue_t value = (rbusValue_t)▮ + rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; + rbusEvent_t event = {}; + CreateSpeedtestNIEnabledAndActivated(); + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return(value)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) + .Times(1) + .WillOnce(Return(ST_TR181_STATUS_STARTING)); + EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) + .Times(1) + .WillOnce(Return(86400)); + EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) + .Times(1); + /* Simulate cujo-ni-cli failing (non-zero exit status) on the pause + * attempt. The thread must still clear ni_speedtest_thread_running + * so future SpeedTest cycles are not permanently blocked. */ + EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("cujo-ni-cli"), _)) + .Times(1) + .WillOnce(Return(256)); + + speedtestEventReceiveHandler(NULL, &event, NULL); + WaitForSpeedtestThreadState(false, 500); + EXPECT_FALSE(IsSpeedtestThreadRunning()); + + RemoveSpeedtestNIEnabledAndActivated(); +} + + { int marker = 0; int timeoutMarker = 0; @@ -1567,13 +1604,13 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateSt .Times(1) .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) - .Times(2) - .WillRepeatedly(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + .Times(1) + .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) - .Times(2) - .WillRepeatedly(Return(86400)); + .Times(1) + .WillOnce(Return(86400)); EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) - .Times(2); + .Times(1); /* Only ONE pause + ONE resume, even though status=1 fires twice. */ EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("cujo-ni-cli"), _)) .Times(2) @@ -1582,7 +1619,8 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateSt speedtestEventReceiveHandler(NULL, &event, NULL); WaitForSpeedtestThreadState(true, 200); - /* Duplicate status=1 while already running: refresh deadline only. */ + /* Duplicate status=1 while already running: ignored entirely before + * even fetching the timeout, no signal sent to the thread. */ speedtestEventReceiveHandler(NULL, &event, NULL); EXPECT_TRUE(IsSpeedtestThreadRunning()); From 8203e31c2d543f95ff367c3016c1ec2b3efc8eeb Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Wed, 23 Sep 2026 11:14:06 +0530 Subject: [PATCH 12/19] Fix gtest compilation issue --- .../test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index d5e4e94..479711f 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1582,6 +1582,7 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_PauseFails_ } +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateStart_Ignored) { int marker = 0; int timeoutMarker = 0; From 264f19cee8d0f6efc8f4757b8f987ee1a06c4ebe Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Wed, 23 Sep 2026 20:43:16 +0530 Subject: [PATCH 13/19] Skip ST_TR181_STATUS_STARTING event if NI pause is in progress --- .../cosa_adv_security_internal.c | 21 ++++++++----------- .../CcspAdvSecurityInternalTest.cpp | 5 +++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 1b6fa65..317a7a7 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -517,7 +517,7 @@ static void *ni_speedtest_handler(void *arg) if (!ni_qosd_pause()) { - CcspTraceError(("%s: failed to pause Network Intelligence for SpeedTest\n", __FUNCTION__)); + CcspTraceError(("%s: failed to pause Network Intelligence for speedtest\n", __FUNCTION__)); pthread_mutex_lock(&ni_speedtest_mutex); ni_speedtest_thread_running = FALSE; pthread_mutex_unlock(&ni_speedtest_mutex); @@ -540,12 +540,12 @@ static void *ni_speedtest_handler(void *arg) if (timedOut) { - CcspTraceWarning(("%s: SpeedTest timeout expired, resuming Network Intelligence\n", __FUNCTION__)); + CcspTraceWarning(("%s: speedtest timeout expired, resuming Network Intelligence\n", __FUNCTION__)); t2_event_d("IMP_CUJO_NI_SubscriberUnPauseTimeOut", 1); } if (!ni_qosd_resume()) { - CcspTraceError(("%s: failed to resume Network Intelligence after SpeedTest\n", __FUNCTION__)); + CcspTraceError(("%s: failed to resume Network Intelligence after speedtest\n", __FUNCTION__)); } pthread_mutex_lock(&ni_speedtest_mutex); @@ -595,7 +595,7 @@ static BOOL ni_speedtest_trigger(uint32_t timeout) { ni_speedtest_thread_running = FALSE; pthread_mutex_unlock(&ni_speedtest_mutex); - CcspTraceError(("%s: failed to create SpeedTest timer thread, error=%d\n", __FUNCTION__, err)); + CcspTraceError(("%s: failed to create speedtest timer thread, error=%d\n", __FUNCTION__, err)); return FALSE; } pthread_mutex_unlock(&ni_speedtest_mutex); @@ -651,31 +651,28 @@ STATIC void speedtestEventReceiveHandler( } status = rbusValue_GetUInt32(value); - CcspTraceInfo(("%s: SpeedTest status event received, status=%u\n", __FUNCTION__, status)); + CcspTraceInfo(("%s: speedtest status event received, status=%u\n", __FUNCTION__, status)); if (status == ST_TR181_STATUS_STARTING) { if (is_ni_speedtest_running()) { - CcspTraceInfo(("%s: Network Intelligence SpeedTest handler already in progress, ignoring ST_TR181_STATUS_STARTING event\n", __FUNCTION__)); + CcspTraceWarning(("%s: Network Intelligence speedtest handler already in progress, ignoring ST_TR181_STATUS_STARTING event\n", __FUNCTION__)); return; } if (!speedtestGetTimeout(&timeout)) { - CcspTraceError(("%s: failed to get SpeedTest timeout, Network Intelligence will not be paused\n", __FUNCTION__)); + CcspTraceError(("%s: failed to get speedtest unpause timeout, Network Intelligence will not be paused\n", __FUNCTION__)); return; } if (timeout == 0) { - /* SubscriberUnPauseTimeOut of 0 means the pause/unpause - * feature is disabled for this cycle: do not pause or, later, - * resume Network Intelligence. */ - CcspTraceInfo(("%s: SpeedTest SubscriberUnPauseTimeOut is 0, skipping Network Intelligence pause\n", __FUNCTION__)); + CcspTraceInfo(("%s: speedtest unpause timeout is 0, skipping Network Intelligence pause\n", __FUNCTION__)); return; } if (!ni_speedtest_trigger(timeout)) { - CcspTraceError(("%s: Network Intelligence SpeedTest trigger failed, Network Intelligence will not be paused\n", __FUNCTION__)); + CcspTraceError(("%s: Network Intelligence speedtest trigger failed, Network Intelligence will not be paused\n", __FUNCTION__)); } } else if (status == ST_TR181_STATUS_COMPLETE) diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 479711f..6fe8a46 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1619,9 +1619,10 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateSt speedtestEventReceiveHandler(NULL, &event, NULL); WaitForSpeedtestThreadState(true, 200); + EXPECT_TRUE(IsSpeedtestThreadRunning()); - /* Duplicate status=1 while already running: ignored entirely before - * even fetching the timeout, no signal sent to the thread. */ + /* Duplicate status=1 while already running: ignored entirely (with + * a warning logged) before even fetching the timeout. */ speedtestEventReceiveHandler(NULL, &event, NULL); EXPECT_TRUE(IsSpeedtestThreadRunning()); From 1183277e57e5e7c02effc2ff225024b7a050a7f6 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Wed, 23 Sep 2026 21:32:29 +0530 Subject: [PATCH 14/19] Have NI enabled and activated check in speedtest handler --- .../cosa_adv_security_internal.c | 36 ++----- .../CcspAdvSecurityInternalTest.cpp | 101 ++++++++++++++---- 2 files changed, 88 insertions(+), 49 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 317a7a7..79994c1 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -482,30 +482,6 @@ static BOOL ni_qosd_enable(BOOL enable) return TRUE; } -static BOOL ni_qosd_pause(void) -{ - if (!is_ni_enabled_and_activated()) - { - CcspTraceInfo(("%s: Network Intelligence pause skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); - return TRUE; - } - - CcspTraceInfo(("%s: pausing Network Intelligence\n", __FUNCTION__)); - return ni_qosd_enable(FALSE); -} - -static BOOL ni_qosd_resume(void) -{ - if (!is_ni_enabled_and_activated()) - { - CcspTraceInfo(("%s: Network Intelligence resume skipped due to Network Intelligence RFC is disabled or not activated\n", __FUNCTION__)); - return TRUE; - } - - CcspTraceInfo(("%s: resuming Network Intelligence\n", __FUNCTION__)); - return ni_qosd_enable(TRUE); -} - static void *ni_speedtest_handler(void *arg) { int waitStatus = 0; @@ -515,7 +491,7 @@ static void *ni_speedtest_handler(void *arg) pthread_detach(pthread_self()); - if (!ni_qosd_pause()) + if (!ni_qosd_enable(FALSE)) { CcspTraceError(("%s: failed to pause Network Intelligence for speedtest\n", __FUNCTION__)); pthread_mutex_lock(&ni_speedtest_mutex); @@ -543,7 +519,7 @@ static void *ni_speedtest_handler(void *arg) CcspTraceWarning(("%s: speedtest timeout expired, resuming Network Intelligence\n", __FUNCTION__)); t2_event_d("IMP_CUJO_NI_SubscriberUnPauseTimeOut", 1); } - if (!ni_qosd_resume()) + if (!ni_qosd_enable(TRUE)) { CcspTraceError(("%s: failed to resume Network Intelligence after speedtest\n", __FUNCTION__)); } @@ -619,7 +595,7 @@ static BOOL speedtestGetTimeout(uint32_t *timeout) int ret; ret = rbus_get(rbus_handle, SPEEDTEST_TIMEOUT_DML, &value); - if (ret != RBUS_ERROR_SUCCESS || value == NULL) + if (ret != RBUS_ERROR_SUCCESS) { CcspTraceError(("%s: rbus_get failed for %s, error=%d\n", __FUNCTION__, SPEEDTEST_TIMEOUT_DML, ret)); return FALSE; @@ -653,6 +629,12 @@ STATIC void speedtestEventReceiveHandler( status = rbusValue_GetUInt32(value); CcspTraceInfo(("%s: speedtest status event received, status=%u\n", __FUNCTION__, status)); + if (!is_ni_enabled_and_activated()) + { + CcspTraceInfo(("%s: Network Intelligence is disabled or not activated, skipping speedtest event\n", __FUNCTION__)); + return; + } + if (status == ST_TR181_STATUS_STARTING) { if (is_ni_speedtest_running()) diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 6fe8a46..2779be2 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1636,10 +1636,8 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateSt TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIDisabled_NoAction) { int marker = 0; - int timeoutMarker = 0; int completeMarker = 5; rbusValue_t value = (rbusValue_t)▮ - rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; rbusValue_t completeValue = (rbusValue_t)&completeMarker; rbusEvent_t event = {}; RemoveSpeedtestNIEnabledAndActivated(); @@ -1654,32 +1652,28 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIDisabled_ EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) .Times(1) .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); + /* NI is disabled: speedtestEventReceiveHandler now returns before + * ever fetching the timeout, for any status. */ EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) - .Times(1) - .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); - EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) - .Times(1) - .WillOnce(Return(86400)); - EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) - .Times(1); + .Times(0); EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) .Times(0); + /* NI is disabled: the top-level is_ni_enabled_and_activated() check + * in speedtestEventReceiveHandler skips the event entirely, so + * ni_speedtest_thread_running never becomes TRUE. */ speedtestEventReceiveHandler(NULL, &event, NULL); - WaitForSpeedtestThreadState(true, 200); + EXPECT_FALSE(IsSpeedtestThreadRunning()); speedtestEventReceiveHandler(NULL, &event, NULL); - WaitForSpeedtestThreadState(false, 500); EXPECT_FALSE(IsSpeedtestThreadRunning()); } TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIEnabledButNotActivated_NoAction) { int marker = 0; - int timeoutMarker = 0; int completeMarker = 5; rbusValue_t value = (rbusValue_t)▮ - rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; rbusValue_t completeValue = (rbusValue_t)&completeMarker; rbusEvent_t event = {}; remove(g_speedtestNIActivatedPath); @@ -1696,22 +1690,19 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_NIEnabledBu EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) .Times(1) .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); + /* NI is enabled but not activated: still gated out before ever + * fetching the timeout. */ EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) - .Times(1) - .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); - EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) - .Times(1) - .WillOnce(Return(86400)); - EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) - .Times(1); + .Times(0); EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) .Times(0); + /* NI is enabled but not activated: the top-level + * is_ni_enabled_and_activated() check skips the event entirely. */ speedtestEventReceiveHandler(NULL, &event, NULL); - WaitForSpeedtestThreadState(true, 200); + EXPECT_FALSE(IsSpeedtestThreadRunning()); speedtestEventReceiveHandler(NULL, &event, NULL); - WaitForSpeedtestThreadState(false, 500); EXPECT_FALSE(IsSpeedtestThreadRunning()); remove(g_speedtestNIEnabledPath); @@ -1724,6 +1715,7 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_StartingWithZeroTime rbusValue_t value = (rbusValue_t)▮ rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; rbusEvent_t event = {}; + CreateSpeedtestNIEnabledAndActivated(); EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) .Times(1) @@ -1745,6 +1737,44 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_StartingWithZeroTime speedtestEventReceiveHandler(NULL, &event, NULL); EXPECT_FALSE(IsSpeedtestThreadRunning()); + + RemoveSpeedtestNIEnabledAndActivated(); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_GetTimeoutFails_NoAction) +{ + int marker = 0; + int timeoutMarker = 0; + rbusValue_t value = (rbusValue_t)▮ + rbusValue_t timeoutValue = (rbusValue_t)&timeoutMarker; + rbusEvent_t event = {}; + CreateSpeedtestNIEnabledAndActivated(); + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return(value)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) + .Times(1) + .WillOnce(Return(ST_TR181_STATUS_STARTING)); + /* NOTE: rbus_get() fails but still populates a non-NULL value here + * (a real rbus behavior in some implementations, e.g. partial/cached + * state before returning an error). speedtestGetTimeout() currently + * does NOT release it on this path (ret != RBUS_ERROR_SUCCESS short- + * circuits before touching value) - this is a known rbus value leak + * on the rbus_get() failure path. */ + EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) + .Times(1) + .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_BUS_ERROR))); + EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) + .Times(0); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); + + speedtestEventReceiveHandler(NULL, &event, NULL); + + EXPECT_FALSE(IsSpeedtestThreadRunning()); + + RemoveSpeedtestNIEnabledAndActivated(); } TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_NoThreadRunning_NoAction) @@ -1752,6 +1782,28 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_NoThreadRun int marker = 0; rbusValue_t value = (rbusValue_t)▮ rbusEvent_t event = {}; + CreateSpeedtestNIEnabledAndActivated(); + + EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) + .Times(1) + .WillOnce(Return(value)); + EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) + .Times(1) + .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); + EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) + .Times(0); + + speedtestEventReceiveHandler(NULL, &event, NULL); + + RemoveSpeedtestNIEnabledAndActivated(); +} + +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_NIDisabled_NoAction) +{ + int marker = 0; + rbusValue_t value = (rbusValue_t)▮ + rbusEvent_t event = {}; + RemoveSpeedtestNIEnabledAndActivated(); EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) .Times(1) @@ -1762,6 +1814,8 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Complete_NoThreadRun EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) .Times(0); + /* NI disabled: the top-level is_ni_enabled_and_activated() check now + * gates COMPLETE the same as STARTING. */ speedtestEventReceiveHandler(NULL, &event, NULL); } @@ -1783,6 +1837,7 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Other_NoAction) int marker = 0; rbusValue_t value = (rbusValue_t)▮ rbusEvent_t event = {}; + CreateSpeedtestNIEnabledAndActivated(); EXPECT_CALL(*g_rbusMock, rbusObject_GetValue(_, _)) .Times(1) @@ -1794,6 +1849,8 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Other_NoAction) .Times(0); speedtestEventReceiveHandler(NULL, &event, NULL); + + RemoveSpeedtestNIEnabledAndActivated(); } #endif From 31bcdcb2e2f5340e95e9eb9e7847c9f23e130a2d Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Wed, 23 Sep 2026 21:47:44 +0530 Subject: [PATCH 15/19] Fix rbus leak on rbus_get failure case --- source/AdvSecurityDml/cosa_adv_security_internal.c | 4 ++++ .../CcspAdvSecurityInternalTest.cpp | 14 ++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 79994c1..23d74d8 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -598,6 +598,10 @@ static BOOL speedtestGetTimeout(uint32_t *timeout) if (ret != RBUS_ERROR_SUCCESS) { CcspTraceError(("%s: rbus_get failed for %s, error=%d\n", __FUNCTION__, SPEEDTEST_TIMEOUT_DML, ret)); + if (value != NULL) + { + rbusValue_Release(value); + } return FALSE; } diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 2779be2..797709f 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1741,7 +1741,7 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_StartingWithZeroTime RemoveSpeedtestNIEnabledAndActivated(); } -TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_GetTimeoutFails_NoAction) +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_GetTimeoutFailsWithValue_ReleasesRbusValue) { int marker = 0; int timeoutMarker = 0; @@ -1756,17 +1756,15 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_GetTimeoutF EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(value)) .Times(1) .WillOnce(Return(ST_TR181_STATUS_STARTING)); - /* NOTE: rbus_get() fails but still populates a non-NULL value here - * (a real rbus behavior in some implementations, e.g. partial/cached - * state before returning an error). speedtestGetTimeout() currently - * does NOT release it on this path (ret != RBUS_ERROR_SUCCESS short- - * circuits before touching value) - this is a known rbus value leak - * on the rbus_get() failure path. */ + /* rbus_get() fails but still populates a non-NULL value (a real + * rbus behavior in some implementations, e.g. partial/cached state + * before returning an error). speedtestGetTimeout() must release it + * instead of leaking it. */ EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) .Times(1) .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_BUS_ERROR))); EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) - .Times(0); + .Times(1); EXPECT_CALL(*g_securewrapperMock, v_secure_system(_, _)) .Times(0); From 5ce0e0daf67a9bf176f2f8f06b41fd48472ff8cf Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Sat, 26 Sep 2026 22:38:31 +0530 Subject: [PATCH 16/19] Re-introduce refresh timeout on ST_TR181_STATUS_STARTING event --- .../cosa_adv_security_internal.c | 24 +++++++------------ .../CcspAdvSecurityInternalTest.cpp | 23 +++++++++++------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 23d74d8..58abd3e 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -531,16 +531,6 @@ static void *ni_speedtest_handler(void *arg) return NULL; } -static BOOL is_ni_speedtest_running(void) -{ - BOOL running; - - pthread_mutex_lock(&ni_speedtest_mutex); - running = ni_speedtest_thread_running; - pthread_mutex_unlock(&ni_speedtest_mutex); - return running; -} - static BOOL ni_speedtest_trigger(uint32_t timeout) { struct timespec ni_resume_timeout; @@ -561,6 +551,15 @@ static BOOL ni_speedtest_trigger(uint32_t timeout) return FALSE; } + if (ni_speedtest_thread_running) + { + ni_speedtest_timeout = ni_resume_timeout; + pthread_cond_signal(&ni_speedtest_cond); + pthread_mutex_unlock(&ni_speedtest_mutex); + CcspTraceInfo(("%s: Network Intelligence already paused for speedtest, refreshed timeout\n", __FUNCTION__)); + return TRUE; + } + ni_speedtest_timeout = ni_resume_timeout; ni_speedtest_wake_early = FALSE; @@ -641,11 +640,6 @@ STATIC void speedtestEventReceiveHandler( if (status == ST_TR181_STATUS_STARTING) { - if (is_ni_speedtest_running()) - { - CcspTraceWarning(("%s: Network Intelligence speedtest handler already in progress, ignoring ST_TR181_STATUS_STARTING event\n", __FUNCTION__)); - return; - } if (!speedtestGetTimeout(&timeout)) { CcspTraceError(("%s: failed to get speedtest unpause timeout, Network Intelligence will not be paused\n", __FUNCTION__)); diff --git a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp index 797709f..357bf1c 100644 --- a/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp +++ b/source/test/CcspAdvSecurityDmlTest/CcspAdvSecurityInternalTest.cpp @@ -1582,7 +1582,7 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_PauseFails_ } -TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateStart_Ignored) +TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateStart_RefreshesTimeout) { int marker = 0; int timeoutMarker = 0; @@ -1604,15 +1604,20 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateSt EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(completeValue)) .Times(1) .WillOnce(Return(ST_TR181_STATUS_COMPLETE)); + /* Timeout is fetched again on the duplicate STARTING event, since + * ni_speedtest_trigger() now refreshes the running timer's deadline + * instead of ignoring the event. */ EXPECT_CALL(*g_rbusMock, rbus_get(_, StrEq("Device.IP.Diagnostics.X_RDK_SpeedTest.SubscriberUnPauseTimeOut"), _)) - .Times(1) - .WillOnce(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<2>(timeoutValue), Return(RBUS_ERROR_SUCCESS))); EXPECT_CALL(*g_rbusMock, rbusValue_GetUInt32(timeoutValue)) - .Times(1) - .WillOnce(Return(86400)); + .Times(2) + .WillRepeatedly(Return(86400)); EXPECT_CALL(*g_rbusMock, rbusValue_Release(timeoutValue)) - .Times(1); - /* Only ONE pause + ONE resume, even though status=1 fires twice. */ + .Times(2); + /* Only ONE pause + ONE resume, even though status=1 fires twice: + * the duplicate STARTING event only refreshes the deadline, it does + * not re-pause Network Intelligence or spawn a new thread. */ EXPECT_CALL(*g_securewrapperMock, v_secure_system(HasSubstr("cujo-ni-cli"), _)) .Times(2) .WillRepeatedly(Return(0)); @@ -1621,8 +1626,8 @@ TEST_F(CcspAdvSecurityInternalTestFixture, SpeedTest_Status_Starting_DuplicateSt WaitForSpeedtestThreadState(true, 200); EXPECT_TRUE(IsSpeedtestThreadRunning()); - /* Duplicate status=1 while already running: ignored entirely (with - * a warning logged) before even fetching the timeout. */ + /* Duplicate status=1 while already running: refreshes the deadline + * on the existing thread, no new pause, no new thread. */ speedtestEventReceiveHandler(NULL, &event, NULL); EXPECT_TRUE(IsSpeedtestThreadRunning()); From 295d2c7f833d4fac41ded6e2244653eadb8dcf5a Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Mon, 28 Sep 2026 10:28:11 +0530 Subject: [PATCH 17/19] Fix race in worker cleanup --- source/AdvSecurityDml/cosa_adv_security_internal.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index 58abd3e..d6f2cb3 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -512,7 +512,6 @@ static void *ni_speedtest_handler(void *arg) } timedOut = (waitStatus == ETIMEDOUT); ni_speedtest_wake_early = FALSE; - pthread_mutex_unlock(&ni_speedtest_mutex); if (timedOut) { @@ -524,7 +523,6 @@ static void *ni_speedtest_handler(void *arg) CcspTraceError(("%s: failed to resume Network Intelligence after speedtest\n", __FUNCTION__)); } - pthread_mutex_lock(&ni_speedtest_mutex); ni_speedtest_thread_running = FALSE; pthread_mutex_unlock(&ni_speedtest_mutex); From d673f299dd6fed28764f6e02b9501b5cd02ea594 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Mon, 28 Sep 2026 10:38:23 +0530 Subject: [PATCH 18/19] Fix another moderate race condition --- source/AdvSecurityDml/cosa_adv_security_internal.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index d6f2cb3..ebf6233 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -491,16 +491,15 @@ static void *ni_speedtest_handler(void *arg) pthread_detach(pthread_self()); + pthread_mutex_lock(&ni_speedtest_mutex); if (!ni_qosd_enable(FALSE)) { CcspTraceError(("%s: failed to pause Network Intelligence for speedtest\n", __FUNCTION__)); - pthread_mutex_lock(&ni_speedtest_mutex); ni_speedtest_thread_running = FALSE; pthread_mutex_unlock(&ni_speedtest_mutex); return NULL; } - pthread_mutex_lock(&ni_speedtest_mutex); while (!ni_speedtest_wake_early && !ni_speedtest_shutdown && waitStatus != ETIMEDOUT) { waitStatus = pthread_cond_timedwait(&ni_speedtest_cond, &ni_speedtest_mutex, &ni_speedtest_timeout); From 026296828494b9fc72c67c03c0f210a8868987e8 Mon Sep 17 00:00:00 2001 From: SanthoshGujulvajagadeesh Date: Mon, 28 Sep 2026 11:19:27 +0530 Subject: [PATCH 19/19] Remove ni_speedtest_shutdown since not required in CosaSecurityRemove --- .../cosa_adv_security_internal.c | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index ebf6233..0564b00 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -164,7 +164,6 @@ static pthread_cond_t ni_speedtest_cond = PTHREAD_COND_INITIALIZER; static struct timespec ni_speedtest_timeout; STATIC BOOL ni_speedtest_thread_running = FALSE; static BOOL ni_speedtest_wake_early = FALSE; -static BOOL ni_speedtest_shutdown = FALSE; static char *g_NetworkIntelligenceActivate = "Adv_AdvSecNetworkIntelligenceActivate"; #endif #ifdef WIFI_DATA_COLLECTION @@ -500,7 +499,7 @@ static void *ni_speedtest_handler(void *arg) return NULL; } - while (!ni_speedtest_wake_early && !ni_speedtest_shutdown && waitStatus != ETIMEDOUT) + while (!ni_speedtest_wake_early && waitStatus != ETIMEDOUT) { waitStatus = pthread_cond_timedwait(&ni_speedtest_cond, &ni_speedtest_mutex, &ni_speedtest_timeout); if (waitStatus != 0 && waitStatus != ETIMEDOUT) @@ -542,12 +541,6 @@ static BOOL ni_speedtest_trigger(uint32_t timeout) ni_resume_timeout.tv_sec += timeout; pthread_mutex_lock(&ni_speedtest_mutex); - if (ni_speedtest_shutdown) - { - pthread_mutex_unlock(&ni_speedtest_mutex); - return FALSE; - } - if (ni_speedtest_thread_running) { ni_speedtest_timeout = ni_resume_timeout; @@ -1814,9 +1807,6 @@ CosaSecurityInitialize } #endif #ifdef NETWORK_INTELLIGENCE - pthread_mutex_lock(&ni_speedtest_mutex); - ni_speedtest_shutdown = FALSE; - pthread_mutex_unlock(&ni_speedtest_mutex); ret = rbusEvent_Subscribe(rbus_handle, SPEEDTEST_STATUS_DML, speedtestEventReceiveHandler, NULL, 0); if(ret != RBUS_ERROR_SUCCESS) { @@ -1837,17 +1827,6 @@ CosaSecurityRemove ANSC_STATUS returnStatus = ANSC_STATUS_SUCCESS; PCOSA_DATAMODEL_AGENT pMyObject = (PCOSA_DATAMODEL_AGENT)hThisObject; -#ifdef NETWORK_INTELLIGENCE - pthread_mutex_lock(&ni_speedtest_mutex); - ni_speedtest_shutdown = TRUE; - if (ni_speedtest_thread_running) - { - ni_speedtest_wake_early = TRUE; - pthread_cond_signal(&ni_speedtest_cond); - } - pthread_mutex_unlock(&ni_speedtest_mutex); -#endif - /* Remove self */ FreeCosaDmAgent(pMyObject); rbus_close(rbus_handle);