Conversation
* RDKEMW-11232 getAudioFormatApi issue on Xione-UK * Add priority-based audio output port selection Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: apatel859 <48992974+apatel859@users.noreply.github.com>
* RDKEMW-12054: Fix Coverity identified issues
* implement retry logic for dsVideoPortInit Enhance the retry mechanism for AudioPort and VideoDeviceInit as well
Signed-off-by: yuvaramachandran_gurusamy <yuvaramachandran_gurusamy@comcast.com>
There was a problem hiding this comment.
Pull request overview
This PR syncs changes from the develop branch, incorporating multiple bug fixes and improvements from versions 1.0.26 through 1.0.29. The changes primarily focus on Coverity issue fixes, thread safety improvements, printf format specifier corrections, build dependency additions, and an audio format API fix.
Changes:
- Fixed Coverity-identified issues including format specifiers, member initialization, thread safety, and removed unreachable default cases
- Added IARMBus build-time dependency to Makefiles
- Implemented retry logic for all device settings initialization functions and improved exception handling
- Fixed audio format API to use proper audio port handle instead of NULL
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| CHANGELOG.md | Added changelog entries for versions 1.0.26-1.0.29 documenting bug fixes and feature additions |
| ds/manager.cpp | Refactored initialization with retry logic for all init functions; added thread safety improvements with needInit flag |
| ds/exception.hpp | Fixed member initialization order by initializing _err in constructor |
| ds/videoDevice.cpp | Fixed member initialization by initializing _dfc in constructor |
| ds/host.cpp | Added getAudioPortHandle() method to select appropriate audio port for format queries |
| ds/host.hpp | Added declaration for new getAudioPortHandle() method |
| ds/audioOutputPort.hpp | Added getOutputPortHandle() getter method |
| ds/audioOutputPort.cpp | Fixed printf format specifiers for intptr_t (%ld with long cast) and uint32_t (%u) |
| ds/hdmiIn.cpp | Fixed printf format specifier for sizeof() to use %zu |
| ds/edid-parser.cpp | Removed default cases from switch statements to address Coverity warnings |
| rpc/srv/dsAudio.c | Fixed thread safety in termination, added cast for bool in printf, improved error logging |
| rpc/cli/dsVideoPort.c | Fixed printf format specifiers for intptr_t and size_t |
| sample/testFrontPanel.cpp | Added exception handling around DeInitialize() call |
| ds/Makefile | Added -lIARMBus linker flag for build-time dependency |
| rpc/cli/Makefile | Added -lIARMBus linker flag for build-time dependency |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| else | ||
| { | ||
| //TV profile with multiple audio output ports | ||
| // First check the ports which are dynamically conected and finally fallback to SPEAKER0 which is always connected. |
There was a problem hiding this comment.
The comment has a typo: "conected" should be "connected". This typo appears in the comment explaining the fallback logic for audio ports.
| // First check the ports which are dynamically conected and finally fallback to SPEAKER0 which is always connected. | |
| // First check the ports which are dynamically connected and finally fallback to SPEAKER0 which is always connected. |
| { | ||
| cout << " Exception Thrown in getAudioPortHandle().. returning NULL...: " << e.what() << "\n"; | ||
| } | ||
|
|
There was a problem hiding this comment.
If all audio ports in the priority list fail the enabled/connected check or throw exceptions, the function returns NULL without logging any information about why no valid port was found. This could make it difficult to diagnose issues where audio format queries fail. Consider adding a log statement before the final return to indicate that no valid audio port was found, especially since this is used by getCurrentAudioFormat which may fail silently if passed a NULL handle.
| cout << "getAudioPortHandle(): No valid audio port found; returning NULL handle.\n"; |
| @@ -55,7 +55,6 @@ static void parse_std_timing(unsigned char* bytes, edid_data_t* data_ptr) { | |||
| case 1: v = (h * 3) / 4; break; | |||
| case 2: v = (h * 4) / 5; break; | |||
| case 3: v = (h * 9) / 16; break; | |||
There was a problem hiding this comment.
The removal of the default case in this switch statement means that if an unexpected value appears in the 2-bit field (which can only be 0-3), the variable 'v' will remain at 0. While this switch covers all possible values for a 2-bit field, consider whether v=0 is the appropriate behavior if future EDID specifications extend this field or if corrupted EDID data is encountered. The original default case with early return provided explicit handling for invalid data.
| case 3: v = (h * 9) / 16; break; | |
| case 3: v = (h * 9) / 16; break; | |
| default: | |
| INT_DEBUG("Unexpected std timing aspect ratio code: 0x%02X\n", | |
| (bytes[idx + 1] & 0xC0) >> 6); | |
| return; |
| @@ -381,8 +380,6 @@ static void parse_ext_timing(unsigned char* bytes, edid_data_t* data_ptr) { | |||
| case 6: break; | |||
| // 'Use Extended Tag' | |||
| case 7: parse_extended_db(&bytes[idx], data_ptr); break; | |||
There was a problem hiding this comment.
The removal of the default case for unsupported extension tags means that unknown tag values (anything other than 0-7) will now fall through without any logging or error handling. The original code logged "Unsupported extension tag" for unknown values, which was helpful for debugging EDID parsing issues. Consider whether silently ignoring unknown tags is the intended behavior, especially since new EDID extension tags might be added in future specifications.
| case 7: parse_extended_db(&bytes[idx], data_ptr); break; | |
| case 7: parse_extended_db(&bytes[idx], data_ptr); break; | |
| // unsupported / unknown extension tag | |
| default: | |
| INT_DEBUG("parse_ext_timing: unsupported extension tag=%d len=%d\n", tag, len); | |
| break; |
| bool needInit = false; | ||
|
|
||
| {std::lock_guard<std::mutex> lock(gManagerInitMutex); | ||
| printf("Entering %s count %d with thread id %lu\n",__FUNCTION__,IsInitialized,pthread_self()); | ||
| if (IsInitialized == 0) { | ||
| needInit = true; | ||
| } | ||
| IsInitialized++; | ||
| } |
There was a problem hiding this comment.
The IsInitialized counter is incremented before the initialization attempt, which means if initialization fails and an exception is thrown, the counter is decremented in the catch block. However, this creates a window where IsInitialized is incremented but initialization hasn't occurred yet. If another thread calls Initialize() during this window after the lock is released, it will see IsInitialized > 0 and skip initialization (needInit will be false). Consider moving the IsInitialized++ to after successful initialization completes, or keep it locked until after the try block completes successfully.
| dsError_t initializeFunctionWithRetry(const char* functionName, | ||
| std::function<dsError_t()> initFunc) | ||
| { | ||
| dsError_t err = dsERR_GENERAL; | ||
| unsigned int retryCount = 0; | ||
| unsigned int maxRetries = 25; | ||
|
|
||
| do { | ||
| err = initFunc(); | ||
| printf("Manager::Initialize:%s result :%d retryCount :%d\n", | ||
| functionName, err, retryCount); | ||
| if (dsERR_NONE == err) break; | ||
| usleep(100000); | ||
| } while (retryCount++ < maxRetries); | ||
|
|
||
| return err; | ||
| } |
There was a problem hiding this comment.
The retry logic always retries up to 25 times (26 attempts total) regardless of the error returned. The previous implementation only retried on dsERR_INVALID_STATE errors and would stop immediately on other errors. Now all initialization functions retry on any error, which could mask genuine configuration problems or cause unnecessary delays. For example, if dsAudioPortInit fails due to missing hardware or invalid configuration, the system will still retry 25 times, adding ~2.5 seconds of delay. Consider adding error-specific retry logic or documenting why all errors warrant retries.
| {std::lock_guard<std::mutex> lock(gManagerInitMutex); | ||
| printf("Entering %s count %d with thread id %lu\n",__FUNCTION__,IsInitialized,pthread_self()); | ||
| if (IsInitialized == 0) { | ||
| needInit = true; | ||
| } | ||
| IsInitialized++; | ||
| } | ||
|
|
||
| try { | ||
| if (0 == IsInitialized) { | ||
|
|
||
| dsError_t err = dsERR_GENERAL; | ||
| unsigned int retryCount = 0; | ||
| // This retry logic will wait for the device manager initialization from the client side | ||
| // until the device manager service initialization is completed. The retry mechanism checks | ||
| // only for dsERR_INVALID_STATE, which is reported if the underlying service is not ready. | ||
| // Once the service is ready, other port initializations can be called directly without any delay. | ||
| // That's why the retry logic is applied only for dsDisplayInit. | ||
| do { | ||
| err = dsDisplayInit(); | ||
| printf ("Manager::Initialize: result :%d retryCount :%d\n", err, retryCount); | ||
| if (dsERR_NONE == err) break; | ||
| usleep(100000); | ||
| } while(( dsERR_INVALID_STATE == err) && (retryCount++ < 25)); | ||
| try { | ||
| if (needInit) { |
There was a problem hiding this comment.
In the new implementation, the needInit flag is checked outside the lock, allowing initialization to proceed without lock protection. Between releasing the lock at line 153 and entering the try block at line 155, another thread could also determine needInit=true and both would proceed to initialize concurrently, potentially causing race conditions in the underlying initialization functions (dsDisplayInit, dsAudioPortInit, etc.). The original implementation kept the lock held during the entire initialization sequence, preventing this race condition.
| cout << " Exception Thrown in getAudioPortHandle().. returning NULL...: " << e.what() << "\n"; | ||
| } | ||
|
|
||
| return NULL; |
There was a problem hiding this comment.
Returning NULL for an intptr_t type is potentially problematic. While NULL typically evaluates to 0, it's defined as a null pointer constant and may not be the most semantically appropriate choice for an integer type. Consider returning 0 explicitly instead of NULL to make the intent clearer that this represents an invalid/error handle value rather than a null pointer.
| return NULL; | |
| return 0; |
No description provided.