diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..ff832a34 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,18 @@ +# Copilot Instructions + +## Repository overview +This is the **ctrlm-main** (Control Manager) plugin — a Thunder/WPEFramework plugin for RDK remote control management. + +## ci/ directory +The `ci/` directory contains **native CI build support files only**. It is not part of the application. + +- `ci/build_dependencies.sh` / `ci/cov_build.sh` — scripts that build the plugin in a CI container without a full RDK target image +- `ci/mocks/xlog_ci_compat.h` — minimal shim that pulls `std::string`, `std::map`, `std::tuple`, `std::get` into the global namespace (mirrors the transitive effect of the real rdkx_logger.h in the Yocto build) +- `ci/mocks/testframework_overrides.h` — supplements testframework mocks with declarations ctrlm needs that are not yet upstream +- `ci/mocks/devicesettings_ctrlm.patch` — patch applied to the testframework `devicesettings.h` at CI time for ctrlm-specific additions (ducking types, `setAudioDucking`, `Manager::IsInitialized`). Remove once these land in testframework develop. +- `ci/mocks/safec_lib.h` — compatibility shim mapping `safec_lib.h` to system libsafec headers +- `ci/headers/` — empty stub headers and real xr-voice-sdk headers generated/copied at CI build time; not committed to source + +CI currently pins `xr-voice-sdk` to tag `1.0.13` in `build_dependencies.sh`. Real xr-voice-sdk headers (including `rdkx_logger.h`, `xr_voice_sdk.h`, and generated `rdkx_logger_modules.h`) are produced by `build_dependencies.sh` and placed in `ci/headers/xr-voice-sdk/`. Mock/stub headers for platform libraries (IARM, DeviceSettings, RFC, etc.) are sourced from `entservices-testframework/` at CI build time. + +When suggesting code or answering questions, treat CI mocks as scaffolding, not as authoritative API definitions. For real API shapes refer to the installed headers under `install/usr/include/` or the upstream repositories (Thunder, entservices-apis, xr-voice-sdk). diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml new file mode 100644 index 00000000..cff24b88 --- /dev/null +++ b/.github/workflows/native_full_build.yml @@ -0,0 +1,29 @@ +name: Build Control Manager in Native Environment + +on: + push: + branches: [ main, 'sprint/**', 'release/**', develop ] + pull_request: + branches: [ main, 'sprint/**', 'release/**', topic/RDK*, develop ] + +permissions: + contents: read + +jobs: + build-control-on-pr: + name: Build Control Manager in github rdkcentral + runs-on: ubuntu-latest + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: build dependencies + run: | + sh -x ci/build_dependencies.sh + + - name: cov build + run: | + sh -x ci/cov_build.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eed8a53..27ed797d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,140 +1,243 @@ -# Changelog +### Changelog -All notable changes to this project will be documented in this file. +All notable changes to this project will be documented in this file. Dates are displayed in UTC. -* Each RDK Service has a CHANGELOG file that contains all changes done so far. When version is updated, add a entry in the CHANGELOG.md at the top with user friendly information on what was changed with the new version. Please don't mention JIRA tickets in CHANGELOG. +Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -* Please Add entry in the CHANGELOG for each version change and indicate the type of change with these labels: - * **Added** for new features. - * **Changed** for changes in existing functionality. - * **Deprecated** for soon-to-be removed features. - * **Removed** for now removed features. - * **Fixed** for any bug fixes. - * **Security** in case of vulnerabilities. + -* Changes in CHANGELOG should be updated when commits are added to the main or release branches. There should be one CHANGELOG entry per JIRA Ticket. This is not enforced on sprint branches since there could be multiple changes for the same JIRA ticket during development. +#### [1.1.16](https://github.com/rdkcentral/control/compare/1.1.15...1.1.16) -* In the future, generate this file by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +> 8 May 2026 -## [1.1.4] - 2025-09-17 +- RDKEMW-16763 : rf4ce init failure causes deadlock [`#226`](https://github.com/rdkcentral/control/pull/226) +- RDKEMW-14537: Coverity integration for ctrlm [`#199`](https://github.com/rdkcentral/control/pull/199) -### Changed -- move certselector logic into ctrlm-main repo (#102) -- use version/branch from recipe (#109) -### Added -- update AMC APP key mapping (#98) +#### [1.1.15](https://github.com/rdkcentral/control/compare/1.1.14...1.1.15) -### Fixed -- getNetStatus call time out due to SAT download retries (#97) -- remove device from bluez during factory reset (#100) -- Missing Thunder cflags in ctrlm implemenation (#103) +> 5 May 2026 -### Removed -- remove ctrlm compile flags (#104) -- remove ctrlm build flags - CPC, DUNFELL (#105) -- remove ctrlm build flags - RF4CE_PACKET_ANALYSIS (#107) -- remove ctrlm build flags - DISABLE_BLE_VOICE (#106) +- RDKEMW-16763 : rf4ce init failure causes deadlock [`#217`](https://github.com/rdkcentral/control/pull/217) +- RDKEMW-13607: merge pair code with mac hash methods [`#191`](https://github.com/rdkcentral/control/pull/191) +- RDKEMW-17614 : mask_pii RFC not applied correctly [`#218`](https://github.com/rdkcentral/control/pull/218) -## [1.1.3] - 2025-08-19 -### Changed -- Modify Remote Control plugin for RF4CE support (#80) -- Mac address fetch (#92) +#### [1.1.14](https://github.com/rdkcentral/control/compare/1.1.13...1.1.14) -### Added -- Runtime detection of ASB (#96) +> 22 April 2026 -## [1.1.2] - 2025-08-04 +- Revert "RDKEMW-16763 : rf4ce init failure causes deadlock (#198)" [`#211`](https://github.com/rdkcentral/control/pull/211) -### Changed -- use HdmiCecSource instead of deprecated HdmiCec (#93) -- update key code mapping (#91) -- stop BLE audio stream on the RCU when server ends session early (#76) -### Added -- BLE network discovery (#94) -- try to load VL provided IRDB, upon failure fallback to existing IRDB in MW (#79) -- print raw EDID data at debug log level +#### [1.1.13](https://github.com/rdkcentral/control/compare/1.1.12...1.1.13) -### Fixed -- Voice not initiated due to voice key not initialized to default value - -### Removed -- remove receiver ID, its no longer used (#85) -- Remove RF4CE HAL from the middleware layer (#75) +> 21 April 2026 - +- RDKEMW-16763 : rf4ce init failure causes deadlock [`#198`](https://github.com/rdkcentral/control/pull/198) +- RDKEMW-17187 : PTT audio buffering feature aborting voice session before mic key release [`#203`](https://github.com/rdkcentral/control/pull/203) -## [1.1.1] - 2025-06-25 +#### [1.1.12](https://github.com/rdkcentral/control/compare/1.1.11...1.1.12) -### Changed -- Use the deprecated HDMI Input plugin instead of the new AV Input plugin if compile flag is set +> 9 April 2026 -### Fixed -- BLE audio pipe size too small, set minimum size -- enable RF4CE Advanced Secure Binding (ASB) +- RDKEMW-16333: Update Thunder plugin to use _string and _boolean [`#197`](https://github.com/rdkcentral/control/pull/197) +- RDKEMW-5849 : remove deprecated "experience" code [`#185`](https://github.com/rdkcentral/control/pull/185) -### Added -- receive RF4CE IR binding key codes over IR input device -- use new Power Manager Thunder Plugin for power state events, switch to deprecated IARM interface with compile flag - +#### [1.1.11.2](https://github.com/rdkcentral/control/compare/1.1.11.1...1.1.11.2) -## [1.1.0] - 2025-05-30 +> 7 April 2026 -### Changed -- move thunder and IARM dependencies from irdb to ctrlm -- load irdb library as a plugin with dlopen -- use AVInput instead of HDMI Input thunder plugin for HDMI Infoframes +- RDKEMW-16711: CHANGELOG for ctrlm hotfix release 1.1.11.2 [`#196`](https://github.com/rdkcentral/control/pull/196) +- RDKEMW-16711 : Add wakeup reason string, on support/1.1.11 [`#195`](https://github.com/rdkcentral/control/pull/195) -### Added -- new interface to ctrlm IRDB -- Default BLE controller type list -- RF4CE network discovery at runtime instead of compile time +#### [1.1.11.1](https://github.com/rdkcentral/control/compare/1.1.11...1.1.11.1) -### Fixed -- crash during OTA interrupted by unpair or reset +> 1 April 2026 +- RDKEMW-16330: Update Control Manager to use bool for NSM [`#188`](https://github.com/rdkcentral/control/pull/188) +- RDKEMW-16330: update CHANGELOG for release 1.1.11p1 [`98ea5f5`](https://github.com/rdkcentral/control/commit/98ea5f51f8da9ef6ded7038d760baa3b41cc4a90) -## [1.0.10] - 2025-05-16 +#### [1.1.11](https://github.com/rdkcentral/control/compare/1.1.10...1.1.11) -### Changed -- ctrlm crash from main queue msgs getting to networks prior to initialization -- ctrlm support both old and new deviceType -- Control manager crashes on rapid MIC button presses -- [Logging] Add Invalid firmware version OTA error code (0x8) mapping in ctrlmgr logs -- Changed deviceType from tv to IpTv. -- Extend adpcm frame info. +> 6 March 2026 +- RDKEMW-14589: No UI action with "Info" keypress from rf4ce remote in RF mode [`#181`](https://github.com/rdkcentral/control/pull/181) +- RDKEMW-14445 : Add session end and protocol return to telemetry [`#182`](https://github.com/rdkcentral/control/pull/182) +- RDKEMW-11359: Logging proximity key as debug instead of telemetry [`#167`](https://github.com/rdkcentral/control/pull/167) +- RDKEMW-13753: Report manually set IRDB codes RF4CE remotes [`#176`](https://github.com/rdkcentral/control/pull/176) +- RDKEMW-12930: RF4CE network export XCONF on pair/unpair/etc. [`#177`](https://github.com/rdkcentral/control/pull/177) +- RDKEMW-13833: Remove duplicate RFC fetch attempts in listeners [`#179`](https://github.com/rdkcentral/control/pull/179) -## [1.0.9] - 2025-04-28 -### Changed -- Audio samples is always reported as 0 for FFV sessions -- Remove 'volatile' type qualifier of "binding_in_progress" -- Upgrading the halif-headers as iarmmgr +#### [1.1.10](https://github.com/rdkcentral/control/compare/1.1.9...1.1.10) +> 11 February 2026 -## [1.0.8] - 2025-04-15 +- RDKEMW-12828: device minor id, vendor, and product value updates [`#175`](https://github.com/rdkcentral/control/pull/175) +- RDKEMW-13049: remove RCU firmware upgrade retries on failure [`#173`](https://github.com/rdkcentral/control/pull/173) +- RDKEMW-9474: ctrlm multiple simultaneous IR databases [`#161`](https://github.com/rdkcentral/control/pull/161) +- RDKEMW-11792 : FFV config file isolation [`#165`](https://github.com/rdkcentral/control/pull/165) +- RDKEMW-12457: No longer ignoring device re-pair if it's already paired and connected. [`#171`](https://github.com/rdkcentral/control/pull/171) +- RDKEMW-9843: report loaded and unloaded voltage from RCU [`#163`](https://github.com/rdkcentral/control/pull/163) -### Changed -- use list of all possible names for IR input device discovery instead of product specific config +#### [1.1.9](https://github.com/rdkcentral/control/compare/1.1.8...1.1.9) +> 13 January 2026 -## [1.0.7] - 2025-03-31 +- RDKEMW-11471: Remove netType param [`#164`](https://github.com/rdkcentral/control/pull/164) +- RDKEMW-10631: Filter out unpaired devices when reconnecting all devices [`fc1c94a`](https://github.com/rdkcentral/control/commit/fc1c94a8e2de9b196a5f274316eaee5c07c2fbf5) -### Changed +#### [1.1.8](https://github.com/rdkcentral/control/compare/1.1.7...1.1.8) + +> 5 January 2026 + +- RDKEMW-8929: Refactor base ipc class [`#162`](https://github.com/rdkcentral/control/pull/162) +- RDKEMW-11283: add product description docs [`#159`](https://github.com/rdkcentral/control/pull/159) +- RDKEMW-8930 : remove ctrlm build flags - FACTORY_AUDIO_PLAYBACK [`#143`](https://github.com/rdkcentral/control/pull/143) + +#### [1.1.7](https://github.com/rdkcentral/control/compare/1.1.6...1.1.7) + +> 4 December 2025 + +- RDKEMW-11159: new_certselector_type [`#157`](https://github.com/rdkcentral/control/pull/157) +- RDKEMW-10425: Automation Logging [`#144`](https://github.com/rdkcentral/control/pull/144) +- RDKEMW-9600: FIRST_PACKET_TIMEOUTs [`#135`](https://github.com/rdkcentral/control/pull/135) + +#### [1.1.6](https://github.com/rdkcentral/control/compare/1.1.5...1.1.6) + +> 19 November 2025 + +- RDKEMW-9124 : remove xr-voice-sdk build flags - XRAUDIO_CURTAIL XLOG_CURTAIL [`#132`](https://github.com/rdkcentral/control/pull/132) +- RDKEMW-8676 : remove ctrlm build flags - MIC_TAP, LOCAL_MIC, LOCAL_MIC_DISABLE_VIA_PRIVACY [`#120`](https://github.com/rdkcentral/control/pull/120) +- RDKEMW-8664 : remove ctrlm build flags - MEM_DEBUG, ASSERT_ON_WRONG_THREAD [`#118`](https://github.com/rdkcentral/control/pull/118) +- RDKEMW-8297 : remove ctrlm build flags - A5000_ENABLE [`#117`](https://github.com/rdkcentral/control/pull/117) +- RDKEMW-8668 : modify ctrlm build flag - BREAKPAD [`#119`](https://github.com/rdkcentral/control/pull/119) +- RDKEMW-8296 : remove ctrlm build flags - DEEPSLEEP_CLOSE_DB [`#112`](https://github.com/rdkcentral/control/pull/112) +- RDKEMW-8295 : remove ctrlm build flags - MEMORY_LOCK [`#111`](https://github.com/rdkcentral/control/pull/111) +- RDKEMW-7905 : remove ctrlm build flags - ANSI_CODES_DISABLED [`#110`](https://github.com/rdkcentral/control/pull/110) +- Revert "RDKEMW-8929 (#129)" [`#145`](https://github.com/rdkcentral/control/pull/145) +- RDKEMW-10311 : RF4CE update key mapping [`#140`](https://github.com/rdkcentral/control/pull/140) +- RDKEMW-10164: update CHANGELOG for release v1.1.5 [`#139`](https://github.com/rdkcentral/control/pull/139) + +#### [1.1.5](https://github.com/rdkcentral/control/compare/1.1.4...1.1.5) + +> 5 November 2025 + +- RDKEMW-9924 : ctrlm RF4CE upgrade skipped when BLE enabled [`#137`](https://github.com/rdkcentral/control/pull/137) +- RDKEMW-7225: BLE pairing retries [`#126`](https://github.com/rdkcentral/control/pull/126) +- RDKEMW-8929: Refactor ctrlm_voice_ipc_t to inherit ctrlm_ipc_iarm_t [`#129`](https://github.com/rdkcentral/control/pull/129) +- Update CODEOWNERS [`#130`](https://github.com/rdkcentral/control/pull/130) +- RDKEMW-8815: only return SUCCESS for autolookup if it found at least 1 code. [`#125`](https://github.com/rdkcentral/control/pull/125) +- Deploy fossid_integration_stateless_diffscan_target_repo action [`#121`](https://github.com/rdkcentral/control/pull/121) +- Deploy cla action [`#74`](https://github.com/rdkcentral/control/pull/74) +- RDKEMW-8354: ctrlm-main crash while holding standby during OTA [`#115`](https://github.com/rdkcentral/control/pull/115) +- RDKEMW-8133: Optional param name for voiceSessionRequest [`#108`](https://github.com/rdkcentral/control/pull/108) + +#### [1.1.4](https://github.com/rdkcentral/control/compare/1.1.3...1.1.4) + +> 18 September 2025 + +- RDKEMW-8349 : ctrlm release v1.1.4 [`#113`](https://github.com/rdkcentral/control/pull/113) +- RDKEMW-7979 : use version/branch from recipe [`#109`](https://github.com/rdkcentral/control/pull/109) +- RDKEMW-7122 : Missing Thunder cflags in ctrlm implementation [`#103`](https://github.com/rdkcentral/control/pull/103) +- RDKEMW-7772 : remove ctrlm build flags - DISABLE_BLE_VOICE [`#106`](https://github.com/rdkcentral/control/pull/106) +- RDKEMW-7834 : remove ctrlm build flags - RF4CE_PACKET_ANALYSIS [`#107`](https://github.com/rdkcentral/control/pull/107) +- RDKEMW-7694 : remove ctrlm build flags - CPC, DUNFELL [`#105`](https://github.com/rdkcentral/control/pull/105) +- RDKEMW-7573 : remove ctrlm compile flags [`#104`](https://github.com/rdkcentral/control/pull/104) +- RDKEMW-7333: remove device from bluez during factory reset [`#100`](https://github.com/rdkcentral/control/pull/100) +- RDKEMW-6767: getNetStatus call time out due to SAT download retries [`#97`](https://github.com/rdkcentral/control/pull/97) +- RDKEMW-3409 : move certselector logic into ctrlm-main repo [`#102`](https://github.com/rdkcentral/control/pull/102) +- RDKEMW-7174 : update AMC APP key mapping [`#98`](https://github.com/rdkcentral/control/pull/98) + +#### [1.1.3](https://github.com/rdkcentral/control/compare/1.1.2...1.1.3) + +> 19 August 2025 + +- RDKEMW-7232: update CHANGELOG for controlMgr release v1.1.3 [`#99`](https://github.com/rdkcentral/control/pull/99) +- RDKEMW-5576: ctrlm mac address fetch [`#92`](https://github.com/rdkcentral/control/pull/92) +- RDKEMW-4309 : Modify Remote Control plugin for RF4CE support [`#80`](https://github.com/rdkcentral/control/pull/80) +- RDKEMW-5604 : runtime detection of ASB [`#96`](https://github.com/rdkcentral/control/pull/96) + +#### [1.1.2](https://github.com/rdkcentral/control/compare/1.1.1...1.1.2) + +> 18 August 2025 + +- RDKEMW-6034: update CHANGELOG for controlMgr release v1.1.2 [`#95`](https://github.com/rdkcentral/control/pull/95) +- RDKEMW-6538: use HdmiCecSource instead of deprecated HdmiCec [`#93`](https://github.com/rdkcentral/control/pull/93) +- RDKEMW-6383 : update key code mapping [`#91`](https://github.com/rdkcentral/control/pull/91) +- RDKEMW-3797 : BLE network discovery [`#94`](https://github.com/rdkcentral/control/pull/94) +- RDKEMW-6195: try to load VL provided IRDB, upon failure fallback to existing IRDB in MW [`#79`](https://github.com/rdkcentral/control/pull/79) +- RDKEMW-5714: Voice not initiated in ctrlm-main [`#81`](https://github.com/rdkcentral/control/pull/81) +- RDKEMW-6884: Logs indicate missing receiver ID [`#85`](https://github.com/rdkcentral/control/pull/85) +- RDKEMW-5413 : Remove RF4CE HAL from the middleware layer [`#75`](https://github.com/rdkcentral/control/pull/75) +- RDKEMW-5506 : stop BLE audio stream on the RCU when server ends session early [`#76`](https://github.com/rdkcentral/control/pull/76) +- RDKEMW-3965: print raw EDID data at debug log level [`447ae08`](https://github.com/rdkcentral/control/commit/447ae0826d15351fa97ccb12b3b18f435ff40089) + +#### [1.1.1](https://github.com/rdkcentral/control/compare/1.1.0...1.1.1) + +> 30 June 2025 + +- RDKEMW-4913: ctrlm release v1.1.1 [`#77`](https://github.com/rdkcentral/control/pull/77) +- RDKEMW-4788: use HDMI Input plugin RDKV and AVInput RDKE [`#67`](https://github.com/rdkcentral/control/pull/67) +- RDKEMW-5247 : BLE audio pipe size too small [`#68`](https://github.com/rdkcentral/control/pull/68) +- RDKEMW-3411 : receive RF4CE IR binding key codes over IR input device [`#69`](https://github.com/rdkcentral/control/pull/69) +- RDKEMW-3563: Enable ASB [`#63`](https://github.com/rdkcentral/control/pull/63) +- RDKEMW-3119: Control Manager to use Power Manager Thunder Plugin [`07c1d22`](https://github.com/rdkcentral/control/commit/07c1d222b5d99849713a4db8dcef3bd847380f4b) + +#### [1.1.0](https://github.com/rdkcentral/control/compare/1.0.10...1.1.0) + +> 30 June 2025 + +- RDKEMW-4793: ctrlm release v1.1.0 [`#61`](https://github.com/rdkcentral/control/pull/61) +- RDKEMW-3408: [ctrlm] refactor interface to ctrlm IR Database for Vendor layer integration [`#18`](https://github.com/rdkcentral/control/pull/18) +- RDKEMW-3798 : RF4CE network discovery [`#53`](https://github.com/rdkcentral/control/pull/53) +- RDKEMW-3410 : Default BLE controller type list [`#29`](https://github.com/rdkcentral/control/pull/29) +- RDKEMW-4381: ctrlm crash during OTA interrupted by unpair or reset [`#54`](https://github.com/rdkcentral/control/pull/54) + +#### [1.0.10](https://github.com/rdkcentral/control/compare/1.0.9...1.0.10) + +> 4 June 2025 + +- RDKEMW-4424: update CHANGELOG for release 1.0.10 [`#55`](https://github.com/rdkcentral/control/pull/55) +- RDKEMW-3916: ctrlm crash from main queue msgs getting to networks prior to initialization [`#38`](https://github.com/rdkcentral/control/pull/38) +- RDKEMW-4146: ctrlm support both old and new deviceType [`#48`](https://github.com/rdkcentral/control/pull/48) +- RDKEMW-3918 : Control manager crashes on rapid MIC button presses [`#42`](https://github.com/rdkcentral/control/pull/42) +- RDK-56578 : Changed deviceType from tv to IpTv. [`#34`](https://github.com/rdkcentral/control/pull/34) +- RDKEMW-3609 : extend adpcm frame info [`#28`](https://github.com/rdkcentral/control/pull/28) +- RDKEMW-3916: IARM calls registered too early [`ae82b1e`](https://github.com/rdkcentral/control/commit/ae82b1e66f70d6f621eb3c680b7bf870379bd84c) +- RDKEMW-3835 - [Logging] Add Invalid firmware version OTA error code (0x8) mapping in ctrlmgr logs [`80e4d59`](https://github.com/rdkcentral/control/commit/80e4d59e841f3e9744fc6d3dd92b8bccec26c3ea) + +#### [1.0.9](https://github.com/rdkcentral/control/compare/1.0.8...1.0.9) + +> 7 May 2025 + +- update CHANGELOG for controlMgr release 1.0.9 [`#35`](https://github.com/rdkcentral/control/pull/35) +- RDKEMW-3737:Upgrading the halif-headers as iarmmgr [`#30`](https://github.com/rdkcentral/control/pull/30) +- RDKEMW-3564 : Audio samples is always reported as 0 for FFV sessions [`2920c1a`](https://github.com/rdkcentral/control/commit/2920c1a455d1e1afe1d095c0b09fd841a305974d) +- RDKEMW-3605: Remove 'volatile' type qualifier of "binding_in_progress" [`a7d2310`](https://github.com/rdkcentral/control/commit/a7d23104a3b961a26621a1c688ba775c3fe2ad3f) + +#### [1.0.8](https://github.com/rdkcentral/control/compare/1.0.7...1.0.8) + +> 28 April 2025 + +- update CHANGELOG for controlMgr release 1.0.8 [`#25`](https://github.com/rdkcentral/control/pull/25) +- RDKEMW-3276: common udev node and provide list of possible names for IR input device discovery [`#20`](https://github.com/rdkcentral/control/pull/20) + +#### [1.0.7](https://github.com/rdkcentral/control/compare/1.0.6...1.0.7) + +> 10 April 2025 + +- XCTRL-400: add irdb stub implementation if ctrlm-hal-irdb lib does not exist [`#17`](https://github.com/rdkcentral/control/pull/17) +- Merge pull request #14 from rdkcentral/feature/XCTRL-400_rdkv_code_sy… [`#16`](https://github.com/rdkcentral/control/pull/16) +- XCTRL-400: RDKE Release 2025-03-31 (ctrlm v1.0.7) [`#14`](https://github.com/rdkcentral/control/pull/14) - Rationalize Voice Logging - move auth from ctrlm-cpc to ctrlm-main - Add ctrlm HAL certificate interace - Remove irMgr dependencies in controlMgr - create HAL interface for platform specific IRDBs - -### Added - BLE audio stream end time telemetry - voice stream telemetry in single line/event - -### Fixed - Type-Z OTA bug - Remote type changed to type-Z early - controlMgr maintenance time crash at onInitializedTimer @@ -145,28 +248,35 @@ All notable changes to this project will be documented in this file. - Logline error event rcu firmware status -## [1.0.6] - 2025-03-18 +#### [1.0.6](https://github.com/rdkcentral/control/compare/1.0.5...1.0.6) -### Added +> 18 March 2025 + +- RDKEMW-1397: IR input device name move to config file [`#1`](https://github.com/rdkcentral/control/pull/1) +- RDKEMW-1783: controlMgr crash at onInitializedTimer when going to dee… [`#11`](https://github.com/rdkcentral/control/pull/11) - custom target to build ctrlm config file only - additional config override file that can be provided by vendor layer +#### [1.0.5](https://github.com/rdkcentral/control/compare/1.0.4...1.0.5) + +> 25 February 2025 -## [1.0.5] - 2025-02-24 +- RDKEMW-1783: controlMgr crash at onInitializedTimer when going to deepsleep [`#10`](https://github.com/rdkcentral/control/pull/10) -### Changed -- crash at onInitializedTimer when going to deepsleep +#### [1.0.4](https://github.com/rdkcentral/control/compare/1.0.3...1.0.4) +> 25 February 2025 -## [1.0.4] - 2025-02-20 +- RDKEMW-1890: Remove irMgr dependencies in controlMgr [`#6`](https://github.com/rdkcentral/control/pull/6) -### Changed -- removed references to deprecated irMgr component +#### 1.0.3 -## [1.0.3] - 2025-02-07 +> 13 February 2025 -### Changed +- XCTRL-379: CTRLM RDKE Release 2025-02-07 [`#4`](https://github.com/rdkcentral/control/pull/4) +- XCTRL-379: CTRLM RDKE Release 2025-02-07 [`#3`](https://github.com/rdkcentral/control/pull/3) +- Import of source (develop) [`83e2f7b`](https://github.com/rdkcentral/control/commit/83e2f7bd1f5a179b47e0278f49e17466f4b1c457) - check that a file descriptor is valid before FD_SET() - standardize use of singleton pattern ctrlm - speed up BLE auto pairing and surface failures immediately @@ -176,16 +286,13 @@ All notable changes to this project will be documented in this file. - remove legacy url_vrex config field - Add ctrlm Support for XRA BLE key - QAM - ControlMgr crash pairWithMacHash when going to deepsleep - -### Added - RemoteControl plugin methods to pair and unpair targetted RCU devices based on MAC - RemoteControl plugin methods to trigger RCU firmware upgrade and report status of upgrade - ctrlm-factory added to this repo, its no longer a separate repo +#### 1.0.2 -## [1.0.2] - 2024-12-06 - -### Changed +> 6 December 2024 - ctrlm IR uinput device match exact name, simplify IR-initiated BLE pairing event handling - Check for Invalid avDevType - move stop audio stream to separate non iarm related function @@ -194,7 +301,5 @@ All notable changes to this project will be documented in this file. - Detect the platform type (TV vs STB) using DeviceInfo plugin - IR keypresses use same PII mask variable as Voice - fix "last wakeup key code" not received, along with defering gdbus proxy calls for characteristics until they are needed. - -### Added - unit test function to set IR protocol support characteristic on RCU - Added Alexa voice service support in SDT endpoint, along with async voice message support diff --git a/CMakeLists.txt b/CMakeLists.txt index 61dbb8e0..112b3c00 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,32 +34,19 @@ set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON) project(ctrlm-main VERSION ${CMAKE_PROJECT_VERSION}) # OPTIONS -option(A5000_ENABLE "ES1 A5000 use libse051" OFF) -option(ANSI_CODES_DISABLED "Disable ANSI code logging" OFF) -option(ASSERT_ON_WRONG_THREAD "Assert on wrong thread" OFF) option(AUTH_ENABLED "Enable AUTH" OFF) option(BLE_ENABLED "Enable BLE" ON) option(BLE_SERVICES "Enable BLE Services" OFF) option(BREAKPAD "Enable BREAKPAD" OFF) option(BUILD_CTRLM_FACTORY "Build Control Factory Test" OFF) -option(DEEPSLEEP_CLOSE_DB "Deep Sleep Close DB" OFF) -option(ENABLE_NETWORKED_STANDBY_MODE "Enable Networked Standby Mode)" OFF) -option(FACTORY_AUDIO_PLAYBACK "Factory test audio playback" OFF) -option(FACTORY_CUSTOM_AUDIO_ANALYSIS "Factory custom audio analysis" OFF) option(FDC_ENABLED "Enable FDC" OFF) option(IP_ENABLED "Enable IP" OFF) -option(LOCAL_MIC "Local Microphone" OFF) -option(LOCAL_MIC_DISABLE_VIA_PRIVACY "Use Privacy to disable microphone" OFF) -option(MEM_DEBUG "Enable memory debugging" OFF) -option(MEMORY_LOCK "Memory Lock" OFF) -option(MIC_TAP "Enable MIC_TAP" OFF) option(RF4CE_ENABLED "Enable RF4CE" ON) option(TELEMETRY_SUPPORT "Enable TELEMETRY_SUPPORT" OFF) option(THUNDER "Enable THUNDER" ON) option(THUNDER_SECURITY "Enable THUNDER_SECURITY" OFF) option(USE_SAFEC "Use safec" OFF) option(USE_IARM_POWER_MANAGER "Use IARM Power Manager" OFF) -option(VOICE_KEYWORD_BEEP "Enable VOICE_KEYWORD_BEEP" OFF) option(XRSR_HTTP "Enable XRSR_HTTP" OFF) option(XRSR_SDT "Enable XRSR_SDT" OFF) option(ENABLE_ASYNC_SRVR_MSG "Enable Asynchronous Server Messaging Feature" OFF) @@ -178,22 +165,13 @@ target_link_libraries( install(TARGETS controlMgr RUNTIME DESTINATION bin) -install(FILES ${CMAKE_BINARY_DIR}/ctrlm_config.json.template DESTINATION ${CMAKE_INSTALL_SYSCONFDIR} COMPONENT config ) +install(FILES ${CMAKE_BINARY_DIR}/ctrlm_config.json DESTINATION ${CMAKE_INSTALL_SYSCONFDIR} COMPONENT config ) # DEFINES FROM OPTIONS -if(A5000_ENABLE) - find_library( SE_HAL_PATH SE_HAL ${CMAKE_SYSROOT}/usr/lib/se051) - target_link_libraries(controlMgr ${SE_HAL_PATH}) -endif() - if(ANSI_CODES_DISABLED) add_compile_definitions(ANSI_CODES_DISABLED) endif() -if(ASSERT_ON_WRONG_THREAD) - add_compile_definitions(ASSERT_ON_WRONG_THREAD) -endif() - if(BLE_ENABLED) target_link_libraries(controlMgr BTMgr) if(BLE_SERVICES) @@ -207,10 +185,6 @@ if(BREAKPAD) add_compile_definitions(BREAKPAD_SUPPORT) endif() -if(DEEPSLEEP_CLOSE_DB) - add_compile_definitions(DEEPSLEEP_CLOSE_DB) -endif() - if(ENABLE_ASYNC_SRVR_MSG) add_compile_definitions(SUPPORT_ASYNC_SRVR_MSG) endif() @@ -219,10 +193,6 @@ if(ENABLE_AVS_WITH_SDT) add_compile_definitions(SUPPORT_VOICE_DEST_ALSA SUPPORT_ASYNC_SRVR_MSG) endif() -if(ENABLE_NETWORKED_STANDBY_MODE) - add_compile_definitions(NETWORKED_STANDBY_MODE_ENABLED) -endif() - if(FDC_ENABLED) add_compile_definitions(FDC_ENABLED) endif() @@ -231,25 +201,6 @@ if(IP_ENABLED) add_compile_definitions(CTRLM_NETWORK_IP CTRLM_IP_HAL_LOG_ENABLED) endif() -if(LOCAL_MIC) - add_compile_definitions(CTRLM_LOCAL_MIC) - if(MIC_TAP) - add_compile_definitions(CTRLM_LOCAL_MIC_TAP) - endif() - if(LOCAL_MIC_DISABLE_VIA_PRIVACY) - add_compile_definitions(CTRLM_LOCAL_MIC_DISABLE_VIA_PRIVACY) - endif() -endif() - -if(MEM_DEBUG) - add_compile_definitions(MEM_DEBUG) -endif() - -if(MEMORY_LOCK) - add_compile_definitions(MEMORY_LOCK) - target_link_libraries(controLMgr clnl) -endif() - # RF4CE options if(RF4CE_ENABLED) # All RF4CE platforms have HAL NVM @@ -283,8 +234,6 @@ if(THUNDER) if(AUTH_ACTIVATION_STATUS) add_compile_definitions(AUTH_ACTIVATION_STATUS) endif() - #By default disabled but can be enabled - #add_compile_definitions(AUTH_EXPERIENCE) target_link_libraries(controlMgr RdkCertSelector) endif() endif() @@ -303,19 +252,13 @@ else() add_compile_definitions(SAFEC_DUMMY_API) endif() -if(VOICE_KEYWORD_BEEP) - add_compile_definitions(BEEP_ON_KWD_ENABLED) - add_definitions(-DBEEP_ON_KWD_FILE=\"${CMAKE_DATADIR}/${BEEP_ON_KWD_FILE}\") - install(FILES ${CMAKE_SOURCE_DIR}/../${BEEP_ON_KWD_FILE} DESTINATION share ) -endif() - if(USE_DEPRECATED_HDMI_INPUT_PLUGIN) add_compile_definitions(USE_DEPRECATED_HDMI_INPUT_PLUGIN) endif() install(TARGETS controlMgr RUNTIME DESTINATION bin) -install(FILES ${CMAKE_BINARY_DIR}/ctrlm_config.json.template DESTINATION ${CMAKE_INSTALL_SYSCONFDIR} COMPONENT config ) +install(FILES ${CMAKE_BINARY_DIR}/ctrlm_config.json DESTINATION ${CMAKE_INSTALL_SYSCONFDIR} COMPONENT config ) # GENERATED FILES add_custom_command( OUTPUT ctrlm_version_build.h @@ -331,16 +274,16 @@ add_custom_command( OUTPUT ctrlm_version_build.h ) add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/ctrlm_config.json.template - COMMAND python3 ${CTRLM_UTILS_JSON_COMBINE} -i ${CMAKE_CURRENT_SOURCE_DIR}/src/ctrlm_config_default.json -a ${CTRLM_CONFIG_JSON_VSDK}:vsdk -a ${CTRLM_CONFIG_JSON_CPC} -s ${CTRLM_CONFIG_JSON_CPC_SUB} -a ${CTRLM_CONFIG_JSON_CPC_ADD} -s ${CTRLM_CONFIG_JSON_OEM_SUB} -a ${CTRLM_CONFIG_JSON_OEM_ADD} -s ${CTRLM_CONFIG_JSON_MAIN_SUB} -a ${CTRLM_CONFIG_JSON_MAIN_ADD} -o ${CMAKE_BINARY_DIR}/ctrlm_config.json.template + OUTPUT ${CMAKE_BINARY_DIR}/ctrlm_config.json + COMMAND python3 ${CTRLM_UTILS_JSON_COMBINE} -i ${CMAKE_CURRENT_SOURCE_DIR}/src/ctrlm_config_default.json -a ${CTRLM_CONFIG_JSON_CPC} -s ${CTRLM_CONFIG_JSON_CPC_SUB} -a ${CTRLM_CONFIG_JSON_CPC_ADD} -s ${CTRLM_CONFIG_JSON_MAIN_SUB} -a ${CTRLM_CONFIG_JSON_MAIN_ADD} -o ${CMAKE_BINARY_DIR}/ctrlm_config.json DEPENDS src/ctrlm_config_default.json VERBATIM ) add_custom_command( OUTPUT ctrlm_config_default.h ${CMAKE_CURRENT_SOURCE_DIR}/src/ctrlm_config_default.c - COMMAND python3 ${CTRLM_UTILS_JSON_TO_HEADER} -i ${CMAKE_BINARY_DIR}/ctrlm_config.json.template -o ctrlm_config_default.h -c ${CMAKE_CURRENT_SOURCE_DIR}/src/ctrlm_config_default.c -v "ctrlm_global,network_rf4ce,network_ip,network_ble,ir,voice,device_update" -d "network_ble" - DEPENDS ${CMAKE_BINARY_DIR}/ctrlm_config.json.template + COMMAND python3 ${CTRLM_UTILS_JSON_TO_HEADER} -i ${CMAKE_BINARY_DIR}/ctrlm_config.json -o ctrlm_config_default.h -c ${CMAKE_CURRENT_SOURCE_DIR}/src/ctrlm_config_default.c -v "ctrlm_global,network_rf4ce,network_ip,network_ble,ir,voice,device_update" -d "network_ble" + DEPENDS ${CMAKE_BINARY_DIR}/ctrlm_config.json VERBATIM ) @@ -351,5 +294,5 @@ add_custom_command( ) add_custom_target( ctrlm_config - DEPENDS ${CMAKE_BINARY_DIR}/ctrlm_config.json.template + DEPENDS ${CMAKE_BINARY_DIR}/ctrlm_config.json ) diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 00000000..0279a77b --- /dev/null +++ b/PRODUCT.md @@ -0,0 +1,64 @@ +# rdkcentral/control Product Functionality + +This document summarizes the core product features, capabilities, and user-facing functionality provided by the `rdkcentral/control` project. + +## 1. Product Overview + +`rdkcentral/control` provides the central control and management solution for RDK-based devices, focusing on remote control integration, device state management, configuration handling, and hardware abstraction. It enables flexible, reliable control of device functions, typically in set-top boxes and smart TV platforms. + +## 2. Key Functional Areas + +### 2.1 Remote Control Manager + +- **Multi-protocol Support:** Integrates with RF4CE and BLE-based remote controls, supporting automatic pairing and discovery of devices. +- **Key Mapping & Input:** Manages IR, RF, and BLE key events, including vendor-specific key mappings (e.g., for AMC App). +- **Telemetry & Logging:** Captures and processes telemetry data from voice streams, key events, and audio sessions for diagnostics and monitoring. +- **OTA & Firmware Management:** Handles remote control firmware updates and network discovery, with stability features when interrupted (e.g., OTA interrupted by reset). + +### 2.2 Device State & Power Management + +- **Deep Sleep & Wake Handling:** Responds to device state changes, ensuring proper initialization/timer handling during deep sleep or wake events. +- **Power Plugin Integration:** Uses RDK's Power Manager Thunder plugin for system power state management and interaction. + +### 2.3 Configuration Management + +- **Dynamic Configuration:** Reads, updates, and applies runtime configuration changes from files, including support for vendor override files. +- **Device Discovery:** Provides mechanisms to discover IR input devices, support for multiple device types and fallback/stub implementations if hardware is absent. +- **HAL Abstraction:** Exposes interfaces to control hardware-specific features via C++ classes (see `ctrlm_hal.h`, `ctrlm_hal_rf4ce.h`, `ctrlm_hal_ble.h`). + +### 2.4 Audio & Voice Control + +- **Voice Session Management:** Supports BLE/RF voice streaming; logs sessions; manages session state, end times, and error reporting. +- **Audio Stream Management:** Reports and optimizes audio pipe size; ensures reliable audio sample reporting for voice sessions. + +### 2.5 API & Service Integration + +- **Thunder & HDMI Plugins:** Integrates with plugin frameworks to extend support for HDMI input, AV input, MAC address fetch, and advanced service bridging. +- **ASB Detection:** Offers runtime detection for Advanced Service Bridge capabilities. + +## 3. Product Extensibility + +- **Vendor Layer Integration:** Provides hooks for vendor-specific features, such as configuration overrides and device database stubs. +- **Flexible Build & Runtime Flags:** Build flags allow enabling/disabling features (e.g., BLE audio, packet analysis, deepsleep, memory lock). +- **Plugin & Target Customization:** Can build custom targets (e.g., just the control config file). + +## 4. Typical Use Cases + +- **User Experience:** Enables seamless remote pairing and input handling, responsive device wake/sleep, and dynamic feature provisioning. +- **Monitoring/Diagnostics:** Logs device events and telemetry, aiding both advanced diagnostics and data-driven product improvement. +- **Integration Point:** Forms the backbone for device control in RDK deployments where remote management and hardware abstraction are required. + +## 5. Recent Product Updates (Selected Highlights) + +- Multi-protocol remote integration (BLE and RF4CE support). +- Overhaul of key mapping and vendor integration logic. +- Telemetry improvements for voice stream analytics. +- Refactoring to enhance HAL interface extensibility. +- Crash and stability fixes for deep sleep, rapid input, and device discovery. +- Improved runtime configuration and plugin extensibility. + +_For full release notes, please see the [CHANGELOG](https://github.com/rdkcentral/control/blob/develop/CHANGELOG.md)._ + +## 6. Summary + +`rdkcentral/control` delivers a comprehensive control and management solution for RDK devices, emphasizing extensibility, reliability, and integration with modern remote protocols and power management frameworks. It remains the canonical product for remote control, device state, and configuration management within the RDK platform. diff --git a/ci/build_dependencies.sh b/ci/build_dependencies.sh new file mode 100644 index 00000000..9abff1c9 --- /dev/null +++ b/ci/build_dependencies.sh @@ -0,0 +1,204 @@ +#!/bin/bash +# +# If not stated otherwise in this file or this component's license file the +# following copyright and licenses apply: +# +# Copyright 2026 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -x +set -e +############################## +GITHUB_WORKSPACE="${PWD}" +cd "${GITHUB_WORKSPACE}" + +git config --global --add safe.directory "${GITHUB_WORKSPACE}" + +# ############################# +# 1. Install Dependencies and packages + +apt update +apt install -y \ + pkg-config \ + libsqlite3-dev \ + libcurl4-openssl-dev \ + libsystemd-dev \ + libglib2.0-dev \ + libjansson-dev \ + libarchive-dev \ + libssl-dev \ + zlib1g-dev \ + libdbus-1-dev \ + uuid-dev \ + libevdev-dev \ + libdrm-dev \ + libsafec-dev \ + python3-pip +python3 -m pip install jsonref + +########################################### +# 2. Clone the required repositories + +XRSDK_REF="1.0.13" +git clone --depth 1 --filter=blob:none --branch "${XRSDK_REF}" https://github.com/rdkcentral/xr-voice-sdk.git + +git clone --depth 1 --filter=blob:none --branch develop https://github.com/rdkcentral/entservices-testframework.git + +# Patch the upstream testframework devicesettings.h with ctrlm-specific +# additions (ducking types, setAudioDucking, Manager::IsInitialized). +# We can remove this if added to upstream testframework +git -C entservices-testframework apply "$GITHUB_WORKSPACE/ci/mocks/devicesettings_ctrlm.patch" + +git clone --depth 1 --filter=blob:none --sparse --branch develop https://github.com/rdkcentral/iarmmgrs.git +git -C iarmmgrs sparse-checkout set hal + +git clone --depth 1 --filter=blob:none --sparse https://github.com/rdkcentral/rdk-halif-deepsleep_manager.git +git -C rdk-halif-deepsleep_manager sparse-checkout set include + +git clone --depth 1 --filter=blob:none --sparse https://github.com/rdkcentral/rdk-halif-power_manager.git +git -C rdk-halif-power_manager sparse-checkout set include + +git clone --depth 1 --filter=blob:none --sparse --branch develop https://github.com/rdkcentral/rdkversion.git +git -C rdkversion sparse-checkout set src + +IARMMGRS_DIR="$GITHUB_WORKSPACE/iarmmgrs" +DEEPSLEEP_HAL_DIR="$GITHUB_WORKSPACE/rdk-halif-deepsleep_manager" +POWER_HAL_DIR="$GITHUB_WORKSPACE/rdk-halif-power_manager" +RDKVERSION_DIR="$GITHUB_WORKSPACE/rdkversion" + +############################ +# 3. Create stub/empty headers for external dependencies +echo "======================================================================================" +echo "Creating stub headers" + +HEADERS_DIR="$GITHUB_WORKSPACE/ci/headers" +XRSDK_HEADERS_DIR="$HEADERS_DIR/xr-voice-sdk" +mkdir -p "${HEADERS_DIR}" +mkdir -p "${HEADERS_DIR}/rdk/iarmbus" +mkdir -p "${HEADERS_DIR}/rdk/ds" +mkdir -p "${HEADERS_DIR}/rdk/iarmmgrs-hal" +mkdir -p "${XRSDK_HEADERS_DIR}" + +# Copy real xr-voice-sdk headers. +# xr_fdc.h is NOT copied: only needed when FDC_ENABLED=ON +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-speech-vrex/xrsv.h" "${XRSDK_HEADERS_DIR}/" +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-speech-router/xrsr.h" "${XRSDK_HEADERS_DIR}/" +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-mq/xr_mq.h" "${XRSDK_HEADERS_DIR}/" +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-speech-vrex/xrsv_http/xrsv_http.h" "${XRSDK_HEADERS_DIR}/" +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-speech-vrex/xrsv_ws_nextgen/xrsv_ws_nextgen.h" "${XRSDK_HEADERS_DIR}/" +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-timestamp/xr_timestamp.h" "${XRSDK_HEADERS_DIR}/" + +# Generate rdkx_logger_modules.h from xr-voice-sdk's module configuration, +# then copy the real rdkx_logger and xr_voice_sdk headers. +# This replaces the hand-written ci/mocks/control/ stubs. +python3 "$GITHUB_WORKSPACE/xr-voice-sdk/scripts/rdkx_logger_modules_to_c.py" \ + "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-logger/rdkv/rdkx_logger_modules.json" \ + "${XRSDK_HEADERS_DIR}/rdkx_logger_modules" "mw" +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr-logger/rdkx_logger_mw.h" "${XRSDK_HEADERS_DIR}/rdkx_logger.h" +cp "$GITHUB_WORKSPACE/xr-voice-sdk/src/xr_voice_sdk.h" "${XRSDK_HEADERS_DIR}/xr_voice_sdk.h" + +cd "${HEADERS_DIR}" + +# IARM headers (types provided via -include Iarm.h) +touch rdk/iarmbus/libIARM.h +touch rdk/iarmbus/libIBus.h +touch rdk/iarmbus/libIBusDaemon.h + +# IARM manager headers +# sysMgr.h conflicts with the forced Iarm.h mock, which already provides the +# needed SYSMgr types. Use a shim here. To remove it later, either stop +# force-including those overlapping Iarm.h declarations or drop sysMgr.h from +# ctrlm source. +cat > rdk/iarmmgrs-hal/sysMgr.h <<'EOF' +#ifndef CTRLM_CI_SYSMGR_SHIM_H +#define CTRLM_CI_SYSMGR_SHIM_H + +/* SYSMgr declarations are provided by the forced Iarm.h mock in CI. */ + +#endif +EOF +cp "$DEEPSLEEP_HAL_DIR/include/deepSleepMgr.h" rdk/iarmmgrs-hal/deepSleepMgr.h +cp "$POWER_HAL_DIR/include/plat_power.h" rdk/iarmmgrs-hal/pwrMgr.h +[ -f rdk/iarmmgrs-hal/sysMgr.h ] +[ -f rdk/iarmmgrs-hal/deepSleepMgr.h ] +[ -f rdk/iarmmgrs-hal/pwrMgr.h ] + +# Device settings headers (types provided via force-included devicesettings.h mock) +touch rdk/ds/audioOutputPort.hpp +touch rdk/ds/dsDisplay.h +touch rdk/ds/dsError.h +touch rdk/ds/dsMgr.h +touch rdk/ds/dsRpc.h +touch rdk/ds/dsTypes.h +touch rdk/ds/dsUtl.h +touch rdk/ds/exception.hpp +touch rdk/ds/host.hpp +touch rdk/ds/manager.hpp +touch rdk/ds/videoOutputPort.hpp +touch rdk/ds/videoOutputPortConfig.hpp +touch rdk/ds/videoOutputPortType.hpp +touch rdk/ds/videoResolution.hpp +touch rdk/ds/frontPanelIndicator.hpp +touch rdk/ds/frontPanelConfig.hpp + +# rfcapi.h (types provided via -include Rfc.h) +touch rfcapi.h + +# comcastIrKeyCodes.h (unconditionally included by ctrlm_main.cpp) +find "$IARMMGRS_DIR" -name comcastIrKeyCodes.h -print -quit | xargs -r -I{} cp "{}" comcastIrKeyCodes.h +[ -f comcastIrKeyCodes.h ] + +# rdkversion.h (used by ctrlm_main.cpp) +cp "$RDKVERSION_DIR/src/rdkversion.h" rdkversion.h +[ -f rdkversion.h ] + +# secure_wrapper (types provided via empty stub — no v_secure_* calls in core) +touch secure_wrapper.h + +# safec compatibility header - committed in ci/mocks, copied here so it is +# resolved on the generated-headers include path. +cp "$GITHUB_WORKSPACE/ci/mocks/safec_lib.h" safec_lib.h + +echo "Stub headers created successfully" + +cd "${GITHUB_WORKSPACE}" + +mkdir -p "${GITHUB_WORKSPACE}/install/usr/include" +printf '{}\n' > "${GITHUB_WORKSPACE}/install/usr/include/ctrlm_config_empty.json" + +############################ +# 4. Create stub shared libraries for linking +echo "======================================================================================" +echo "Creating stub libraries" + +STUB_LIB_DIR="$GITHUB_WORKSPACE/install/usr/lib" +mkdir -p "${STUB_LIB_DIR}" + +# Create a minimal C stub source +cat > /tmp/stub.c << 'STUB_EOF' +void __stub_placeholder(void) {} +STUB_EOF + +# Build stub .so for each missing library +# nopoll and dshalcli are unused (factory-only) but unconditionally linked by CMakeLists.txt +# We can remove them from the link list in the future if desired, but for now just provide stubs to satisfy the linker. +for lib in xr-voice-sdk rdkversion IARMBus ds nopoll dshalcli rfcapi secure_wrapper evdev; do + gcc -shared -fPIC -o "${STUB_LIB_DIR}/lib${lib}.so" /tmp/stub.c +done + +rm /tmp/stub.c + +echo "Stub libraries created in ${STUB_LIB_DIR}" +echo "======================================================================================" +echo "build_dependencies.sh complete" diff --git a/ci/cov_build.sh b/ci/cov_build.sh new file mode 100644 index 00000000..3a51b921 --- /dev/null +++ b/ci/cov_build.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# +# If not stated otherwise in this file or this component's license file the +# following copyright and licenses apply: +# +# Copyright 2026 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -x +set -e +############################## +GITHUB_WORKSPACE="${PWD}" +ls -la "${GITHUB_WORKSPACE}" +############################ + +# Build control (ctrlm-main) +echo "building control (ctrlm-main)" + +XRSDK_REAL_HEADERS="$GITHUB_WORKSPACE/ci/headers/xr-voice-sdk" +XLOG_COMPAT="$GITHUB_WORKSPACE/ci/mocks/xlog_ci_compat.h" +MOCK_DIR="$GITHUB_WORKSPACE/entservices-testframework/Tests/mocks" +MOCK_OVERRIDES="$GITHUB_WORKSPACE/ci/mocks/testframework_overrides.h" +HEADERS_DIR="$GITHUB_WORKSPACE/ci/headers" +EMPTY_JSON="$GITHUB_WORKSPACE/install/usr/include/ctrlm_config_empty.json" +GLIB_CFLAGS="$(pkg-config --cflags glib-2.0)" + +cmake -G Ninja -S "$GITHUB_WORKSPACE" -B build/control \ +-DCMAKE_INSTALL_PREFIX="${GITHUB_WORKSPACE}/install/usr" \ +-DCMAKE_INSTALL_SYSCONFDIR="${GITHUB_WORKSPACE}/install/etc" \ +-DCMAKE_MODULE_PATH="${GITHUB_WORKSPACE}/install/tools/cmake" \ +-DCMAKE_VERBOSE_MAKEFILE=ON \ +-DCMAKE_PROJECT_VERSION="1.0.0" \ +-DTHUNDER=OFF \ +-DTHUNDER_SECURITY=OFF \ +-DBLE_ENABLED=OFF \ +-DRF4CE_ENABLED=OFF \ +-DIP_ENABLED=OFF \ +-DTELEMETRY_SUPPORT=OFF \ +-DAUTH_ENABLED=OFF \ +-DXRSR_HTTP=OFF \ +-DXRSR_SDT=OFF \ +-DBUILD_CTRLM_FACTORY=OFF \ +-DBUILD_FACTORY_TEST=OFF \ +-DUSE_SAFEC=OFF \ +-DUSE_IARM_POWER_MANAGER=ON \ +-DBREAKPAD=OFF \ +-DFDC_ENABLED=OFF \ +-DENABLE_ASYNC_SRVR_MSG=OFF \ +-DHIDE_NON_EXTERNAL_SYMBOLS=OFF \ +-DBUILD_SYSTEM=NONE \ +-DCTRLM_UTILS_JSON_COMBINE="${GITHUB_WORKSPACE}/xr-voice-sdk/scripts/vsdk_json_combine.py" \ +-DCTRLM_UTILS_JSON_TO_HEADER="${GITHUB_WORKSPACE}/xr-voice-sdk/scripts/vsdk_json_to_header.py" \ +-DCTRLM_CONFIG_JSON_CPC="${EMPTY_JSON}" \ +-DCTRLM_CONFIG_JSON_CPC_SUB="${EMPTY_JSON}" \ +-DCTRLM_CONFIG_JSON_CPC_ADD="${EMPTY_JSON}" \ +-DCTRLM_CONFIG_JSON_MAIN_SUB="${EMPTY_JSON}" \ +-DCTRLM_CONFIG_JSON_MAIN_ADD="${EMPTY_JSON}" \ +-DCMAKE_CXX_FLAGS=" \ +-I ${XRSDK_REAL_HEADERS} \ +-I ${MOCK_DIR} \ +-I ${MOCK_DIR}/devicesettings \ +-I ${HEADERS_DIR} \ +-I ${HEADERS_DIR}/rdk/iarmbus \ +-I ${HEADERS_DIR}/rdk/ds \ +-I ${HEADERS_DIR}/rdk/iarmmgrs-hal \ +-I ${GITHUB_WORKSPACE}/install/usr/include \ +-I /usr/include/libdrm \ +${GLIB_CFLAGS} \ +-include ${XLOG_COMPAT} \ +-include ${MOCK_DIR}/Iarm.h \ +-include ${MOCK_OVERRIDES} \ +-include ${MOCK_DIR}/devicesettings.h \ +-include ${MOCK_DIR}/Rfc.h \ +-Wall -Wno-error \ +-DSAFEC_DUMMY_API \ +-DDISABLE_SECURITY_TOKEN" \ +-DCMAKE_C_FLAGS=" \ +-I ${XRSDK_REAL_HEADERS} \ +-I ${MOCK_DIR} \ +-I ${HEADERS_DIR} \ +-I ${HEADERS_DIR}/rdk/iarmbus \ +-I ${HEADERS_DIR}/rdk/ds \ +-I ${HEADERS_DIR}/rdk/iarmmgrs-hal \ +-I ${GITHUB_WORKSPACE}/install/usr/include \ +-I /usr/include/libdrm \ +${GLIB_CFLAGS} \ +-Wall -Wno-error \ +-DSAFEC_DUMMY_API \ +-DDISABLE_SECURITY_TOKEN" \ +-DCMAKE_EXE_LINKER_FLAGS="-L${GITHUB_WORKSPACE}/install/usr/lib -Wl,--unresolved-symbols=ignore-all" + +# CMakeLists.txt unconditionally appends -Werror via target_compile_options, which +# comes after CMAKE_CXX_FLAGS and overrides -Wno-error. Strip it from the generated +# build files after cmake configure. To remove this, add an ENABLE_WERROR option to +# CMakeLists.txt that appends -Wno-error when OFF, and pass -DENABLE_WERROR=OFF here. +find "${GITHUB_WORKSPACE}/build/control" \( -name "*.ninja" -o -name "flags.make" \) -exec sed -i 's/\(^\|[[:space:]]\)-Werror\([[:space:]]\|$\)/\1\2/g' {} \; + +cmake --build build/control -j$(nproc) 2>&1 +echo "======================================================================================" +echo "control build complete" +exit 0 diff --git a/ci/mocks/devicesettings_ctrlm.patch b/ci/mocks/devicesettings_ctrlm.patch new file mode 100644 index 00000000..d2faf8a8 --- /dev/null +++ b/ci/mocks/devicesettings_ctrlm.patch @@ -0,0 +1,40 @@ +--- a/Tests/mocks/devicesettings.h 2026-04-21 19:12:58.154981897 +0000 ++++ b/Tests/mocks/devicesettings.h 2026-04-21 19:13:50.591192520 +0000 +@@ -72,6 +72,16 @@ + dsAUDIOPORT_TYPE_MAX /**< Maximum index for audio port type. */ + } dsAudioPortType_t; + ++typedef enum _dsAudioDuckingAction_t { ++ dsAUDIO_DUCKINGACTION_START = 0, ++ dsAUDIO_DUCKINGACTION_STOP = 1 ++} dsAudioDuckingAction_t; ++ ++typedef enum _dsAudioDuckingType_t { ++ dsAUDIO_DUCKINGTYPE_ABSOLUTE = 0, ++ dsAUDIO_DUCKINGTYPE_RELATIVE = 1 ++} dsAudioDuckingType_t; ++ + typedef enum _dsVideoPortType_t { + dsVIDEOPORT_TYPE_RF = 0, ///< RF modulator (channel 3/4) video output + dsVIDEOPORT_TYPE_BB, ///< Baseband (composite, RCA) video output +@@ -934,6 +944,11 @@ + void enableARC(dsAudioARCTypes_t type, bool enable); + uint32_t getDolbyVolumeMode() const; + void setStereoMode(const std::string &mode, bool persist); ++ void setAudioDucking(dsAudioDuckingAction_t action, dsAudioDuckingType_t type, float level) { ++ (void)action; ++ (void)type; ++ (void)level; ++ } + + + +@@ -1420,6 +1435,8 @@ + static ManagerImpl* impl; + + public: ++ static bool IsInitialized; ++ + Manager(); + + static void setImpl(ManagerImpl* newImpl); diff --git a/ci/mocks/safec_lib.h b/ci/mocks/safec_lib.h new file mode 100644 index 00000000..37d75748 --- /dev/null +++ b/ci/mocks/safec_lib.h @@ -0,0 +1,44 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Compatibility header: maps ctrlm's local include name to the real libsafec + * package headers (libsafec-dev / safec-common-wrapper in production). + * + * In CI this file is copied from ci/mocks/ into ci/headers/ by + * build_dependencies.sh so it is resolved on the generated-headers include + * path used by cov_build.sh. + * + * This exists because ctrlm includes safec_lib.h directly, while the native CI + * environment provides the underlying libsafec package headers instead of that + * project-local wrapper. + */ + +#ifndef CTRLM_CI_SAFEC_LIB_H_ +#define CTRLM_CI_SAFEC_LIB_H_ + +#include +#include +#include + +#ifndef ERR_CHK +#define ERR_CHK(rc) do { (void)(rc); } while(0) +#endif + +#endif /* CTRLM_CI_SAFEC_LIB_H_ */ diff --git a/ci/mocks/testframework_overrides.h b/ci/mocks/testframework_overrides.h new file mode 100644 index 00000000..22b8a0a2 --- /dev/null +++ b/ci/mocks/testframework_overrides.h @@ -0,0 +1,48 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * CI patch layer for gaps in the entservices-testframework mock headers. + * + * ctrlm needs a few IARM declarations that are missing from the testframework + * headers used by this reduced build. This file adds only those missing pieces + * and can be removed once they exist upstream. + */ +#ifndef CTRLM_CI_TESTFRAMEWORK_OVERRIDES_H_ +#define CTRLM_CI_TESTFRAMEWORK_OVERRIDES_H_ + +/* Pinned testframework Iarm.h stops at UNKNOWN; ctrlm also references MAX. */ +#ifndef DEEPSLEEP_WAKEUPREASON_MAX +#define DEEPSLEEP_WAKEUPREASON_MAX (DEEPSLEEP_WAKEUPREASON_UNKNOWN + 1) +#endif + +/* IARM common API string not present in the pinned testframework mock */ +#ifndef IARM_BUS_COMMON_API_PowerPreChange +#define IARM_BUS_COMMON_API_PowerPreChange "PowerPreChange" +#endif + +/* Struct not present in the pinned testframework mock */ +#ifndef CTRLM_IARM_BUS_COMMON_API_POWERPRECHANGE_PARAM_T_DEFINED +#define CTRLM_IARM_BUS_COMMON_API_POWERPRECHANGE_PARAM_T_DEFINED +typedef struct { + IARM_Bus_PWRMgr_PowerState_t newState; + IARM_Bus_PWRMgr_PowerState_t curState; +} IARM_Bus_CommonAPI_PowerPreChange_Param_t; +#endif + +#endif /* CTRLM_CI_TESTFRAMEWORK_OVERRIDES_H_ */ diff --git a/ci/mocks/xlog_ci_compat.h b/ci/mocks/xlog_ci_compat.h new file mode 100644 index 00000000..43836b73 --- /dev/null +++ b/ci/mocks/xlog_ci_compat.h @@ -0,0 +1,42 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * CI compatibility shim. + * + * The production rdkx_logger.h is included transitively everywhere via + * ctrlm_log.h. In the Yocto build the SDK headers pull several std:: names + * into the global namespace. A handful of ctrlm TUs (e.g. ctrlm_utils.cpp) + * rely on those names being globally visible. This tiny header replicates + * that behaviour for the CI native build. + */ +#ifndef CTRLM_CI_XLOG_COMPAT_H_ +#define CTRLM_CI_XLOG_COMPAT_H_ + +#ifdef __cplusplus +#include +#include +#include +using std::get; +using std::map; +using std::string; +using std::tuple; +#endif + +#endif /* CTRLM_CI_XLOG_COMPAT_H_ */ diff --git a/include/ctrlm_ipc.h b/include/ctrlm_ipc.h index 341d4ff9..7d87d12c 100644 --- a/include/ctrlm_ipc.h +++ b/include/ctrlm_ipc.h @@ -302,7 +302,8 @@ typedef enum { CTRLM_RCU_IARM_EVENT_RF4CE_PAIRING_WINDOW_TIMEOUT = 35, ///< Indicates that a battery milestone event occured CTRLM_RCU_IARM_EVENT_FIRMWARE_UPDATE_PROGRESS = 36, ///< Generated when an milestone is reached for remote firmware upgrade CTRLM_RCU_IARM_EVENT_VALIDATION_STATUS = 37, ///< Generated when the validation status changes - CTRLM_MAIN_IARM_EVENT_MAX = 38 ///< Placeholder for the last event (used in registration) + CTRLM_VOICE_IARM_EVENT_SESSION_SILENT = 38, ///< Voice session was silent (no speech detected) + CTRLM_MAIN_IARM_EVENT_MAX = 39 ///< Placeholder for the last event (used in registration) } ctrlm_main_iarm_event_t; /// @brief Remote Control Key Status @@ -753,7 +754,6 @@ typedef struct { typedef struct { unsigned char api_revision; ///< Revision of this API ctrlm_network_id_t network_id; ///< IN - Identifier of network - unsigned int key_code; ///< IN - Key code from device unsigned int pair_code; ///< IN - Pairing code from device ctrlm_iarm_call_result_t result; ///< OUT - return code of the operation } ctrlm_iarm_call_StartPairWithCode_params_t; diff --git a/include/ctrlm_ipc_voice.h b/include/ctrlm_ipc_voice.h index 270b92d9..4a912cad 100644 --- a/include/ctrlm_ipc_voice.h +++ b/include/ctrlm_ipc_voice.h @@ -64,7 +64,7 @@ #define CTRLM_VOICE_SESSION_TEXT_MAX_LENGTH (512) ///< Session text string maximum length #define CTRLM_VOICE_SESSION_MSG_MAX_LENGTH (128) ///< Session message string maximum length #define CTRLM_VOICE_QUERY_STRING_MAX_LENGTH (128) ///< Query string maximum name or value length -#define CTRLM_VOICE_QUERY_STRING_MAX_PAIRS (16) ///< Query string maximum number of name/value pairs +#define CTRLM_VOICE_QUERY_STRING_MAX_PAIRS (24) ///< Query string maximum number of name/value pairs #define CTRLM_VOICE_REQUEST_IP_MAX_LENGTH (48) ///< cURL request primary IP address string maximum length (big enough for IPv6) #define CTRLM_VOICE_MIN_UTTERANCE_DURATION_MAXIMUM (600) ///< Maximum value of the utterance duration minimum setting (in milliseconds) @@ -228,7 +228,7 @@ typedef struct { ctrlm_network_type_t network_type; ///< Type of network on which the controller is bound ctrlm_controller_id_t controller_id; ///< A unique identifier of the remote unsigned long session_id; ///< A unique id for the voice session. - ctrlm_voice_session_end_reason_t reason; ///< The reason for ending + ctrlm_voice_session_end_reason_t reason; ///< The reason for ending session unsigned char is_voice_assistant; ///< Boolean indicating if the device is a far-field device (1) as opposed to a hand-held remote (0). } ctrlm_voice_iarm_event_session_end_t; @@ -281,6 +281,16 @@ typedef struct { long return_code_internal; ///< Internally generated return code } ctrlm_voice_iarm_event_session_short_t; +typedef struct { + unsigned char api_revision; ///< The revision of this API. + ctrlm_network_id_t network_id; ///< Identifier of network on which the controller is bound + ctrlm_network_type_t network_type; ///< Type of network on which the controller is bound + ctrlm_controller_id_t controller_id; ///< A unique identifier of the remote + unsigned long session_id; ///< A unique id for the voice session. + ctrlm_voice_session_end_reason_t reason; ///< The reason that the voice session was silent + long return_code_internal; ///< Internally generated return code +} ctrlm_voice_iarm_event_session_silent_t; + typedef struct { unsigned char api_revision; ///< The revision of this API char media_service_url[2083]; ///< The url for the media service (null terminated string) @@ -376,6 +386,7 @@ typedef struct { /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_STATS | ctrlm_voice_iarm_event_session_stats_t * | Generated when the statistics of the voice session are available | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_ABORT | ctrlm_voice_iarm_event_session_abort_t * | Generated when a voice session is aborted (denied) | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_SHORT | ctrlm_voice_iarm_event_session_short_t * | Generated when a short voice session is detected | +/// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_SESSION_SILENT | ctrlm_voice_iarm_event_session_silent_t * | Generated when a silent voice session is detected | /// | CTRLM_MAIN_IARM_BUS_NAME | CTRLM_VOICE_IARM_EVENT_MEDIA_SERVICE | ctrlm_voice_iarm_event_media_service_t * | Generated when a media service response is received | /// /// IARM events are available on a subscription basis. In order to receive an event, a client must explicitly register to receive the event by calling diff --git a/src/auth/ctrlm_auth.h b/src/auth/ctrlm_auth.h index 0964335f..bda34998 100644 --- a/src/auth/ctrlm_auth.h +++ b/src/auth/ctrlm_auth.h @@ -34,7 +34,6 @@ class ctrlm_auth_t { virtual bool get_device_id(std::string &device_id) = 0; virtual bool get_account_id(std::string &account_id) = 0; virtual bool get_partner_id(std::string &partner_id) = 0; - virtual bool get_experience(std::string &experience) = 0; virtual bool get_sat(std::string &sat, time_t &expiration) = 0; virtual bool supports_sat_expiration() const = 0; diff --git a/src/auth/ctrlm_auth_certificate.cpp b/src/auth/ctrlm_auth_certificate.cpp index 63ff900c..63ae514e 100644 --- a/src/auth/ctrlm_auth_certificate.cpp +++ b/src/auth/ctrlm_auth_certificate.cpp @@ -42,7 +42,7 @@ ctrlm_auth_certificate_t::ctrlm_auth_certificate_t() { char *cert_path = NULL; char *cert_password = NULL; - rdkcertselector_h cert_selector = rdkcertselector_new( NULL, NULL, "MTLS" ); + rdkcertselector_h cert_selector = rdkcertselector_new( NULL, NULL, "FBK_MTLS" ); if(cert_selector == NULL){ XLOGD_TELEMETRY("cert selector init failed"); diff --git a/src/auth/ctrlm_auth_thunder.cpp b/src/auth/ctrlm_auth_thunder.cpp index 2f954df3..7e37f8b4 100644 --- a/src/auth/ctrlm_auth_thunder.cpp +++ b/src/auth/ctrlm_auth_thunder.cpp @@ -48,11 +48,6 @@ bool ctrlm_auth_thunder_t::get_partner_id(std::string &partner_id) { return(ret); } -bool ctrlm_auth_thunder_t::get_experience(std::string &experience) { - bool ret = this->plugin->get_experience(experience); - return(ret); -} - bool ctrlm_auth_thunder_t::get_sat(std::string &sat, time_t &expiration) { bool ret = this->plugin->get_sat(sat, expiration); return(ret); diff --git a/src/auth/ctrlm_auth_thunder.h b/src/auth/ctrlm_auth_thunder.h index 3a476e67..67a385db 100644 --- a/src/auth/ctrlm_auth_thunder.h +++ b/src/auth/ctrlm_auth_thunder.h @@ -13,7 +13,6 @@ class ctrlm_auth_thunder_t : public ctrlm_auth_t { virtual bool get_device_id(std::string &device_id); virtual bool get_account_id(std::string &account_id); virtual bool get_partner_id(std::string &partner_id); - virtual bool get_experience(std::string &experience); virtual bool get_sat(std::string &sat, time_t &expiration); virtual bool supports_sat_expiration() const; diff --git a/src/auth/ctrlm_thunder_plugin_authservice.cpp b/src/auth/ctrlm_thunder_plugin_authservice.cpp index 60c57d7a..e5913d07 100644 --- a/src/auth/ctrlm_thunder_plugin_authservice.cpp +++ b/src/auth/ctrlm_thunder_plugin_authservice.cpp @@ -125,24 +125,6 @@ bool ctrlm_thunder_plugin_authservice_t::get_account_id(std::string &account_id) return(ret); } -bool ctrlm_thunder_plugin_authservice_t::get_experience(std::string &experience) { - bool ret = false; - JsonObject params, response; - if(this->call_plugin("getExperience", (void *)¶ms, (void *)&response)) { - if(response["success"].Boolean()) { // If success doesn't exist, it defaults to false which is fine. - experience = response["experience"].String(); - if(!experience.empty()) { - ret = true; - } - } else { - XLOGD_WARN("Success for getExperience was false"); - } - } else { - XLOGD_WARN("Call for getExperience failed"); - } - return(ret); -} - bool ctrlm_thunder_plugin_authservice_t::get_sat(std::string &sat, time_t &expiration) { bool ret = false; JsonObject params, response; diff --git a/src/auth/ctrlm_thunder_plugin_authservice.h b/src/auth/ctrlm_thunder_plugin_authservice.h index c39de41d..169cee88 100644 --- a/src/auth/ctrlm_thunder_plugin_authservice.h +++ b/src/auth/ctrlm_thunder_plugin_authservice.h @@ -75,13 +75,6 @@ class ctrlm_thunder_plugin_authservice_t : public Thunder::Plugin::ctrlm_thunder */ bool get_account_id(std::string &account_id); - /** - * Function that retrieves the Experience String from Authservice. - * @param experience The reference to a string which will contain the Experience String. - * @return True on success otherwise False. - */ - bool get_experience(std::string &experience); - /** * Function that retrieves the SAT Token from Authservice. * @param sat The reference to a string which will contain the SAT Token. diff --git a/src/ble/ctrlm_ble_controller.cpp b/src/ble/ctrlm_ble_controller.cpp index d8886150..f1a87c22 100644 --- a/src/ble/ctrlm_ble_controller.cpp +++ b/src/ble/ctrlm_ble_controller.cpp @@ -531,13 +531,30 @@ void ctrlm_obj_controller_ble_t::setSupportedIrdbs(uint8_t vendor_support_bitmas this->irdbs_supported_ = vendor_support_bitmask; ctrlm_irdb_interface_t *irdb = ctrlm_main_irdb_get(); - ctrlm_irdb_vendor_info_t vendor_info; - if (irdb && irdb->get_vendor_info(vendor_info)) { - XLOGD_INFO("Controller <%s> IRDBs supported bitmask = <0x%X>, which %s support the loaded IRDB plugin vendor <%s>", - ieee_address_get().to_string().c_str(), vendor_support_bitmask, - isSupportedIrdb(vendor_info) ? "DOES" : "does NOT", vendor_info.name.c_str()); + + if (irdb == NULL) { + XLOGD_ERROR("IRDB interface is NULL!!!"); + return; + } + + ctrlm_irdb_vendor_info_t rcu_vendor_info{}; + rcu_vendor_info.rcu_support_bitmask = vendor_support_bitmask; + if (!irdb->set_vendor(rcu_vendor_info)) { + XLOGD_ERROR("Failed to set IRDB vendor info for controller <%s> with bitmask <0x%X>.", + ieee_address_get().to_string().c_str(), vendor_support_bitmask); + } + + ctrlm_irdb_vendor_info_t vendor_info{}; + if (irdb->get_vendor_info(vendor_info)) { + if (isSupportedIrdb(vendor_info)) { + XLOGD_INFO("Controller <%s> IRDBs supported bitmask = <0x%X>, which DOES support the loaded IRDB plugin vendor <%s>", + ieee_address_get().to_string().c_str(), vendor_support_bitmask, vendor_info.name.c_str()); + } else { + XLOGD_ERROR("Controller <%s> IRDBs supported bitmask = <0x%X>, which does NOT support the loaded IRDB plugin vendor <%s>", + ieee_address_get().to_string().c_str(), vendor_support_bitmask, vendor_info.name.c_str()); + } } else { - XLOGD_INFO("Controller <%s> IRDBs supported bitmask = <0x%X>, couldn't retrieve IRDB plugin vendor info.", + XLOGD_WARN("Controller <%s> IRDBs supported bitmask = <0x%X>, couldn't retrieve IRDB plugin vendor info.", ieee_address_get().to_string().c_str(), vendor_support_bitmask); } } @@ -615,13 +632,13 @@ void ctrlm_obj_controller_ble_t::print_status() { XLOGD_INFO("Model : %s", model_->to_string().c_str()); XLOGD_INFO("MAC Address : %s", ieee_address_->to_string().c_str()); XLOGD_INFO("Device Minor ID : %d", device_minor_id_); - XLOGD_INFO("Battery Level : %u%%", get_battery_percent()); + XLOGD_AUTOMATION_INFO("Battery Level : %u%%", get_battery_percent()); XLOGD_INFO("HW Revision : %s", hw_revision_->to_string().c_str()); XLOGD_INFO("FW Revision : %s", fw_revision_->to_string().c_str()); - XLOGD_INFO("SW Revision : %s", sw_revision_->to_string().c_str()); + XLOGD_AUTOMATION_INFO("SW Revision : %s", sw_revision_->to_string().c_str()); XLOGD_INFO("Serial Number : %s", serial_number_->to_string().c_str()); XLOGD_INFO(""); - XLOGD_INFO("Connected : %s", (connected_==true) ? "true" : "false"); + XLOGD_AUTOMATION_INFO("Connected : %s", (connected_==true) ? "true" : "false"); XLOGD_INFO("Last Activity Time : %s", ctrlm_utils_time_as_string(this->last_activity_time_get()).c_str()); XLOGD_INFO("Bound Time : %s", ctrlm_utils_time_as_string(this->time_binding_get()).c_str()); XLOGD_INFO(""); @@ -654,4 +671,21 @@ void ctrlm_obj_controller_ble_t::print_status() { XLOGD_WARN("------------------------------------------------------------"); } +void ctrlm_obj_controller_ble_t::update_controller_id_and_db_entry(std::string db_name, ctrlm_network_id_t network_id, ctrlm_controller_id_t new_id) { + ctrlm_obj_controller_t::update_controller_id_and_db_entry(db_name, network_id, new_id); + + std::stringstream new_controller_db_table; + new_controller_db_table << db_name << "_" << COUT_HEX_MODIFIER << (int)network_id << "_controller_" << COUT_HEX_MODIFIER << (int)new_id; + std::string new_table = new_controller_db_table.str(); + + product_name_->set_table(new_table); + serial_number_->set_table(new_table); + manufacturer_->set_table(new_table); + model_->set_table(new_table); + fw_revision_->set_table(new_table); + sw_revision_->set_table(new_table); + hw_revision_->set_table(new_table); + battery_percent_->set_table(new_table); +} + // End Function Implementations diff --git a/src/ble/ctrlm_ble_controller.h b/src/ble/ctrlm_ble_controller.h index 5179578c..432f5c76 100644 --- a/src/ble/ctrlm_ble_controller.h +++ b/src/ble/ctrlm_ble_controller.h @@ -147,6 +147,8 @@ class ctrlm_obj_controller_ble_t : public ctrlm_obj_controller_t { ctrlm_timestamp_t getVoiceStartTimeKey() const; ctrlm_timestamp_t getVoiceStartTimeLocal() const; + void update_controller_id_and_db_entry(std::string name, ctrlm_network_id_t network_id, ctrlm_controller_id_t new_id); + private: ctrlm_obj_network_ble_t *obj_network_ble_ = NULL; diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index dca4c78f..57cdb6da 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -525,7 +525,6 @@ void ctrlm_obj_network_ble_t::req_process_voice_session_begin(void *data, int si // only support ADPCM from ble-rcu component ctrlm_hal_ble_VoiceEncoding_t encoding = CTRLM_HAL_BLE_ENCODING_ADPCM; - ctrlm_hal_ble_VoiceStreamEnd_t streamEnd = CTRLM_HAL_BLE_VOICE_STREAM_END_ON_KEY_UP; ctrlm_voice_format_t voice_format = { .type = CTRLM_VOICE_FORMAT_INVALID }; @@ -543,18 +542,21 @@ void ctrlm_obj_network_ble_t::req_process_voice_session_begin(void *data, int si audio_format.getHeaderInfoAdpcm(adpcm_frame->offset_step_size_index, adpcm_frame->offset_predicted_sample_lsb, adpcm_frame->offset_predicted_sample_msb, adpcm_frame->offset_sequence_value, adpcm_frame->shift_sequence_value, adpcm_frame->sequence_value_min, adpcm_frame->sequence_value_max); pressAndHoldSupport = audio_format.getPressAndHoldSupport(); - if(!pressAndHoldSupport) { - streamEnd = CTRLM_HAL_BLE_VOICE_STREAM_END_ON_AUDIO_DURATION; - } controllers_[controller_id]->setPressAndHoldSupport(pressAndHoldSupport); } } + ctrlm_voice_start_audio_params_t audio_start_params; + audio_start_params.m_controller_id = controller_id; + audio_start_params.m_fd = -1; + audio_start_params.m_started = false; + auto audio_start_cb = std::bind(&ctrlm_obj_network_ble_t::start_controller_audio_streaming, this, std::placeholders::_1); + voice_status = ctrlm_get_voice_obj()->voice_session_req(network_id_get(), controller_id, device, voice_format, NULL, controllers_[controller_id]->get_model().c_str(), controllers_[controller_id]->get_sw_revision().to_string().c_str(), controllers_[controller_id]->get_hw_revision().to_string().c_str(), 0.0, - false, NULL, NULL, NULL, true, pressAndHoldSupport); + false, NULL, NULL, NULL, true, pressAndHoldSupport, audio_start_cb, &audio_start_params); if (!controllers_[controller_id]->get_capabilities().has_capability(ctrlm_controller_capabilities_t::capability::PAR) && (VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE == voice_status)) { XLOGD_WARN("PAR voice is enabled but not supported by BLE controller treating as normal voice session"); voice_status = VOICE_SESSION_RESPONSE_AVAILABLE; @@ -562,24 +564,21 @@ void ctrlm_obj_network_ble_t::req_process_voice_session_begin(void *data, int si if (VOICE_SESSION_RESPONSE_AVAILABLE != voice_status) { XLOGD_TELEMETRY("Failed opening voice session in ctrlm_voice_t, error = <%d>", voice_status); } else { + int fd = -1; bool success = false; - if (ble_rcu_interface_) { - int fd = -1; - - if (!ble_rcu_interface_->startAudioStreaming(ieee_address, encoding, streamEnd, fd)) { - XLOGD_ERROR("failed to start audio streaming on remote"); - } else { + if (!audio_start_params.m_started) { // voice session req did not need to start audio + start_controller_audio_streaming(&audio_start_params); + } + fd = audio_start_params.m_fd; - if (fd < 0) { - XLOGD_ERROR("Voice streaming pipe invalid (fd = <%d>), aborting voice session", fd); - success = false; - } else { - XLOGD_INFO("Acquired voice streaming pipe fd = <%d>, sending to voice engine", fd); - //Send the fd acquired from bluez to the voice engine - success = ctrlm_get_voice_obj()->voice_session_data(network_id_get(), controller_id, fd); - } - } + if (fd < 0) { + XLOGD_ERROR("Voice streaming pipe invalid (fd = <%d>), aborting voice session", fd); + success = false; + } else { + XLOGD_AUTOMATION_INFO("Acquired voice streaming pipe fd = <%d>, sending to voice engine", fd); + //Send the fd acquired from bluez to the voice engine + success = ctrlm_get_voice_obj()->voice_session_data(network_id_get(), controller_id, fd); } if (false == success) { @@ -678,7 +677,7 @@ void ctrlm_obj_network_ble_t::req_process_start_pairing(void *data, int size) { dqm->params->set_result(CTRLM_IARM_CALL_RESULT_SUCCESS, network_id_get()); } } else { - XLOGD_INFO("Starting pairing with a list of mac addresses! Pairing with first available..."); + XLOGD_INFO("Starting pairing with a list of MAC addresses! Pairing with first available..."); if(!ble_rcu_interface_->pairWithMacAddrs(dqm->params->ieee_address_list)) { XLOGD_ERROR("failed to start BLE remote scan"); dqm->params->set_result(CTRLM_IARM_CALL_RESULT_ERROR, network_id_get()); @@ -741,29 +740,13 @@ void ctrlm_obj_network_ble_t::req_process_pair_with_code(void *data, int size) { XLOGD_FATAL("Network is not ready!"); } else { if (ble_rcu_interface_) { - - if (dqm->params->key_code == KEY_BLUETOOTH) { - // KEY_BLUETOOTH means the pairing code is random 3 digit code embedded in the name - // so use pairWithCode - if (!ble_rcu_interface_->pairWithCode(dqm->params->pair_code)) { - // don't log error here, pairWithCode will handle printing the error. We do - // this because there is an error that is merely a warning that we don't want - // logged because it only confuses those analyzing the logs. - // XLOGD_ERROR("failed to start pairing with code"); - } else { - dqm->params->result = CTRLM_IARM_CALL_RESULT_SUCCESS; - } + if (!ble_rcu_interface_->pairWithCode(dqm->params->pair_code)) { + // don't log error here, pairWithCode will handle printing the error. We do + // this because there is an error that is merely a warning that we don't want + // logged because it only confuses those analyzing the logs. + // XLOGD_ERROR("failed to start pairing with code"); } else { - // if key_code is either not available or KEY_CONNECT, it means the pairing code is a - // hash of the MAC, so use pairWithMacHash - if (!ble_rcu_interface_->pairWithMacHash(dqm->params->pair_code)) { - // don't log error here, pairWithMacHash will handle printing the error. We do - // this because there is an error that is merely a warning that we don't want - // logged because it only confuses those analyzing the logs. - // XLOGD_ERROR("failed to start pairing with MAC hash"); - } else { - dqm->params->result = CTRLM_IARM_CALL_RESULT_SUCCESS; - } + dqm->params->result = CTRLM_IARM_CALL_RESULT_SUCCESS; } } } @@ -780,13 +763,15 @@ void ctrlm_obj_network_ble_t::req_process_program_ir_codes(void *data, int size) g_assert(dqm); g_assert(size == sizeof(ctrlm_main_queue_msg_program_ir_codes_t)); - if(dqm->success) {*(dqm->success) = false;} + bool success = false; if (!ready_) { XLOGD_FATAL("Network is not ready!"); } else { ctrlm_controller_id_t controller_id = dqm->controller_id; - if (!controller_exists(controller_id)) { + if (!is_managed_by_network(controller_id)) { + XLOGD_ERROR("Controller %d is not managed by the %s network", controller_id, name_get()); + } else if (!controller_exists(controller_id)) { XLOGD_ERROR("Controller doesn't exist!"); } else if (!controllers_[controller_id]->isSupportedIrdb(dqm->vendor_info)) { XLOGD_ERROR("Unsupported IRDB - not continuing with ir code download!"); @@ -816,12 +801,10 @@ void ctrlm_obj_network_ble_t::req_process_program_ir_codes(void *data, int size) XLOGD_ERROR("failed to program IR signal waveforms on remote"); } else { - - if (dqm->success) { *(dqm->success) = true; } - - controllers_[controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_TV, ir_rf_database_.get_tv_ir_code_id()); - controllers_[controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_AVR, ir_rf_database_.get_avr_ir_code_id()); - XLOGD_INFO("irdb_entry_id_name = <%s>", dqm->ir_codes->id.c_str()); + success = true; + controllers_[controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_TV, ir_rf_database_.get_tv_ir_code_id()); + controllers_[controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_AVR, ir_rf_database_.get_avr_ir_code_id()); + XLOGD_INFO("irdb_entry_id_name = <%s>", dqm->ir_codes->id.c_str()); } } // Store the IR codes in the database @@ -829,6 +812,7 @@ void ctrlm_obj_network_ble_t::req_process_program_ir_codes(void *data, int size) } } } + if(dqm->success) {dqm->success->push_back(success);} if(dqm->semaphore) { sem_post(dqm->semaphore); } @@ -843,13 +827,15 @@ void ctrlm_obj_network_ble_t::req_process_ir_clear_codes(void *data, int size) { g_assert(dqm); g_assert(size == sizeof(ctrlm_main_queue_msg_ir_clear_t)); - if(dqm->success) {*(dqm->success) = false;} + bool success = false; if (!ready_) { XLOGD_FATAL("Network is not ready!"); } else { ctrlm_controller_id_t controller_id = dqm->controller_id; - if (!controller_exists(controller_id)) { + if (!is_managed_by_network(controller_id)) { + XLOGD_ERROR("Controller %d is not managed by the %s network", controller_id, name_get()); + } else if (!controller_exists(controller_id)) { XLOGD_ERROR("Controller doesn't exist!"); } else { @@ -857,9 +843,7 @@ void ctrlm_obj_network_ble_t::req_process_ir_clear_codes(void *data, int size) { if (!ble_rcu_interface_->eraseIrSignals(controllers_[controller_id]->ieee_address_get().get_value())) { XLOGD_ERROR("failed to erase IR signal waveforms on remote"); } else { - - if (dqm->success) { *(dqm->success) = true; } - + success = true; ir_rf_database_.clear_ir_codes(); XLOGD_INFO("\n%s", ir_rf_database_.to_string(true).c_str()); controllers_[controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_TV, "0"); @@ -869,6 +853,7 @@ void ctrlm_obj_network_ble_t::req_process_ir_clear_codes(void *data, int size) { ir_rf_database_.store_db(); } } + if(dqm->success) {dqm->success->push_back(success);} if(dqm->semaphore) { sem_post(dqm->semaphore); } @@ -974,7 +959,7 @@ void ctrlm_obj_network_ble_t::req_process_find_my_remote(void *data, int size) { ctrlm_controller_id_t controller_id = get_last_used_controller(); if (CTRLM_HAL_CONTROLLER_ID_INVALID == controller_id) { - XLOGD_ERROR("no connected controllers to find!!"); + XLOGD_ERROR("no connected %s controllers to find!!", name_get()); dqm->params->set_result(CTRLM_IARM_CALL_RESULT_ERROR, network_id_get()); } else { if (ble_rcu_interface_) { @@ -1826,7 +1811,7 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { print_status = false; break; case CTRLM_HAL_BLE_PROPERTY_IS_UPGRADING: - XLOGD_INFO("Controller <%s> firmware upgrading = %s", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.is_upgrading ? "TRUE" : "FALSE"); + XLOGD_AUTOMATION_INFO("Controller <%s> firmware upgrading = %s", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.is_upgrading ? "TRUE" : "FALSE"); upgrade_in_progress_ = dqm->rcu_data.is_upgrading; if (!dqm->rcu_data.is_upgrading) { // If we get FALSE here, make sure the controller upgrade progress flag is cleared. But we don't want to set the controller progress @@ -1837,7 +1822,7 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { print_status = false; break; case CTRLM_HAL_BLE_PROPERTY_UPGRADE_PROGRESS: - XLOGD_INFO("Controller <%s> firmware upgrade %d%% complete...", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.upgrade_progress); + XLOGD_AUTOMATION_INFO("Controller <%s> firmware upgrade %d%% complete...", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.upgrade_progress); // From a controller perspective, we cannot use the CTRLM_HAL_BLE_PROPERTY_IS_UPGRADING flag above to determine if its actively upgrading. // Instead, its more accurate to use the progress percentage to determine if the remote is actively receiving firmware packets. controller->setUpgradeInProgress(dqm->rcu_data.upgrade_progress > 0 && dqm->rcu_data.upgrade_progress < 100); @@ -1850,7 +1835,7 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { print_status = false; break; case CTRLM_HAL_BLE_PROPERTY_UPGRADE_ERROR: - XLOGD_ERROR("Controller <%s> firmware upgrade FAILED with error <%s>.", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.upgrade_error); + XLOGD_AUTOMATION_ERROR("Controller <%s> firmware upgrade FAILED with error <%s>.", controller->ieee_address_get().to_string().c_str(), dqm->rcu_data.upgrade_error); report_status = false; print_status = false; controller->set_upgrade_error(dqm->rcu_data.upgrade_error); @@ -1883,7 +1868,7 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { controller->ota_failure_cnt_incr(); break; case CTRLM_HAL_BLE_PROPERTY_UNPAIR_REASON: - XLOGD_INFO("Controller <%s> notified reason for unpairing = <%s>", controller->ieee_address_get().to_string().c_str(), ctrlm_ble_unpair_reason_str(dqm->rcu_data.unpair_reason)); + XLOGD_AUTOMATION_INFO("Controller <%s> notified reason for unpairing = <%s>", controller->ieee_address_get().to_string().c_str(), ctrlm_ble_unpair_reason_str(dqm->rcu_data.unpair_reason)); last_rcu_unpair_metrics_.write_rcu_unpair_event(controller->ieee_address_get().get_value(), string(ctrlm_ble_unpair_reason_str(dqm->rcu_data.unpair_reason))); report_status = false; print_status = false; @@ -1898,7 +1883,7 @@ void ctrlm_obj_network_ble_t::ind_process_rcu_status(void *data, int size) { } break; case CTRLM_HAL_BLE_PROPERTY_REBOOT_REASON: - XLOGD_TELEMETRY("Controller <%s> notified reason for rebooting = <%s%s%s%s>", + XLOGD_AUTOMATION_TELEMETRY("Controller <%s> notified reason for rebooting = <%s%s%s%s>", controller->ieee_address_get().to_string().c_str(), ctrlm_ble_reboot_reason_str(dqm->rcu_data.reboot_reason), dqm->rcu_data.reboot_reason == CTRLM_BLE_RCU_REBOOT_REASON_ASSERT ? " - \"" : "", @@ -2030,6 +2015,8 @@ ctrlm_controller_id_t ctrlm_obj_network_ble_t::controller_add(ctrlm_hal_ble_rcu_ controller->setName(string(rcu_data.name)); controller->setAudioCodecs(rcu_data.audio_codecs); controller->setConnected(rcu_data.connected); + controller->setSupportedIrdbs(rcu_data.irdbs_supported); + // only update these parameters if they are not empty or invalid. if (rcu_data.serial_number[0] != '\0') { controller->setSerialNumber(string(rcu_data.serial_number)); } if (rcu_data.manufacturer[0] != '\0') { controller->setManufacturer(string(rcu_data.manufacturer)); } @@ -2041,7 +2028,6 @@ ctrlm_controller_id_t ctrlm_obj_network_ble_t::controller_add(ctrlm_hal_ble_rcu_ if (rcu_data.battery_level != 0xFF) { controller->setBatteryPercent(rcu_data.battery_level); } if (rcu_data.wakeup_config != 0xFF) { controller->setWakeupConfig(rcu_data.wakeup_config); } if (rcu_data.wakeup_custom_list_size != 0) { controller->setWakeupCustomList(rcu_data.wakeup_custom_list, rcu_data.wakeup_custom_list_size); } - if (rcu_data.irdbs_supported != 0) { controller->setSupportedIrdbs(rcu_data.irdbs_supported); } if (rcu_data.last_wakeup_key != 0xFF) { controller->setLastWakeupKey(rcu_data.last_wakeup_key); } controller->db_store(); @@ -2206,7 +2192,7 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { controller->setVoiceStartTime(keyDownTime); XLOGD_INFO("------------------------------------------------------------------------"); - XLOGD_INFO("CODE_VOICE_KEY button PRESSED event for device: %s", controller->ieee_address_get().to_string().c_str()); + XLOGD_AUTOMATION_INFO("CODE_VOICE_KEY button PRESSED event for device: %s", controller->ieee_address_get().to_string().c_str()); XLOGD_INFO("------------------------------------------------------------------------"); ctrlm_voice_iarm_call_voice_session_t v_params; @@ -2256,7 +2242,7 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { if (controller->isVoiceKey(dqm->event.code)) { if(!controller->getPressAndHoldSupport()) { // if the voice session is "Press and Release" then don't end session on voice key up event XLOGD_INFO("------------------------------------------------------------------------"); - XLOGD_INFO("CODE_VOICE_KEY button RELEASED event for device: %s (ignored for PAR session)", controller->ieee_address_get().to_string().c_str()); + XLOGD_AUTOMATION_INFO("CODE_VOICE_KEY button RELEASED event for device: %s (ignored for PAR session)", controller->ieee_address_get().to_string().c_str()); XLOGD_INFO("------------------------------------------------------------------------"); } else { rdkx_timestamp_t keyUpTime, keyUpTimeLocal, voiceStartTimeLocal, firstAudioDataTime; @@ -2291,11 +2277,11 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { } XLOGD_INFO("------------------------------------------------------------------------"); - XLOGD_INFO("CODE_VOICE_KEY button RELEASED event for device: %s duration <%lld ms> start lag <%lld ms>", controller->ieee_address_get().to_string().c_str(), audioDurationKeys, startAudioLag); + XLOGD_AUTOMATION_INFO("CODE_VOICE_KEY button RELEASED event for device: %s duration <%lld ms> start lag <%lld ms>", controller->ieee_address_get().to_string().c_str(), audioDurationKeys, startAudioLag); XLOGD_INFO("------------------------------------------------------------------------"); } else { XLOGD_INFO("------------------------------------------------------------------------"); - XLOGD_INFO("CODE_VOICE_KEY button RELEASED event for device: %s duration <%lld ms>", controller->ieee_address_get().to_string().c_str(), audioDurationKeys); + XLOGD_AUTOMATION_INFO("CODE_VOICE_KEY button RELEASED event for device: %s duration <%lld ms>", controller->ieee_address_get().to_string().c_str(), audioDurationKeys); XLOGD_INFO("------------------------------------------------------------------------"); } @@ -2441,7 +2427,7 @@ void ctrlm_obj_network_ble_t::controller_remove(ctrlm_controller_id_t controller ctrlm_controller_id_t ctrlm_obj_network_ble_t::controller_id_assign() { // Get the next available controller id - for(ctrlm_controller_id_t index = 1; index < CTRLM_MAIN_CONTROLLER_ID_ALL; index++) { + for(ctrlm_controller_id_t index = BLE_RCU_ID_RANGE_MIN; index < BLE_RCU_ID_RANGE_MAX; index++) { if(!controller_exists(index)) { XLOGD_INFO("controller id %u", index); return(index); @@ -2464,10 +2450,26 @@ void ctrlm_obj_network_ble_t::controllers_load() { XLOGD_WARN("deleting legacy IR controller object"); add_controller->db_destroy(); delete add_controller; - } else { - XLOGD_INFO("adding BLE controller with ID = 0x%X", id); - controllers_[id] = add_controller; + continue; + } + if (!is_managed_by_network(id)) { + ctrlm_controller_id_t new_id = controller_id_assign(); + + add_controller->db_destroy(); // safely can destroy the old entry since it was loaded earlier + + if (new_id == 0) { + XLOGD_ERROR("Unable to assign a new ID - deleting controller <%d>", id); + delete add_controller; + continue; + } + + add_controller->update_controller_id_and_db_entry(db_name_get(), network_id_get(), new_id); + add_controller->db_create(); // create the new entry with its new ID + XLOGD_WARN("Legacy BLE RCU controller id <%d> found - updating controller id to <%d>", id, new_id); + id = new_id; } + XLOGD_INFO("adding BLE controller with ID = 0x%X", id); + controllers_[id] = add_controller; } } @@ -2498,7 +2500,7 @@ void ctrlm_obj_network_ble_t::printStatus() { it->second->print_status(); } XLOGD_INFO("BLE Network Status: <%s>", ctrlm_rf_pair_state_str(state_)); - XLOGD_TELEMETRY("IR Programming Status: <%s>", ctrlm_ir_state_str(ir_state_)); + XLOGD_AUTOMATION_TELEMETRY("IR Programming Status: <%s>", ctrlm_ir_state_str(ir_state_)); XLOGD_WARN("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); } @@ -2616,3 +2618,47 @@ ctrlm_controller_id_t ctrlm_obj_network_ble_t::find_controller_from_upgrade_sess } return id; } + +void ctrlm_obj_network_ble_t::start_controller_audio_streaming(ctrlm_voice_start_audio_params_t *params) { + THREAD_ID_VALIDATE(); + int fd = -1; + ctrlm_controller_id_t id = params->m_controller_id; + params->m_fd = fd; + params->m_started = false; + + if (!ready_) { + XLOGD_FATAL("Network is not ready!"); + return; + } + + if(!controller_exists(id)) { + XLOGD_WARN("Controller %u doesn't exist.", id); + return; + } + + if (!ble_rcu_interface_) { + XLOGD_WARN("ble rcu interface not ready"); + return; + } + + ctrlm_hal_ble_VoiceEncoding_t encoding = CTRLM_HAL_BLE_ENCODING_ADPCM; + ctrlm_hal_ble_VoiceStreamEnd_t streamEnd = CTRLM_HAL_BLE_VOICE_STREAM_END_ON_KEY_UP; + auto rcu = controllers_.at(id); + + if (!rcu->getPressAndHoldSupport()) { // if the voice session is "Press and Release" then end stream on audio duration instead of key up event + streamEnd = CTRLM_HAL_BLE_VOICE_STREAM_END_ON_AUDIO_DURATION; + } + + uint64_t ieee_address = rcu->ieee_address_get().get_value(); + if (!ble_rcu_interface_->startAudioStreaming(ieee_address, encoding, streamEnd, fd)) { + XLOGD_ERROR("failed to start audio streaming on remote"); + return; + } + + params->m_fd = fd; + params->m_started = true; +} + +bool ctrlm_obj_network_ble_t::is_managed_by_network(ctrlm_controller_id_t id) { + return (id >= BLE_RCU_ID_RANGE_MIN && id < BLE_RCU_ID_RANGE_MAX); +} diff --git a/src/ble/ctrlm_ble_network.h b/src/ble/ctrlm_ble_network.h index 92868ca1..1182889a 100644 --- a/src/ble/ctrlm_ble_network.h +++ b/src/ble/ctrlm_ble_network.h @@ -41,6 +41,8 @@ // End Includes +#define BLE_RCU_ID_RANGE_MIN (NETWORK_ID_BASE_BLE) +#define BLE_RCU_ID_RANGE_MAX ((BLE_RCU_ID_RANGE_MIN)+BLE_MAX_MANAGED_RCUS+1) // +1 as a buffer for pairing typedef struct { ctrlm_main_queue_msg_header_t header; @@ -192,6 +194,11 @@ class ctrlm_obj_network_ble_t : public ctrlm_obj_network_t { std::shared_ptr getConfigSettings(); + virtual void start_controller_audio_streaming(ctrlm_voice_start_audio_params_t *params); + +protected: + virtual bool is_managed_by_network(ctrlm_controller_id_t id); + private: ctrlm_obj_network_ble_t(); diff --git a/src/ble/ctrlm_ble_rcu_interface.cpp b/src/ble/ctrlm_ble_rcu_interface.cpp index 6abecad1..f90cdc67 100644 --- a/src/ble/ctrlm_ble_rcu_interface.cpp +++ b/src/ble/ctrlm_ble_rcu_interface.cpp @@ -697,31 +697,6 @@ bool ctrlm_ble_rcu_interface_t::pairWithCode(unsigned int code) XLOGD_ERROR("controller failed to start pairing, %s: %s", error.name().c_str(), error.message().c_str()); } return false; - } else { - XLOGD_INFO("started pairing with code %hhu", code); - } - return true; -} - -bool ctrlm_ble_rcu_interface_t::pairWithMacHash(unsigned int code) -{ - if (!m_controller) { - XLOGD_ERROR("m_controller is NULL!!!"); - return false; - } - - if (!m_controller->startPairingWithMacHash((uint8_t) code)) { - BleRcuError error = m_controller->lastError(); - - // Remote will continually send out IR pairing signals until the BLE pair request - // has been received. This means that the "Already in pairing state" error is normal. - // Let's omit this error print because it only serves to confuse those analyzing the logs. - if (error.message() != "Already in pairing state") { - XLOGD_ERROR("controller failed to start pairing, %s: %s", error.name().c_str(), error.message().c_str()); - } - return false; - } else { - XLOGD_INFO("started pairing with MAC hash 0x%X", code); } return true; } diff --git a/src/ble/ctrlm_ble_rcu_interface.h b/src/ble/ctrlm_ble_rcu_interface.h index a6526e1b..c9209c6f 100644 --- a/src/ble/ctrlm_ble_rcu_interface.h +++ b/src/ble/ctrlm_ble_rcu_interface.h @@ -92,7 +92,6 @@ class ctrlm_ble_rcu_interface_t void handleDeepsleep(bool wakingUp); bool pairWithCode(unsigned int code); - bool pairWithMacHash(unsigned int code); bool pairWithMacAddrs(const std::vector &macAddrList); bool pairAutoWithTimeout(int timeoutMs); bool pairCancel(); diff --git a/src/ble/hal/blercu/blercucontroller.cpp b/src/ble/hal/blercu/blercucontroller.cpp index 6ebfc14f..9924c9df 100644 --- a/src/ble/hal/blercu/blercucontroller.cpp +++ b/src/ble/hal/blercu/blercucontroller.cpp @@ -65,7 +65,7 @@ BleRcuControllerImpl::BleRcuControllerImpl(const shared_ptrisAvailable() || !m_adapter->isPowered()) { - m_lastError = BleRcuError(BleRcuError::General, "Adaptor not available or not powered"); - return false; - } - - // start the pairing process - m_pairingStateMachine.startWithMacHash(macHash); - - return true; -} - // ----------------------------------------------------------------------------- /*! \fn bool BleRcuController::startPairingWithList(std::vector macAddrList) @@ -348,7 +293,7 @@ bool BleRcuControllerImpl::startPairingWithMacHash(uint8_t macHash) bool BleRcuControllerImpl::startPairingWithList(const std::vector &macAddrList) { if (m_pairingStateMachine.isRunning()) { - if (m_pairingStateMachine.isAutoPairing()) { + if (m_pairingStateMachine.isScanningForAutoPair()) { XLOGD_WARN("received targeted pairing request in auto pair mode, cancelling auto pair first..."); diff --git a/src/ble/hal/blercu/blercucontroller.h b/src/ble/hal/blercu/blercucontroller.h index a0b678cf..770d4d07 100644 --- a/src/ble/hal/blercu/blercucontroller.h +++ b/src/ble/hal/blercu/blercucontroller.h @@ -73,7 +73,6 @@ class BleRcuController virtual bool startPairingAutoWithTimeout(int timeoutMs) = 0; virtual bool startPairingWithCode(uint8_t pairingCode) = 0; - virtual bool startPairingWithMacHash(uint8_t macHash) = 0; virtual bool startPairingWithList(const std::vector &macAddrList) = 0; virtual bool cancelPairing() = 0; diff --git a/src/ble/hal/blercu/blercucontroller_p.h b/src/ble/hal/blercu/blercucontroller_p.h index de190984..ccbe62ea 100644 --- a/src/ble/hal/blercu/blercucontroller_p.h +++ b/src/ble/hal/blercu/blercucontroller_p.h @@ -35,13 +35,13 @@ #include +#define BLE_MAX_MANAGED_RCUS (1) + class ConfigSettings; class BleRcuAdapter; class BleRcuDevice; - - class BleRcuControllerImpl final : public BleRcuController { public: @@ -61,7 +61,6 @@ class BleRcuControllerImpl final : public BleRcuController bool startPairingAutoWithTimeout(int timeoutMs) override; bool startPairingWithCode(uint8_t pairingCode) override; - bool startPairingWithMacHash(uint8_t macHash) override; bool startPairingWithList(const std::vector &macAddrList) override; bool cancelPairing() override; diff --git a/src/ble/hal/blercu/blercupairingstatemachine.cpp b/src/ble/hal/blercu/blercupairingstatemachine.cpp index 0a5572c5..e23c6939 100644 --- a/src/ble/hal/blercu/blercupairingstatemachine.cpp +++ b/src/ble/hal/blercu/blercupairingstatemachine.cpp @@ -198,13 +198,12 @@ bool BleRcuPairingStateMachine::isRunning() const This special state is needed because auto pairing consists of running a scan for an undeterminate amount of time until one of the supported devices listed in the config file is found. We want to be able to cancel this operation if another - pair request comes in that targets a specific device (like pairWithCode or - pairWithMacHash) + pair request comes in that targets a specific device (like pairWithCode) */ -bool BleRcuPairingStateMachine::isAutoPairing() const +bool BleRcuPairingStateMachine::isScanningForAutoPair() const { - return isRunning() && m_isAutoPairing; + return isRunning() && m_isAutoPairing && m_stateMachine.inState(DiscoverySuperState); } @@ -228,13 +227,13 @@ void BleRcuPairingStateMachine::startAutoWithTimeout(int timeoutMs) // clear the target device m_targetAddress.clear(); - // clear the pairing code and mac hash + // clear data for other pairing methods m_pairingCode = -1; m_pairingMacHash = -1; + m_pairingMacList.clear(); // create list of supported remotes regex to match to the name of the device m_targetedPairingNames.clear(); - for (const auto &name : m_supportedPairingNames) { // add to the list to use for compare when a device is found m_targetedPairingNames.push_back(name); @@ -269,23 +268,22 @@ void BleRcuPairingStateMachine::startWithCode(uint8_t pairingCode) // clear the target device m_targetAddress.clear(); - // clear the list of addresses to filter for + // clear data for other pairing methods m_pairingMacList.clear(); - // store the pairing code + // the pairing could be either the mac hash or the code embedded in the name, + // so store both for use when processing found devices m_pairingCode = pairingCode; - m_pairingMacHash = -1; + m_pairingMacHash = pairingCode; // create list of supported remotes regex to match to the name of the device m_targetedPairingNames.clear(); - char nameWithCode[100]; - for (const auto &pairingFormat : m_pairingPrefixFormats) { // construct the wildcard match snprintf(nameWithCode, sizeof(nameWithCode), pairingFormat.c_str(), pairingCode); - XLOGD_INFO("added pairing regex for supported remote '%s'", nameWithCode); + XLOGD_INFO("added pairing name '%s' to targeted names list ", nameWithCode); // add to the list to use for compare when a device is found m_targetedPairingNames.push_back(std::regex(nameWithCode, std::regex_constants::ECMAScript)); @@ -296,86 +294,7 @@ void BleRcuPairingStateMachine::startWithCode(uint8_t pairingCode) m_pairingAttempts++; m_pairingSucceeded = false; - XLOGD_INFO("started pairing using name prefix code %03d", m_pairingCode); -} - -// ----------------------------------------------------------------------------- -/*! - Starts the state machine using the supplied \a pairingCode and - \a namePrefixes. - - */ -void BleRcuPairingStateMachine::startWithMacHash(uint8_t macHash) -{ - // sanity check the statemachine is not already running - if (m_stateMachine.isRunning()) { - XLOGD_WARN("state machine already running"); - return; - } - - m_discoveryTimeout = m_discoveryTimeoutDefault; - m_isAutoPairing = false; - - // clear the target device - m_targetAddress.clear(); - - // clear the pairing code - m_pairingCode = -1; - - // clear the list of addresses to filter for - m_pairingMacList.clear(); - - // store the MAC hash - m_pairingMacHash = macHash; - - // clear the maps, we are trying to pair to a specific device using a hash of the MAC address - m_targetedPairingNames.clear(); - - // start the state machine - m_stateMachine.start(); - - m_pairingAttempts++; - m_pairingSucceeded = false; - XLOGD_INFO("started pairing, searching for device with MAC hash 0x%02X", m_pairingMacHash); -} - -// ----------------------------------------------------------------------------- -/*! - Starts the pairing state machine, but skips the discovery phase as we - already have a \a target device. - - */ -void BleRcuPairingStateMachine::start(const BleAddress &target, const string &name) -{ - // sanity check the state machine is not already running - if (m_stateMachine.isRunning()) { - XLOGD_WARN("state machine already running"); - return; - } - - m_discoveryTimeout = m_discoveryTimeoutDefault; - m_isAutoPairing = false; - - // set the target device - m_targetAddress = target; - - // clear the pairing code - m_pairingCode = -1; - m_pairingMacHash = -1; - - // clear the list of addresses to filter for - m_pairingMacList.clear(); - - // set the pairing prefix map to contain just the one name match - m_targetedPairingNames.clear(); - m_targetedPairingNames.push_back(std::regex(name, std::regex_constants::ECMAScript)); - - // start the state machine - m_stateMachine.start(); - - m_pairingAttempts++; - m_pairingSucceeded = false; - XLOGD_INFO("started pairing targeting %s", target.toString().c_str()); + XLOGD_INFO("started pairing, searching for device with prefix code %03d or MAC hash 0x%02X", m_pairingCode, m_pairingMacHash); } // ----------------------------------------------------------------------------- @@ -394,12 +313,10 @@ void BleRcuPairingStateMachine::startWithMacList(const std::vector & // clear the target device m_targetAddress.clear(); - // store the pairing code + // clear data for other pairing methods, this method is for MAC address match only m_pairingCode = -1; m_pairingMacHash = -1; - - // create list of supported remotes regex to match to the name of the device - m_supportedPairingNames.clear(); + m_targetedPairingNames.clear(); // set the list of addresses to filter for m_pairingMacList = macList; @@ -517,19 +434,19 @@ void BleRcuPairingStateMachine::onStateTransition(int oldState, int newState) { if (newState == FinishedState) { if (oldState == UnpairingState) { - XLOGD_WARN("timed-out in un-pairing phase (failed rcu may be left paired)"); + XLOGD_AUTOMATION_WARN("timed-out in un-pairing phase (failed rcu may be left paired)"); } else if (oldState == StartingDiscoveryState) { - XLOGD_ERROR("timed-out waiting for discovery started signal"); + XLOGD_AUTOMATION_ERROR("timed-out waiting for discovery started signal"); } else if (oldState == DiscoveringState) { - XLOGD_ERROR("timed-out in discovery phase (didn't find target rcu device to pair to)"); + XLOGD_AUTOMATION_ERROR("timed-out in discovery phase (didn't find target rcu device to pair to)"); } else if (oldState == StoppingDiscoveryState) { - XLOGD_ERROR("timed-out waiting for discovery to stop (suggesting something has gone wrong inside bluez)"); + XLOGD_AUTOMATION_ERROR("timed-out waiting for discovery to stop (suggesting something has gone wrong inside bluez)"); } } else if (newState == UnpairingState) { if (oldState == EnablePairableState || oldState == PairingState) { - XLOGD_WARN("timed-out in pairing phase (rcu device didn't pair within %dms)", m_pairingTimeout); + XLOGD_AUTOMATION_WARN("timed-out in pairing phase (rcu device didn't pair within %dms)", m_pairingTimeout); } else if (oldState == SetupState) { - XLOGD_WARN("timed-out in setup phase (rcu didn't response to all requests within %dms)", m_setupTimeout); + XLOGD_AUTOMATION_WARN("timed-out in setup phase (rcu didn't response to all requests within %dms)", m_setupTimeout); } } } @@ -862,44 +779,47 @@ void BleRcuPairingStateMachine::processDevice(const BleAddress &address, vector::const_iterator it_name = m_targetedPairingNames.begin(); for (; it_name != m_targetedPairingNames.end(); ++it_name) { if (std::regex_match(name.c_str(), *it_name)) { - XLOGD_INFO("Device (%s, %s) has a name targeted for pairing!", + XLOGD_INFO("Device (%s, %s) name has a match in the pairing name target list!", name.c_str(), address.toString().c_str()); break; } } if (it_name == m_targetedPairingNames.end()) { - // Device not found through conventional means, see if we are pairing based on MAC hash - // Because if we are pairing based on MAC hash, m_targetedPairingNames is first cleared + XLOGD_INFO("Device (%s, %s) name not in name target list, checking other pairing methods...", name.c_str(), address.toString().c_str()); + if (m_pairingMacHash != -1) { + // Device not found through name match, see if there is a MAC hash match // Check if MAC hash matches int macHash = 0; for (int i = 0; i < 6; ++i) { macHash += (int)address[i]; } macHash &= 0xFF; - XLOGD_INFO("Pairing based on MAC hash, requested MAC hash = 0x%02X, this device = 0x%02X (%s, %s)", - m_pairingMacHash, macHash, name.c_str(), address.toString().c_str()); if (m_pairingMacHash != macHash) { + XLOGD_INFO("Device (%s, %s) MAC hash (0x%02X) does not match requested MAC hash (0x%02X), ignoring... ", + name.c_str(), address.toString().c_str(), macHash, m_pairingMacHash); return; + } else { + XLOGD_INFO("Device (%s, %s) MAC hash matches!", name.c_str(), address.toString().c_str()); } - // Device not found through conventional means or MAC hash so let's check a mac address list - // Pairing via a mac address list clears supported names and the pairing mac hash } else if (m_pairingMacList.size() != 0) { - if (m_pairingMacList.size() != 0) { - // check if the mac address matches any of the ones in the filter list (if it exists) - bool found = false; - for (const auto &filterAddress : m_pairingMacList) { - if (address == filterAddress) { - found = true; - break; - } + // Device not found through name match or MAC hash so let's check the mac address list + // Pairing via a mac address list clears other pairing methods so execution should always fall here + + bool found = false; + for (const auto &filterAddress : m_pairingMacList) { + if (address == filterAddress) { + found = true; + break; } + } - if (!found) { - XLOGD_DEBUG("device with address %s is not in the mac address filter list - ignoring", address.toString().c_str()); - return; - } + if (!found) { + XLOGD_INFO("Device (%s, %s) is not in the mac address filter list - ignoring...", name.c_str(), address.toString().c_str()); + return; + } else { + XLOGD_INFO("Device (%s, %s) has a match in the MAC address filter list!", name.c_str(), address.toString().c_str()); } } else { // log an error if we don't already have a target device @@ -917,12 +837,6 @@ void BleRcuPairingStateMachine::processDevice(const BleAddress &address, // is the device currently paired? if so we have to remove (unpair) // it and then remain in the current state if (m_adapter->isDevicePaired(address)) { - if (m_adapter->isDeviceConnected(address)) { - XLOGD_INFO("Ignoring device (%s, %s)... it is currently paired and connected, no need to re-pair.", - name.c_str(), address.toString().c_str()); - return; - } - XLOGD_INFO("Found target device (%s, %s) but it's currently paired. Will unpair and wait till it shows up in a scan again.", name.c_str(), address.toString().c_str()); @@ -930,7 +844,7 @@ void BleRcuPairingStateMachine::processDevice(const BleAddress &address, return; } - XLOGD_INFO("Found target device (%s, %s)", name.c_str(), address.toString().c_str()); + XLOGD_INFO("Device (%s, %s) is targeted for pairing!", name.c_str(), address.toString().c_str()); // store the target address m_targetAddress = address; diff --git a/src/ble/hal/blercu/blercupairingstatemachine.h b/src/ble/hal/blercu/blercupairingstatemachine.h index d21b46b1..cfb15c31 100644 --- a/src/ble/hal/blercu/blercupairingstatemachine.h +++ b/src/ble/hal/blercu/blercupairingstatemachine.h @@ -70,14 +70,12 @@ class BleRcuPairingStateMachine public: bool isRunning() const; - bool isAutoPairing() const; + bool isScanningForAutoPair() const; int pairingCode() const; // public slots: - void start(const BleAddress &target, const std::string &name); void startAutoWithTimeout(int timeoutMs); void startWithCode(uint8_t pairingCode); - void startWithMacHash(uint8_t macHash); void startWithMacList(const std::vector &macList); void stop(); diff --git a/src/ble/hal/blercu/bleservices/blercuremotecontrolservice.h b/src/ble/hal/blercu/bleservices/blercuremotecontrolservice.h index 63752f73..653fd872 100644 --- a/src/ble/hal/blercu/bleservices/blercuremotecontrolservice.h +++ b/src/ble/hal/blercu/bleservices/blercuremotecontrolservice.h @@ -64,6 +64,10 @@ class BleRcuRemoteControlService { m_lastKeypressChangedSlots.addSlot(func); } + inline void addRawBatteryVoltageChangedSlot(const Slot &> &func) + { + m_rawBatteryVoltageChangedSlots.addSlot(func); + } inline void addAdvConfigChangedSlot(const Slot &func) { m_advConfigChangedSlots.addSlot(func); @@ -77,6 +81,7 @@ class BleRcuRemoteControlService Slots m_unpairReasonChangedSlots; Slots m_rebootReasonChangedSlots; Slots m_lastKeypressChangedSlots; + Slots &> m_rawBatteryVoltageChangedSlots; Slots m_advConfigChangedSlots; Slots &> m_advConfigCustomListChangedSlots; }; diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp index 8d8061d5..40dff3c6 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_infraredservice.cpp @@ -422,6 +422,11 @@ void GattInfraredService::requestIrSupport() // sanity check we actually have a ir support characteristic if (!m_irSupportCharacteristic || !m_irSupportCharacteristic->isValid()) { XLOGD_WARN("missing ir support characteristic"); + + // Set ir support back to invalid value and notify + m_irSupport = 0; + m_irSupportChangedSlots.invoke(m_irSupport); + return; } @@ -434,8 +439,9 @@ void GattInfraredService::requestIrSupport() XLOGD_ERROR("failed to get initial ir support code due to <%s>", reply->errorMessage().c_str()); + // Set ir support back to invalid value and notify m_irSupport = 0; - + m_irSupportChangedSlots.invoke(m_irSupport); } else { std::vector value; diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp index 74d36e3e..a6c89198 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp @@ -47,6 +47,7 @@ const BleUuid GattRemoteControlService::m_lastKeypressCharUuid(BleUuid::LastKeyp const BleUuid GattRemoteControlService::m_advConfigCharUuid(BleUuid::AdvertisingConfig); const BleUuid GattRemoteControlService::m_advConfigCustomListCharUuid(BleUuid::AdvertisingConfigCustomList); const BleUuid GattRemoteControlService::m_assertReportCharUuid(BleUuid::AssertReport); +const BleUuid GattRemoteControlService::m_rawBatteryVoltageCharUuid(BleUuid::RawBatteryVoltage); using namespace std; @@ -182,6 +183,12 @@ bool GattRemoteControlService::start(const shared_ptr &gat XLOGD_WARN("failed to get optional Assert Reporting characteristic, continuing anyway..."); } } + if (!m_rawBatteryVoltageCharacteristic || !m_rawBatteryVoltageCharacteristic->isValid()) { + m_rawBatteryVoltageCharacteristic = gattService->characteristic(m_rawBatteryVoltageCharUuid); + if (!m_rawBatteryVoltageCharacteristic || !m_rawBatteryVoltageCharacteristic->isValid()) { + XLOGD_WARN("failed to get Raw Battery Voltage characteristic, continuing anyway..."); + } + } // check we're not already started if (m_stateMachine.state() != IdleState) { @@ -228,6 +235,7 @@ void GattRemoteControlService::onEnteredState(int state) m_rebootReasonCharacteristic.reset(); m_rcuActionCharacteristic.reset(); m_assertReportCharacteristic.reset(); + m_rawBatteryVoltageCharacteristic.reset(); } else if (state == RetrieveInitialValuesState) { @@ -237,12 +245,14 @@ void GattRemoteControlService::onEnteredState(int state) requestAdvConfigCustomList(); requestUnpairReason(); requestRebootReason(); + requestRawBatteryVoltage(); m_readySlots.invoke(); } else if (state == EnableNotificationsState) { requestStartUnpairNotify(); requestStartRebootNotify(); + requestRawBatteryVoltageChangedNotify(); } } @@ -303,6 +313,35 @@ void GattRemoteControlService::requestStartRebootNotify() PendingReply<>(m_isAlive, replyHandler)); } +void GattRemoteControlService::requestRawBatteryVoltageChangedNotify() +{ + if (!m_rawBatteryVoltageCharacteristic || !m_rawBatteryVoltageCharacteristic->isValid()) { + XLOGD_WARN("Invalid raw battery voltage characteristic, skipping notification setup"); + return; + } + + auto replyHandler = [this](PendingReply<> *reply) + { + // check for errors + if (reply->isError()) { + // this is bad if this happens as we won't get updates, so we install a timer to + // retry enabling notifications in a couple of seconds time + XLOGD_ERROR("failed to enable raw battery voltage characteristic notifications due to <%s>", + reply->errorMessage().c_str()); + + m_stateMachine.cancelDelayedEvents(RetryStartNotifyEvent); + m_stateMachine.postDelayedEvent(RetryStartNotifyEvent, 2000); + } else { + XLOGD_INFO("request to start notifications on Raw Battery Voltage characteristic succeeded"); + } + }; + + m_rawBatteryVoltageCharacteristic->enableNotifications( + Slot &>(m_isAlive, + std::bind(&GattRemoteControlService::onRawBatteryVoltageChanged, this, std::placeholders::_1)), + PendingReply<>(m_isAlive, replyHandler)); +} + // ----------------------------------------------------------------------------- /*! Write RCU Action characteristic. @@ -525,6 +564,45 @@ void GattRemoteControlService::requestLastKeypress() } } +// ----------------------------------------------------------------------------- +/*! + \internal + + Sends a request to org.bluez.GattCharacteristic1.Value() to get the value + property of the characteristic which contains the raw battery voltage. + + */ +void GattRemoteControlService::requestRawBatteryVoltage() +{ + // lambda invoked when the request returns + auto replyHandler = [this](PendingReply> *reply) + { + // check for errors + if (reply->isError()) { + XLOGD_ERROR("Failed to read raw battery voltage due to <%s>", reply->errorMessage().c_str()); + } else { + std::vector value; + value = reply->result(); + + if (value.size() == 3) { + onRawBatteryVoltageChanged(value); + } else { + XLOGD_ERROR("Raw battery voltage received has invalid length (%d bytes)", value.size()); + } + } + }; + + + if (m_rawBatteryVoltageCharacteristic && m_rawBatteryVoltageCharacteristic->isValid()) { + + // send a request to the bluez daemon to read the characteristic + m_rawBatteryVoltageCharacteristic->readValue(PendingReply>(m_isAlive, replyHandler)); + + } else { + XLOGD_WARN("Raw battery voltage characteristic is not valid, check that the remote firmware version supports this feature."); + } +} + // ----------------------------------------------------------------------------- /*! \internal @@ -601,7 +679,38 @@ void GattRemoteControlService::requestAdvConfigCustomList() } } +// ----------------------------------------------------------------------------- +/*! + \internal + Internal slot called when a notification from the remote device is sent + due to raw battery voltage changing. + */ +void GattRemoteControlService::onRawBatteryVoltageChanged(const std::vector &newValue) { + m_unloadedVoltage = newValue[0]; + m_loadedVoltage = newValue[1]; + m_voltagePercentage = std::clamp(newValue[2], uint8_t(0), uint8_t(100)); // Clamp percentage between 0 - 100 + + // Formats an 8-bit unsigned integer as a voltage string. + // Upper 2 bits = whole voltage (0-3v), lower 6 bits = 1/64v increments. + // Example: 137 (0x89 = 0b10001001) + // - Upper 2 bits: 0b10 = 2v + // - Lower 6 bits: 0b001001 = 9 = 9/64 = 0.14v + // - Result: "2.14v" + auto formatVoltage = [](uint8_t value) -> std::string { + const uint8_t wholeVolts = (value >> 6) & 0x03; // Upper 2 bits + const uint8_t fractionalBits = value & 0x3F; // Lower 6 bits + const float totalVolts = wholeVolts + (fractionalBits / 64.0f); + + char buffer[16]; + snprintf(buffer, sizeof(buffer), "%.2fv", totalVolts); + return buffer; + }; + + XLOGD_TELEMETRY("Successfully read raw battery voltage characteristic, unloaded = %s, loaded = %s, loaded percentage = %u%%", + formatVoltage(m_unloadedVoltage).c_str(), formatVoltage(m_loadedVoltage).c_str(), (unsigned int)m_voltagePercentage); + m_rawBatteryVoltageChangedSlots.invoke(newValue); +} // ----------------------------------------------------------------------------- /*! diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.h b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.h index 07f3a511..02534640 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.h +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.h @@ -92,13 +92,16 @@ class GattRemoteControlService : public BleRcuRemoteControlService private: void requestStartUnpairNotify(); void requestStartRebootNotify(); + void requestRawBatteryVoltageChangedNotify(); void requestUnpairReason(); void requestRebootReason(); void requestAssertReport(); void requestLastKeypress(); + void requestRawBatteryVoltage(); void requestAdvConfig(); void requestAdvConfigCustomList(); + void onRawBatteryVoltageChanged(const std::vector &newValue); void onWriteAdvConfigReply(PendingReply<> *reply); void onWriteAdvConfigCustomListReply(PendingReply<> *reply); @@ -112,6 +115,7 @@ class GattRemoteControlService : public BleRcuRemoteControlService std::shared_ptr m_advConfigCharacteristic; std::shared_ptr m_advConfigCustomListCharacteristic; std::shared_ptr m_assertReportCharacteristic; + std::shared_ptr m_rawBatteryVoltageCharacteristic; StateMachine m_stateMachine; @@ -119,6 +123,9 @@ class GattRemoteControlService : public BleRcuRemoteControlService uint8_t m_rebootReason; uint8_t m_rcuAction; uint8_t m_lastKeypress; + uint8_t m_unloadedVoltage; + uint8_t m_loadedVoltage; + uint8_t m_voltagePercentage; uint8_t m_advConfig; std::vector m_advConfigCustomList; @@ -135,6 +142,7 @@ class GattRemoteControlService : public BleRcuRemoteControlService static const BleUuid m_advConfigCharUuid; static const BleUuid m_advConfigCustomListCharUuid; static const BleUuid m_assertReportCharUuid; + static const BleUuid m_rawBatteryVoltageCharUuid; private: static const Event::Type StartServiceRequestEvent = Event::Type(Event::User + 1); diff --git a/src/ble/hal/blercu/bluez/blercuadapter.cpp b/src/ble/hal/blercu/bluez/blercuadapter.cpp index f8e0bcbd..56949f40 100644 --- a/src/ble/hal/blercu/bluez/blercuadapter.cpp +++ b/src/ble/hal/blercu/bluez/blercuadapter.cpp @@ -1257,14 +1257,22 @@ bool BleRcuAdapterBluez::addDevice(const BleAddress &address) /*! \internal - Sends a request to bluez to reconnect all devices stored in our internal map + Sends a request to bluez to reconnect all devices stored in our internal map if paired */ void BleRcuAdapterBluez::reconnectAllDevices() { for (auto const &device : m_devices) { - XLOGD_INFO("reconnecting to %s", device.first.toString().c_str()); - device.second->connect(); + bool isPaired = false; + if (device.second->m_deviceProxy) { + device.second->m_deviceProxy->paired(isPaired); + } + if (isPaired) { + XLOGD_INFO("reconnecting to %s", device.first.toString().c_str()); + device.second->connect(); + } else { + XLOGD_INFO("not paired, skipping reconnecting to %s", device.first.toString().c_str()); + } } } @@ -1721,7 +1729,7 @@ void BleRcuAdapterBluez::onDevicePairedChanged(const BleAddress &address, void BleRcuAdapterBluez::onDeviceReadyChanged(const BleAddress &address, bool ready) { - XLOGD_INFO("device with address %s is %sREADY", address.toString().c_str(), ready ? "" : "NOT "); + XLOGD_AUTOMATION_INFO("device with address %s is %sREADY", address.toString().c_str(), ready ? "" : "NOT "); map>::const_iterator it = m_devices.find(address); @@ -1768,7 +1776,7 @@ bool BleRcuAdapterBluez::setConnectionParams(BleAddress address, double minInter if (address == deviceInfo.address) { - XLOGD_INFO("HCI connection handle: %u, device: %s requesting an update of connection parameters to " + XLOGD_AUTOMATION_INFO("HCI connection handle: %u, device: %s requesting an update of connection parameters to " "minInterval=%f, maxInterval=%f, latency=%d, supervisionTimeout=%d", deviceInfo.handle, deviceInfo.address.toString().c_str(), minInterval, maxInterval, latency, supervisionTimeout); diff --git a/src/ble/hal/blercu/bluez/blercudevice.cpp b/src/ble/hal/blercu/bluez/blercudevice.cpp index 8212a3a1..08aa0287 100644 --- a/src/ble/hal/blercu/bluez/blercudevice.cpp +++ b/src/ble/hal/blercu/bluez/blercudevice.cpp @@ -668,7 +668,7 @@ void BleRcuDeviceBluez::onEnteredRecoveryDisconnectingState() m_recoveryAttempts++; // log the attempt - XLOGD_ERROR("entered recovery state after device %s failed to resolve services (attempt #%d)", + XLOGD_AUTOMATION_ERROR("entered recovery state after device %s failed to resolve services (attempt #%d)", m_address.toString().c_str(), m_recoveryAttempts); diff --git a/src/ble/hal/configsettings/configmodelsettings.cpp b/src/ble/hal/configsettings/configmodelsettings.cpp index de8c1dee..685bfa73 100644 --- a/src/ble/hal/configsettings/configmodelsettings.cpp +++ b/src/ble/hal/configsettings/configmodelsettings.cpp @@ -71,36 +71,6 @@ ConfigModelSettingsData::ConfigModelSettingsData(const ConfigModelSettingsData & Constructs some vendor settings from the supplied json object, if the json object has errors then an invalid object is created. - - The object should be formatted like the following: - - \code - [ - { - "name": "PR3", - "scanNameFormat": "U-PR3 EntOS RCU", - "connectNameFormat": "[UP]-PR3 EntOS RCU", - "otaProductName": "PR3-10", - "standbyMode": "C", - "services": { - "type": "gatt", - "required": [ - "Battery", - "Device Info", - "Immediate Alert", - "RDK Voice", - "RDK Infrared", - "RDK Firmware Upgrade" - ], - "optional": [ - "RDK Remote Control" - ] - } - }, - ... - ] - \endcode - */ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) : m_valid(false) @@ -116,10 +86,12 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) , m_hasConnParams(false) , m_servicesType(ConfigModelSettings::GattServiceType) { + XLOGD_INFO("--------------------------------------"); + // name field json_t *obj = json_object_get(json, "name"); if (!obj || !json_is_string(obj)) { - XLOGD_ERROR("invalid 'name' field"); + XLOGD_ERROR("Required field 'name' INVALID, aborting..."); return; } m_name = json_string_value(obj); @@ -130,54 +102,79 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) obj = json_object_get(json, "disabled"); if (obj) { if (!json_is_boolean(obj)) { - XLOGD_ERROR("invalid 'disabled' field"); - return; + XLOGD_WARN("Optional field 'disabled' invalid, continuing..."); } else { m_disabled = json_is_true(obj); + XLOGD_INFO("parsed 'disabled' field value <%s>", m_disabled ? "TRUE" : "FALSE"); } } - // (optional) pairingFormat field - obj = json_object_get(json, "pairingNameFormat"); - if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid 'pairingNameFormat' field, not fatal, continuing to parse info"); - } else { - m_pairingNameFormat = json_string_value(obj); + // Advertising names field + json_t *advertisingNamesObj = json_object_get(json, "advertisingNames"); + if (!advertisingNamesObj || !json_is_object(advertisingNamesObj)) { + XLOGD_ERROR("Required field 'advertisingNames' INVALID, aborting..."); + return; } - - // scanNameFormat field - obj = json_object_get(json, "scanNameFormat"); + // advertisingNames.regexPairing field + obj = json_object_get(advertisingNamesObj, "regexPairing"); if (!obj || !json_is_string(obj)) { - XLOGD_ERROR("invalid 'scanNameFormat' field, this is required so returning..."); + XLOGD_ERROR("Required field 'advertisingNames.regexPairing' INVALID, aborting..."); return; } m_scanNameMatcher = std::regex(json_string_value(obj), std::regex_constants::ECMAScript); - XLOGD_INFO("Pairing name regex <%s>", json_string_value(obj)); - - // (optional) connectNameFormat field - obj = json_object_get(json, "connectNameFormat"); - if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid 'connectNameFormat' field, not fatal, setting to scanNameFormat"); - m_connectNameMatcher = m_scanNameMatcher; - } else { - m_connectNameMatcher = std::regex(json_string_value(obj), std::regex_constants::ECMAScript); + m_connectNameMatcher = m_scanNameMatcher; + XLOGD_INFO("Pairing and reconnect advertising name regex <%s>", json_string_value(obj)); + + + // (optional) advertisingNames.optional object + json_t *optionalNames = json_object_get(advertisingNamesObj, "optional"); + if (optionalNames) { + if (!json_is_object(optionalNames)) { + XLOGD_WARN("Optional field 'advertisingNames.optional' invalid, continuing..."); + } else { + // (optional) formatSpecifierTargetedPairing field + obj = json_object_get(optionalNames, "formatSpecifierTargetedPairing"); + if (obj) { + if (!json_is_string(obj)) { + XLOGD_WARN("Optional field 'advertisingNames.optional.formatSpecifierTargetedPairing' invalid, continuing..."); + } else { + m_pairingNameFormat = json_string_value(obj); + XLOGD_INFO("Pairing name printf format specifier for targeted pairing <%s>", m_pairingNameFormat.c_str()); + } + } + + // (optional) regexReconnect field + obj = json_object_get(optionalNames, "regexReconnect"); + if (obj) { + if (!json_is_string(obj)) { + XLOGD_WARN("Optional field 'advertisingNames.optional.regexReconnect' invalid, continuing..."); + } else { + m_connectNameMatcher = std::regex(json_string_value(obj), std::regex_constants::ECMAScript); + XLOGD_INFO("Reconnect advertising name regex overridden with <%s>", json_string_value(obj)); + } + } + } } // (optional) otaProductName field obj = json_object_get(json, "otaProductName"); - if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid 'otaProductName' field, not fatal, continuing to parse info"); - } else { - m_otaProductName = json_string_value(obj); + if (obj) { + if (!json_is_string(obj)) { + XLOGD_WARN("Optional field 'otaProductName' invalid, continuing..."); + } else { + m_otaProductName = json_string_value(obj); + XLOGD_INFO("parsed 'otaProductName' field value <%s>", m_otaProductName.c_str()); + } } // (optional) typeZ field obj = json_object_get(json, "typeZ"); if (obj) { if (!json_is_boolean(obj)) { - XLOGD_WARN("invalid 'typeZ' field, not fatal, continuing to parse info"); + XLOGD_WARN("Optional field 'typeZ' invalid, continuing..."); } else { m_typeZ = json_is_true(obj); + XLOGD_INFO("parsed 'typeZ' field value <%s>", m_typeZ ? "true" : "false"); } } @@ -185,9 +182,10 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) obj = json_object_get(json, "connParamUpdateBeforeOtaVersion"); if (obj) { if (!json_is_string(obj)) { - XLOGD_WARN("invalid 'connParamUpdateBeforeOtaVersion' field, not fatal, continuing to parse info"); + XLOGD_WARN("Optional field 'connParamUpdateBeforeOtaVersion' invalid, continuing..."); } else { m_connParamUpdateBeforeOtaVersion = json_string_value(obj); + XLOGD_INFO("parsed 'connParamUpdateBeforeOtaVersion' field value <%s>", m_connParamUpdateBeforeOtaVersion.c_str()); } } @@ -195,9 +193,10 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) obj = json_object_get(json, "upgradePauseVersion"); if (obj) { if (!json_is_string(obj)) { - XLOGD_WARN("invalid 'upgradePauseVersion' field, not fatal, continuing to parse info"); + XLOGD_WARN("Optional field 'upgradePauseVersion' invalid, continuing..."); } else { m_upgradePauseVersion = json_string_value(obj); + XLOGD_INFO("parsed 'upgradePauseVersion' field value <%s>", m_upgradePauseVersion.c_str()); } } @@ -205,32 +204,37 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) obj = json_object_get(json, "upgradeStuckVersion"); if (obj) { if (!json_is_string(obj)) { - XLOGD_WARN("invalid 'upgradeStuckVersion' field, not fatal, continuing to parse info"); + XLOGD_WARN("Optional field 'upgradeStuckVersion' invalid, continuing..."); } else { m_upgradeStuckVersion = json_string_value(obj); + XLOGD_INFO("parsed 'upgradeStuckVersion' field value <%s>", m_upgradeStuckVersion.c_str()); } } // (optional) standbyMode field obj = json_object_get(json, "standbyMode"); - if (!obj || !json_is_string(obj)) { - XLOGD_WARN("invalid or missing 'standbyMode' field, not fatal, continuing to parse info"); - } else { - m_standbyMode = json_string_value(obj); + if (obj) { + if (!json_is_string(obj)) { + XLOGD_WARN("Optional field 'standbyMode' invalid, continuing..."); + } else { + m_standbyMode = json_string_value(obj); + XLOGD_INFO("parsed 'standbyMode' field value <%s>", m_standbyMode.c_str()); + } } // (optional) voiceKeyCode field obj = json_object_get(json, "voiceKeyCode"); if (obj) { if(!json_is_integer(obj)) { - XLOGD_WARN("invalid 'voiceKeyCode' field, not fatal, continuing to parse info"); + XLOGD_WARN("Optional field 'voiceKeyCode' invalid, continuing..."); } else { json_int_t keyCode = json_integer_value(obj); if(keyCode < 0 || keyCode > 0xFFFF) { - XLOGD_WARN("out of range 'voiceKeyCode' field, not fatal, continuing to parse info"); + XLOGD_WARN("Optional field 'voiceKeyCode' out of range, continuing..."); } else { m_voiceKeyCode = keyCode; m_voiceKeyCodePresent = true; + XLOGD_INFO("parsed 'voiceKeyCode' field value <%u>", m_voiceKeyCode); } } } @@ -238,28 +242,29 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) // services field json_t *servicesObj = json_object_get(json, "services"); if (!servicesObj || !json_is_object(servicesObj)) { - XLOGD_ERROR("missing or invalid 'services' field"); + XLOGD_ERROR("Required field 'services' INVALID, aborting..."); return; } // services.type field obj = json_object_get(servicesObj, "type"); if (!obj || !json_is_string(obj)) { - XLOGD_ERROR("missing 'service.type' field"); + XLOGD_ERROR("Required field 'service.type' INVALID, aborting..."); return; } string typeStr = json_string_value(obj); + XLOGD_INFO("parsed 'service.type' field value <%s>", typeStr.c_str()); if (typeStr.compare("gatt") == 0) { m_servicesType = ConfigModelSettings::GattServiceType; } else { - XLOGD_ERROR("invalid 'service.type' field value"); + XLOGD_ERROR("Required field 'service.type' not supported, aborting..."); return; } // services.required array json_t *requiredArray = json_object_get(servicesObj, "required"); if (!requiredArray || !json_is_array(requiredArray)) { - XLOGD_ERROR( "missing or invalid 'services.required' field"); + XLOGD_ERROR("Required field 'services.required' INVALID, aborting..."); return; } size_t array_size = json_array_size(requiredArray); @@ -267,13 +272,13 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) for (unsigned int i = 0; i < array_size; i++) { obj = json_array_get(requiredArray, i); if (!obj || !json_is_string(obj)) { - XLOGD_ERROR("invalid 'services.required' array entry"); + XLOGD_ERROR("Invalid 'services.required' array entry, aborting..."); return; } string serviceStr = json_string_value(obj); if (!BleUuid().doesServiceExist(serviceStr)) { - XLOGD_ERROR("invalid service name <%s>", serviceStr.c_str()); + XLOGD_ERROR("invalid service name <%s>, aborting...", serviceStr.c_str()); return; } m_servicesRequired.insert(serviceStr); @@ -283,78 +288,47 @@ ConfigModelSettingsData::ConfigModelSettingsData(json_t *json) for (const auto& service : m_servicesRequired) { servicesRequiredStr += service + ", "; } - servicesRequiredStr = servicesRequiredStr.substr(0, servicesRequiredStr.length() - 2); // Remove the trailing comma and space + if (!m_servicesRequired.empty()) { + servicesRequiredStr = servicesRequiredStr.substr(0, servicesRequiredStr.length() - 2); // Remove the trailing comma and space + } - XLOGD_DEBUG("m_servicesRequired = <%s>", servicesRequiredStr.c_str()); + XLOGD_INFO("Services required = <%s>", servicesRequiredStr.c_str()); // services.optional array json_t *optionalArray = json_object_get(servicesObj, "optional"); - if (!optionalArray || !json_is_array(optionalArray)) { - XLOGD_ERROR( "missing or invalid 'services.optional' field"); - return; - } - array_size = json_array_size(optionalArray); - - for (unsigned int i = 0; i < array_size; i++) { - obj = json_array_get(optionalArray, i); - if (!obj || !json_is_string(obj)) { - XLOGD_ERROR("invalid 'services.optional' array entry"); - return; - } - string serviceStr = json_string_value(obj); - - if (!BleUuid().doesServiceExist(serviceStr)) { - XLOGD_ERROR("invalid service name <%s>", serviceStr.c_str()); - return; - } - m_servicesOptional.insert(serviceStr); - } - - std::string servicesOptionalStr; - for (const auto& service : m_servicesOptional) { - servicesOptionalStr += service + ", "; - } - servicesOptionalStr = servicesOptionalStr.substr(0, servicesOptionalStr.length() - 2); // Remove the trailing comma and space - - XLOGD_DEBUG("m_servicesOptional = <%s>\n", servicesOptionalStr.c_str()); - - // (optional) connectionParams - // if (json.contains("connectionParams")) { - - // const QJsonValue connParams = json["connectionParams"]; - // if (!connParams.isObject()) { - // qWarning("invalid 'connectionParams' field"); - // return; - // } - - // const QJsonObject connParamsObj = connParams.toObject(); - // const QJsonValue maxInterval = connParamsObj["maxInterval"]; - // const QJsonValue minInterval = connParamsObj["minInterval"]; - - // if (maxInterval.isDouble() && minInterval.isDouble()) { - // m_connParams.setIntervalRange(minInterval.toDouble(), - // maxInterval.toDouble()); - // } else if (maxInterval.type() != minInterval.type()) { - // qWarning("both 'maxInterval' and 'minInterval' must be set to set " - // "connection interval"); - // } - - // const QJsonValue latency = connParamsObj["latency"]; + if (optionalArray) { + if (!json_is_array(optionalArray)) { + XLOGD_WARN("Optional field 'services.optional' invalid, continuing..."); + } else { + array_size = json_array_size(optionalArray); - // if (latency.isDouble()) - // m_connParams.setLatency(latency.toInt(m_connParams.latency())); - // else if (!latency.isUndefined()) - // qWarning("invalid type for latency setting"); + for (unsigned int i = 0; i < array_size; i++) { + obj = json_array_get(optionalArray, i); + if (!obj || !json_is_string(obj)) { + XLOGD_ERROR("Invalid 'services.optional' array entry, aborting..."); + return; + } + string serviceStr = json_string_value(obj); - // const QJsonValue supervisionTimeout = connParamsObj["supervisionTimeout"]; + if (!BleUuid().doesServiceExist(serviceStr)) { + XLOGD_ERROR("invalid service name <%s>, aborting...", serviceStr.c_str()); + return; + } + m_servicesOptional.insert(serviceStr); + } - // if (supervisionTimeout.isDouble()) - // m_connParams.setSupervisionTimeout(supervisionTimeout.toInt(m_connParams.supervisionTimeout())); - // else if (!supervisionTimeout.isUndefined()) - // qWarning("invalid type for supervisionTimeout setting"); + std::string servicesOptionalStr; + for (const auto& service : m_servicesOptional) { + servicesOptionalStr += service + ", "; + } + if (!m_servicesOptional.empty()) { + servicesOptionalStr = servicesOptionalStr.substr(0, servicesOptionalStr.length() - 2); // Remove the trailing comma and space + } - // m_hasConnParams = true; - // } + XLOGD_INFO("Services optional = <%s>", servicesOptionalStr.c_str()); + } + } + XLOGD_INFO("--------------------------------------"); m_valid = true; } diff --git a/src/ble/hal/configsettings/configsettings.cpp b/src/ble/hal/configsettings/configsettings.cpp index 053e93a5..ce8cc3a7 100644 --- a/src/ble/hal/configsettings/configsettings.cpp +++ b/src/ble/hal/configsettings/configsettings.cpp @@ -102,7 +102,7 @@ ConfigSettings::TimeOuts ConfigSettings::parseTimeouts(json_t *json) } else { *(fields[i].storage) = (int)value; } - XLOGD_DEBUG("'%s' field = %d", fields[i].name, *(fields[i].storage)); + XLOGD_INFO("'%s' field = %d ms", fields[i].name, *(fields[i].storage)); } } diff --git a/src/ble/hal/utils/bleuuid.cpp b/src/ble/hal/utils/bleuuid.cpp index fd7b0636..62d4294c 100644 --- a/src/ble/hal/utils/bleuuid.cpp +++ b/src/ble/hal/utils/bleuuid.cpp @@ -74,6 +74,7 @@ bool ble_uuid_names_init(void) BleUuid(BleUuid::AdvertisingConfig, std::string("Advertising Config")); BleUuid(BleUuid::AdvertisingConfigCustomList, std::string("Advertising Config Custom List")); BleUuid(BleUuid::AssertReport, std::string("Assert Report")); + BleUuid(BleUuid::RawBatteryVoltage, std::string("Raw Battery Voltage")); BleUuid(BleUuid::InfraredSignalReference, std::string("Infrared Signal Reference")); BleUuid(BleUuid::InfraredSignalConfiguration, std::string("Infrared Signal Configuration")); BleUuid(BleUuid::FirmwarePacketWindowSize, std::string("Firmware Packet Window Size")); diff --git a/src/ble/hal/utils/bleuuid.h b/src/ble/hal/utils/bleuuid.h index 3552b8e8..7b9df4f2 100644 --- a/src/ble/hal/utils/bleuuid.h +++ b/src/ble/hal/utils/bleuuid.h @@ -104,6 +104,7 @@ class BleUuid AdvertisingConfig = 0xed05, AdvertisingConfigCustomList = 0xed06, AssertReport = 0xed07, + RawBatteryVoltage = 0xed08, }; enum DescriptorType { diff --git a/src/config/ctrlm_config.cpp b/src/config/ctrlm_config.cpp index f49b523e..4d8f5404 100644 --- a/src/config/ctrlm_config.cpp +++ b/src/config/ctrlm_config.cpp @@ -45,6 +45,7 @@ void ctrlm_config_t::destroy_instance() { ctrlm_config_t::ctrlm_config_t() { this->root = NULL; + this->local_config = false; } ctrlm_config_t::~ctrlm_config_t() { @@ -52,9 +53,10 @@ ctrlm_config_t::~ctrlm_config_t() { json_decref(this->root); this->root = NULL; } + this->local_config = false; } -bool ctrlm_config_t::load_config(const std::string &file_path) { +bool ctrlm_config_t::load_config(const std::string &file_path, bool local_config, bool verbose) { bool ret = false; std::string contents = file_to_string(file_path); if(this->root) { @@ -63,13 +65,20 @@ bool ctrlm_config_t::load_config(const std::string &file_path) { } if(!contents.empty()) { json_error_t json_error; - XLOGD_INFO_OPTS(XLOG_OPTS_DEFAULT, 20 * 1024, "Loading Configuration for <%s> <%s>", file_path.c_str(), contents.c_str()); + if(verbose) { + XLOGD_INFO_OPTS(XLOG_OPTS_DEFAULT, 20 * 1024, "Loading Configuration for <%s> <%s>", file_path.c_str(), contents.c_str()); + } else { + XLOGD_INFO("Loading Configuration for <%s>", file_path.c_str()); + } this->root = json_loads(contents.c_str(), JSON_REJECT_DUPLICATES, &json_error); if(this->root != NULL) { - XLOGD_INFO("config loaded successfully as JSON"); + if(verbose) { + XLOGD_INFO("config loaded successfully as JSON for <%s>", file_path.c_str()); + } + this->local_config = local_config; ret = true; } else { - XLOGD_ERROR("JSON ERROR: Line <%u> Column <%u> Text <%s>", json_error.line, json_error.column, json_error.text); + XLOGD_ERROR("JSON ERROR: Line <%u> Column <%u> Text <%s> Contents <%s>", json_error.line, json_error.column, json_error.text, contents.c_str()); } } else { XLOGD_ERROR("no config file contents"); @@ -77,10 +86,49 @@ bool ctrlm_config_t::load_config(const std::string &file_path) { return(ret); } +bool ctrlm_config_t::append_config(const std::string &file_path, bool local_config, bool verbose) { + if(this->root == NULL) { + XLOGD_ERROR("no config file loaded"); + return(false); + } + bool ret = false; + std::string contents = file_to_string(file_path); + if(contents.empty()) { + XLOGD_ERROR("no config file contents"); + } else { + json_error_t json_error; + if(verbose) { + XLOGD_INFO_OPTS(XLOG_OPTS_DEFAULT, 20 * 1024, "Appending Configuration for <%s> <%s>", file_path.c_str(), contents.c_str()); + } else { + XLOGD_INFO("Appending Configuration for <%s>", file_path.c_str()); + } + json_t *append_root = json_loads(contents.c_str(), JSON_REJECT_DUPLICATES, &json_error); + if(append_root == NULL) { + XLOGD_ERROR("JSON ERROR: Line <%u> Column <%u> Text <%s> Contents <%s>", json_error.line, json_error.column, json_error.text, contents.c_str()); + } else { + if(json_object_update(this->root, append_root) != 0) { + XLOGD_ERROR("Failed to merge JSON objects"); + } else { + if(verbose) { + XLOGD_INFO("config appended successfully as JSON for <%s>", file_path.c_str()); + } + this->local_config |= local_config; + ret = true; + } + json_decref(append_root); + } + } + return(ret); +} + bool ctrlm_config_t::path_exists(const std::string &path) { return(ctrlm_utils_json_from_path(this->root, path, false) != NULL ? true : false); } +bool ctrlm_config_t::has_local_config() const { + return(this->local_config); +} + json_t *ctrlm_config_t::json_from_path(const std::string &path, bool add_ref) { return(ctrlm_utils_json_from_path(this->root, path, add_ref)); } diff --git a/src/config/ctrlm_config.h b/src/config/ctrlm_config.h index a64eb6f7..52951db9 100644 --- a/src/config/ctrlm_config.h +++ b/src/config/ctrlm_config.h @@ -47,9 +47,24 @@ class ctrlm_config_t { /** * Function which loads a configuration file into the configuration object. * @param file_path The path to the configuration file + * @param local_config True if the configuration file is a local config, else False + * @param verbose True to enable verbose logging, else False * @return True on success, otherwise False */ - bool load_config(const std::string &file_path); + bool load_config(const std::string &file_path, bool local_config = false, bool verbose = false); + /** + * Function which appends a configuration file into the configuration object. + * @param file_path The path to the configuration file + * @param local_config True if the configuration file is a local config, else False + * @param verbose True to enable verbose logging, else False + * @return True on success, otherwise False + */ + bool append_config(const std::string &file_path, bool local_config = false, bool verbose = false); + /** + * Function to check if local config exists + * @return True if the local config exists, else False + */ + bool has_local_config() const; /** * Function to check if object exists from a path * @param path A period seperated string used to navigate a JSON object i.e. "network_rf4ce.polling.enabled" @@ -77,6 +92,7 @@ class ctrlm_config_t { private: json_t *root; + bool local_config; }; #endif diff --git a/src/ctrlm.h b/src/ctrlm.h index 68fa1818..376608b3 100644 --- a/src/ctrlm.h +++ b/src/ctrlm.h @@ -453,11 +453,11 @@ gboolean ctrlm_is_one_touch_autobind_active(void); gboolean ctrlm_is_binding_table_empty(void); gboolean ctrlm_is_binding_table_full(void); bool ctrlm_is_pii_mask_enabled(void); +bool ctrlm_is_networked_standby_supported(void); gboolean ctrlm_main_has_device_id_get(void); gboolean ctrlm_main_has_device_type_get(void); gboolean ctrlm_main_has_service_account_id_get(void); gboolean ctrlm_main_has_partner_id_get(void); -gboolean ctrlm_main_has_experience_get(void); gboolean ctrlm_main_needs_service_access_token_get(void); void ctrlm_main_invalidate_service_access_token(void); void ctrlm_main_sat_enabled_set(gboolean sat_enabled); diff --git a/src/ctrlm_config_default.json b/src/ctrlm_config_default.json index a30bde8a..21aa49c6 100755 --- a/src/ctrlm_config_default.json +++ b/src/ctrlm_config_default.json @@ -7,8 +7,6 @@ "authservice_poll_period" : 5, "authservice_fast_poll_period" : 1000, "authservice_fast_max_retries" : 25, - "keycode_logging_poll_period" : 30, - "keycode_logging_max_retries" : 5, "url_auth_service" : "", "timeout_recently_booted" : 1800000, "timeout_line_of_sight" : 10000, @@ -16,7 +14,6 @@ "timeout_button_binding" : 600000, "timeout_screen_bind" : 600000, "timeout_one_touch_autobind" : 600000, - "mask_key_codes" : false, "mask_pii" : [true, false], "crash_recovery_threshold" : 2, "device_id" : "", @@ -263,7 +260,6 @@ "utterance_path" : "/opt/logs/rf4ce_adpcm_header_vrex.raw", "force_voice_settings" : false, "vrex_response_timeout" : 10000, - "keyword_detect_sensitivity" : 0.3, "opus_encoder_params" : "4080084410", "packet_loss_threshold" : 5, "vrex_test_flag" : false, @@ -295,7 +291,9 @@ "par_voice_eos_method" : 1, "par_voice_eos_timeout" : 2500, "server_hosts" : [], - "telemetry_session_stats" : false + "telemetry_session_stats" : false, + "voice_activity_detection_mode" : "enabled" + }, "device_update" : { "dir_root" : "/srv/device_update/", diff --git a/src/ctrlm_controller.cpp b/src/ctrlm_controller.cpp index 0088b414..60173d47 100644 --- a/src/ctrlm_controller.cpp +++ b/src/ctrlm_controller.cpp @@ -28,7 +28,7 @@ using namespace std; -#define OTA_MAX_RETRIES (2) +#define OTA_MAX_RETRIES (0) ctrlm_obj_controller_t::ctrlm_obj_controller_t(ctrlm_controller_id_t controller_id, ctrlm_obj_network_t &network, unsigned long long ieee_address) : controller_id_(controller_id), @@ -118,10 +118,6 @@ string ctrlm_obj_controller_t::partner_id_get() const { return(obj_network_->partner_id_get()); } -string ctrlm_obj_controller_t::experience_get() const { - return(obj_network_->experience_get()); -} - string ctrlm_obj_controller_t::stb_name_get() const { return(obj_network_->stb_name_get()); } @@ -172,11 +168,18 @@ void ctrlm_obj_controller_t::process_event_key(ctrlm_key_status_t key_status, ui last_key_code_->set_value((uint64_t)key_code); last_key_time_update(); - XLOGD_TELEMETRY("ind_process_keypress: %s - MAC Address <%s>, code = <%d> (%s key), status = <%s>", controller_type_str_get().c_str(), - ieee_address_get().to_string().c_str(), - mask ? -1 : key_code, - ctrlm_linux_key_code_str(key_code, mask), - ctrlm_key_status_str(key_status)); + xlog_level_t level = XLOG_LEVEL_TELEMETRY; + const char *color = XLOG_COLOR_BLU; + // Proximity key (KEY_F17) is logged to debug while the rest is logged to telemetry + if(key_code == KEY_F17) { + level = XLOG_LEVEL_DEBUG; + color = XLOG_COLOR_GRN; + } + XLOGD(level, XLOG_OPTS_DEFAULT, color, XLOG_BUF_SIZE_DEFAULT, "ind_process_keypress: %s - MAC Address <%s>, code = <%d> (%s key), status = <%s>", controller_type_str_get().c_str(), + ieee_address_get().to_string().c_str(), + mask ? -1 : key_code, + ctrlm_linux_key_code_str(key_code, mask), + ctrlm_key_status_str(key_status)); } ctrlm_controller_capabilities_t ctrlm_obj_controller_t::get_capabilities() const { @@ -410,3 +413,20 @@ uint8_t ctrlm_obj_controller_t::get_upgrade_increment() const { bool ctrlm_obj_controller_t::is_upgrade_progress_at_increment() const { return ((upgrade_progress_ % upgrade_increment_ == 0) || (upgrade_progress_ == 100)); } + +void ctrlm_obj_controller_t::update_controller_id_and_db_entry(std::string db_name, ctrlm_network_id_t network_id, ctrlm_controller_id_t new_id) { + controller_id_ = new_id; + std::stringstream new_controller_db_table; + new_controller_db_table << db_name << "_" << COUT_HEX_MODIFIER << (int)network_id << "_controller_" << COUT_HEX_MODIFIER << (int)new_id; + std::string new_table = new_controller_db_table.str(); + + ieee_address_->set_table(new_table); + time_binding_->set_table(new_table); + last_activity_time_->set_table(new_table); + last_key_time_->set_table(new_table); + last_key_code_->set_table(new_table); + irdb_entry_id_name_tv_->set_table(new_table); + irdb_entry_id_name_avr_->set_table(new_table); + voice_metrics_->set_table(new_table); + ota_failure_cnt_from_last_success_->set_table(new_table); +} diff --git a/src/ctrlm_controller.h b/src/ctrlm_controller.h index d31159fc..ae9b800b 100644 --- a/src/ctrlm_controller.h +++ b/src/ctrlm_controller.h @@ -63,10 +63,10 @@ class ctrlm_obj_controller_t std::string device_id_get() const; std::string service_account_id_get() const; std::string partner_id_get() const; - std::string experience_get() const; std::string stb_name_get() const; void set_device_minor_id(int device_minor_id); int get_device_minor_id() const; + void update_controller_id_and_db_entry(std::string db_name, ctrlm_network_id_t network_id, ctrlm_controller_id_t id); virtual ctrlm_controller_capabilities_t get_capabilities() const; diff --git a/src/ctrlm_ir_controller.cpp b/src/ctrlm_ir_controller.cpp index db978c7c..c42b8eae 100644 --- a/src/ctrlm_ir_controller.cpp +++ b/src/ctrlm_ir_controller.cpp @@ -206,7 +206,6 @@ void ctrlm_ir_controller_t::process_event_key(uint16_t key_code) { XLOGD_INFO("received IR key for BLE pairing (scan code 0x%06x : pairCode=%hhu)", scan_code_, commandCode); ctrlm_iarm_call_StartPairWithCode_params_t params; - params.key_code = key_code; params.pair_code = commandCode; ctrlm_main_queue_msg_pair_with_code_t msg; @@ -489,7 +488,7 @@ void* ctrlm_ir_key_monitor_thread(void *data) { case 2: { key_status = CTRLM_KEY_STATUS_REPEAT; break; } default: break; } - XLOGD_TELEMETRY("%s - code = <%d> (%s key), status = <%s>", ir_controller->name_get().c_str(), + XLOGD_AUTOMATION_TELEMETRY("%s - code = <%d> (%s key), status = <%s>", ir_controller->name_get().c_str(), ir_controller->mask_key_codes_get() ? -1 : event.code, ctrlm_linux_key_code_str(event.code, ir_controller->mask_key_codes_get()), ctrlm_key_status_str(key_status)); diff --git a/src/ctrlm_log.h b/src/ctrlm_log.h index d804cd84..5bf0eb60 100644 --- a/src/ctrlm_log.h +++ b/src/ctrlm_log.h @@ -22,11 +22,7 @@ #include -#ifdef ANSI_CODES_DISABLED -#define XLOG_OPTS_DEFAULT (XLOG_OPTS_DATE | XLOG_OPTS_TIME | XLOG_OPTS_LF | XLOG_OPTS_MOD_NAME | XLOG_OPTS_LEVEL) -#else #define XLOG_OPTS_DEFAULT (XLOG_OPTS_DATE | XLOG_OPTS_TIME | XLOG_OPTS_LF | XLOG_OPTS_MOD_NAME | XLOG_OPTS_LEVEL | XLOG_OPTS_COLOR) -#endif #ifndef XLOG_MODULE_ID #define XLOG_MODULE_ID XLOG_MODULE_ID_CTRLM diff --git a/src/ctrlm_main.cpp b/src/ctrlm_main.cpp index e12749c5..902b4331 100644 --- a/src/ctrlm_main.cpp +++ b/src/ctrlm_main.cpp @@ -23,8 +23,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include @@ -86,9 +89,6 @@ #include "xr_fdc.h" #endif #include -#ifdef MEMORY_LOCK -#include "clnl.h" -#endif using namespace std; @@ -116,11 +116,6 @@ using namespace std; #define CTRLM_MAIN_QUEUE_REPEAT_DELAY (5000) -#define NETWORK_ID_BASE_RF4CE 1 -#define NETWORK_ID_BASE_IP 11 -#define NETWORK_ID_BASE_BLE 21 -#define NETWORK_ID_BASE_CUSTOM 41 - #define CTRLM_MAIN_FIRST_BOOT_TIME_MAX (180) // maximum amount of uptime allowed (in seconds) for declaring "first boot" typedef void (*ctrlm_queue_push_t)(gpointer); @@ -288,9 +283,8 @@ typedef struct { gboolean local_conf; guint telemetry_report_interval; ctrlm_ir_controller_t *ir_controller; -#ifdef NETWORKED_STANDBY_MODE_ENABLED + bool networked_standby_supported; gboolean wake_with_voice_allowed; -#endif } ctrlm_global_t; static ctrlm_global_t g_ctrlm; @@ -311,10 +305,6 @@ static void ctrlm_main_has_service_account_id_set(gboolean has_id); static gboolean ctrlm_load_partner_id(void); static void ctrlm_main_has_partner_id_set(gboolean has_id); #endif -#ifdef AUTH_EXPERIENCE -static gboolean ctrlm_load_experience(void); -static void ctrlm_main_has_experience_set(gboolean has_experience); -#endif #ifdef AUTH_SAT_TOKEN static gboolean ctrlm_load_service_access_token(void); static void ctrlm_main_has_service_access_token_set(gboolean has_token); @@ -360,6 +350,7 @@ static gboolean ctrlm_authservice_expired(gpointer user_data); static gboolean ctrlm_ntp_check(gpointer user_data); static void ctrlm_signals_register(void); static void ctrlm_signal_handler(int signal); +static gboolean ctrlm_unix_signal_terminate(gpointer user_data); static void ctrlm_main_iarm_call_status_get_(ctrlm_main_iarm_call_status_t *status); static void ctrlm_main_iarm_call_property_get_(ctrlm_main_iarm_call_property_t *property); @@ -402,17 +393,6 @@ static void control_service_values_read_from_db(); #ifdef USE_DEPRECATED_IRMGR static void ctrlm_check_for_key_tag(int key_tag) __attribute__((unused)); //USE_DEPRECATED_IRMGR #endif -#ifdef MEMORY_LOCK -const char *memory_lock_progs[] = { -"/usr/bin/controlMgr", -"/usr/lib/libctrlm_hal_rf4ce.so.0.0.0", -"/usr/lib/libxraudio.so.0.0.0", -"/usr/lib/libxraudio-hal.so.0.0.0", -"/usr/lib/libxr_mq.so.0.0.0", -"/usr/lib/libxr-timer.so.0.0.0", -"/usr/lib/libxr-timestamp.so.0.0.0" -}; -#endif #if CTRLM_HAL_RF4CE_API_VERSION >= 9 static void ctrlm_crash_recovery_check(); @@ -423,13 +403,6 @@ static void ctrlm_ir_controller_thread_poll(void *data); static void ctrlm_vsdk_thread_poll(void *data); static void ctrlm_vsdk_thread_response(void *data); -#ifdef MEM_DEBUG -static gboolean ctrlm_memory_profile(gpointer user_data) { - g_mem_profile(); - return(TRUE); -} -#endif - #ifdef BREAKPAD_SUPPORT static bool ctrlm_minidump_callback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) { XLOGD_FATAL("Minidump location: %s Status: %s", descriptor.path(), succeeded ? "SUCCEEDED" : "FAILED"); @@ -448,24 +421,10 @@ int main(int argc, char *argv[]) { XLOGD_INFO("name <%-24s> version <%-9s> branch <%-20s> commit <%s>", "ctrlm-main", CTRLM_MAIN_VERSION, CTRLM_MAIN_BRANCH, CTRLM_MAIN_COMMIT_ID); -#ifdef MEMORY_LOCK - clnl_init(); - for(unsigned int i = 0; i < (sizeof(memory_lock_progs) / sizeof(memory_lock_progs[0])); i++) { - if(ctrlm_file_exists(memory_lock_progs[i])) { - if(clnl_lock(memory_lock_progs[i], SECTION_TEXT)) { - XLOGD_ERROR("failed to lock instructions to memory <%s>", memory_lock_progs[i]); - } else { - XLOGD_INFO("successfully locked to memory <%s>", memory_lock_progs[i]); - } - } else { - XLOGD_DEBUG("file doesn't exist, cannot lock to memory <%s>", memory_lock_progs[i]); - } - } -#endif - ctrlm_signals_register(); #ifdef BREAKPAD_SUPPORT + XLOGD_INFO("breakpad enabled"); std::string minidump_path = "/opt/minidumps"; #ifdef BREAKPAD_MINIDUMP_PATH_OVERRIDE minidump_path = BREAKPAD_MINIDUMP_PATH_OVERRIDE; @@ -481,6 +440,8 @@ int main(int argc, char *argv[]) { google_breakpad::ExceptionHandler eh(descriptor, NULL, ctrlm_minidump_callback, NULL, true, -1); //ctrlm_crash(); +#else + XLOGD_INFO("breakpad disabled"); #endif XLOGD_INFO("glib run-time version... %d.%d.%d", glib_major_version, glib_minor_version, glib_micro_version); @@ -494,11 +455,6 @@ int main(int argc, char *argv[]) { XLOGD_INFO("Glib run-time library is compatible"); } -#ifdef MEM_DEBUG - XLOGD_WARN("Memory debug is ENABLED."); - g_mem_set_vtable(glib_mem_profiler_table); -#endif - sem_init(&g_ctrlm.ctrlm_utils_sem, 0, 1); if(!ctrlm_iarm_init()) { @@ -518,7 +474,10 @@ int main(int argc, char *argv[]) { XLOGD_INFO("name <%-24s> version <%-9s> branch <%-20s> commit <%s>", entry->name ? entry->name : "NULL", entry->version ? entry->version : "NULL", entry->branch ? entry->branch : "NULL", entry->commit_id ? entry->commit_id : "NULL"); } } - vsdk_init(); + + const char *filename = NULL; + uint32_t file_size_max = 0; + vsdk_init(true, filename, file_size_max); //struct sched_param param; //param.sched_priority = 10; @@ -604,19 +563,21 @@ int main(int argc, char *argv[]) { g_ctrlm.last_key_info.last_ir_remote_type = CTRLM_IR_REMOTE_TYPE_UNKNOWN; g_ctrlm.last_key_info.is_screen_bind_mode = false; g_ctrlm.last_key_info.remote_keypad_config = CTRLM_REMOTE_KEYPAD_CONFIG_INVALID; - #ifdef NETWORKED_STANDBY_MODE_ENABLED + + xrsr_config_t xrsr_config; + if(xrsr_config_get(&xrsr_config)) { + g_ctrlm.networked_standby_supported = xrsr_config.networked_standby; + } else { + g_ctrlm.networked_standby_supported = false; + } + XLOGD_INFO("networked standby supported <%s>", g_ctrlm.networked_standby_supported ? "YES" : "NO"); + g_ctrlm.wake_with_voice_allowed = false; - #endif errno_t safec_rc = strcpy_s(g_ctrlm.last_key_info.source_name, sizeof(g_ctrlm.last_key_info.source_name), ctrlm_rcu_ir_remote_types_str(g_ctrlm.last_key_info.last_ir_remote_type)); ERR_CHK(safec_rc); g_ctrlm.discovery_remote_type[0] = '\0'; -#ifdef MEM_DEBUG - g_mem_profile(); - g_timeout_add_seconds(10, ctrlm_memory_profile, NULL); -#endif - XLOGD_INFO("load version"); if(!ctrlm_load_version()) { XLOGD_FATAL("failed to load version"); @@ -840,7 +801,7 @@ int main(int argc, char *argv[]) { ctrlm_trigger_startup_actions(); - XLOGD_INFO("Enter main loop"); + XLOGD_AUTOMATION_INFO("Enter main loop"); g_main_loop_run(g_ctrlm.main_loop); //Save the shutdown time if it is valid @@ -875,15 +836,6 @@ int main(int argc, char *argv[]) { sem_destroy(&g_ctrlm.ctrlm_utils_sem); -#ifdef MEMORY_LOCK - for(unsigned int i = 0; i < (sizeof(memory_lock_progs) / sizeof(memory_lock_progs[0])); i++) { - if(ctrlm_file_exists(memory_lock_progs[i])) { - clnl_unlock(memory_lock_progs[i], SECTION_TEXT); - } - } - clnl_destroy(); -#endif - ctrlm_config_t::destroy_instance(); ctrlm_rfc_t::destroy_instance(); #ifdef TELEMETRY_SUPPORT @@ -1031,7 +983,7 @@ gboolean ctrlm_thread_monitor(gpointer user_data) { XLOGD_DEBUG("Checking %s", it->name); if(it->response != CTRLM_THREAD_MONITOR_RESPONSE_ALIVE) { - XLOGD_TELEMETRY("Thread %s is unresponsive", it->name); + XLOGD_AUTOMATION_TELEMETRY("Thread %s is unresponsive", it->name); #ifdef BREAKPAD_SUPPORT if(g_ctrlm.thread_monitor_minidump) { XLOGD_FATAL("Thread Monitor Minidump is enabled"); @@ -1150,47 +1102,62 @@ gboolean ctrlm_authservice_poll(gpointer user_data) { } #endif +// GLib main-loop callbacks for Unix signals — run in the main loop context so +// they are safe to call g_main_loop_quit() and other non-async-signal-safe APIs. +static gboolean ctrlm_unix_signal_terminate(gpointer user_data) { + int sig = GPOINTER_TO_INT(user_data); + XLOGD_INFO("Received %s", sig == SIGINT ? "SIGINT" : "SIGTERM"); + ctrlm_quit_main_loop(); + return G_SOURCE_CONTINUE; +} + void ctrlm_signals_register(void) { - // Handle these signals + // Use g_unix_signal_add() so callbacks run inside the GLib main loop context + // rather than from an async signal handler, avoiding undefined behavior from + // calling non-async-signal-safe functions (e.g. g_main_loop_quit). XLOGD_INFO("Registering SIGINT..."); - if(signal(SIGINT, ctrlm_signal_handler) == SIG_ERR) { + if(0 == g_unix_signal_add(SIGINT, ctrlm_unix_signal_terminate, GINT_TO_POINTER(SIGINT))) { XLOGD_ERROR("Unable to register for SIGINT."); } XLOGD_INFO("Registering SIGTERM..."); - if(signal(SIGTERM, ctrlm_signal_handler) == SIG_ERR) { + if(0 == g_unix_signal_add(SIGTERM, ctrlm_unix_signal_terminate, GINT_TO_POINTER(SIGTERM))) { XLOGD_ERROR("Unable to register for SIGTERM."); } - XLOGD_INFO("Registering SIGQUIT..."); - if(signal(SIGQUIT, ctrlm_signal_handler) == SIG_ERR) { - XLOGD_ERROR("Unable to register for SIGQUIT."); + bool interactive = isatty(STDIN_FILENO); + if(!interactive) { + XLOGD_INFO("Skipping SIGQUIT registration."); + } else { + XLOGD_INFO("Registering SIGQUIT..."); + if(signal(SIGQUIT, ctrlm_signal_handler) == SIG_ERR) { // SIGQUIT is not supported by g_unix_signal_add, so use signal() with a direct handler + int errsv = errno; + XLOGD_ERROR("Unable to register for SIGQUIT <%s>", strerror(errsv)); + } } - XLOGD_INFO("Registering SIGPIPE..."); - if(signal(SIGPIPE, ctrlm_signal_handler) == SIG_ERR) { - XLOGD_ERROR("Unable to register for SIGPIPE."); + // Ignore SIGPIPE — broken-pipe errors are handled at the call site via errno. + XLOGD_INFO("Ignoring SIGPIPE..."); + errno = 0; + if(SIG_ERR == signal(SIGPIPE, SIG_IGN)) { + int errsv = errno; + XLOGD_ERROR("Unable to ignore SIGPIPE <%s>", strerror(errsv)); } } -void ctrlm_signal_handler(int signal) { +// Registered as an OS signal handler (must use async-signal-safe calls only). +static void ctrlm_signal_handler(int signal) { switch(signal) { - case SIGTERM: - case SIGINT: { - XLOGD_INFO("Received %s", signal == SIGINT ? "SIGINT" : "SIGTERM"); - ctrlm_quit_main_loop(); - break; - } case SIGQUIT: { - XLOGD_INFO("Received SIGQUIT"); -#ifdef BREAKPAD_SUPPORT + XLOGD_SAFE_INFO("Received SIGQUIT"); + #ifdef BREAKPAD_SUPPORT ctrlm_crash(); -#endif + #endif break; } case SIGPIPE: { - XLOGD_ERROR("Received SIGPIPE. Pipe is broken"); + XLOGD_SAFE_ERROR("Received SIGPIPE. Pipe is broken"); break; } default: - XLOGD_ERROR("Received unhandled signal %d", signal); + XLOGD_SAFE_ERROR("Received unhandled signal"); break; } } @@ -1285,9 +1252,18 @@ void ctrlm_on_network_assert(ctrlm_network_id_t network_id) { // Invalidate main thread so terminate does not attempt to terminate it g_ctrlm.main_thread = NULL; } - // g_main_loop_quit() will be called in ctrlm_signal_handler(SIGTERM) + // g_main_loop_quit() will be called when the SIGTERM GLib source fires, + // or directly in the kill() fallback below. g_ctrlm.return_code = -1; - ctrlm_signal_handler(SIGTERM); + + errno = 0; + int rc = kill(getpid(), SIGTERM); + if(rc != 0) { + int errsv = errno; + XLOGD_ERROR("Failed to send SIGTERM to self <%s> - invoking shutdown handler directly", strerror(errsv)); + ctrlm_quit_main_loop(); + } + // give main() time to clean up sleep(5); // Exit here in case main fails to exit @@ -1469,30 +1445,6 @@ gboolean ctrlm_load_partner_id(void) { } #endif -#ifdef AUTH_EXPERIENCE -gboolean ctrlm_main_has_experience_get(void) { - return(g_ctrlm.has_experience); -} - -void ctrlm_main_has_experience_set(gboolean has_experience) { - g_ctrlm.has_experience = has_experience; -} - -gboolean ctrlm_load_experience(void) { - if(!g_ctrlm.authservice->get_experience(g_ctrlm.experience)) { - ctrlm_main_has_experience_set(false); - return(false); - } - g_ctrlm.voice_session->voice_stb_data_experience_set(g_ctrlm.experience); - - for(auto const &itr : g_ctrlm.networks) { - itr.second->experience_set(g_ctrlm.experience); - } - ctrlm_main_has_experience_set(true); - return(true); -} -#endif - #ifdef AUTH_SAT_TOKEN gboolean ctrlm_main_needs_service_access_token_get(void) { gboolean ret = false; @@ -1558,12 +1510,6 @@ gboolean ctrlm_has_authservice_data(void) { } #endif -#ifdef AUTH_EXPERIENCE - if(!ctrlm_main_has_experience_get()) { - ret = FALSE; - } -#endif - #ifdef AUTH_SAT_TOKEN if(ctrlm_main_needs_service_access_token_get()) { ret = FALSE; @@ -1614,18 +1560,6 @@ gboolean ctrlm_load_authservice_data(void) { } #endif -#ifdef AUTH_EXPERIENCE - if(!ctrlm_main_has_experience_get()) { - XLOGD_INFO("load experience"); - if(!ctrlm_load_experience()) { - XLOGD_TELEMETRY("failed to load experience"); - ret = FALSE; - } else { - XLOGD_INFO("load experience successfully <%s>", ctrlm_is_pii_mask_enabled() ? "***" : g_ctrlm.experience.c_str()); - } - } -#endif - #ifdef AUTH_SAT_TOKEN if(ctrlm_main_needs_service_access_token_get()) { XLOGD_INFO("load sat token"); @@ -1649,8 +1583,7 @@ gboolean ctrlm_load_authservice_data(void) { gboolean ctrlm_load_config(json_t **json_obj_root, json_t **json_obj_net_rf4ce, json_t **json_obj_voice, json_t **json_obj_device_update, json_t **json_obj_validation, json_t **json_obj_vsdk) { std::string config_fn_opt = "/opt/ctrlm_config.json"; - std::string config_fn_etc = "/etc/ctrlm_config.json"; - std::string config_fn_etc_override = "/etc/ctrlm_config.json.OVERRIDE"; + std::string config_fn_oem = "/etc/vendor/input/ctrlm_config.json"; json_t *json_obj_ctrlm; ctrlm_config_t *ctrlm_config = ctrlm_config_t::get_instance(); gboolean local_conf = false; @@ -1660,18 +1593,45 @@ gboolean ctrlm_load_config(json_t **json_obj_root, json_t **json_obj_net_rf4ce, if(ctrlm_config == NULL) { XLOGD_ERROR("Failed to get config manager instance"); return(false); - } else if(!ctrlm_is_production_build() && g_file_test(config_fn_opt.c_str(), G_FILE_TEST_EXISTS) && ctrlm_config->load_config(config_fn_opt)) { - XLOGD_INFO("Read configuration from <%s>", config_fn_opt.c_str()); - local_conf = true; - } else if(g_file_test(config_fn_etc.c_str(), G_FILE_TEST_EXISTS) && ctrlm_config->load_config(config_fn_etc)) { - XLOGD_INFO("Read configuration from <%s>", config_fn_etc.c_str()); - } else if(g_file_test(config_fn_etc_override.c_str(), G_FILE_TEST_EXISTS) && ctrlm_config->load_config(config_fn_etc_override)) { - XLOGD_INFO("Read configuration from <%s>", config_fn_etc_override.c_str()); - } else { - XLOGD_WARN("Configuration error. Configuration file(s) missing, using defaults"); + } + + bool oem_append = g_file_test(config_fn_oem.c_str(), G_FILE_TEST_EXISTS); + bool opt_append = ctrlm_is_production_build() ? false : g_file_test(config_fn_opt.c_str(), G_FILE_TEST_EXISTS); + + if(!oem_append && !opt_append) { + XLOGD_INFO("Using default configuration"); return(false); } + // Check for OEM config file override + if(oem_append) { + XLOGD_INFO("Loading OEM configuration from <%s>", config_fn_oem.c_str()); + if(!ctrlm_config->load_config(config_fn_oem)) { + XLOGD_ERROR("Failed to load OEM configuration from <%s>", config_fn_oem.c_str()); + return(false); + } + } + + // Check for OPT config file override + if(opt_append) { + bool local_config = true; + if(!oem_append) { + XLOGD_INFO("Loading OPT configuration from <%s>", config_fn_opt.c_str()); + if(!ctrlm_config->load_config(config_fn_opt, local_config)) { + XLOGD_ERROR("Failed to load OPT configuration from <%s>", config_fn_opt.c_str()); + return(false); + } + } else { + XLOGD_INFO("Appending OPT configuration from <%s>", config_fn_opt.c_str()); + + if(!ctrlm_config->append_config(config_fn_opt, local_config)) { + XLOGD_ERROR("Failed to append OPT configuration from <%s>", config_fn_opt.c_str()); + return(false); + } + } + local_conf = true; + } + // Parse the JSON data *json_obj_root = ctrlm_config->json_from_path("", true); // Get root AND add ref to it, since this code derefs it if(*json_obj_root == NULL) { @@ -1685,37 +1645,52 @@ gboolean ctrlm_load_config(json_t **json_obj_root, json_t **json_obj_net_rf4ce, return(false); } + // Print the configuration since it was loaded from files + char *json_dump = json_dumps(*json_obj_root, JSON_INDENT(3) | JSON_SORT_KEYS); + if(json_dump == NULL) { + XLOGD_ERROR("unable to dump JSON object"); + json_decref(*json_obj_root); + *json_obj_root = NULL; + return(false); + } else { + XLOGD_INFO_OPTS(XLOG_OPTS_DEFAULT, 20 * 1024, "Final configuration:\n%s", json_dump); + free(json_dump); + } + // Extract the RF4CE network configuration object if(g_ctrlm.rf4ce_enabled) { *json_obj_net_rf4ce = json_object_get(*json_obj_root, JSON_OBJ_NAME_NETWORK_RF4CE); if(*json_obj_net_rf4ce == NULL || !json_is_object(*json_obj_net_rf4ce)) { - XLOGD_WARN("RF4CE network object not found"); + XLOGD_INFO("RF4CE network object not found"); + *json_obj_net_rf4ce = NULL; } } // Extract the voice configuration object *json_obj_voice = json_object_get(*json_obj_root, JSON_OBJ_NAME_VOICE); if(*json_obj_voice == NULL || !json_is_object(*json_obj_voice)) { - XLOGD_WARN("voice object not found"); + XLOGD_INFO("voice object not found"); + *json_obj_voice = NULL; } // Extract the device update configuration object *json_obj_device_update = json_object_get(*json_obj_root, JSON_OBJ_NAME_DEVICE_UPDATE); if(*json_obj_device_update == NULL || !json_is_object(*json_obj_device_update)) { - XLOGD_WARN("device update object not found"); + XLOGD_INFO("device update object not found"); + *json_obj_device_update = NULL; } //Extract the vsdk configuration object *json_obj_vsdk = json_object_get( *json_obj_root, JSON_OBJ_NAME_VSDK); if(*json_obj_vsdk == NULL || !json_is_object(*json_obj_vsdk)) { - XLOGD_WARN("vsdk object not found"); - json_obj_vsdk = NULL; + XLOGD_INFO("vsdk object not found"); + *json_obj_vsdk = NULL; } // Extract the ctrlm global configuration object json_obj_ctrlm = json_object_get(*json_obj_root, JSON_OBJ_NAME_CTRLM_GLOBAL); if(json_obj_ctrlm == NULL || !json_is_object(json_obj_ctrlm)) { - XLOGD_WARN("control manger object not found"); + XLOGD_INFO("control manager object not found"); } else { json_config conf_global; if(!conf_global.config_object_set(json_obj_ctrlm)) { @@ -1725,7 +1700,7 @@ gboolean ctrlm_load_config(json_t **json_obj_root, json_t **json_obj_net_rf4ce, // Extract the validation configuration object *json_obj_validation = json_object_get(json_obj_ctrlm, JSON_OBJ_NAME_CTRLM_GLOBAL_VALIDATION_CONFIG); if(*json_obj_validation == NULL || !json_is_object(*json_obj_validation)) { - XLOGD_WARN("validation object not found"); + XLOGD_INFO("validation object not found"); } // Now parse the control manager object @@ -1992,9 +1967,16 @@ void ctrlm_global_rfc_values_retrieved(const ctrlm_rfc_attr_t &attr) { attr.get_rfc_value(JSON_INT_NAME_CTRLM_GLOBAL_TIMEOUT_ONE_TOUCH_AUTOBIND, g_ctrlm.one_touch_autobind_timeout_val, 0); attr.get_rfc_value(JSON_INT_NAME_CTRLM_GLOBAL_CRASH_RECOVERY_THRESHOLD, g_ctrlm.crash_recovery_threshold, 0); if(attr.get_rfc_value(JSON_ARRAY_NAME_CTRLM_GLOBAL_MASK_PII, g_ctrlm.mask_pii, ctrlm_is_production_build() ? CTRLM_JSON_ARRAY_INDEX_PRD : CTRLM_JSON_ARRAY_INDEX_DEV)) { - g_ctrlm.voice_session->voice_stb_data_pii_mask_set(g_ctrlm.mask_pii); + if(g_ctrlm.voice_session != NULL) { + g_ctrlm.voice_session->voice_stb_data_pii_mask_set(g_ctrlm.mask_pii); + } + if(g_ctrlm.ir_controller != NULL) { + g_ctrlm.ir_controller->mask_key_codes_set(g_ctrlm.mask_pii); + } + for(auto const &itr : g_ctrlm.networks) { + itr.second->mask_key_codes_set(g_ctrlm.mask_pii); + } } - attr.get_rfc_value(JSON_INT_NAME_CTRLM_GLOBAL_AUTHSERVICE_POLL_PERIOD, g_ctrlm.authservice_poll_val); attr.get_rfc_value(JSON_INT_NAME_CTRLM_GLOBAL_AUTHSERVICE_FAST_POLL_PERIOD, g_ctrlm.authservice_fast_poll_val); attr.get_rfc_value(JSON_INT_NAME_CTRLM_GLOBAL_AUTHSERVICE_FAST_MAX_RETRIES, g_ctrlm.authservice_fast_retries_max); } @@ -2372,7 +2354,7 @@ gpointer ctrlm_main_thread(gpointer param) { // Unblock the caller that launched this thread sem_post(&g_ctrlm.semaphore); - XLOGD_INFO("Enter main loop"); + XLOGD_AUTOMATION_INFO("Enter main loop"); do { gpointer msg = g_async_queue_pop(g_ctrlm.queue); @@ -2684,9 +2666,9 @@ gpointer ctrlm_main_thread(gpointer param) { if( (old_state == CTRLM_POWER_STATE_DEEP_SLEEP) && (dqm->new_state != CTRLM_POWER_STATE_DEEP_SLEEP) ) { XLOGD_INFO("power_state_change: wake DB and networks"); - #ifdef NETWORKED_STANDBY_MODE_ENABLED - g_ctrlm.wake_with_voice_allowed = true; - #endif + if(g_ctrlm.networked_standby_supported) { + g_ctrlm.wake_with_voice_allowed = true; + } ctrlm_db_power_state_change(true); for(auto const &itr : g_ctrlm.networks) { itr.second->power_state_change(true); @@ -2699,9 +2681,8 @@ gpointer ctrlm_main_thread(gpointer param) { } } - #ifdef NETWORKED_STANDBY_MODE_ENABLED //Wake with voice? Handle NSM voice, do not change power state - if(dqm->new_state == CTRLM_POWER_STATE_ON) { + if(g_ctrlm.networked_standby_supported && dqm->new_state == CTRLM_POWER_STATE_ON) { bool wake_with_voice_allowed = g_ctrlm.wake_with_voice_allowed; g_ctrlm.wake_with_voice_allowed = false; @@ -2715,17 +2696,14 @@ gpointer ctrlm_main_thread(gpointer param) { break; } } - #endif //If execution reaches here, then change power state and inform VSDK of on or deep sleep states g_ctrlm.power_state = dqm->new_state; - XLOGD_INFO("Enter power state <%s>", ctrlm_power_state_str(g_ctrlm.power_state)); - #ifdef NETWORKED_STANDBY_MODE_ENABLED - if(g_ctrlm.power_state == CTRLM_POWER_STATE_DEEP_SLEEP) { + XLOGD_AUTOMATION_INFO("Enter power state <%s>", ctrlm_power_state_str(g_ctrlm.power_state)); + if(g_ctrlm.networked_standby_supported && (g_ctrlm.power_state == CTRLM_POWER_STATE_DEEP_SLEEP)) { XLOGD_INFO("NSM is <%s>", (ctrlm_main_get_networked_standby_mode())?"ENABLED":"DISABLED"); } - #endif if(dqm->new_state != CTRLM_POWER_STATE_STANDBY) { // Set VSDK power state @@ -3181,6 +3159,10 @@ bool ctrlm_is_pii_mask_enabled(void) { return(g_ctrlm.mask_pii); } +bool ctrlm_is_networked_standby_supported(void) { + return(g_ctrlm.networked_standby_supported); +} + gboolean ctrlm_timeout_recently_booted(gpointer user_data) { XLOGD_INFO("Timeout - Recently booted."); g_ctrlm.recently_booted = FALSE; @@ -5859,11 +5841,9 @@ ctrlm_power_state_t ctrlm_main_get_system_power_state(void) { gboolean ctrlm_main_get_networked_standby_mode(void) { bool networked_standby_mode = false; - #ifdef NETWORKED_STANDBY_MODE_ENABLED - if(g_ctrlm.power_state == CTRLM_POWER_STATE_DEEP_SLEEP) { + if(g_ctrlm.networked_standby_supported && (g_ctrlm.power_state == CTRLM_POWER_STATE_DEEP_SLEEP)) { networked_standby_mode = g_ctrlm.power_manager->get_networked_standby_mode(); } - #endif return networked_standby_mode ? true : false; } diff --git a/src/ctrlm_main_iarm.cpp b/src/ctrlm_main_iarm.cpp index 92ceb987..db124af4 100644 --- a/src/ctrlm_main_iarm.cpp +++ b/src/ctrlm_main_iarm.cpp @@ -26,9 +26,6 @@ #include "irMgr.h" #endif #include "sysMgr.h" -#ifdef NETWORKED_STANDBY_MODE_ENABLED -#include "deepSleepMgr.h" -#endif #include "comcastIrKeyCodes.h" #include "ctrlm.h" #include "ctrlm_log.h" @@ -62,11 +59,9 @@ static IARM_Result_t ctrlm_main_iarm_call_start_pair_with_code(void *arg); static IARM_Result_t ctrlm_main_iarm_call_chip_status_get(void *arg); static IARM_Result_t ctrlm_main_iarm_call_audio_capture_start(void *arg); static IARM_Result_t ctrlm_main_iarm_call_audio_capture_stop(void *arg); -#ifdef NETWORKED_STANDBY_MODE_ENABLED #if CTRLM_HAL_RF4CE_API_VERSION >= 10 && !defined(CTRLM_DPI_CONTROL_NOT_SUPPORTED) extern IARM_Result_t ctrlm_iarm_powermanager_event_handler_power_pre_change(void* pArgs); #endif -#endif typedef struct { const char * name; @@ -758,7 +753,7 @@ IARM_Result_t ctrlm_main_iarm_call_start_pair_with_code(void *arg) { return(IARM_RESULT_INVALID_PARAM); } - XLOGD_INFO("params->network_id = <%d>, params->pair_code = 0x%X", params->network_id, params->pair_code); + XLOGD_AUTOMATION_INFO("params->network_id = <%d>, params->pair_code = 0x%X", params->network_id, params->pair_code); // Signal completion of the operation sem_t semaphore; diff --git a/src/ctrlm_network.cpp b/src/ctrlm_network.cpp index 74ff4129..26201231 100644 --- a/src/ctrlm_network.cpp +++ b/src/ctrlm_network.cpp @@ -187,16 +187,6 @@ string ctrlm_obj_network_t::partner_id_get() const { return(partner_id_); } -void ctrlm_obj_network_t::experience_set(const string& experience) { - THREAD_ID_VALIDATE(); - experience_ = experience; -} - -string ctrlm_obj_network_t::experience_get() const { - THREAD_ID_VALIDATE(); - return(experience_); -} - void ctrlm_obj_network_t::stb_name_set(const string& stb_name) { THREAD_ID_VALIDATE(); XLOGD_INFO("STB Name <%s>", stb_name.c_str()); @@ -1074,3 +1064,13 @@ void ctrlm_obj_network_t::iarm_event_rcu_firmware_status(const ctrlm_obj_control } #endif } + +void ctrlm_obj_network_t::start_controller_audio_streaming(ctrlm_voice_start_audio_params_t *params) { + XLOGD_WARN("not implemented for %s network", name_get()); + return; +} + +bool ctrlm_obj_network_t::is_managed_by_network(ctrlm_controller_id_t) { + XLOGD_WARN("not implemented for %s network", name_get()); + return false; +} diff --git a/src/ctrlm_network.h b/src/ctrlm_network.h index ab22b51b..ca6ad547 100644 --- a/src/ctrlm_network.h +++ b/src/ctrlm_network.h @@ -36,6 +36,11 @@ #define THREAD_ID_VALIDATE() thread_id_validate(__FUNCTION__) +#define NETWORK_ID_BASE_RF4CE 1 +#define NETWORK_ID_BASE_IP 11 +#define NETWORK_ID_BASE_BLE 21 +#define NETWORK_ID_BASE_CUSTOM 41 + typedef struct : public ctrlm_network_all_ipc_result_wrapper_t { unsigned char api_revision; unsigned int timeout; @@ -187,8 +192,6 @@ class ctrlm_obj_network_t std::string service_account_id_get() const; void partner_id_set(const std::string& partner_id); std::string partner_id_get() const; - void experience_set(const std::string& experience); - std::string experience_get() const; void mask_key_codes_set(gboolean mask_key_codes); gboolean mask_key_codes_get() const; void stb_name_set(const std::string& stb_name); @@ -289,6 +292,8 @@ class ctrlm_obj_network_t virtual void iarm_event_rcu_validation_status(void); virtual void iarm_event_rcu_firmware_status(const ctrlm_obj_controller_t &rcu); + virtual void start_controller_audio_streaming(ctrlm_voice_start_audio_params_t *params); + // Internal methods std::string version_; @@ -311,13 +316,13 @@ class ctrlm_obj_network_t const char * get_thread_name(const GThread *thread_id) const; void thread_id_validate(const char *pCallingFunction) const; virtual gboolean key_event_hook(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, ctrlm_key_status_t key_status, ctrlm_key_code_t key_code); + virtual bool is_managed_by_network(ctrlm_controller_id_t id); private: gboolean mask_key_codes_ = true; std::string device_id_; std::string service_account_id_; std::string partner_id_; - std::string experience_; std::string stb_name_; ctrlm_rcu_validation_result_t validation_result_ = CTRLM_RCU_VALIDATION_RESULT_MAX; ctrlm_key_code_t validation_key_ = CTRLM_KEY_CODE_INVALID; diff --git a/src/ctrlm_powermanager.h b/src/ctrlm_powermanager.h index 89705b32..c55223ce 100755 --- a/src/ctrlm_powermanager.h +++ b/src/ctrlm_powermanager.h @@ -28,10 +28,8 @@ class ctrlm_powermanager_t { static ctrlm_powermanager_t *get_instance(); static void destroy_instance(); virtual ctrlm_power_state_t get_power_state() = 0; - #ifdef NETWORKED_STANDBY_MODE_ENABLED virtual bool get_networked_standby_mode() = 0; virtual bool get_wakeup_reason_voice() = 0; - #endif }; #endif diff --git a/src/ctrlm_powermanager_factory.cpp b/src/ctrlm_powermanager_factory.cpp index 93d9dcea..7048c028 100755 --- a/src/ctrlm_powermanager_factory.cpp +++ b/src/ctrlm_powermanager_factory.cpp @@ -16,10 +16,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #include "ctrlm_powermanager.h" +#ifdef USE_IARM_POWER_MANAGER #include "ctrlm_ipc_iarm_powermanager.h" +#else #include "ctrlm_thunder_powermanager.h" - +#endif static ctrlm_powermanager_t *instance = NULL; diff --git a/src/ctrlm_utils.cpp b/src/ctrlm_utils.cpp index 7166aba5..b77595ac 100644 --- a/src/ctrlm_utils.cpp +++ b/src/ctrlm_utils.cpp @@ -1373,7 +1373,7 @@ const char *ctrlm_linux_key_code_str(uint16_t code, bool mask) { } } -#ifdef NETWORKED_STANDBY_MODE_ENABLED +#ifdef USE_IARM_POWER_MANAGER const char *ctrlm_wakeup_reason_str(DeepSleep_WakeupReason_t wakeup_reason) { switch(wakeup_reason) { case DEEPSLEEP_WAKEUPREASON_IR: return("IR"); @@ -1400,7 +1400,6 @@ const char *ctrlm_wakeup_reason_str(DeepSleep_WakeupReason_t wakeup_reason) { } #endif - bool ctrlm_file_copy(const char* src, const char* dst, bool overwrite, bool follow_dst_symbolic_link) { bool ret = FALSE; GFile *g_src = g_file_new_for_path(src); diff --git a/src/ctrlm_utils.h b/src/ctrlm_utils.h index 5e984a1e..baed40e7 100644 --- a/src/ctrlm_utils.h +++ b/src/ctrlm_utils.h @@ -36,7 +36,7 @@ #include "libIBus.h" #include "libIBusDaemon.h" #include -#ifdef NETWORKED_STANDBY_MODE_ENABLED +#ifdef USE_IARM_POWER_MANAGER #include "deepSleepMgr.h" #endif #ifdef TELEMETRY_SUPPORT @@ -205,7 +205,7 @@ const char *ctrlm_ir_state_str(ctrlm_ir_state_t state); const char *ctrlm_power_state_str(ctrlm_power_state_t state); const char *ctrlm_device_type_str(ctrlm_device_type_t device_type); -#ifdef NETWORKED_STANDBY_MODE_ENABLED +#ifdef USE_IARM_POWER_MANAGER const char *ctrlm_wakeup_reason_str(DeepSleep_WakeupReason_t wakeup_reason); #endif const char *ctrlm_rcu_wakeup_config_str(ctrlm_rcu_wakeup_config_t config); diff --git a/src/database/ctrlm_database.cpp b/src/database/ctrlm_database.cpp index c334cc7a..dfe2bb21 100644 --- a/src/database/ctrlm_database.cpp +++ b/src/database/ctrlm_database.cpp @@ -91,9 +91,7 @@ typedef enum { CTRLM_DB_QUEUE_MSG_TYPE_CONTROLLER_DESTROY = 5, CTRLM_DB_QUEUE_MSG_TYPE_CONTROLLER_CREATE = 6, CTRLM_DB_QUEUE_MSG_TYPE_BACKUP = 7, -#ifdef DEEPSLEEP_CLOSE_DB CTRLM_DB_QUEUE_MSG_TYPE_POWER_STATE_CHANGE = 8, -#endif CTRLM_DB_QUEUE_MSG_TYPE_WRITE_ATTR = 9, CTRLM_DB_QUEUE_MSG_TYPE_TICKLE = CTRLM_MAIN_QUEUE_MSG_TYPE_TICKLE } ctrlm_db_queue_msg_type_t; @@ -147,12 +145,10 @@ typedef struct { bool * ret; } ctrlm_db_queue_msg_backup_t; -#ifdef DEEPSLEEP_CLOSE_DB typedef struct { ctrlm_db_queue_msg_header_t header; gboolean waking_up; } ctrlm_db_queue_msg_power_state_change_t; -#endif typedef struct { sqlite3 * handle; @@ -162,10 +158,9 @@ typedef struct { GAsyncQueue * queue; sem_t semaphore; -#ifdef DEEPSLEEP_CLOSE_DB + bool deepsleep_close_db; char path[PATH_MAX]; sem_t ds_signal; -#endif vector rf4ce_network_list; vector ip_network_list; @@ -216,9 +211,7 @@ static void ctrlm_db_write_blob (const char *table, const char *key, const gucha static void ctrlm_db_write_blob_(const char *table, const char *key, const guchar *value, guint32 length); static void ctrlm_db_write_file_(const char *path, const guchar *data, guint32 length); -#ifdef DEEPSLEEP_CLOSE_DB static void ctrlm_db_power_state_change_(gboolean waking_up); -#endif static int ctrlm_db_insert_or_update(const char *table, const char *key, const int *value_int, const sqlite3_int64 *value_int64, const guchar *value_str, guint32 blob_length); #if 0 static int ctrlm_db_insert(const char *table, const char *key, const int *value_int, const char *value_str, guint32 blob_length); @@ -284,11 +277,13 @@ void ctrlm_db_close() { gboolean ctrlm_db_init(const char *db_path) { g_ctrlm_db.created_default_db = false; -#ifdef DEEPSLEEP_CLOSE_DB - errno_t safec_rc = strcpy_s(g_ctrlm_db.path, sizeof(g_ctrlm_db.path), db_path); - ERR_CHK(safec_rc); - sem_init(&g_ctrlm_db.ds_signal, 0, 0); -#endif + g_ctrlm_db.deepsleep_close_db = true; // Default to true. This can be overridden by vendor layer config as required in the future. + + if(g_ctrlm_db.deepsleep_close_db) { + errno_t safec_rc = strcpy_s(g_ctrlm_db.path, sizeof(g_ctrlm_db.path), db_path); + ERR_CHK(safec_rc); + sem_init(&g_ctrlm_db.ds_signal, 0, 0); + } // check for presence of database file and if not present, create it if(false == ctrlm_db_open(db_path)) { @@ -360,9 +355,9 @@ void ctrlm_db_terminate(void) { } } } -#ifdef DEEPSLEEP_CLOSE_DB - sem_destroy(&g_ctrlm_db.ds_signal); -#endif + if(g_ctrlm_db.deepsleep_close_db) { + sem_destroy(&g_ctrlm_db.ds_signal); + } sem_destroy(&g_ctrlm_db.semaphore); ctrlm_db_close(); @@ -399,29 +394,28 @@ bool ctrlm_db_backup() { } void ctrlm_db_power_state_change(gboolean waking_up) { -#ifdef DEEPSLEEP_CLOSE_DB - ctrlm_db_queue_msg_power_state_change_t *msg = (ctrlm_db_queue_msg_power_state_change_t *)g_malloc(sizeof(ctrlm_db_queue_msg_power_state_change_t)); - if(NULL == msg) { - XLOGD_ERROR("Failed to allocate memory"); - g_assert(0); - return; - } + if(g_ctrlm_db.deepsleep_close_db) { + ctrlm_db_queue_msg_power_state_change_t *msg = (ctrlm_db_queue_msg_power_state_change_t *)g_malloc(sizeof(ctrlm_db_queue_msg_power_state_change_t)); + if(NULL == msg) { + XLOGD_ERROR("Failed to allocate memory"); + g_assert(0); + return; + } - msg->header.type = CTRLM_DB_QUEUE_MSG_TYPE_POWER_STATE_CHANGE; - msg->waking_up = waking_up; + msg->header.type = CTRLM_DB_QUEUE_MSG_TYPE_POWER_STATE_CHANGE; + msg->waking_up = waking_up; - if(waking_up) { - ctrlm_db_queue_msg_push_front(msg); - sem_post(&g_ctrlm_db.ds_signal); + if(waking_up) { + ctrlm_db_queue_msg_push_front(msg); + sem_post(&g_ctrlm_db.ds_signal); + } else { + ctrlm_db_queue_msg_push(msg); + } } else { - ctrlm_db_queue_msg_push(msg); + XLOGD_INFO("No action for DB"); } -#else - XLOGD_INFO("No action for DB"); -#endif } -#ifdef DEEPSLEEP_CLOSE_DB void ctrlm_db_power_state_change_(gboolean waking_up) { if(waking_up == true ) { @@ -432,13 +426,10 @@ void ctrlm_db_power_state_change_(gboolean waking_up) { ctrlm_db_close(); } } -#endif gpointer ctrlm_db_thread(gpointer param) { bool running = true; -#ifdef DEEPSLEEP_CLOSE_DB bool ds_sem_wait = false; -#endif XLOGD_INFO("Started"); // Unblock the caller that launched this thread @@ -531,26 +522,24 @@ gpointer ctrlm_db_thread(gpointer param) { sem_post(&g_ctrlm_db.semaphore); break; } -#ifdef DEEPSLEEP_CLOSE_DB case CTRLM_DB_QUEUE_MSG_TYPE_POWER_STATE_CHANGE: { - XLOGD_DEBUG("POWER STATE CHANGE"); - ctrlm_db_queue_msg_power_state_change_t *power_state_change = (ctrlm_db_queue_msg_power_state_change_t *)msg; - ds_sem_wait = power_state_change->waking_up ? false : true; - ctrlm_db_power_state_change_(power_state_change->waking_up); + if(g_ctrlm_db.deepsleep_close_db) { + XLOGD_DEBUG("POWER STATE CHANGE"); + ctrlm_db_queue_msg_power_state_change_t *power_state_change = (ctrlm_db_queue_msg_power_state_change_t *)msg; + ds_sem_wait = power_state_change->waking_up ? false : true; + ctrlm_db_power_state_change_(power_state_change->waking_up); + } break; } -#endif default: { break; } } ctrlm_db_queue_msg_destroy(msg); -#ifdef DEEPSLEEP_CLOSE_DB if(ds_sem_wait == true) { sem_wait(&g_ctrlm_db.ds_signal); } -#endif } while(running); return(NULL); } diff --git a/src/factory/CMakeLists.txt b/src/factory/CMakeLists.txt index a16ae0eb..530cbf5b 100644 --- a/src/factory/CMakeLists.txt +++ b/src/factory/CMakeLists.txt @@ -17,8 +17,6 @@ # limitations under the License. ########################################################################## -option(FACTORY_AUDIO_PLAYBACK "AUDIO PLAYBACK" OFF) - add_executable(controlFactory ctrlmf_main.c) add_library(ctrlm-fta SHARED) set_target_properties( ctrlm-fta PROPERTIES @@ -30,6 +28,9 @@ include_directories( . iarm thunder + ${CMAKE_SYSROOT}/usr/include/rdk/ds + ${CMAKE_SYSROOT}/usr/include/rdk/ds-rpc + ${CMAKE_SYSROOT}/usr/include/rdk/ds-hal ) target_sources(ctrlm-fta PRIVATE @@ -37,37 +38,21 @@ target_sources(ctrlm-fta PRIVATE ctrlmf_reset.cpp ctrlmf_systemd.c ctrlmf_utils.c + ctrlmf_audio_capture.cpp + ctrlmf_audio_control.cpp + ctrlmf_audio_playback.cpp + ctrlmf_mic_test.cpp + ctrlmf_ws.c iarm/ctrlmf_iarm_client.cpp iarm/ctrlmf_iarm_control_manager.cpp ) -target_link_libraries(ctrlm-fta c rdkversion dbus-1 glib-2.0 IARMBus xr-voice-sdk pthread nopoll secure_wrapper) +target_link_libraries(ctrlm-fta c rdkversion dbus-1 glib-2.0 IARMBus xr-voice-sdk pthread nopoll secure_wrapper ds) target_link_libraries(controlFactory c ctrlm-fta secure_wrapper) -if(CUSTOM_AUDIO_ANALYSIS_LIB) - add_compile_definitions(PRIVATE CTRLMF_CUSTOM_AUDIO_ANALYSIS) - target_link_libraries(controlFactory ${CUSTOM_AUDIO_ANALYSIS_LIB}) -endif() - -if(CUSTOM_AUTH_LIB) +if(AUTH_ENABLED) add_compile_definitions(PRIVATE CTRLMF_WSS_ENABLED) - target_link_libraries(controlFactory ${CUSTOM_AUTH_LIB}) -endif() - -if(EXISTS ${CMAKE_SYSROOT}/usr/lib/libctrlm-hal-certificate.so) - target_link_libraries(controlFactory ctrlm-hal-certificate) -else() - if(AUTH_ENABLED) - message(WARNING "ctrlm-hal-certificate library is not provided, disabling authentication") - unset(AUTH_ENABLED) - endif() -endif() - -if(LOCAL_MIC) - add_compile_definitions(PRIVATE CTRLMF_LOCAL_MIC) - if(MIC_TAP) - add_compile_definitions(PRIVATE CTRLMF_LOCAL_MIC_TAP) - endif() + target_link_libraries(ctrlm-fta RdkCertSelector rdkconfig) endif() if(THUNDER) @@ -75,6 +60,7 @@ if(THUNDER) target_sources(ctrlm-fta PRIVATE thunder/ctrlmf_thunder_controller.cpp thunder/ctrlmf_thunder_plugin.cpp + thunder/ctrlmf_thunder_plugin_system_audio_player.cpp ) target_link_libraries(controlFactory WPEFrameworkCore WPEFrameworkPlugins) @@ -84,40 +70,11 @@ if(THUNDER) if(WPE_FRAMEWORK_PROTO_TRACING) target_link_libraries(controlFactory WPEFrameworkProtocols WPEFrameworkTracing) endif() -endif() -if(THUNDER_SECURITY) - add_compile_definitions(PRIVATE THUNDER_SECURITY) - target_link_libraries(controlFactory WPEFrameworkSecurityUtil secure_wrapper) -endif() - -if(FACTORY_AUDIO_PLAYBACK) - add_compile_definitions(PRIVATE CTRLMF_AUDIO_PLAYBACK) - include_directories( ${CMAKE_SYSROOT}/usr/include/rdk/ds - ${CMAKE_SYSROOT}/usr/include/rdk/ds-rpc - ${CMAKE_SYSROOT}/usr/include/rdk/ds-hal - ) - target_sources(ctrlm-fta PRIVATE ctrlmf_audio_playback.cpp) - if(THUNDER) - target_sources(ctrlm-fta PRIVATE thunder/ctrlmf_thunder_plugin_system_audio_player.cpp) + if(THUNDER_SECURITY) + add_compile_definitions(PRIVATE THUNDER_SECURITY) + target_link_libraries(controlFactory WPEFrameworkSecurityUtil secure_wrapper) endif() - - install(FILES ${CMAKE_SOURCE_DIR}/../tone_1khz.wav DESTINATION share ) -endif() - -if(FACTORY_AUDIO_CONTROL) - add_compile_definitions(PRIVATE CTRLMF_AUDIO_CONTROL) - target_sources(ctrlm-fta PRIVATE - ctrlmf_audio_control.cpp - ) -endif() - -if(LOCAL_MIC) - target_sources(ctrlm-fta PRIVATE - ctrlmf_audio_capture.cpp - ctrlmf_mic_test.cpp - ctrlmf_ws.c - ) endif() install(TARGETS controlFactory DESTINATION bin) diff --git a/src/factory/ctrlm_fta_caa.h b/src/factory/ctrlm_fta_caa.h index ee0b6a97..39373a55 100644 --- a/src/factory/ctrlm_fta_caa.h +++ b/src/factory/ctrlm_fta_caa.h @@ -48,6 +48,8 @@ extern "C" { bool ctrlmf_mic_test_audio_analyze(ctrlmf_test_type_t test_type, const char *output_filename, uint32_t level, ctrlmf_audio_frame_t audio_frames_noise, ctrlmf_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, ctrlmf_test_result_t *test_result); +typedef bool (*ctrlmf_mic_test_audio_analyze_t)(ctrlmf_test_type_t test_type, const char *output_filename, uint32_t level, ctrlmf_audio_frame_t audio_frames_noise, ctrlmf_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, ctrlmf_test_result_t *test_result); + #ifdef __cplusplus } #endif diff --git a/src/factory/ctrlm_fta_lib.h b/src/factory/ctrlm_fta_lib.h index f45f26eb..b4700a7f 100644 --- a/src/factory/ctrlm_fta_lib.h +++ b/src/factory/ctrlm_fta_lib.h @@ -38,7 +38,7 @@ extern "C" { #endif -bool ctrlmf_init(xlog_level_t level, bool requires_audio_playback); +bool ctrlmf_init(xlog_level_t level, bool requires_audio_playback, bool requires_audio_control, ctrlmf_mic_test_audio_analyze_t *audio_analyze_func); void ctrlmf_term(void); bool ctrlmf_factory_reset(void); @@ -51,7 +51,7 @@ bool ctrlmf_audio_control_attenuate(bool enable, bool relative, double vol); bool ctrlmf_audio_playback_start(const char *filename); -bool ctrlmf_mic_test_factory(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double *snr_min, double *snr_max, double *snr_var, ctrlmf_test_result_t *test_result); +bool ctrlmf_mic_test_factory(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double *snr_min, double *snr_max, double *snr_var, ctrlmf_test_result_t *test_result, ctrlmf_mic_test_audio_analyze_t audio_analyze_func); #ifdef __cplusplus } diff --git a/src/factory/ctrlmf_audio_capture.cpp b/src/factory/ctrlmf_audio_capture.cpp index d9ed82f5..0d76772a 100644 --- a/src/factory/ctrlmf_audio_capture.cpp +++ b/src/factory/ctrlmf_audio_capture.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -16,13 +17,8 @@ #define CTRLMF_WS_PORT_STR "9880" #define CTRLMF_WS_PORT_INT (9880) -#ifdef CTRLMF_WSS_ENABLED -#define CTRLMF_WS_URL_BASE "aowss://" -#else -#define CTRLMF_WS_URL_BASE "aows://" -#endif - -#define CTRLMF_WS_URL CTRLMF_WS_URL_BASE CTRLMF_WS_HOST ":" CTRLMF_WS_PORT_STR "/mic_test" +#define CTRLMF_WS_URL "aows://" CTRLMF_WS_HOST ":" CTRLMF_WS_PORT_STR "/mic_test" +#define CTRLMF_WSS_URL "aowss://" CTRLMF_WS_HOST ":" CTRLMF_WS_PORT_STR "/mic_test" typedef struct { sem_t semaphore_connected; @@ -50,7 +46,7 @@ static ctrlmf_audio_cap_global_t g_audio_cap; // Capture an audio clip using websocket -bool ctrlmf_audio_capture_init(uint32_t audio_frame_size, bool use_mic_tap) { +bool ctrlmf_audio_capture_init(uint32_t audio_frame_size, bool *has_local_mic_tap) { g_audio_cap.obj_ctrlm = new Iarm::ControlManager::ctrlm_iarm_client_control_manager_t; @@ -60,20 +56,8 @@ bool ctrlmf_audio_capture_init(uint32_t audio_frame_size, bool use_mic_tap) { } g_audio_cap.obj_ctrlm->add_event_handler(ctrlmf_audio_capture_event_handler, NULL); - g_audio_cap.use_mic_tap = use_mic_tap; + g_audio_cap.use_mic_tap = false; - // Get the current url - if(g_audio_cap.use_mic_tap) { - if(!g_audio_cap.obj_ctrlm->status_voice_mic_tap(g_audio_cap.url, &g_audio_cap.url_enabled)) { - XLOGD_ERROR("unable to get mic tap status"); - return(false); - } - } else { - if(!g_audio_cap.obj_ctrlm->status_voice_hf(g_audio_cap.url, &g_audio_cap.url_enabled)) { - XLOGD_ERROR("unable to get hf status"); - return(false); - } - } if(!g_audio_cap.obj_ctrlm->voice_session_types(g_audio_cap.request_types)) { XLOGD_ERROR("unable to get voice session request types"); return(false); @@ -90,23 +74,34 @@ bool ctrlmf_audio_capture_init(uint32_t audio_frame_size, bool use_mic_tap) { first = false; } - XLOGD_INFO("get url <%s> enabled <%s> request types <%s>", g_audio_cap.url.c_str(), g_audio_cap.url_enabled ? "YES" : "NO", request_types.str().c_str()); + bool has_local_mic = false; + if(std::find(g_audio_cap.request_types.begin(), g_audio_cap.request_types.end(), "mic_stream_single") != g_audio_cap.request_types.end()) { + has_local_mic = true; + } + if(std::find(g_audio_cap.request_types.begin(), g_audio_cap.request_types.end(), "mic_tap_stream_single") != g_audio_cap.request_types.end()) { + g_audio_cap.use_mic_tap = true; + } - // Set the FFV url to our websocket - std::string url_new = CTRLMF_WS_URL; + if(!has_local_mic) { + XLOGD_ERROR("platform does not support local mic stream"); + return(false); + } + // Get the current url if(g_audio_cap.use_mic_tap) { - if(!g_audio_cap.obj_ctrlm->configure_voice_mic_tap(url_new, true)) { - XLOGD_ERROR("unable to set mic tap url"); + if(!g_audio_cap.obj_ctrlm->status_voice_mic_tap(g_audio_cap.url, &g_audio_cap.url_enabled)) { + XLOGD_ERROR("unable to get mic tap status"); return(false); } } else { - if(!g_audio_cap.obj_ctrlm->configure_voice_hf(url_new, true)) { - XLOGD_ERROR("unable to set hf url"); + if(!g_audio_cap.obj_ctrlm->status_voice_hf(g_audio_cap.url, &g_audio_cap.url_enabled)) { + XLOGD_ERROR("unable to get hf status"); return(false); } } + XLOGD_INFO("get url <%s> enabled <%s> request types <%s>", g_audio_cap.url.c_str(), g_audio_cap.url_enabled ? "YES" : "NO", request_types.str().c_str()); + // Start websocket server sem_init(&g_audio_cap.callback_data.semaphore_connected, 0, 0); sem_init(&g_audio_cap.callback_data.semaphore_disconnected, 0, 0); @@ -115,7 +110,28 @@ bool ctrlmf_audio_capture_init(uint32_t audio_frame_size, bool use_mic_tap) { g_audio_cap.callbacks.disconnected = ctrlmf_ws_disconnected; g_audio_cap.callbacks.data = &g_audio_cap.callback_data; g_audio_cap.session_id = ""; - ctrlmf_ws_init(audio_frame_size, CTRLMF_WS_PORT_INT, true, &g_audio_cap.callbacks); + + bool has_valid_cert = false; + ctrlmf_ws_init(audio_frame_size, CTRLMF_WS_PORT_INT, true, &g_audio_cap.callbacks, &has_valid_cert); + + // Set the FFV url to our websocket + std::string url_new = has_valid_cert ? CTRLMF_WSS_URL : CTRLMF_WS_URL; + + if(g_audio_cap.use_mic_tap) { + if(!g_audio_cap.obj_ctrlm->configure_voice_mic_tap(url_new, true)) { + XLOGD_ERROR("unable to set mic tap url"); + return(false); + } + } else { + if(!g_audio_cap.obj_ctrlm->configure_voice_hf(url_new, true)) { + XLOGD_ERROR("unable to set hf url"); + return(false); + } + } + + if(has_local_mic_tap != NULL) { + *has_local_mic_tap = g_audio_cap.use_mic_tap; + } return(true); } diff --git a/src/factory/ctrlmf_audio_capture.h b/src/factory/ctrlmf_audio_capture.h index dfcf343e..79e14e1f 100644 --- a/src/factory/ctrlmf_audio_capture.h +++ b/src/factory/ctrlmf_audio_capture.h @@ -26,7 +26,7 @@ extern "C" { #endif -bool ctrlmf_audio_capture_init(uint32_t audio_frame_size, bool use_mic_tap); +bool ctrlmf_audio_capture_init(uint32_t audio_frame_size, bool *has_local_mic_tap); bool ctrlmf_audio_capture_term(void); bool ctrlmf_audio_capture_start(const char *request_type, ctrlmf_audio_frame_t audio_frames, uint32_t audio_frame_qty, uint32_t duration); diff --git a/src/factory/ctrlmf_main.c b/src/factory/ctrlmf_main.c index b923c9db..e70337d9 100644 --- a/src/factory/ctrlmf_main.c +++ b/src/factory/ctrlmf_main.c @@ -44,10 +44,7 @@ static struct argp_option options[] = { {"factory-reset", 'F', 0, 0, "Perform control manager factory reset and restart the application." }, {"soft-factory-reset",'f', 0, 0, "Perform control manager factory reset" }, {"ctrlm-restart", 'r', 0, 0, "Restart the control manager application" }, - #ifdef CTRLMF_LOCAL_MIC - #ifdef CTRLMF_AUDIO_PLAYBACK {"mic-test-audio", 'a', "", 0, "Microphone test audio file" }, - #endif {"mic-test-factory", 'd', 0, 0, "Factory microphone test" }, {"mic-test-duration", 'u', "", 0, "Microphone test duration in milliseconds" }, {"mic-test-snr-min", 'x', "", 0, "Microphone test SNR minimum value" }, @@ -55,10 +52,7 @@ static struct argp_option options[] = { {"mic-test-snr-var", 'z', "", 0, "Microphone test SNR maximum variance" }, {"mic-test-output", 'g', "", 0, "Microphone test output filename" }, {"mic-test-level", 'l', "", 0, "Microphone test level" }, - #endif - #ifdef CTRLMF_AUDIO_CONTROL {"mute-main-audio", 'm', 0, 0, "Mute the main audio output" }, - #endif { 0 } }; @@ -99,8 +93,9 @@ int main(int argc, char* argv[]) { } bool requires_audio_playback = (g_ctrlmf_opts.audio_file_path != NULL) ? true : false; + ctrlmf_mic_test_audio_analyze_t audio_analyze_func = NULL; - if(!ctrlmf_init(level, requires_audio_playback)) { + if(!ctrlmf_init(level, requires_audio_playback, g_ctrlmf_opts.mute_main_audio, &audio_analyze_func)) { XLOGD_ERROR("ctrlmf_main: init failed"); } else { XLOGD_INFO("ctrlmf_main: Run main loop"); @@ -110,30 +105,22 @@ int main(int argc, char* argv[]) { if(g_ctrlmf_opts.ctrlm_restart) { ctrlmf_systemd_service_exec("ctrlm-main.service", CTRLMF_SYSTEMD_METHOD_RESTART); } - #ifdef CTRLMF_AUDIO_CONTROL if(g_ctrlmf_opts.mute_main_audio) { ctrlmf_audio_control_mute(true); } - #endif if(g_ctrlmf_opts.mic_test_factory) { - #ifdef CTRLMF_LOCAL_MIC ctrlmf_test_result_t test_result; - if(!ctrlmf_mic_test_factory(g_ctrlmf_opts.mic_test_duration, g_ctrlmf_opts.output_file_path, g_ctrlmf_opts.mic_test_level, g_ctrlmf_opts.audio_file_path, g_ctrlmf_opts.mic_test_snr_min, g_ctrlmf_opts.mic_test_snr_max, g_ctrlmf_opts.mic_test_snr_var, &test_result)) { + if(!ctrlmf_mic_test_factory(g_ctrlmf_opts.mic_test_duration, g_ctrlmf_opts.output_file_path, g_ctrlmf_opts.mic_test_level, g_ctrlmf_opts.audio_file_path, g_ctrlmf_opts.mic_test_snr_min, g_ctrlmf_opts.mic_test_snr_max, g_ctrlmf_opts.mic_test_snr_var, &test_result, audio_analyze_func)) { XLOGD_ERROR("ctrlmf_main: mic test failed"); } else { XLOGD_INFO("ctrlmf_main: test result <%s>", test_result.pass ? "PASS" : "FAIL"); } - #endif } else if(g_ctrlmf_opts.audio_file_path != NULL) { - #ifdef CTRLMF_AUDIO_PLAYBACK ctrlmf_audio_playback_start(g_ctrlmf_opts.audio_file_path); - #endif } - #ifdef CTRLMF_AUDIO_CONTROL if(g_ctrlmf_opts.mute_main_audio) { ctrlmf_audio_control_mute(false); } - #endif XLOGD_INFO("ctrlmf_main: main loop ended"); } @@ -169,14 +156,11 @@ error_t ctrlmf_parse_opt(int key, char *arg, struct argp_state *state) { arguments->ctrlm_restart = true; break; } - #ifdef CTRLMF_LOCAL_MIC - #ifdef CTRLMF_AUDIO_PLAYBACK case 'a': { XLOGD_INFO("mic test audio file <%s>", arg); arguments->audio_file_path = arg; break; } - #endif case 'g': { XLOGD_INFO("output file path <%s>", arg); arguments->output_file_path = arg; @@ -221,13 +205,10 @@ error_t ctrlmf_parse_opt(int key, char *arg, struct argp_state *state) { arguments->mic_test_snr_var = &arguments->snr_var; break; } - #endif - #ifdef CTRLMF_AUDIO_CONTROL case 'm': { arguments->mute_main_audio = true; break; } - #endif case ARGP_KEY_ARG: { argp_usage(state); return(ARGP_ERR_UNKNOWN); @@ -257,14 +238,10 @@ bool ctrlmf_cmdline_args(int argc, char *argv[]) { XLOGD_INFO("ctrlm restart <%s>", g_ctrlmf_opts.ctrlm_restart ? "YES" : "NO"); XLOGD_INFO("audio file <%s>", g_ctrlmf_opts.audio_file_path ? g_ctrlmf_opts.audio_file_path : "NULL"); XLOGD_INFO("output file path <%s>", g_ctrlmf_opts.output_file_path ? g_ctrlmf_opts.output_file_path : "NULL"); - #ifdef CTRLMF_LOCAL_MIC if(g_ctrlmf_opts.mic_test_factory) { XLOGD_INFO("mic test duration <%d ms> snr min <%f> max <%f> var <%f>", g_ctrlmf_opts.mic_test_duration, *g_ctrlmf_opts.mic_test_snr_min, *g_ctrlmf_opts.mic_test_snr_max, *g_ctrlmf_opts.mic_test_snr_var); } - #endif - #ifdef CTRLMF_AUDIO_CONTROL XLOGD_INFO("mute main audio <%s>", g_ctrlmf_opts.mute_main_audio ? "YES" : "NO"); - #endif return(true); } diff --git a/src/factory/ctrlmf_mic_test.cpp b/src/factory/ctrlmf_mic_test.cpp index b3eab24c..1db94630 100644 --- a/src/factory/ctrlmf_mic_test.cpp +++ b/src/factory/ctrlmf_mic_test.cpp @@ -22,19 +22,17 @@ #define FILENAME_WAV_NOISE "/opt/logs/mic_test_noise.wav" #define FILENAME_WAV_SIGNAL "/opt/logs/mic_test_signal.wav" -#ifndef CTRLMF_CUSTOM_AUDIO_ANALYSIS -static bool ctrlmf_mic_test_audio_analyze(const char *output_filename, uint32_t level, ctrlmf_audio_frame_t audio_frames_noise, ctrlmf_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result); -#endif +static bool ctrlmf_mic_test_audio_analyze_default(const char *output_filename, uint32_t level, ctrlmf_audio_frame_t audio_frames_noise, ctrlmf_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result); -#if defined(CTRLMF_THUNDER) && defined(CTRLMF_AUDIO_PLAYBACK) -static bool ctrlmf_mic_test_via_audio_file(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result); +#ifdef CTRLMF_THUNDER +static bool ctrlmf_mic_test_via_audio_file(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result, ctrlmf_mic_test_audio_analyze_t audio_analyze_func); #endif -static bool ctrlmf_mic_test_via_ambient(uint32_t duration, const char *output_filename, uint32_t level, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result); +static bool ctrlmf_mic_test_via_ambient(uint32_t duration, const char *output_filename, uint32_t level, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result, ctrlmf_mic_test_audio_analyze_t audio_analyze_func); static void ctrlmf_mic_test_audio_export(const char *filename, ctrlmf_audio_frame_t audio_frames, uint32_t frame_qty, uint32_t channel_qty); static void ctrlmf_wave_header_gen(uint8_t *header, uint16_t audio_format, uint16_t num_channels, uint32_t sample_rate, uint16_t bits_per_sample, uint32_t pcm_data_size); -bool ctrlmf_mic_test_factory(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double *snr_min, double *snr_max, double *snr_var, ctrlmf_test_result_t *test_result) { +bool ctrlmf_mic_test_factory(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double *snr_min, double *snr_max, double *snr_var, ctrlmf_test_result_t *test_result, ctrlmf_mic_test_audio_analyze_t audio_analyze_func) { if(!ctrlmf_is_initialized()) { XLOGD_ERROR("not initialized"); return(false); @@ -44,18 +42,18 @@ bool ctrlmf_mic_test_factory(uint32_t duration, const char *output_filename, uin double snr_var_val = (snr_var != NULL) ? *snr_var : SNR_VAR; if(audio_filename != NULL) { - #if defined(CTRLMF_THUNDER) && defined(CTRLMF_AUDIO_PLAYBACK) - return(ctrlmf_mic_test_via_audio_file(duration, output_filename, level, audio_filename, snr_min_val, snr_max_val, snr_var_val, test_result)); + #ifdef CTRLMF_THUNDER + return(ctrlmf_mic_test_via_audio_file(duration, output_filename, level, audio_filename, snr_min_val, snr_max_val, snr_var_val, test_result, audio_analyze_func)); #else XLOGD_ERROR("audio playback is disabled"); return(false); #endif } - return(ctrlmf_mic_test_via_ambient(duration, output_filename, level, snr_min_val, snr_max_val, snr_var_val, test_result)); + return(ctrlmf_mic_test_via_ambient(duration, output_filename, level, snr_min_val, snr_max_val, snr_var_val, test_result, audio_analyze_func)); } -#if defined(CTRLMF_THUNDER) && defined(CTRLMF_AUDIO_PLAYBACK) -bool ctrlmf_mic_test_via_audio_file(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result) { +#ifdef CTRLMF_THUNDER +bool ctrlmf_mic_test_via_audio_file(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result, ctrlmf_mic_test_audio_analyze_t audio_analyze_func) { ctrlmf_audio_frame_t audio_frames_noise = NULL; ctrlmf_audio_frame_t audio_frames_signal = NULL; bool result = false; @@ -84,21 +82,19 @@ bool ctrlmf_mic_test_via_audio_file(uint32_t duration, const char *output_filena memset(audio_frames_noise, 0, audio_buffer_size); memset(audio_frames_signal, 0, audio_buffer_size); - #ifdef CTRLMF_LOCAL_MIC_TAP - if(!ctrlmf_audio_capture_init(audio_frame_size, true)) { - #else - if(!ctrlmf_audio_capture_init(audio_frame_size, false)) { - #endif + bool use_local_mic_tap = false; + if(!ctrlmf_audio_capture_init(audio_frame_size, &use_local_mic_tap)) { XLOGD_ERROR("unable to init audio capture"); break; } audio_capture_initialized = true; - #ifdef CTRLMF_LOCAL_MIC_TAP - const char *request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_tap_stream_single" : "mic_tap_stream_multi"; - #else - const char *request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_stream_single" : "mic_stream_multi"; - #endif + const char *request_type = NULL; + if(use_local_mic_tap) { + request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_tap_stream_single" : "mic_tap_stream_multi"; + } else { + request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_stream_single" : "mic_stream_multi"; + } // Capture noise if(!ctrlmf_audio_capture_start(request_type, audio_frames_noise, audio_frame_qty, duration)) { @@ -120,13 +116,16 @@ bool ctrlmf_mic_test_via_audio_file(uint32_t duration, const char *output_filena } XLOGD_INFO("analyze audio capture"); - #ifndef CTRLMF_CUSTOM_AUDIO_ANALYSIS - if(!ctrlmf_mic_test_audio_analyze(output_filename, level, audio_frames_noise, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, snr_min, snr_max, snr_var, test_result)) { - #else - if(!ctrlmf_mic_test_audio_analyze(CTRLMF_TEST_FACTORY, output_filename, level, audio_frames_noise, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, test_result)) { - #endif - XLOGD_ERROR("unable to analyze audio"); - break; + if(audio_analyze_func != NULL) { + if(!(*audio_analyze_func)(CTRLMF_TEST_FACTORY, output_filename, level, audio_frames_noise, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, test_result)) { + XLOGD_ERROR("unable to analyze audio"); + break; + } + } else { + if(!ctrlmf_mic_test_audio_analyze_default(output_filename, level, audio_frames_noise, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, snr_min, snr_max, snr_var, test_result)) { + XLOGD_ERROR("unable to analyze audio"); + break; + } } if(!ctrlmf_is_production()) { ctrlmf_mic_test_audio_export(FILENAME_WAV_NOISE, audio_frames_noise, audio_frame_qty, MIC_CHANNEL_QTY); @@ -149,7 +148,7 @@ bool ctrlmf_mic_test_via_audio_file(uint32_t duration, const char *output_filena } #endif -bool ctrlmf_mic_test_via_ambient(uint32_t duration, const char *output_filename, uint32_t level, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result) { +bool ctrlmf_mic_test_via_ambient(uint32_t duration, const char *output_filename, uint32_t level, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result, ctrlmf_mic_test_audio_analyze_t audio_analyze_func) { ctrlmf_audio_frame_t audio_frames_signal = NULL; bool result = false; bool audio_capture_initialized = false; @@ -170,21 +169,19 @@ bool ctrlmf_mic_test_via_ambient(uint32_t duration, const char *output_filename, memset(audio_frames_signal, 0, audio_buffer_size); - #ifdef CTRLMF_LOCAL_MIC_TAP - if(!ctrlmf_audio_capture_init(audio_frame_size, true)) { - #else - if(!ctrlmf_audio_capture_init(audio_frame_size, false)) { - #endif + bool use_local_mic_tap = false; + if(!ctrlmf_audio_capture_init(audio_frame_size, &use_local_mic_tap)) { XLOGD_ERROR("unable to init audio capture"); break; } audio_capture_initialized = true; - #ifdef CTRLMF_LOCAL_MIC_TAP - const char *request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_tap_stream_single" : "mic_tap_stream_multi"; - #else - const char *request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_stream_single" : "mic_stream_multi"; - #endif + const char *request_type = NULL; + if(use_local_mic_tap) { + request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_tap_stream_single" : "mic_tap_stream_multi"; + } else { + request_type = (MIC_RAW_AUDIO) ? "mic_factory_test" : (MIC_CHANNEL_QTY == 1) ? "mic_stream_single" : "mic_stream_multi"; + } // Capture signal if(!ctrlmf_audio_capture_start(request_type, audio_frames_signal, audio_frame_qty, duration)) { @@ -193,13 +190,16 @@ bool ctrlmf_mic_test_via_ambient(uint32_t duration, const char *output_filename, } XLOGD_INFO("analyze audio capture"); - #ifndef CTRLMF_CUSTOM_AUDIO_ANALYSIS - if(!ctrlmf_mic_test_audio_analyze(output_filename, level, NULL, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, snr_min, snr_max, snr_var, test_result)) { - #else - if(!ctrlmf_mic_test_audio_analyze(CTRLMF_TEST_FACTORY, output_filename, level, NULL, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, test_result)) { - #endif - XLOGD_ERROR("unable to analyze audio"); - break; + if(audio_analyze_func != NULL) { + if(!(*audio_analyze_func)(CTRLMF_TEST_FACTORY, output_filename, level, NULL, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, test_result)) { + XLOGD_ERROR("unable to analyze audio"); + break; + } + } else { + if(!ctrlmf_mic_test_audio_analyze_default(output_filename, level, NULL, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY, snr_min, snr_max, snr_var, test_result)) { + XLOGD_ERROR("unable to analyze audio"); + break; + } } if(!ctrlmf_is_production()) { ctrlmf_mic_test_audio_export(FILENAME_WAV_SIGNAL, audio_frames_signal, audio_frame_qty, MIC_CHANNEL_QTY); @@ -217,8 +217,7 @@ bool ctrlmf_mic_test_via_ambient(uint32_t duration, const char *output_filename, return(result); } -#ifndef CTRLMF_CUSTOM_AUDIO_ANALYSIS -bool ctrlmf_mic_test_audio_analyze(const char *output_filename, uint32_t level, ctrlmf_audio_frame_t audio_frames_noise, ctrlmf_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result) { +bool ctrlmf_mic_test_audio_analyze_default(const char *output_filename, uint32_t level, ctrlmf_audio_frame_t audio_frames_noise, ctrlmf_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, double snr_min, double snr_max, double snr_var, ctrlmf_test_result_t *test_result) { double pcm_sq_sum_noise[mic_qty]; double pcm_sq_sum_signal[mic_qty]; @@ -295,7 +294,6 @@ bool ctrlmf_mic_test_audio_analyze(const char *output_filename, uint32_t level, } return(true); } -#endif void ctrlmf_mic_test_audio_export(const char *filename, ctrlmf_audio_frame_t audio_frames, uint32_t frame_qty, uint32_t channel_qty) { uint8_t header[WAVE_HEADER_SIZE_MIN]; diff --git a/src/factory/ctrlmf_version.c b/src/factory/ctrlmf_version.c index 9fd9b69a..7d52efc0 100644 --- a/src/factory/ctrlmf_version.c +++ b/src/factory/ctrlmf_version.c @@ -1,7 +1,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -10,20 +12,25 @@ #include typedef struct { - bool initialized; - bool audio_control_init; - bool audio_playback_init; - bool is_production; + bool initialized; + void *handle_audio_analysis; + bool audio_control_init; + bool audio_playback_init; + bool is_production; } ctrlmf_global_t; ctrlmf_global_t g_ctrlmf = { - .initialized = false, - .audio_control_init = false, - .audio_playback_init = false, - .is_production = true + .initialized = false, + .handle_audio_analysis = NULL, + .audio_control_init = false, + .audio_playback_init = false, + .is_production = true }; -bool ctrlmf_init(xlog_level_t level, bool requires_audio_playback) { +static bool ctrlmf_file_exists(const char *filename); +static void *ctrlmf_load_plugin_audio_analysis(ctrlmf_mic_test_audio_analyze_t *audio_analyze_func); + +bool ctrlmf_init(xlog_level_t level, bool requires_audio_playback, bool requires_audio_control, ctrlmf_mic_test_audio_analyze_t *audio_analyze_func) { rdk_version_info_t info; int ret_val = rdk_version_parse_version(&info); @@ -36,7 +43,7 @@ bool ctrlmf_init(xlog_level_t level, bool requires_audio_playback) { rdk_version_object_free(&info); - int rc = xlog_init(XLOG_MODULE_ID, NULL, 0); + int rc = xlog_init(XLOG_MODULE_ID, NULL, 0, true, false); xlog_level_set_all(level); if(rc != 0) { @@ -44,42 +51,40 @@ bool ctrlmf_init(xlog_level_t level, bool requires_audio_playback) { return(false); } - #ifdef CTRLMF_AUDIO_CONTROL - if(!ctrlmf_audio_control_init()) { + if(requires_audio_control && !ctrlmf_audio_control_init()) { XLOGD_ERROR("failed to init audio control"); return(false); } - #endif - #ifdef CTRLMF_AUDIO_PLAYBACK if(requires_audio_playback && !ctrlmf_audio_playback_init()) { XLOGD_ERROR("failed to init audio playback"); - #ifdef CTRLMF_AUDIO_CONTROL - ctrlmf_audio_control_term(); - #endif + if(requires_audio_control) { + ctrlmf_audio_control_term(); + } return(false); } - #endif - g_ctrlmf.audio_control_init = true; - g_ctrlmf.audio_playback_init = requires_audio_playback; - g_ctrlmf.initialized = true; + g_ctrlmf.handle_audio_analysis = ctrlmf_load_plugin_audio_analysis(audio_analyze_func); + g_ctrlmf.audio_control_init = requires_audio_control; + g_ctrlmf.audio_playback_init = requires_audio_playback; + g_ctrlmf.initialized = true; return(true); } void ctrlmf_term(void) { - #ifdef CTRLMF_AUDIO_PLAYBACK if(g_ctrlmf.audio_playback_init) { ctrlmf_audio_playback_term(); } - #endif - #ifdef CTRLMF_AUDIO_CONTROL if(g_ctrlmf.audio_control_init) { ctrlmf_audio_control_term(); } - #endif + + if(g_ctrlmf.handle_audio_analysis != NULL) { + dlclose(g_ctrlmf.handle_audio_analysis); + g_ctrlmf.handle_audio_analysis = NULL; + } g_ctrlmf.audio_playback_init = false; g_ctrlmf.audio_control_init = false; @@ -95,3 +100,51 @@ bool ctrlmf_is_production(void) { return(g_ctrlmf.is_production); } +bool ctrlmf_file_exists(const char *filename) { + if(filename == NULL) { + return false; + } + struct stat buffer; + if(stat(filename, &buffer) == 0) { + return true; + } + return false; +} + +void *ctrlmf_load_plugin_audio_analysis(ctrlmf_mic_test_audio_analyze_t *audio_analyze_func) { + if(audio_analyze_func == NULL) { + XLOGD_INFO("Audio Analysis plugin not requested."); + return(NULL); + } + void *handle = NULL; + + const char *so_path_vd = "/vendor/lib/libctrlmf_audio_analysis.so"; + const char *so_path_mw = "/usr/lib/libctrlmf_audio_analysis.so"; + if(ctrlmf_file_exists(so_path_vd)) { + handle = dlopen(so_path_vd, RTLD_NOW); + } else if(ctrlmf_file_exists(so_path_mw)) { + handle = dlopen(so_path_mw, RTLD_NOW); + } else { + XLOGD_INFO("Audio Analysis plugin is not present."); + return(NULL); + } + + if(NULL == handle) { + XLOGD_ERROR("Failed to load Audio Analysis plugin <%s>", dlerror()); + return(NULL); + } + + dlerror(); // Clear any existing error + + *audio_analyze_func = (ctrlmf_mic_test_audio_analyze_t)dlsym(handle, "ctrlmf_mic_test_audio_analyze"); + char *error = dlerror(); + + if(error != NULL) { + XLOGD_ERROR("Failed to find plugin method (ctrlmf_mic_test_audio_analyze), error <%s>", error); + dlclose(handle); + return(NULL); + } + + XLOGD_INFO("Audio Analysis plugin is loaded."); + return(handle); +} diff --git a/src/factory/ctrlmf_ws.c b/src/factory/ctrlmf_ws.c index ea175c06..4befff0a 100644 --- a/src/factory/ctrlmf_ws.c +++ b/src/factory/ctrlmf_ws.c @@ -16,11 +16,13 @@ #include #ifdef CTRLMF_WSS_ENABLED +#include "rdkcertselector.h" #include #define CTRLMF_WS_CIPHER_LIST "AES256-SHA256:AES128-GCM-SHA256:AES128-SHA256" #define CTRLMF_WS_TLS_CERT_KEY_FILE "/tmp/serverXXXXXX" #define CTRLMF_WS_CERT_NAME_LEN (1024) #define CTRLMF_WS_CERT_PW_LEN (128) +#define CTRLMF_CERT_FILENAME_PREFIX "file://" #endif typedef enum { @@ -37,6 +39,7 @@ typedef struct { uint32_t * audio_frame_qty; uint32_t audio_frame_size; ctrlmf_ws_callbacks_t *callbacks; + bool *has_valid_cert; } ctrlmf_ws_thread_params_t; typedef struct { @@ -72,7 +75,7 @@ static bool ctrlmf_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additi ctrlmf_ws_global_t g_ctrlmf_ws; -bool ctrlmf_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlmf_ws_callbacks_t *callbacks) { +bool ctrlmf_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlmf_ws_callbacks_t *callbacks, bool *has_valid_cert) { ctrlmf_ws_thread_params_t params; sem_t semaphore; @@ -87,6 +90,7 @@ bool ctrlmf_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, c params.audio_frame_qty = &g_ctrlmf_ws.audio_frame_qty; params.audio_frame_size = audio_frame_size; params.callbacks = callbacks; + params.has_valid_cert = has_valid_cert; g_ctrlmf_ws.nopoll_ctx = NULL; g_ctrlmf_ws.audio_frames = NULL; @@ -146,52 +150,56 @@ void *ctrlmf_ws_main(void *param) { return(NULL); } - #ifdef CTRLMF_WSS_ENABLED - int cert_key_fd = -1; - FILE *cert_key_fp = NULL; char tmp_cert[32] = {0}; - int err_store; - - safec_rc = sprintf_s(tmp_cert, sizeof(tmp_cert), "%s", CTRLMF_WS_TLS_CERT_KEY_FILE); - if(safec_rc < EOK) { - ERR_CHK(safec_rc); - } + bool cert_valid = false; + #ifdef CTRLMF_WSS_ENABLED + do { + int cert_key_fd = -1; + FILE *cert_key_fp = NULL; + int err_store; - umask(0600); - cert_key_fd = mkstemp(tmp_cert); - if (cert_key_fd == -1) - { - err_store = errno; - XLOGD_ERROR("mkstemp failed: <%s>", strerror(err_store)); - return(NULL); - } + safec_rc = sprintf_s(tmp_cert, sizeof(tmp_cert), "%s", CTRLMF_WS_TLS_CERT_KEY_FILE); + if(safec_rc < EOK) { + ERR_CHK(safec_rc); + } - cert_key_fp = fdopen(cert_key_fd, "w"); - if(cert_key_fp == NULL) { - err_store = errno; - XLOGD_ERROR("fdopen failed: <%s>", strerror(err_store)); - if(0 != unlink(&tmp_cert[0])) { + umask(0600); + cert_key_fd = mkstemp(tmp_cert); + if (cert_key_fd == -1) + { err_store = errno; - XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + XLOGD_ERROR("mkstemp failed: <%s>", strerror(err_store)); + break; } - return(NULL); - } - if(!ctrlmf_ws_cert_config(cert_key_fp)) { - XLOGD_ERROR("failed to set cert or key, exit"); - fclose(cert_key_fp); - if(0 != unlink(&tmp_cert[0])) { + cert_key_fp = fdopen(cert_key_fd, "w"); + if(cert_key_fp == NULL) { err_store = errno; - XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + XLOGD_ERROR("fdopen failed: <%s>", strerror(err_store)); + if(0 != unlink(&tmp_cert[0])) { + err_store = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + } + break; } - return(NULL); - } - fclose(cert_key_fp); - // Init OpenSSL - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); + if(!ctrlmf_ws_cert_config(cert_key_fp)) { + XLOGD_ERROR("failed to set cert or key"); + fclose(cert_key_fp); + if(0 != unlink(&tmp_cert[0])) { + err_store = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + } + break; + } + fclose(cert_key_fp); + + // Init OpenSSL + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + cert_valid = true; + } while(0); #endif if(params.log_enable) { @@ -203,17 +211,17 @@ void *ctrlmf_ws_main(void *param) { nopoll_ctx_set_on_ready(g_ctrlmf_ws.nopoll_ctx, ctrlmf_ws_on_ready, &state); nopoll_ctx_set_on_msg(g_ctrlmf_ws.nopoll_ctx, ctrlmf_ws_on_message, &state); - #ifdef CTRLMF_WSS_ENABLED - nopoll_conn_opts_set_ssl_protocol(opts, NOPOLL_METHOD_TLSV1_2); - nopoll_conn_opts_ssl_host_verify(opts, nopoll_false); //localhost will not match host specified in certificate - - if(!nopoll_conn_opts_set_ssl_certs(opts, &tmp_cert[0], &tmp_cert[0], NULL, NULL)) { - XLOGD_ERROR("Failed to add cert/key files to nopoll_conn"); - nopoll_ctx_unref(g_ctrlmf_ws.nopoll_ctx); - nopoll_conn_opts_free(opts); - return(NULL); + if(cert_valid) { + nopoll_conn_opts_set_ssl_protocol(opts, NOPOLL_METHOD_TLSV1_2); + nopoll_conn_opts_ssl_host_verify(opts, nopoll_false); //localhost will not match host specified in certificate + + if(!nopoll_conn_opts_set_ssl_certs(opts, &tmp_cert[0], &tmp_cert[0], NULL, NULL)) { + XLOGD_ERROR("Failed to add cert/key files to nopoll_conn"); + nopoll_ctx_unref(g_ctrlmf_ws.nopoll_ctx); + nopoll_conn_opts_free(opts); + return(NULL); + } } - #endif char port[6]; safec_rc = sprintf_s(port, sizeof(port), "%u", params.port); @@ -222,11 +230,11 @@ void *ctrlmf_ws_main(void *param) { } // Start IPv4/6 listener - #ifdef CTRLMF_WSS_ENABLED - state.nopoll_conn = nopoll_listener_tls_new_opts6(g_ctrlmf_ws.nopoll_ctx, opts, "::", port); - #else - state.nopoll_conn = nopoll_listener_new_opts6(g_ctrlmf_ws.nopoll_ctx, opts, "::", port); - #endif + if(cert_valid) { + state.nopoll_conn = nopoll_listener_tls_new_opts6(g_ctrlmf_ws.nopoll_ctx, opts, "::", port); + } else { + state.nopoll_conn = nopoll_listener_new_opts6(g_ctrlmf_ws.nopoll_ctx, opts, "::", port); + } if(!nopoll_conn_is_ok(state.nopoll_conn)) { XLOGD_ERROR("Listener connection IPv6 NOT ok"); nopoll_ctx_unref(g_ctrlmf_ws.nopoll_ctx); @@ -234,6 +242,10 @@ void *ctrlmf_ws_main(void *param) { return(NULL); } + if(params.has_valid_cert != NULL) { + *(params.has_valid_cert) = cert_valid; + } + // Unblock the caller that launched this thread sem_post(params.semaphore); params.semaphore = NULL; @@ -247,12 +259,12 @@ void *ctrlmf_ws_main(void *param) { nopoll_ctx_unref(g_ctrlmf_ws.nopoll_ctx); g_ctrlmf_ws.nopoll_ctx = NULL; - #ifdef CTRLMF_WSS_ENABLED - if(0 != unlink(tmp_cert)) { - int err_store = errno; - XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + if(cert_valid) { + if(0 != unlink(tmp_cert)) { + int err_store = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + } } - #endif return(NULL); } @@ -372,9 +384,9 @@ void ctrlmf_ws_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { #ifdef CTRLMF_WSS_ENABLED bool ctrlmf_ws_cert_config(FILE* cert_key_fp) { - bool ret = false; - ctrlm_fta_platform_cert_info_t *cert_info = NULL; + + rdkcertselector_h cert_selector = NULL; do { FILE *device_cert_fp = NULL; PKCS12 *p12_cert = NULL; @@ -382,18 +394,33 @@ bool ctrlmf_ws_cert_config(FILE* cert_key_fp) { X509 *x509_cert = NULL; STACK_OF(X509) *additional_certs = NULL; - cert_info = ctrlm_fta_platform_cert_info_get(false); - if(cert_info == NULL) { - XLOGD_ERROR("unable to get certificate info"); + char *cert_path = NULL; + char *cert_password = NULL; + cert_selector = rdkcertselector_new(NULL, NULL, "FBK_MTLS"); + + if(cert_selector == NULL){ + XLOGD_TELEMETRY("cert selector init failed"); break; } - if(cert_info->type != CTRLM_FTA_PLATFORM_VOICE_CERT_TYPE_P12) { - XLOGD_ERROR("unable to parse certificates that are not of PKCS12 type"); + rdkcertselectorStatus_t cert_status = rdkcertselector_getCert(cert_selector, &cert_path, &cert_password); + + if(cert_status != certselectorOk) { + XLOGD_TELEMETRY("cert selector retrieval failed"); + break; + } + + if(cert_path == NULL || cert_password == NULL) { + XLOGD_TELEMETRY("cert selector get failed"); break; } - device_cert_fp = fopen(cert_info->filename, "rb"); + char *local_path = cert_path; + if(strncmp(local_path, CTRLMF_CERT_FILENAME_PREFIX, strlen(CTRLMF_CERT_FILENAME_PREFIX)) == 0) { + local_path += strlen(CTRLMF_CERT_FILENAME_PREFIX); + } + + device_cert_fp = fopen(local_path, "rb"); if(device_cert_fp == NULL) { XLOGD_ERROR("unable to open P12 certificate"); break; @@ -408,7 +435,7 @@ bool ctrlmf_ws_cert_config(FILE* cert_key_fp) { break; } - if(1 != PKCS12_parse(p12_cert, cert_info->password, &pkey, &x509_cert, &additional_certs)) { + if(1 != PKCS12_parse(p12_cert, cert_password, &pkey, &x509_cert, &additional_certs)) { XLOGD_ERROR("unable to parse P12 certificate"); break; } @@ -423,7 +450,7 @@ bool ctrlmf_ws_cert_config(FILE* cert_key_fp) { break; } - if(1 != PEM_write_PrivateKey(cert_key_fp, pkey, NULL, (unsigned char*)cert_info->password, strlen(cert_info->password), NULL, NULL)) { + if(1 != PEM_write_PrivateKey(cert_key_fp, pkey, NULL, (unsigned char*)cert_password, strlen(cert_password), NULL, NULL)) { XLOGD_ERROR("failed to write temp key"); break; } @@ -431,8 +458,8 @@ bool ctrlmf_ws_cert_config(FILE* cert_key_fp) { ret = true; }while(0); - if(cert_info != NULL) { - ctrlm_fta_platform_cert_info_free(cert_info); + if(cert_selector != NULL) { + rdkcertselector_free(&cert_selector); } return ret; diff --git a/src/factory/ctrlmf_ws.h b/src/factory/ctrlmf_ws.h index f68490e3..203a2ac2 100644 --- a/src/factory/ctrlmf_ws.h +++ b/src/factory/ctrlmf_ws.h @@ -35,7 +35,7 @@ typedef struct { extern "C" { #endif -bool ctrlmf_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlmf_ws_callbacks_t *callbacks); +bool ctrlmf_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlmf_ws_callbacks_t *callbacks, bool *has_valid_cert); bool ctrlmf_ws_capture_set(ctrlmf_audio_frame_t audio_frames, uint32_t audio_frame_qty); void ctrlmf_ws_term(void); diff --git a/src/input_event/ctrlm_input_event_writer.cpp b/src/input_event/ctrlm_input_event_writer.cpp index c463a78e..007dd436 100644 --- a/src/input_event/ctrlm_input_event_writer.cpp +++ b/src/input_event/ctrlm_input_event_writer.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include bool ctrlm_input_event_writer::init(std::string uinput_name, uint32_t vendor, uint32_t product) { if (initialized_) { @@ -59,8 +61,32 @@ bool ctrlm_input_event_writer::init(std::string uinput_name, uint32_t vendor, ui errno_t safec_rc = strcpy_s(usetup.name, sizeof(usetup.name), uinput_name.c_str()); ERR_CHK(safec_rc); - ioctl(fd, UI_DEV_SETUP, &usetup); - ioctl(fd, UI_DEV_CREATE); + int err = ioctl(fd, UI_DEV_SETUP, &usetup); + if (err == -1) { + int errsv = errno; + XLOGD_ERROR("UI_DEV_SETUP failed with errno %d (%s)", errsv, std::strerror(errsv)); + close(fd); + return false; + } + + err = ioctl(fd, UI_DEV_CREATE); + if (err == -1) { + int errsv = errno; + XLOGD_ERROR("UI_DEV_CREATE failed with errno %d (%s)", errsv, std::strerror(errsv)); + close(fd); + return false; + } + + char sysfs_name[16] = {0}; + err = ioctl(fd, UI_GET_SYSNAME(sizeof(sysfs_name)), sysfs_name); + if (err == -1) { + int errsv = errno; + XLOGD_ERROR("UI_GET_SYSNAME failed with errno %d (%s)", errsv, std::strerror(errsv)); + ioctl(fd, UI_DEV_DESTROY); + close(fd); + return false; + } + sysfs_name_ = sysfs_name; fd_ = fd; initialized_ = true; @@ -147,10 +173,43 @@ uint16_t ctrlm_input_event_writer::write_event(ctrlm_key_code_t code, ctrlm_key_ } bool ctrlm_input_event_writer::get_meta_data(struct stat &file_meta_data) { - int ret = fstat(fd_, &file_meta_data); + std::ostringstream oss; + oss << "/sys/devices/virtual/input/" << sysfs_name_; + std::string dir_path = oss.str(); + XLOGD_DEBUG("virtual input path = %s", dir_path.c_str()); + + DIR *dir = opendir(dir_path.c_str()); + if (dir == nullptr) { + int errsv = errno; + XLOGD_ERROR("Failed to open virtual input device dir at path <%s>: error = <%d>, <%s>", + dir_path.c_str(), errsv, strerror(errsv)); + return false; + } + + struct dirent *entry; + std::string stat_path = "/dev/input/"; + bool event_node_found = false; + + while ((entry = readdir(dir)) != nullptr) { + std::string filename = entry->d_name; + if (filename.find("event") == 0) { + event_node_found = true; + stat_path += filename; + break; + } + } + closedir(dir); + + XLOGD_DEBUG("dev input event path = %s", stat_path.c_str()); + if (!event_node_found) { + XLOGD_ERROR("no event node found in virtual device dir <%s>", dir_path.c_str()); + return false; + } + + int ret = stat(stat_path.c_str(), &file_meta_data); if (ret == -1) { int errsv = errno; - XLOGD_ERROR("fstat() failed: error = <%d>, <%s>", errsv, std::strerror(errsv)); + XLOGD_ERROR("stat() failed: error = <%d>, <%s>", errsv, std::strerror(errsv)); return false; } return true; diff --git a/src/input_event/ctrlm_input_event_writer.h b/src/input_event/ctrlm_input_event_writer.h index 54d7c51d..2f6c89cc 100644 --- a/src/input_event/ctrlm_input_event_writer.h +++ b/src/input_event/ctrlm_input_event_writer.h @@ -66,7 +66,7 @@ const std::map ctrlm_key_to_linux_map {CTRLM_KEY_CODE_CH_DOWN, linux_ui_code_values_t(KEY_PAGEDOWN, 0x59, KEY_LEFTCTRL)}, {CTRLM_KEY_CODE_LAST, linux_ui_code_values_t(KEY_ESC, 0x0, KEY_LEFTCTRL)}, {CTRLM_KEY_CODE_INPUT_SELECT, linux_ui_code_values_t(KEY_F15, 0x0, KEY_RESERVED)}, - {CTRLM_KEY_CODE_INFO, linux_ui_code_values_t(KEY_F9, 0xcb, KEY_LEFTCTRL)}, + {CTRLM_KEY_CODE_INFO, linux_ui_code_values_t(KEY_F9, 0x0, KEY_RESERVED)}, {CTRLM_KEY_CODE_VOL_UP, linux_ui_code_values_t(KEY_KPPLUS, 0x0, KEY_RESERVED)}, {CTRLM_KEY_CODE_VOL_DOWN, linux_ui_code_values_t(KEY_KPMINUS, 0x0, KEY_RESERVED)}, {CTRLM_KEY_CODE_MUTE, linux_ui_code_values_t(KEY_KPASTERISK, 0x0, KEY_RESERVED)}, @@ -86,6 +86,7 @@ const std::map ctrlm_key_to_linux_map {CTRLM_KEY_CODE_OCAP_C, linux_ui_code_values_t(KEY_RESERVED, 0x0, KEY_RESERVED)}, {CTRLM_KEY_CODE_OCAP_D, linux_ui_code_values_t(KEY_RESERVED, 0x0, KEY_RESERVED)}, {CTRLM_KEY_CODE_ASTERISK, linux_ui_code_values_t(KEY_F14, 0x0, KEY_RESERVED)}, + {CTRLM_KEY_CODE_PUSH_TO_TALK, linux_ui_code_values_t(KEY_F8, 0x0, KEY_RESERVED)} }; enum class key_stroke { @@ -104,8 +105,9 @@ const std::map ev_key_value_map = class ctrlm_input_event_writer { private: - bool initialized_ = false; - int fd_ = -1; + bool initialized_ = false; + int fd_ = -1; + std::string sysfs_name_ = ""; protected: bool write_event_internal(uint32_t scan_code, uint16_t key_code, key_stroke stroke); diff --git a/src/ipc/ctrlm_ipc_iarm.cpp b/src/ipc/ctrlm_ipc_iarm.cpp index 6d960196..420b44ce 100644 --- a/src/ipc/ctrlm_ipc_iarm.cpp +++ b/src/ipc/ctrlm_ipc_iarm.cpp @@ -20,6 +20,15 @@ #include "ctrlm_ipc_iarm.h" #include "ctrlm.h" #include "ctrlm_log.h" +#include "ctrlm_ipc_voice.h" + +void ctrlm_ipc_iarm_t::set_api_revision(unsigned char api_revision) { + api_revision_ = api_revision; +} + +unsigned char ctrlm_ipc_iarm_t::get_api_revision(void) const { + return api_revision_; +} bool ctrlm_ipc_iarm_t::register_iarm_call(const char *call, IARM_BusCall_t handler) const { diff --git a/src/ipc/ctrlm_ipc_iarm.h b/src/ipc/ctrlm_ipc_iarm.h index 78568186..51e487b5 100644 --- a/src/ipc/ctrlm_ipc_iarm.h +++ b/src/ipc/ctrlm_ipc_iarm.h @@ -26,6 +26,9 @@ #include class ctrlm_ipc_iarm_t { +private: + unsigned char api_revision_ = 0; + public: ctrlm_ipc_iarm_t() {}; virtual ~ctrlm_ipc_iarm_t() {}; @@ -38,26 +41,52 @@ class ctrlm_ipc_iarm_t { static void turn_on(std::atomic_bool &abool) { abool.store(true); } static void turn_off(std::atomic_bool &abool) { abool.store(false); } + void set_api_revision(unsigned char api_revision); + unsigned char get_api_revision(void) const; + bool register_iarm_call(const char *call, IARM_BusCall_t handler) const; bool broadcast_iarm_event_legacy(const char *bus_name, int event, void *data, size_t data_size) const; template - bool broadcast_iarm_event(const char *bus_name, unsigned char api_revision, int event, const char *str) const { + bool broadcast_iarm_event(const char *bus_name, int event, json_t* event_data) const + { + bool ret = false; + if(!event_data) { + return(ret); + } + + char *payload_str = json_dumps(event_data, JSON_COMPACT); + + if(payload_str != NULL) { + ret = broadcast_iarm_event(bus_name, event, payload_str); + free(payload_str); + } + + if(event_data) { + json_decref(event_data); + } + + return(ret); + } + + template + bool broadcast_iarm_event(const char *bus_name, int event, const char *str) const + { bool ret = false; size_t str_size = strlen(str) + 1; size_t size = sizeof(T) + str_size; + T *data = (T *)calloc(1, size); - data->api_revision = api_revision; - if(!data) { - return(ret); - } else { + if (data != nullptr) { + data->api_revision = api_revision_; + //Can't be replaced with safeC version of this, as safeC string functions doesn't allow string size more than 4K snprintf(data->payload, str_size, "%s", str); - if(IARM_Bus_BroadcastEvent(bus_name, event, data, size)) { + + IARM_Result_t res = IARM_Bus_BroadcastEvent(bus_name, event, data, size); + if(res == IARM_RESULT_SUCCESS) { ret = true; } - if(data) { - free(data); - } + free(data); } return(ret); } diff --git a/src/ipc/ctrlm_ipc_iarm_powermanager.cpp b/src/ipc/ctrlm_ipc_iarm_powermanager.cpp index 0fb5cd5e..24897391 100755 --- a/src/ipc/ctrlm_ipc_iarm_powermanager.cpp +++ b/src/ipc/ctrlm_ipc_iarm_powermanager.cpp @@ -41,19 +41,16 @@ ctrlm_power_state_t ctrlm_ipc_iarm_powermanager_t::get_power_state() { XLOGD_WARN("IARM bus failed to read power state, defaulting to <%s>", ctrlm_power_state_str(power_state)); } else { power_state = ctrlm_iarm_power_state_map(param.curState); - #ifdef NETWORKED_STANDBY_MODE_ENABLED //If ctrlm restarts with system STANDBY state, set to ON, will receive a DEEP_SLEEP or ON message shortly - if(power_state == CTRLM_POWER_STATE_STANDBY) { + if(ctrlm_is_networked_standby_supported() && (power_state == CTRLM_POWER_STATE_STANDBY)) { power_state = CTRLM_POWER_STATE_ON; } - #endif XLOGD_DEBUG("power state <%s>", ctrlm_power_state_str(power_state)); } return power_state; } -#ifdef NETWORKED_STANDBY_MODE_ENABLED bool ctrlm_ipc_iarm_powermanager_t::get_networked_standby_mode() { IARM_Bus_PWRMgr_NetworkStandbyMode_Param_t param = {0}; IARM_Result_t res = IARM_Bus_Call(IARM_BUS_PWRMGR_NAME, IARM_BUS_PWRMGR_API_GetNetworkStandbyMode, (void *)¶m, sizeof(param)); @@ -83,13 +80,12 @@ bool ctrlm_ipc_iarm_powermanager_t::get_wakeup_reason_voice() { return false; } - XLOGD_INFO("wakeup_reason <%s>", ctrlm_wakeup_reason_str(wakeup_reason)); + XLOGD_AUTOMATION_INFO("wakeup_reason <%s>", ctrlm_wakeup_reason_str(wakeup_reason)); wakeup_reason_voice = (wakeup_reason == DEEPSLEEP_WAKEUPREASON_VOICE) ? true: false; return wakeup_reason_voice; } -#endif #if CTRLM_HAL_RF4CE_API_VERSION >= 10 && !defined(CTRLM_DPI_CONTROL_NOT_SUPPORTED) IARM_Result_t ctrlm_iarm_powermanager_event_handler_power_pre_change(void* pArgs) diff --git a/src/ipc/ctrlm_ipc_iarm_powermanager.h b/src/ipc/ctrlm_ipc_iarm_powermanager.h index 20613c20..4944340a 100755 --- a/src/ipc/ctrlm_ipc_iarm_powermanager.h +++ b/src/ipc/ctrlm_ipc_iarm_powermanager.h @@ -26,10 +26,8 @@ class ctrlm_ipc_iarm_powermanager_t : public ctrlm_powermanager_t { ~ctrlm_ipc_iarm_powermanager_t(); ctrlm_power_state_t get_power_state(); - #ifdef NETWORKED_STANDBY_MODE_ENABLED bool get_networked_standby_mode(); bool get_wakeup_reason_voice(); - #endif private: diff --git a/src/ipc/ctrlm_rcp_ipc_event.cpp b/src/ipc/ctrlm_rcp_ipc_event.cpp index 5c24208e..510be650 100644 --- a/src/ipc/ctrlm_rcp_ipc_event.cpp +++ b/src/ipc/ctrlm_rcp_ipc_event.cpp @@ -135,12 +135,26 @@ void ctrlm_rcp_ipc_net_status_t::populate_status(const ctrlm_obj_network_t &netw } } -std::string ctrlm_base_event_json_t::to_string() const +ctrlm_ir_state_t ctrlm_rcp_ipc_net_status_t::get_ir_prog_state(void) { - char *json_str = json_dumps(to_json(), JSON_ENCODE_ANY); - std::string copy = json_str; - free(json_str); - return copy; + return irdb_state_; +} + +ctrlm_rf_pair_state_t ctrlm_rcp_ipc_net_status_t::get_rf_pair_state(void) +{ + return pair_state_; +} + +void ctrlm_rcp_ipc_net_status_t::get_controller_status_list(std::vector &list) +{ + for (const auto &status : controller_status_list_) { + list.push_back(status); + } +} + +char *ctrlm_rcp_ipc_net_status_t::to_string() const +{ + return json_dumps(to_json(), JSON_ENCODE_ANY); } ctrlm_rcp_ipc_upgrade_status_t::~ctrlm_rcp_ipc_upgrade_status_t() @@ -173,6 +187,11 @@ json_t *ctrlm_rcp_ipc_upgrade_status_t::to_json() const return (err) ? NULL : status; } +char *ctrlm_rcp_ipc_upgrade_status_t::to_string() const +{ + return json_dumps(to_json(), JSON_ENCODE_ANY); +} + ctrlm_rcp_ipc_validation_status_t::~ctrlm_rcp_ipc_validation_status_t() { } @@ -214,3 +233,8 @@ json_t *ctrlm_rcp_ipc_validation_status_t::to_json() const return (err) ? NULL : status; } + +char *ctrlm_rcp_ipc_validation_status_t::to_string() const +{ + return json_dumps(to_json(), JSON_ENCODE_ANY); +} diff --git a/src/ipc/ctrlm_rcp_ipc_event.h b/src/ipc/ctrlm_rcp_ipc_event.h index bf31a823..e2c36e7c 100644 --- a/src/ipc/ctrlm_rcp_ipc_event.h +++ b/src/ipc/ctrlm_rcp_ipc_event.h @@ -63,18 +63,17 @@ namespace rcp_net_status_json_keys constexpr char const* ERROR_STRING = "errorString"; } -class ctrlm_base_event_json_t +class ctrlm_virtual_json_t { public: - virtual ~ctrlm_base_event_json_t() {}; + virtual ~ctrlm_virtual_json_t() {}; virtual json_t *to_json() const = 0; - virtual std::string to_string() const; }; class ctrlm_obj_controller_t; class ctrlm_obj_network_t; -class ctrlm_rcp_ipc_controller_status_t : public ctrlm_base_event_json_t +class ctrlm_rcp_ipc_controller_status_t : public ctrlm_virtual_json_t { public: ctrlm_rcp_ipc_controller_status_t() = default; @@ -104,7 +103,7 @@ class ctrlm_rcp_ipc_controller_status_t : public ctrlm_base_event_json_t std::string upgrade_session_id_ = ""; }; -class ctrlm_rcp_ipc_net_status_t : public ctrlm_base_event_json_t +class ctrlm_rcp_ipc_net_status_t : public ctrlm_virtual_json_t { public: ctrlm_rcp_ipc_net_status_t() = default; @@ -112,12 +111,18 @@ class ctrlm_rcp_ipc_net_status_t : public ctrlm_base_event_json_t virtual json_t *to_json() const; - uint8_t get_api_revision() const { return api_revision_; } - bool get_result() const { return (result_ == CTRLM_IARM_CALL_RESULT_SUCCESS) ? true : false; } - void set_result(ctrlm_iarm_call_result_t result) { result_ = result; } - ctrlm_network_id_t get_net_id() const { return net_id_; } - void set_net_id(ctrlm_network_id_t net_id) { net_id_ = net_id; } - void populate_status(const ctrlm_obj_network_t &network); + char *to_string() const; + uint8_t get_api_revision() const { return api_revision_; } + bool get_result() const { return (result_ == CTRLM_IARM_CALL_RESULT_SUCCESS) ? true : false; } + void set_result(ctrlm_iarm_call_result_t result) { result_ = result; } + ctrlm_network_id_t get_net_id() const { return net_id_; } + void set_net_id(ctrlm_network_id_t net_id) { net_id_ = net_id; } + ctrlm_network_type_t get_type() const { return net_type_; } + + void populate_status(const ctrlm_obj_network_t &network); + ctrlm_ir_state_t get_ir_prog_state(void); + ctrlm_rf_pair_state_t get_rf_pair_state(void); + void get_controller_status_list(std::vector &list); private: uint8_t api_revision_ = 0; @@ -131,13 +136,14 @@ class ctrlm_rcp_ipc_net_status_t : public ctrlm_base_event_json_t std::vector controller_status_list_; }; -class ctrlm_rcp_ipc_upgrade_status_t : public ctrlm_base_event_json_t +class ctrlm_rcp_ipc_upgrade_status_t : public ctrlm_virtual_json_t { public: ctrlm_rcp_ipc_upgrade_status_t() = default; ~ctrlm_rcp_ipc_upgrade_status_t(); virtual json_t *to_json() const; + char *to_string() const; bool get_result() const { return (result_ == CTRLM_IARM_CALL_RESULT_SUCCESS) ? true : false; } void set_result(ctrlm_iarm_call_result_t result) { result_ = result; } ctrlm_network_id_t get_net_id() const { return net_id_; } @@ -155,13 +161,14 @@ class ctrlm_rcp_ipc_upgrade_status_t : public ctrlm_base_event_json_t ctrlm_iarm_call_result_t result_ = CTRLM_IARM_CALL_RESULT_INVALID; }; -class ctrlm_rcp_ipc_validation_status_t : public ctrlm_base_event_json_t +class ctrlm_rcp_ipc_validation_status_t : public ctrlm_virtual_json_t { public: ctrlm_rcp_ipc_validation_status_t() = default; ~ctrlm_rcp_ipc_validation_status_t(); virtual json_t *to_json() const; + char *to_string() const; uint8_t get_api_revision() const { return api_revision_; } bool get_result() const { return (result_ == CTRLM_IARM_CALL_RESULT_SUCCESS) ? true : false; } void set_result(ctrlm_iarm_call_result_t result) { result_ = result; } @@ -195,11 +202,11 @@ class ctrlm_network_all_ipc_result_wrapper_t { return (result_map_[network_id_] == CTRLM_IARM_CALL_RESULT_SUCCESS); } else { for (const auto &it : result_map_) { - if (it.second != CTRLM_IARM_CALL_RESULT_SUCCESS) { - return false; + if (it.second == CTRLM_IARM_CALL_RESULT_SUCCESS) { + return true; } } - return true; + return false; } } }; diff --git a/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp b/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp index 82a005bc..5ed63e70 100644 --- a/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp +++ b/src/ipc/ctrlm_rcp_ipc_iarm_thunder.cpp @@ -36,6 +36,7 @@ ctrlm_rcp_ipc_iarm_thunder_t::ctrlm_rcp_ipc_iarm_thunder_t() : ctrlm_ipc_iarm_t( { XLOGD_INFO(""); configure(); + set_api_revision(CTRLM_MAIN_IARM_BUS_API_REVISION); } ctrlm_rcp_ipc_iarm_thunder_t::~ctrlm_rcp_ipc_iarm_thunder_t() @@ -114,7 +115,18 @@ bool ctrlm_rcp_ipc_iarm_thunder_t::on_status(const ctrlm_rcp_ipc_net_status_t &n return(false); } - return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_MAIN_IARM_BUS_API_REVISION, CTRLM_RCU_IARM_EVENT_RCU_STATUS, net_status.to_string().c_str()); + json_t *ret = json_object(); + int err = 0; + + err |= json_object_set_new_nocheck(ret, STATUS, net_status.to_json()); + + if (err) { + XLOGD_ERROR("JSON object set error"); + json_decref(ret); + return(false); + } + + return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_RCU_IARM_EVENT_RCU_STATUS, ret); } bool ctrlm_rcp_ipc_iarm_thunder_t::on_validation_status(const ctrlm_rcp_ipc_validation_status_t &validation_status) const @@ -129,7 +141,18 @@ bool ctrlm_rcp_ipc_iarm_thunder_t::on_validation_status(const ctrlm_rcp_ipc_vali return(false); } - return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_MAIN_IARM_BUS_API_REVISION, CTRLM_RCU_IARM_EVENT_VALIDATION_STATUS, validation_status.to_string().c_str()); + json_t *ret = json_object(); + int err = 0; + + err |= json_object_set_new_nocheck(ret, STATUS, validation_status.to_json()); + + if (err) { + XLOGD_ERROR("JSON object set error"); + json_decref(ret); + return(false); + } + + return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_RCU_IARM_EVENT_VALIDATION_STATUS, ret); } bool ctrlm_rcp_ipc_iarm_thunder_t::on_firmware_update_progress(const ctrlm_rcp_ipc_upgrade_status_t &upgrade_status) const @@ -155,7 +178,7 @@ bool ctrlm_rcp_ipc_iarm_thunder_t::on_firmware_update_progress(const ctrlm_rcp_i return(false); } - return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_MAIN_IARM_BUS_API_REVISION, CTRLM_RCU_IARM_EVENT_FIRMWARE_UPDATE_PROGRESS, upgrade_status.to_string().c_str()); + return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_RCU_IARM_EVENT_FIRMWARE_UPDATE_PROGRESS, ret); } bool ctrlm_rcp_ipc_iarm_thunder_t::on_validation(const ctrlm_rcp_ipc_validation_status_t &validation_status) const @@ -176,7 +199,7 @@ bool ctrlm_rcp_ipc_iarm_thunder_t::on_validation(const ctrlm_rcp_ipc_validation_ return(false); } - return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_MAIN_IARM_BUS_API_REVISION, CTRLM_RCU_IARM_EVENT_VALIDATION_STATUS, validation_status.to_string().c_str()); + return broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_RCU_IARM_EVENT_VALIDATION_STATUS, ret); } IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::start_pairing(void *arg) @@ -374,32 +397,55 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::get_net_status(void *arg) return(IARM_RESULT_INVALID_PARAM); } - int net_type = CTRLM_NETWORK_TYPE_INVALID; - if (!config.config_value_get(NET_TYPE, net_type)) { - XLOGD_INFO("Missing %s parameter - defaulting to all networks", NET_TYPE); - } std::shared_ptr> params = std::make_shared>(); - params->set_net_id((net_type == CTRLM_NETWORK_TYPE_INVALID) ? CTRLM_MAIN_NETWORK_ID_ALL : ctrlm_network_id_get(static_cast(net_type))); + params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); sync_send_netw_handler_to_main_queue_new, ctrlm_main_queue_msg_get_rcu_status_t> (params, (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_get_rcu_status); - json_t *ret = json_object(); - json_t *status_array = json_array(); + json_t *ret = json_object(); + json_t *status = json_object(); + json_t *net_type_supported = json_array(); + json_t *remote_array = json_array(); + std::map status_map = params->get_reply(); + std::vector remotes; + + ctrlm_network_type_t type = CTRLM_NETWORK_TYPE_INVALID; + ctrlm_ir_state_t ir_prog_state = CTRLM_IR_STATE_UNKNOWN; + ctrlm_rf_pair_state_t rf_pair_state = CTRLM_RF_PAIR_STATE_UNKNOWN; int err = 0; - if (params->get_net_id() != CTRLM_MAIN_NETWORK_ID_ALL) { - err |= json_object_set_new_nocheck(ret, STATUS, status_map[params->get_net_id()].to_json()); - } else { - for (const auto &it : status_map) { - err |= json_array_append_new(status_array, it.second.to_json()); + for (auto const &it : ctrlm_network_types_get()) { + err |= json_array_append_new(net_type_supported, json_integer(it)); + } + for (auto &it : status_map) { + it.second.get_controller_status_list(remotes); + } + for (const auto &remote : remotes) { + err |= json_array_append_new(remote_array, remote.to_json()); + } + // For now default to RF4CE network reporting if available + for (auto &it : status_map) { + ir_prog_state = it.second.get_ir_prog_state(); + rf_pair_state = it.second.get_rf_pair_state(); + type = it.second.get_type(); + + if (type == CTRLM_NETWORK_TYPE_RF4CE) { + break; } - err |= json_object_set_new_nocheck(ret, STATUS, status_array); } + + err |= json_object_set_new_nocheck(status, REMOTE_DATA, remote_array); + err |= json_object_set_new_nocheck(status, NET_TYPES_SUPPORTED, net_type_supported); + err |= json_object_set_new_nocheck(status, NET_TYPE, json_integer(type)); + err |= json_object_set_new_nocheck(status, IR_PROG_STATE, json_string(ctrlm_ir_state_str(ir_prog_state))); + err |= json_object_set_new_nocheck(status, PAIRING_STATE, json_string(ctrlm_rf_pair_state_str(rf_pair_state))); + + err |= json_object_set_new_nocheck(ret, STATUS, status); err |= json_object_set_new_nocheck(ret, SUCCESS, json_boolean(params->get_result())); if (err) { @@ -440,13 +486,8 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::get_last_keypress(void *arg) return(IARM_RESULT_INVALID_PARAM); } - int net_type = CTRLM_NETWORK_TYPE_INVALID; - if (!config.config_value_get(NET_TYPE, net_type)) { - XLOGD_INFO("Missing %s parameter - defaulting to all networks", NET_TYPE); - } - std::shared_ptr> params = std::make_shared>(); - params->set_net_id((net_type == CTRLM_NETWORK_TYPE_INVALID) ? CTRLM_MAIN_NETWORK_ID_ALL : ctrlm_network_id_get(static_cast(net_type))); + params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); sync_send_netw_handler_to_main_queue_new, ctrlm_main_queue_msg_get_last_keypress_t> @@ -461,18 +502,13 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::get_last_keypress(void *arg) ctrlm_network_id_t net_id_index = 0; int err = 0; - if (params->get_net_id() != CTRLM_MAIN_NETWORK_ID_ALL) { - itr = key_info_map.find(params->get_net_id()); - key_info = itr->second; - } else { - for (itr = key_info_map.begin(); itr != key_info_map.end(); itr++) { - if (itr->second.timestamp > time_last_key) { - time_last_key = itr->second.timestamp; - net_id_index = itr->first; - } + for (itr = key_info_map.begin(); itr != key_info_map.end(); itr++) { + if (itr->second.timestamp > time_last_key) { + time_last_key = itr->second.timestamp; + net_id_index = itr->first; } - key_info = key_info_map[net_id_index]; } + key_info = key_info_map[net_id_index]; err |= json_object_set_new_nocheck(ret, CONTROLLER_ID, json_integer(key_info.controller_id)); err |= json_object_set_new_nocheck(ret, TIMESTAMP, json_integer(key_info.timestamp)); @@ -521,11 +557,6 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::find_my_remote(void *arg) return(IARM_RESULT_INVALID_PARAM); } - int net_type = CTRLM_NETWORK_TYPE_INVALID; - if(!config.config_value_get(NET_TYPE, net_type)) { - XLOGD_INFO("Missing %s parameter - defaulting to all networks", NET_TYPE); - } - std::string level; if(!config.config_value_get(LEVEL, level)) { XLOGD_ERROR("Missing %s parameter", LEVEL); @@ -533,7 +564,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::find_my_remote(void *arg) } std::shared_ptr params = std::make_shared(); - params->set_net_id((net_type == CTRLM_NETWORK_TYPE_INVALID) ? CTRLM_MAIN_NETWORK_ID_ALL : ctrlm_network_id_get(static_cast(net_type))); + params->set_net_id(CTRLM_MAIN_NETWORK_ID_ALL); params->level = ctrlm_utils_str_to_fmr_level(level); params->duration = 0; @@ -622,10 +653,6 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::write_rcu_wakeup_config(void *arg) return(IARM_RESULT_INVALID_PARAM); } - int net_type = CTRLM_NETWORK_TYPE_INVALID; - if(!config.config_value_get(NET_TYPE, net_type)) { - XLOGD_INFO("Missing %s parameter - defaulting to all networks", NET_TYPE); - } std::string wakeup_config; if(!config.config_value_get(WAKEUP_CONFIG, wakeup_config)) { @@ -642,7 +669,7 @@ IARM_Result_t ctrlm_rcp_ipc_iarm_thunder_t::write_rcu_wakeup_config(void *arg) } ctrlm_iarm_call_WriteRcuWakeupConfig_params_t params = {}; - params.network_id = (net_type == CTRLM_NETWORK_TYPE_INVALID) ? CTRLM_MAIN_NETWORK_ID_ALL : ctrlm_network_id_get(static_cast(net_type)); + params.network_id = CTRLM_MAIN_NETWORK_ID_ALL; params.config = ctrlm_utils_str_to_wakeup_config(wakeup_config); params.customListSize = ctrlm_utils_custom_key_str_to_array(custom_keys, params.customList); diff --git a/src/irdb/ctrlm_irdb_interface.cpp b/src/irdb/ctrlm_irdb_interface.cpp index 1bc55ffd..01fa6588 100644 --- a/src/irdb/ctrlm_irdb_interface.cpp +++ b/src/irdb/ctrlm_irdb_interface.cpp @@ -41,6 +41,7 @@ #include #include +#include using namespace std; @@ -68,6 +69,8 @@ typedef struct { std::string (*pluginVersion)() = NULL; bool (*pluginInitialize)() = NULL; bool (*pluginGetVendorInfo)(ctrlm_irdb_vendor_info_t &info) = NULL; + bool (*pluginGetSupportedVendors)(std::vector &info) = NULL; + bool (*pluginSetPreferredVendor)(const ctrlm_irdb_vendor_info_t &vendor) = NULL; bool (*pluginGetManufacturers)(ctrlm_irdb_manufacturer_list_t &manufacturers, ctrlm_irdb_dev_type_t type, const std::string &prefix) = NULL; bool (*pluginGetModels)(ctrlm_irdb_model_list_t &models, ctrlm_irdb_dev_type_t type, const std::string &manufacturer, const std::string &prefix) = NULL; bool (*pluginGetEntryIds)(ctrlm_irdb_entry_id_list_t &codes, ctrlm_irdb_dev_type_t type, const std::string &manufacturer, const std::string &model) = NULL; @@ -132,6 +135,8 @@ ctrlm_irdb_interface_t::ctrlm_irdb_interface_t(bool platform_tv) { g_irdb.pluginVersion = STUB_irdb_version; g_irdb.pluginInitialize = STUB_ctrlm_irdb_initialize; g_irdb.pluginGetVendorInfo = STUB_ctrlm_irdb_get_vendor_info; + g_irdb.pluginSetPreferredVendor = STUB_ctrlm_irdb_set_preferred_vendor; + g_irdb.pluginGetSupportedVendors = STUB_ctrlm_irdb_get_supported_vendor_info; g_irdb.pluginGetManufacturers = STUB_ctrlm_irdb_get_manufacturers; g_irdb.pluginGetModels = STUB_ctrlm_irdb_get_models; g_irdb.pluginGetEntryIds = STUB_ctrlm_irdb_get_entry_ids; @@ -182,6 +187,20 @@ ctrlm_irdb_interface_t::ctrlm_irdb_interface_t(bool platform_tv) { } dlerror(); // Clear any existing error + *(void **) (&g_irdb.pluginGetSupportedVendors) = dlsym(m_irdbPluginHandle, "ctrlm_irdb_get_supported_vendor_info"); + if ((error = dlerror()) != NULL) { + XLOGD_ERROR("Failed to find plugin method (ctrlm_irdb_get_supported_vendor_info), error <%s>, Using STUB implementation", error); + g_irdb.pluginGetSupportedVendors = STUB_ctrlm_irdb_get_supported_vendor_info; + } + dlerror(); // Clear any existing error + + *(void **) (&g_irdb.pluginSetPreferredVendor) = dlsym(m_irdbPluginHandle, "ctrlm_irdb_set_preferred_vendor"); + if ((error = dlerror()) != NULL) { + XLOGD_ERROR("Failed to find plugin method (ctrlm_irdb_set_preferred_vendor), error <%s>, Using STUB implementation", error); + g_irdb.pluginSetPreferredVendor = STUB_ctrlm_irdb_set_preferred_vendor; + } + dlerror(); // Clear any existing error + *(void **) (&g_irdb.pluginGetManufacturers) = dlsym(m_irdbPluginHandle, "ctrlm_irdb_get_manufacturers"); if ((error = dlerror()) != NULL) { XLOGD_ERROR("Failed to find plugin method (ctrlm_irdb_get_manufacturers), error <%s>, Using STUB implementation", error); @@ -278,6 +297,17 @@ bool ctrlm_irdb_interface_t::open_plugin() { } } XLOGD_INFO("IRDB plugin opened, ret = <%s>", ret ? "SUCCESS" : "ERROR"); + + if (g_irdb.pluginGetSupportedVendors) { + std::vector supported_vendors; + if ((*g_irdb.pluginGetSupportedVendors)(supported_vendors) == true) { + for (const auto &it : supported_vendors) { + XLOGD_INFO("Found supported IRDB Vendor <%s, 0x%X>", it.name.c_str(), it.rcu_support_bitmask); + } + } else { + XLOGD_WARN("Unable to query IRDB plugin for list of supported vendors, check version of the plugin..."); + } + } return ret; } @@ -299,6 +329,14 @@ bool ctrlm_irdb_interface_t::get_vendor_info(ctrlm_irdb_vendor_info_t &info) { return false; } +bool ctrlm_irdb_interface_t::set_vendor(const ctrlm_irdb_vendor_info_t &info) { + std::unique_lock guard(m_mutex); + if (g_irdb.pluginSetPreferredVendor) { + return (*g_irdb.pluginSetPreferredVendor)(info); + } + return false; +} + void ctrlm_irdb_interface_t::on_thunder_ready() { #if defined(CTRLM_THUNDER) @@ -543,17 +581,34 @@ bool ctrlm_irdb_interface_t::program_ir_codes(ctrlm_network_id_t network_id, ctr bool ctrlm_irdb_interface_t::_program_ir_codes(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, ctrlm_irdb_ir_code_set_t *ir_codes) { bool ret = false; + vector success_vec; - ctrlm_main_queue_msg_program_ir_codes_t msg = {0}; - msg.network_id = network_id; - msg.controller_id = controller_id; - msg.ir_codes = ir_codes; - msg.success = &ret; + std::shared_ptr msg = std::make_shared(); + msg->network_id = network_id; + msg->controller_id = controller_id; + msg->ir_codes = ir_codes; + msg->success = &success_vec; - if (false == get_vendor_info(msg.vendor_info)) { - msg.vendor_info.rcu_support_bitmask = 0; + if (false == get_vendor_info(msg->vendor_info)) { + msg->vendor_info.rcu_support_bitmask = 0; + } + ctrlm_main_queue_handler_push_new + (CTRLM_HANDLER_NETWORK, + (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_program_ir_codes, + std::move(msg), + NULL, + network_id, + true); + + for (char success : success_vec) { + if (success) { + ret = true; + break; + } else { + ret = false; + } } - ctrlm_main_queue_handler_push(CTRLM_HANDLER_NETWORK, (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_program_ir_codes, &msg, sizeof(msg), NULL, network_id, true); return(ret); } @@ -566,14 +621,30 @@ bool ctrlm_irdb_interface_t::clear_ir_codes(ctrlm_network_id_t network_id, ctrlm bool ctrlm_irdb_interface_t::_clear_ir_codes(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id) { bool ret = false; - - ctrlm_main_queue_msg_ir_clear_t msg = {0}; - - msg.network_id = network_id; - msg.controller_id = controller_id; - msg.success = &ret; - - ctrlm_main_queue_handler_push(CTRLM_HANDLER_NETWORK, (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_ir_clear_codes, &msg, sizeof(msg), NULL, network_id, true); - + vector success_vec; + + std::shared_ptr msg = std::make_shared(); + + msg->network_id = network_id; + msg->controller_id = controller_id; + msg->success = &success_vec; + + ctrlm_main_queue_handler_push_new + (CTRLM_HANDLER_NETWORK, + (ctrlm_msg_handler_network_t)&ctrlm_obj_network_t::req_process_ir_clear_codes, + std::move(msg), + NULL, + network_id, + true); + + for (char success : success_vec) { + if (success) { + ret = true; + break; + } else { + ret = false; + } + } return(ret); } diff --git a/src/irdb/ctrlm_irdb_interface.h b/src/irdb/ctrlm_irdb_interface.h index 9e7da14e..2f532b15 100644 --- a/src/irdb/ctrlm_irdb_interface.h +++ b/src/irdb/ctrlm_irdb_interface.h @@ -31,7 +31,6 @@ #include "ctrlm_hal.h" #include "ctrlm_irdb_plugin.h" - inline bool operator==(const ctrlm_irdb_autolookup_entry_ranked_t& lhs, const ctrlm_irdb_autolookup_entry_ranked_t& rhs) { return (lhs.id.compare(rhs.id) == 0); } @@ -42,7 +41,7 @@ typedef struct { ctrlm_network_id_t network_id; ctrlm_controller_id_t controller_id; ctrlm_irdb_ir_code_set_t * ir_codes; - bool * success; + std::vector * success; sem_t * semaphore; ctrlm_irdb_vendor_info_t vendor_info; } ctrlm_main_queue_msg_program_ir_codes_t; @@ -50,7 +49,7 @@ typedef struct { typedef struct { ctrlm_network_id_t network_id; ctrlm_controller_id_t controller_id; - bool * success; + std::vector * success; sem_t * semaphore; } ctrlm_main_queue_msg_ir_clear_t; @@ -70,6 +69,7 @@ class ctrlm_irdb_interface_t { virtual ~ctrlm_irdb_interface_t(); bool get_vendor_info(ctrlm_irdb_vendor_info_t &info); + bool set_vendor(const ctrlm_irdb_vendor_info_t &info); bool get_manufacturers(ctrlm_irdb_manufacturer_list_t &manufacturers, ctrlm_irdb_dev_type_t type, const std::string &prefix = ""); bool get_models(ctrlm_irdb_model_list_t &models, ctrlm_irdb_dev_type_t type, const std::string &manufacturer, const std::string &prefix = ""); bool get_irdb_entry_ids(ctrlm_irdb_entry_id_list_t &codes, ctrlm_irdb_dev_type_t type, const std::string &manufacturer, const std::string &model = ""); diff --git a/src/irdb/ctrlm_irdb_plugin.h b/src/irdb/ctrlm_irdb_plugin.h index 7e1ced22..b115773d 100644 --- a/src/irdb/ctrlm_irdb_plugin.h +++ b/src/irdb/ctrlm_irdb_plugin.h @@ -88,6 +88,12 @@ bool ctrlm_irdb_close(); bool ctrlm_irdb_initialize(); +// Will return info on all the IRDB vendors currently installed, could be multiple vendors returned +bool ctrlm_irdb_get_supported_vendor_info(std::vector &info); + +// Tell the IRDB plugin which vendor(s) the RCU supports +bool ctrlm_irdb_set_preferred_vendor(const ctrlm_irdb_vendor_info_t &vendor); + bool ctrlm_irdb_get_vendor_info(ctrlm_irdb_vendor_info_t &info); bool ctrlm_irdb_get_manufacturers(ctrlm_irdb_manufacturer_list_t &manufacturers, ctrlm_irdb_dev_type_t type, const std::string &prefix); diff --git a/src/irdb/ctrlm_irdb_stub.cpp b/src/irdb/ctrlm_irdb_stub.cpp index 898cb4f0..8161e45c 100644 --- a/src/irdb/ctrlm_irdb_stub.cpp +++ b/src/irdb/ctrlm_irdb_stub.cpp @@ -46,6 +46,16 @@ bool STUB_ctrlm_irdb_get_vendor_info(ctrlm_irdb_vendor_info_t &info) XLOGD_ERROR("not implemented"); return(false); } +bool STUB_ctrlm_irdb_get_supported_vendor_info(std::vector &info) +{ + XLOGD_ERROR("not implemented"); + return(false); +} +bool STUB_ctrlm_irdb_set_preferred_vendor(const ctrlm_irdb_vendor_info_t &info) +{ + XLOGD_ERROR("not implemented"); + return(false); +} bool STUB_ctrlm_irdb_get_manufacturers(ctrlm_irdb_manufacturer_list_t &manufacturers, ctrlm_irdb_dev_type_t type, const std::string &prefix) { XLOGD_ERROR("not implemented"); diff --git a/src/irdb/ctrlm_irdb_stub.h b/src/irdb/ctrlm_irdb_stub.h index 477ec9f3..a03f6f83 100644 --- a/src/irdb/ctrlm_irdb_stub.h +++ b/src/irdb/ctrlm_irdb_stub.h @@ -32,6 +32,10 @@ bool STUB_ctrlm_irdb_initialize(); bool STUB_ctrlm_irdb_get_vendor_info(ctrlm_irdb_vendor_info_t &info); +bool STUB_ctrlm_irdb_get_supported_vendor_info(std::vector &info); + +bool STUB_ctrlm_irdb_set_preferred_vendor(const ctrlm_irdb_vendor_info_t &info); + bool STUB_ctrlm_irdb_get_manufacturers(ctrlm_irdb_manufacturer_list_t &manufacturers, ctrlm_irdb_dev_type_t type, const std::string &prefix); bool STUB_ctrlm_irdb_get_models(ctrlm_irdb_model_list_t &models, ctrlm_irdb_dev_type_t type, const std::string &manufacturer, const std::string &prefix); diff --git a/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp b/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp index 84351718..48b7c29a 100644 --- a/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp +++ b/src/irdb/ipc/ctrlm_irdb_ipc_iarm_thunder.cpp @@ -390,7 +390,6 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::program_ir_codes(void *arg) { bool success = false; ctrlm_irdb_dev_type_t dev_type = CTRLM_IRDB_DEV_TYPE_INVALID; ctrlm_ir_device_type_t ir_dev_type = CTRLM_IR_DEVICE_UNKNOWN; - int network_type_val = CTRLM_NETWORK_TYPE_INVALID; int remote_id = 0; std::string av_dev_str, code; json_config conf; @@ -406,10 +405,6 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::program_ir_codes(void *arg) { return(IARM_RESULT_INVALID_PARAM); } - if(!conf.config_value_get(NET_TYPE, network_type_val)) { - XLOGD_INFO("Missing %s parameter - defaulting to all networks", NET_TYPE); - } - if(!conf.config_value_get(AV_DEV_TYPE, av_dev_str)) { XLOGD_ERROR("Missing %s parameter", AV_DEV_TYPE); return(IARM_RESULT_INVALID_PARAM); @@ -425,9 +420,7 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::program_ir_codes(void *arg) { return(IARM_RESULT_INVALID_PARAM); } - ctrlm_network_id_t network_id = (network_type_val == CTRLM_NETWORK_TYPE_INVALID) ? - CTRLM_MAIN_NETWORK_ID_ALL : - ctrlm_network_id_get(static_cast(network_type_val)); + ctrlm_network_id_t network_id = CTRLM_MAIN_NETWORK_ID_ALL; ctrlm_controller_id_t controller_id = static_cast(remote_id); if(!ctrlm_irdb_dev_type_is_valid(av_dev_str, ir_dev_type)) { @@ -460,7 +453,6 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::clear_ir_codes(void *arg) { json_t *payload = NULL; ctrlm_irdb_interface_t *irdb = ctrlm_main_irdb_get(); bool success = false; - int network_type_val = CTRLM_NETWORK_TYPE_INVALID; int remote_id = 0; json_config conf; @@ -475,18 +467,12 @@ IARM_Result_t ctrlm_irdb_ipc_iarm_thunder_t::clear_ir_codes(void *arg) { return(IARM_RESULT_INVALID_PARAM); } - if(!conf.config_value_get(NET_TYPE, network_type_val)) { - XLOGD_INFO("Missing %s parameter - defaulting to all networks", NET_TYPE); - } - if(!conf.config_value_get(REMOTE_ID, remote_id)){ XLOGD_ERROR("Missing %s parameter", REMOTE_ID); return(IARM_RESULT_INVALID_PARAM); } - ctrlm_network_id_t network_id = (network_type_val == CTRLM_NETWORK_TYPE_INVALID) ? - CTRLM_MAIN_NETWORK_ID_ALL : - ctrlm_network_id_get(static_cast(network_type_val)); + ctrlm_network_id_t network_id = CTRLM_MAIN_NETWORK_ID_ALL; ctrlm_controller_id_t controller_id = static_cast(remote_id); if(irdb) { diff --git a/src/rf4ce/ctrlm_rf4ce_controller.cpp b/src/rf4ce/ctrlm_rf4ce_controller.cpp index 6dd7387d..960b3fd2 100644 --- a/src/rf4ce/ctrlm_rf4ce_controller.cpp +++ b/src/rf4ce/ctrlm_rf4ce_controller.cpp @@ -2756,6 +2756,12 @@ void ctrlm_obj_controller_rf4ce_t::controller_product_name_updated(const ctrlm_r void ctrlm_obj_controller_rf4ce_t::controller_irdb_status_updated(const ctrlm_rf4ce_controller_irdb_status_t& status) { if(controller_type_ != RF4CE_CONTROLLER_TYPE_XR19) { obj_network_rf4ce_->target_irdb_status_set(*controller_irdb_status_); + if(controller_irdb_status_->is_flag_set(ctrlm_rf4ce_controller_irdb_status_t::flag::IR_DB_CODE_TV)) { + irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_TV, controller_irdb_status_->get_tv_code_str()); + } + if(controller_irdb_status_->is_flag_set(ctrlm_rf4ce_controller_irdb_status_t::flag::IR_DB_CODE_AVR)) { + irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_AVR, controller_irdb_status_->get_avr_code_str()); + } } } @@ -3261,7 +3267,23 @@ bool ctrlm_obj_controller_rf4ce_t::init_uinput_writer() { } std::string uinput_name = product_name_get() + " " + std::to_string(controller_id_get()); - ret = uinput_writer_->init(uinput_name, version_hardware_->get_manufacturer(), version_hardware_->get_model()); + uint32_t vendor = 0x293c; + uint32_t product = 0; + uint32_t manufacturer = version_hardware_->get_manufacturer(); + uint32_t model = version_hardware_->get_model(); + uint32_t revision = version_hardware_->get_revision(); + uint32_t lot = version_hardware_->get_lot(); + + if (manufacturer > 0xF || model > 0xF || revision > 0xF || lot > 0xF) { + XLOGD_WARN("Controller <%s><%d> hardware revision fields exceed 4-bit range", ctrlm_rf4ce_controller_type_str(controller_type_), controller_id_get()); + } + + product |= ((manufacturer & 0xF) << 12); + product |= ((model & 0xF) << 8); + product |= ((revision & 0xF) << 4); + product |= (lot & 0xF); + + ret = uinput_writer_->init(uinput_name, vendor, product); if (!ret) { XLOGD_ERROR("Controller <%s><%d> failed to initialize a uinput device", ctrlm_rf4ce_controller_type_str(controller_type_), controller_id_get()); return ret; diff --git a/src/rf4ce/ctrlm_rf4ce_device_update.cpp b/src/rf4ce/ctrlm_rf4ce_device_update.cpp index 81391246..c2d232eb 100644 --- a/src/rf4ce/ctrlm_rf4ce_device_update.cpp +++ b/src/rf4ce/ctrlm_rf4ce_device_update.cpp @@ -549,6 +549,14 @@ void ctrlm_obj_controller_rf4ce_t::device_update_image_download_complete(ctrlm_t } } + ctrlm_main_queue_msg_header_t *msg = (ctrlm_main_queue_msg_header_t *)g_malloc(sizeof(ctrlm_main_queue_msg_header_t)); + if(msg == NULL) { + XLOGD_ERROR("Out of memory"); + } else { + msg->type = CTRLM_MAIN_QUEUE_MSG_TYPE_EXPORT_CONTROLLER_LIST; + ctrlm_main_queue_msg_push((gpointer)msg); + } + print_remote_firmware_debug_info(RF4CE_PRINT_FIRMWARE_LOG_IMAGE_DOWNLOAD_COMPLETE, log_string); if(download_in_progress_) { // End the download session since the controller has finished loading the image diff --git a/src/rf4ce/ctrlm_rf4ce_network.cpp b/src/rf4ce/ctrlm_rf4ce_network.cpp index f01c52d5..2caa75c2 100644 --- a/src/rf4ce/ctrlm_rf4ce_network.cpp +++ b/src/rf4ce/ctrlm_rf4ce_network.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "ctrlm.h" #include "ctrlm_log.h" @@ -411,8 +412,37 @@ ctrlm_hal_result_t ctrlm_obj_network_rf4ce_t::hal_init_request(GThread *ctrlm_ma // Block until initialization is complete or a timeout occurs XLOGD_INFO("Waiting for %s initialization...", name_get()); - sem_wait(&semaphore_); - sem_destroy(&semaphore_); + struct timespec timeout; + + int sem_result = 0; + errno = 0; + int rc = clock_gettime(CLOCK_MONOTONIC, &timeout); + if(rc != 0) { + int errsv = errno; + // If we fail to get the current time, we should still wait on the semaphore, but we will wait indefinitely instead of timing out + XLOGD_ERROR("Failed to get current time <%s>. wait indefinitely", strerror(errsv)); + errno = 0; + sem_result = sem_wait(&semaphore_); + } else { + timeout.tv_sec += 60; // this operation has been tested to complete in about 6 seconds in worst case scenario (set the timeout to 10x) + + errno = 0; + sem_result = sem_clockwait(&semaphore_, CLOCK_MONOTONIC, &timeout); + } + + if(sem_result == -1) { + if(errno == ETIMEDOUT) { + XLOGD_ERROR("Timeout waiting for %s initialization", name_get()); + } else if(errno == EINTR) { + XLOGD_ERROR("Interrupted while waiting for %s initialization", name_get()); + } else { + int errsv = errno; + XLOGD_ERROR("Error waiting for %s initialization <%s>", name_get(), strerror(errsv)); + } + init_result_ = CTRLM_HAL_RESULT_ERROR; + } else { + sem_destroy(&semaphore_); + } ready_ = (CTRLM_HAL_RESULT_SUCCESS == init_result_); @@ -1179,7 +1209,7 @@ void ctrlm_obj_network_rf4ce_t::controllers_load() { ctrlm_controller_id_t ctrlm_obj_network_rf4ce_t::controller_id_assign(void) { // Get the next available controller id - for(ctrlm_controller_id_t index = 1; index < 255; index++) { + for(ctrlm_controller_id_t index = RF4CE_RCU_ID_RANGE_MIN; index < RF4CE_RCU_ID_RANGE_MAX; index++) { if(!controller_exists(index)) { XLOGD_INFO("controller id %u", index); return(index); @@ -1445,6 +1475,13 @@ void ctrlm_obj_network_rf4ce_t::factory_reset(void) { // Delete control manager persistent data // TODO + ctrlm_main_queue_msg_header_t *msg = (ctrlm_main_queue_msg_header_t *)g_malloc(sizeof(ctrlm_main_queue_msg_header_t)); + if(msg == NULL) { + XLOGD_ERROR("Out of memory"); + } else { + msg->type = CTRLM_MAIN_QUEUE_MSG_TYPE_EXPORT_CONTROLLER_LIST; + ctrlm_main_queue_msg_push((gpointer)msg); + } } bool ctrlm_obj_network_rf4ce_t::controller_exists(ctrlm_controller_id_t controller_id) { @@ -3660,6 +3697,11 @@ void ctrlm_obj_network_rf4ce_t::ind_process_voice_session_request(void *data, in device_type = CTRLM_VOICE_DEVICE_FF; } + if(device_type == CTRLM_VOICE_DEVICE_PTT) { + // Send voice key down event since the session was requested + process_event_key(dqm->controller_id, CTRLM_KEY_STATUS_DOWN, CTRLM_KEY_CODE_PUSH_TO_TALK); + } + bool command_status = (controller_type == RF4CE_CONTROLLER_TYPE_XR11 || controller_type == RF4CE_CONTROLLER_TYPE_XR19) ? true : false; @@ -3746,94 +3788,35 @@ void ctrlm_obj_network_rf4ce_t::ind_process_voice_session_request(void *data, in XLOGD_INFO("processing session request - type <%s> voice format <%s>", ctrlm_voice_device_str(device_type), ctrlm_voice_format_str(voice_format)); } - std::string controller_name = controllers_[dqm->controller_id]->product_name_get(); - ctrlm_hal_rf4ce_cfm_data_t cb_confirm_rf4ce = NULL; - void * cb_confirm_param = NULL; + std::string controller_name = controllers_[dqm->controller_id]->product_name_get(); + void * cb_confirm_param = NULL; ctrlm_voice_session_rsp_confirm_t cb_confirm_voice_obj = NULL; + ctrlm_voice_start_audio_params_t start_audio_params; + start_audio_params.m_controller_id = dqm->controller_id; + start_audio_params.m_use_stream_params = use_stream_params; + start_audio_params.m_offset = offset; + start_audio_params.m_started = false; + start_audio_params.m_timestamp = dqm->timestamp; + start_audio_params.m_device_type = device_type; + auto audio_start_cb = std::bind(&ctrlm_obj_network_rf4ce_t::start_controller_audio_streaming, this, std::placeholders::_1); + session = ctrlm_get_voice_obj()->voice_session_req(network_id_get(), dqm->controller_id, device_type, voice_format, use_stream_params ? &stream_params : NULL, controller_name.c_str(), sw_version.to_string().c_str(), hw_version.to_string().c_str(), (((double)battery_status.get_voltage_loaded()) * 4.0 / 255), command_status, - &dqm->timestamp, &cb_confirm_voice_obj, &cb_confirm_param); - if(session == VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE) { - if(controllers_[dqm->controller_id]->get_capabilities().has_capability(ctrlm_controller_capabilities_t::capability::PAR)) { - session = VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE; - } else { - session = VOICE_SESSION_RESPONSE_AVAILABLE; - } - } - if(session == VOICE_SESSION_RESPONSE_AVAILABLE) { - session = VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK; - } + &dqm->timestamp, &cb_confirm_voice_obj, &cb_confirm_param, + false, false, audio_start_cb, &start_audio_params); - XLOGD_INFO("Voice Session Response Status <%#x>", session); - - // Send the response back to the HAL device - guchar response[5]; - guchar response_len = 2; - - response[0] = MSO_VOICE_CMD_ID_VOICE_SESSION_RESPONSE; - response[1] = session; - if(use_stream_params && session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK) { // Add stream params in the response - response[1] |= 0x80; - response[2] = (guchar) stream_begin_; - response[3] = (offset & 0xFF); - response[4] = (offset >> 8); - response_len = 5; - } - - if(session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE) { - voice_params_par_t params; - ctrlm_get_voice_obj()->voice_params_par_get(¶ms); - - XLOGD_INFO("PAR Voice EOS data bytes timeout <%d> method <%d>", params.par_voice_eos_timeout, params.par_voice_eos_method); - response[2] = params.par_voice_eos_method; - response[3] = (params.par_voice_eos_timeout & 0xFF); - response[4] = (params.par_voice_eos_timeout >> 8); - response_len = 5; + if(!start_audio_params.m_started) { + start_audio_params.m_cb_confirm_voice_obj = cb_confirm_voice_obj; + start_audio_params.m_cb_confirm_param = cb_confirm_param; + start_audio_params.m_status = session; + start_controller_audio_streaming(&start_audio_params); } - ctrlm_timestamp_t hal_timestamp = dqm->timestamp; - - // Determine when to send the response (50 ms after receipt) - if(controller_type_get(dqm->controller_id) == RF4CE_CONTROLLER_TYPE_XR19) { - ctrlm_timestamp_add_ms(&dqm->timestamp, response_idle_time_ff_); - } else { - ctrlm_timestamp_add_ms(&dqm->timestamp, CTRLM_RF4CE_CONST_RESPONSE_IDLE_TIME); - } - - - if(cb_confirm_voice_obj != NULL && (session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK || - session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE)) { // Only confirm response for accepted session so there is only ever one response stored - voice_session_rsp_confirm_ = cb_confirm_voice_obj; - voice_session_rsp_confirm_param_ = cb_confirm_param; - - timestamp_voice_session_request_ = hal_timestamp; - timestamp_voice_first_packet_ = hal_timestamp; - - cb_confirm_rf4ce = ctrlm_network_rf4ce_cfm_voice_session_rsp; - cb_confirm_param = voice_session_rsp_params_.network_id; - - // Store controller id, packet and timestamp for retransmission in case of send error - voice_session_rsp_params_.controller_id = dqm->controller_id; - voice_session_rsp_params_.response_len = response_len; - voice_session_rsp_params_.timestamp_hal = hal_timestamp; - voice_session_rsp_params_.timestamp_begin = dqm->timestamp; - voice_session_rsp_params_.timestamp_end = dqm->timestamp; - voice_session_rsp_params_.retries = 0; - ctrlm_timestamp_add_ms(&voice_session_rsp_params_.timestamp_end, CTRLM_RF4CE_CONST_RESPONSE_WAIT_TIME); - errno_t safec_rc = memcpy_s(&voice_session_rsp_params_.response, sizeof(voice_session_rsp_params_.response),response, response_len); - ERR_CHK(safec_rc); - ctrlm_timestamp_get(&voice_session_rsp_params_.timestamp_rsp_req); - } - - req_data(CTRLM_RF4CE_PROFILE_ID_VOICE, dqm->controller_id, dqm->timestamp, response_len, response, NULL, NULL, false, single_channel_rsp_, cb_confirm_rf4ce, cb_confirm_param); - - XLOGD_INFO("session response delivered"); - if(session != VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK && session != VOICE_SESSION_RESPONSE_AVAILABLE && session != VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE && session != VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE) { voice_session_active_count_--; @@ -3846,17 +3829,6 @@ void ctrlm_obj_network_rf4ce_t::ind_process_voice_session_request(void *data, in if(dqm->status != VOICE_SESSION_RESPONSE_AVAILABLE && dqm->status != VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK) { // Session was aborted XLOGD_INFO("voice session abort"); - - // // Broadcast the event over the iarm bus - // ctrlm_voice_iarm_event_session_abort_t event; - // event.api_revision = CTRLM_VOICE_IARM_BUS_API_REVISION; - // event.network_id = dqm->header.network_id; - // event.network_type = ctrlm_network_type_get(dqm->header.network_id); - // event.controller_id = dqm->controller_id; - // event.session_id = ctrlm_voice_session_id_get_next(); - // event.reason = dqm->reason; - - // ctrlm_voice_iarm_event_session_abort(&event); } } @@ -3891,7 +3863,6 @@ void ctrlm_obj_network_rf4ce_t::cfm_voice_session_rsp(void *data, int size) { } ctrlm_timestamp_t now; ctrlm_timestamp_get(&now); - double loadavg[3] = { -1, -1, -1 }; getloadavg(loadavg, 3); struct sysinfo s_info; @@ -3937,6 +3908,17 @@ void ctrlm_obj_network_rf4ce_t::ind_process_voice_session_stop(void *data, int s g_assert(dqm); g_assert(size == sizeof(ctrlm_main_queue_msg_voice_session_stop_t)); + if(!controller_exists(dqm->controller_id)) { + XLOGD_INFO("invalid controller id (%u)", dqm->controller_id); + return; + } + + ctrlm_rf4ce_controller_type_t controller_type = controllers_[dqm->controller_id]->controller_type_get(); + if(!ctrlm_is_voice_assistant((ctrlm_rcu_controller_type_t) controller_type)) { + // Send voice key up event since the session stop was received + process_event_key(dqm->controller_id, CTRLM_KEY_STATUS_UP, CTRLM_KEY_CODE_PUSH_TO_TALK); + } + // Check adjacent key press if(CTRLM_VOICE_SESSION_END_REASON_OTHER_KEY_PRESSED == dqm->session_end_reason && true == is_key_adjacent(dqm->controller_id, dqm->key_code)) { dqm->session_end_reason = CTRLM_VOICE_SESSION_END_REASON_ADJACENT_KEY_PRESSED; @@ -4448,7 +4430,11 @@ void ctrlm_obj_network_rf4ce_t::req_process_program_ir_codes(void *data, int siz g_assert(dqm); g_assert(size == sizeof(ctrlm_main_queue_msg_program_ir_codes_t)); - if(controller_exists(dqm->controller_id)) { + bool success = false; + + if(!is_managed_by_network(dqm->controller_id)) { + XLOGD_ERROR("controller %d is not managed by the %s network", dqm->controller_id, name_get()); + } else if(controller_exists(dqm->controller_id)) { if(dqm->ir_codes) { XLOGD_INFO("Setting IR Codes on Controller %u", dqm->controller_id); unsigned char status[1] = {IR_RF_DATABASE_STATUS_DB_DOWNLOAD_YES | IR_RF_DATABASE_STATUS_FORCE_DOWNLOAD}; @@ -4458,16 +4444,15 @@ void ctrlm_obj_network_rf4ce_t::req_process_program_ir_codes(void *data, int siz controllers_[dqm->controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_TV, ir_rf_database_.get_tv_ir_code_id()); controllers_[dqm->controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_AVR, ir_rf_database_.get_avr_ir_code_id()); this->ir_rf_database_.store_db(); - if(dqm->success) *dqm->success = true; + success = true; } else { XLOGD_ERROR("Invalid IR Codes"); - if(dqm->success) *dqm->success = false; } } else { XLOGD_ERROR("Controller %u doesn't exist", dqm->controller_id); - if(dqm->success) *dqm->success = false; } + if(dqm->success) dqm->success->push_back(success); // post the semaphore if(dqm->semaphore) { sem_post(dqm->semaphore); @@ -4479,7 +4464,11 @@ void ctrlm_obj_network_rf4ce_t::req_process_ir_clear_codes(void *data, int size) g_assert(dqm); g_assert(size == sizeof(ctrlm_main_queue_msg_ir_clear_t)); - if(controller_exists(dqm->controller_id)) { + bool success = false; + + if(!is_managed_by_network(dqm->controller_id)) { + XLOGD_ERROR("controller %d is not managed by the %s network", dqm->controller_id, name_get()); + } else if(controller_exists(dqm->controller_id)) { XLOGD_INFO("Clearing IR Codes on Controller %u", dqm->controller_id); unsigned char status[1] = {IR_RF_DATABASE_STATUS_DB_DOWNLOAD_YES | IR_RF_DATABASE_STATUS_FORCE_DOWNLOAD}; this->ir_rf_database_.clear_ir_codes(); @@ -4488,12 +4477,12 @@ void ctrlm_obj_network_rf4ce_t::req_process_ir_clear_codes(void *data, int size) controllers_[dqm->controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_TV, "0"); controllers_[dqm->controller_id]->irdb_entry_id_name_set(CTRLM_IRDB_DEV_TYPE_AVR, "0"); controllers_[dqm->controller_id]->rf4ce_rib_set_target(CTRLM_RF4CE_RIB_ATTR_ID_IR_RF_DATABASE_STATUS, CTRLM_RF4CE_RIB_ATTR_INDEX_GENERAL, CTRLM_RF4CE_RIB_ATTR_LEN_IR_RF_DATABASE_STATUS, status); - if(dqm->success) *dqm->success = true; + success = true; } else { XLOGD_ERROR("Controller %u doesn't exist", dqm->controller_id); - if(dqm->success) *dqm->success = false; } + if(dqm->success) dqm->success->push_back(success); // post the semaphore if(dqm->semaphore) { sem_post(dqm->semaphore); @@ -5072,3 +5061,121 @@ void ctrlm_obj_network_rf4ce_t::controller_init_uinput(ctrlm_controller_id_t con return; } } + +void ctrlm_obj_network_rf4ce_t::start_controller_audio_streaming(ctrlm_voice_start_audio_params_t *params) { + THREAD_ID_VALIDATE(); + params->m_started = false; + ctrlm_controller_id_t controller_id = params->m_controller_id; + ctrlm_voice_device_t device_type = params->m_device_type; + + if(!ready_) { + XLOGD_FATAL("Network is not ready!"); + return; + } + + if(!controller_exists(controller_id)) { + XLOGD_WARN("Controller %u doesn't exist.", controller_id); + return; + } + + ctrlm_voice_session_response_status_t session = params->m_status; + + if(session == VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE) { + if(controllers_[controller_id]->get_capabilities().has_capability(ctrlm_controller_capabilities_t::capability::PAR)) { + session = VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE; + } else { + session = VOICE_SESSION_RESPONSE_AVAILABLE; + } + } + if(session == VOICE_SESSION_RESPONSE_AVAILABLE) { + session = VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK; + } + + XLOGD_INFO("Voice Session Response Status <%#x>", session); + + // Send the response back to the HAL device + guchar response[5]; + guchar response_len = 2; + gint16 offset = params->m_offset; + + response[0] = MSO_VOICE_CMD_ID_VOICE_SESSION_RESPONSE; + response[1] = session; + if(params->m_use_stream_params && session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK) { // Add stream params in the response + response[1] |= 0x80; + response[2] = (guchar) stream_begin_; + response[3] = (offset & 0xFF); + response[4] = (offset >> 8); + response_len = 5; + } + + if(session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE) { + voice_params_par_t voice_params; + ctrlm_get_voice_obj()->voice_params_par_get(&voice_params); + + XLOGD_INFO("PAR Voice EOS data bytes timeout <%d> method <%d>", voice_params.par_voice_eos_timeout, voice_params.par_voice_eos_method); + response[2] = voice_params.par_voice_eos_method; + response[3] = (voice_params.par_voice_eos_timeout & 0xFF); + response[4] = (voice_params.par_voice_eos_timeout >> 8); + response_len = 5; + } + + ctrlm_timestamp_t hal_timestamp = params->m_timestamp; + + // Determine when to send the response (50 ms after receipt) + if(controller_type_get(controller_id) == RF4CE_CONTROLLER_TYPE_XR19) { + ctrlm_timestamp_add_ms(¶ms->m_timestamp, response_idle_time_ff_); + } else { + ctrlm_timestamp_add_ms(¶ms->m_timestamp, CTRLM_RF4CE_CONST_RESPONSE_IDLE_TIME); + } + + ctrlm_hal_rf4ce_cfm_data_t cb_confirm_rf4ce = NULL; + + if(params->m_cb_confirm_voice_obj != NULL && (session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK || + session == VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE)) { // Only confirm response for accepted session so there is only ever one response stored + voice_session_rsp_confirm_ = params->m_cb_confirm_voice_obj; + voice_session_rsp_confirm_param_ = params->m_cb_confirm_param; + + timestamp_voice_session_request_ = hal_timestamp; + timestamp_voice_first_packet_ = hal_timestamp; + + cb_confirm_rf4ce = ctrlm_network_rf4ce_cfm_voice_session_rsp; + params->m_cb_confirm_param = voice_session_rsp_params_.network_id; + + // Store controller id, packet and timestamp for retransmission in case of send error + voice_session_rsp_params_.controller_id = controller_id; + voice_session_rsp_params_.response_len = response_len; + voice_session_rsp_params_.timestamp_hal = hal_timestamp; + voice_session_rsp_params_.timestamp_begin = params->m_timestamp; + voice_session_rsp_params_.timestamp_end = params->m_timestamp; + voice_session_rsp_params_.retries = 0; + ctrlm_timestamp_add_ms(&voice_session_rsp_params_.timestamp_end, CTRLM_RF4CE_CONST_RESPONSE_WAIT_TIME); + errno_t safec_rc = memcpy_s(&voice_session_rsp_params_.response, sizeof(voice_session_rsp_params_.response),response, response_len); + ERR_CHK(safec_rc); + ctrlm_timestamp_get(&voice_session_rsp_params_.timestamp_rsp_req); + } + + req_data(CTRLM_RF4CE_PROFILE_ID_VOICE, controller_id, params->m_timestamp, response_len, response, NULL, NULL, false, single_channel_rsp_, cb_confirm_rf4ce, params->m_cb_confirm_param); + + XLOGD_INFO("session response delivered"); + + if(session != VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK && session != VOICE_SESSION_RESPONSE_AVAILABLE && + session != VOICE_SESSION_RESPONSE_AVAILABLE_SKIP_CHAN_CHECK_PAR_VOICE && session != VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE) { + voice_session_active_count_--; + if(voice_session_active_count_ == 0) { // Re-enable frequency agility if the no other active RF4CE voice sessions + ctrlm_hal_network_property_frequency_agility_t property; + property.state = CTRLM_HAL_FREQUENCY_AGILITY_ENABLE; + ctrlm_network_property_set(network_id_get(), CTRLM_HAL_NETWORK_PROPERTY_FREQUENCY_AGILITY, (void *)&property, sizeof(property)); + } + + if(device_type == CTRLM_VOICE_DEVICE_PTT) { + // Send voice key up event since the session was not accepted + process_event_key(controller_id, CTRLM_KEY_STATUS_UP, CTRLM_KEY_CODE_PUSH_TO_TALK); + } + } + + params->m_started = true; +} + +bool ctrlm_obj_network_rf4ce_t::is_managed_by_network(ctrlm_controller_id_t id) { + return (id >= RF4CE_RCU_ID_RANGE_MIN && id < RF4CE_RCU_ID_RANGE_MAX); +} diff --git a/src/rf4ce/ctrlm_rf4ce_network.h b/src/rf4ce/ctrlm_rf4ce_network.h index 1e4ce251..10cc771a 100644 --- a/src/rf4ce/ctrlm_rf4ce_network.h +++ b/src/rf4ce/ctrlm_rf4ce_network.h @@ -67,6 +67,9 @@ #define IR_RF_DATABASE_STATUS_DB_DOWNLOAD_YES (0x80) #define IR_RF_DATABASE_STATUS_RESERVED (0x20) +#define RF4CE_RCU_ID_RANGE_MIN (NETWORK_ID_BASE_RF4CE) +#define RF4CE_RCU_ID_RANGE_MAX (RF4CE_RCU_ID_RANGE_MIN + CTRLM_MAIN_MAX_BOUND_CONTROLLERS) + typedef enum { CTRLM_RF4CE_DEVICE_TYPE_STB = 0x09, CTRLM_RF4CE_DEVICE_TYPE_AUTOBIND = 0xD0, @@ -462,8 +465,11 @@ class ctrlm_obj_network_rf4ce_t : public ctrlm_obj_network_t virtual std::vector get_controller_obj_list() const; void rcu_timeout_key_release(void *data, int data_size); + virtual void start_controller_audio_streaming(ctrlm_voice_start_audio_params_t *params); + protected: virtual gboolean key_event_hook(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, ctrlm_key_status_t key_status, ctrlm_key_code_t key_code); + virtual bool is_managed_by_network(ctrlm_controller_id_t id); private: ctrlm_hal_rf4ce_network_main_t hal_api_main_; diff --git a/src/rf4ce/ctrlm_rf4ce_pairing.cpp b/src/rf4ce/ctrlm_rf4ce_pairing.cpp index e3bfa63c..ae08132d 100644 --- a/src/rf4ce/ctrlm_rf4ce_pairing.cpp +++ b/src/rf4ce/ctrlm_rf4ce_pairing.cpp @@ -244,7 +244,6 @@ void ctrlm_obj_network_rf4ce_t::ind_process_pair_stb(ctrlm_main_queue_msg_rf4ce_ user_string[CTRLM_HAL_RF4CE_USER_STRING_SIZE - 1] = '\0'; } controller_user_string_set(params.controller_id, (guchar*)user_string); - controller_init_uinput(params.controller_id); } controller_autobind_in_progress_set(params.controller_id, false); controller_binding_button_in_progress_set(params.controller_id, false); @@ -326,7 +325,6 @@ void ctrlm_obj_network_rf4ce_t::ind_process_pair_autobind(ctrlm_main_queue_msg_r // Create the controller object controller_insert(params.controller_id, dqm->params.src_ieee_addr, true); controller_user_string_set(params.controller_id, dqm->params.org_user_string); - controller_init_uinput(params.controller_id); } controller_autobind_in_progress_set(params.controller_id, true); controller_binding_button_in_progress_set(params.controller_id, false); @@ -423,7 +421,6 @@ void ctrlm_obj_network_rf4ce_t::ind_process_pair_binding_button(ctrlm_main_queue user_string[CTRLM_HAL_RF4CE_USER_STRING_SIZE - 1] = '\0'; } controller_user_string_set(params.controller_id, (guchar*)user_string); - controller_init_uinput(params.controller_id); } controller_autobind_in_progress_set(params.controller_id, false); controller_binding_button_in_progress_set(params.controller_id, true); @@ -506,7 +503,6 @@ void ctrlm_obj_network_rf4ce_t::ind_process_pair_screen_bind(ctrlm_main_queue_ms // Create the controller object controller_insert(params.controller_id, dqm->params.src_ieee_addr, true); controller_user_string_set(params.controller_id, dqm->params.org_user_string); - controller_init_uinput(params.controller_id); } controller_autobind_in_progress_set(params.controller_id, false); controller_binding_button_in_progress_set(params.controller_id, false); @@ -682,6 +678,14 @@ void ctrlm_obj_network_rf4ce_t::ind_process_unpair(void *data, int size) { //ctr controller_unbind(controller_id, CTRLM_UNBIND_REASON_CONTROLLER); params.result = CTRLM_HAL_RESULT_UNPAIR_SUCCESS; + + ctrlm_main_queue_msg_header_t *msg = (ctrlm_main_queue_msg_header_t *)g_malloc(sizeof(ctrlm_main_queue_msg_header_t)); + if(msg == NULL) { + XLOGD_ERROR("Out of memory"); + } else { + msg->type = CTRLM_MAIN_QUEUE_MSG_TYPE_EXPORT_CONTROLLER_LIST; + ctrlm_main_queue_msg_push((gpointer)msg); + } } ctrlm_network_queue_deliver_result_unpair(dqm, params); diff --git a/src/rf4ce/ctrlm_rf4ce_rcu.cpp b/src/rf4ce/ctrlm_rf4ce_rcu.cpp index 767f0ea3..ae352a67 100644 --- a/src/rf4ce/ctrlm_rf4ce_rcu.cpp +++ b/src/rf4ce/ctrlm_rf4ce_rcu.cpp @@ -479,6 +479,13 @@ void ctrlm_obj_network_rf4ce_t::ind_process_data_rcu(ctrlm_main_queue_msg_rf4ce_ function == CTRLM_RCU_FUNCTION_MODE_CLIP_DISCOVERY) { // Unbind the controller since it has effectively unpaired controller_unbind(dqm->controller_id, CTRLM_UNBIND_REASON_CONTROLLER_RESET); + ctrlm_main_queue_msg_header_t *msg = (ctrlm_main_queue_msg_header_t *)g_malloc(sizeof(ctrlm_main_queue_msg_header_t)); + if(msg == NULL) { + XLOGD_ERROR("Out of memory"); + } else { + msg->type = CTRLM_MAIN_QUEUE_MSG_TYPE_EXPORT_CONTROLLER_LIST; + ctrlm_main_queue_msg_push((gpointer)msg); + } } if((function != CTRLM_RCU_FUNCTION_INVALID) && (function != CTRLM_RCU_FUNCTION_INVALID_KEY_COMBO)) { @@ -579,6 +586,14 @@ void ctrlm_obj_network_rf4ce_t::ind_process_data_rcu(ctrlm_main_queue_msg_rf4ce_ controllers_[dqm->controller_id]->rib_configuration_complete(dqm->timestamp, (ctrlm_rf4ce_rib_configuration_complete_status_t)cmd_data[1]); set_rf_pair_state(CTRLM_RF_PAIR_STATE_COMPLETE); iarm_event_rcu_status(); + + ctrlm_main_queue_msg_header_t *msg = (ctrlm_main_queue_msg_header_t *)g_malloc(sizeof(ctrlm_main_queue_msg_header_t)); + if(msg == NULL) { + XLOGD_ERROR("Out of memory"); + } else { + msg->type = CTRLM_MAIN_QUEUE_MSG_TYPE_EXPORT_CONTROLLER_LIST; + ctrlm_main_queue_msg_push((gpointer)msg); + } break; } default: { diff --git a/src/rf4ce/ctrlm_rf4ce_rib.cpp b/src/rf4ce/ctrlm_rf4ce_rib.cpp index 4eaab800..77b86293 100644 --- a/src/rf4ce/ctrlm_rf4ce_rib.cpp +++ b/src/rf4ce/ctrlm_rf4ce_rib.cpp @@ -294,6 +294,7 @@ void ctrlm_obj_controller_rf4ce_t::rf4ce_rib_get(gboolean target, ctrlm_timestam ctrlm_inform_configuration_complete(network_id_get(), controller_id_get(), CTRLM_RCU_CONFIGURATION_RESULT_SUCCESS); obj_network_rf4ce_->set_rf_pair_state(CTRLM_RF_PAIR_STATE_COMPLETE); obj_network_rf4ce_->iarm_event_rcu_status(); + init_uinput_writer(); } } } diff --git a/src/rfc/ctrlm_rfc_attr.cpp b/src/rfc/ctrlm_rfc_attr.cpp index 263d9d6c..f9e996bb 100644 --- a/src/rfc/ctrlm_rfc_attr.cpp +++ b/src/rfc/ctrlm_rfc_attr.cpp @@ -99,9 +99,11 @@ bool ctrlm_rfc_attr_t::check_config_file(const std::string &path) const { bool ret = false; ctrlm_config_t *config = ctrlm_config_t::get_instance(); if(config) { - ret = config->path_exists(this->config_key + std::string(JSON_PATH_SEPERATOR) + path); - if(ret) { - XLOGD_DEBUG("%s exists in the config file", path.c_str()); + if(config->has_local_config()) { + ret = config->path_exists(this->config_key + std::string(JSON_PATH_SEPERATOR) + path); + if(ret) { + XLOGD_WARN("%s exists in the config file", path.c_str()); + } } } else { XLOGD_ERROR("config object is NULL"); diff --git a/src/shared_memory/ctrlm_shared_memory.cpp b/src/shared_memory/ctrlm_shared_memory.cpp index 03249419..b53b6db0 100644 --- a/src/shared_memory/ctrlm_shared_memory.cpp +++ b/src/shared_memory/ctrlm_shared_memory.cpp @@ -54,10 +54,8 @@ typedef struct { char url_ptt[CTRLM_VOICE_SERVER_URL_MAX_LENGTH]; bool url_ff_valid; char url_ff[CTRLM_VOICE_SERVER_URL_MAX_LENGTH]; - #ifdef CTRLM_LOCAL_MIC_TAP bool url_mic_tap_valid; char url_mic_tap[CTRLM_VOICE_SERVER_URL_MAX_LENGTH]; - #endif uint32_t query_string_ptt_count; query_string_pair_t query_string_ptt_pairs[CTRLM_VOICE_QUERY_STRING_MAX_PAIRS]; @@ -313,7 +311,6 @@ void ctrlm_sm_voice_url_ff_write(std::string url) { g_ctrlm_sm.mmap->url_ff_valid = true; } -#ifdef CTRLM_LOCAL_MIC_TAP void ctrlm_sm_voice_url_mic_tap_read(std::string &url) { if(g_ctrlm_sm.mmap == NULL) { XLOGD_ERROR("mmap is invalid"); @@ -334,7 +331,6 @@ void ctrlm_sm_voice_url_mic_tap_write(std::string url) { g_ctrlm_sm.mmap->url_mic_tap[sizeof(g_ctrlm_sm.mmap->url_mic_tap) - 1] = '\0'; g_ctrlm_sm.mmap->url_mic_tap_valid = true; } -#endif void ctrlm_sm_voice_query_string_ptt_count_read(uint8_t &count) { if(g_ctrlm_sm.mmap == NULL) { diff --git a/src/shared_memory/ctrlm_shared_memory.h b/src/shared_memory/ctrlm_shared_memory.h index 9c4e05e9..25479c66 100644 --- a/src/shared_memory/ctrlm_shared_memory.h +++ b/src/shared_memory/ctrlm_shared_memory.h @@ -53,10 +53,8 @@ void ctrlm_sm_voice_url_ptt_write(std::string url); void ctrlm_sm_voice_url_ff_read(std::string &url); void ctrlm_sm_voice_url_ff_write(std::string url); -#ifdef CTRLM_LOCAL_MIC_TAP void ctrlm_sm_voice_url_mic_tap_read(std::string &url); void ctrlm_sm_voice_url_mic_tap_write(std::string url); -#endif void ctrlm_sm_voice_query_string_ptt_count_read(uint8_t &count); void ctrlm_sm_voice_query_string_ptt_count_write(uint8_t count); diff --git a/src/telemetry/ctrlm_telemetry_markers.h b/src/telemetry/ctrlm_telemetry_markers.h index 9c0ba3a3..cb07ec32 100644 --- a/src/telemetry/ctrlm_telemetry_markers.h +++ b/src/telemetry/ctrlm_telemetry_markers.h @@ -93,7 +93,7 @@ // The Voice Session Statistics Marker reports statistics for the voice session(s). The format of the marker is a json array of arrays with each event in the format below: // // [[event1], [event2], [event3], ...] -// [,,,,,,,,,,,,,,,,,,,,] +// [,,,,,,,,,,,,,,,,,,,,,,,,,] // // - Version of the marker format. // - Name of the device that started the session. @@ -111,13 +111,19 @@ // - lost samples. // - decoder failure count. // - sample buffer high watermark. -// - audio stream success/error code. -// - protocol success/error code. -// - server success/error code. +// - audio stream success/error code. +// - session success/error code. +// - server success/error code. // - server message. // - flag to indicate if session was successful. +// - reason why the stream ended (if available). +// - protocol return code. +// - flag to indicate if voice was detected. +// - peak confidence level. +// - peak RMS level. + #define MARKER_VOICE_SESSION_STATS "ctrlm.voice.session.stats" -#define MARKER_VOICE_SESSION_STATS_VERSION "1" +#define MARKER_VOICE_SESSION_STATS_VERSION "3" // End Voice Session Statistics diff --git a/src/thunder/ctrlm_thunder_plugin.cpp b/src/thunder/ctrlm_thunder_plugin.cpp index 3040fae5..ca6afffc 100644 --- a/src/thunder/ctrlm_thunder_plugin.cpp +++ b/src/thunder/ctrlm_thunder_plugin.cpp @@ -261,6 +261,53 @@ bool ctrlm_thunder_plugin_t::call_plugin(std::string method, void *params, void return(ret); } +bool ctrlm_thunder_plugin_t::call_plugin_boolean(std::string method, void *params, bool *response) { + bool ret = false; + auto clientObject = (JSONRPC::LinkType*)this->plugin_client; + JsonObject *jsonParams = (JsonObject *)params; + if(clientObject) { + if(!method.empty() && jsonParams && response) { + Core::JSON::Boolean jsonResponse; + uint32_t thunderRet = clientObject->Invoke(CALL_TIMEOUT, _T(method), *jsonParams, jsonResponse); + if(thunderRet != Core::ERROR_NONE) { + XLOGD_ERROR("Thunder call failed <%s> <%u>", method.c_str(), thunderRet); + } else { + *response = jsonResponse.Value(); + ret = true; + } + } else { + XLOGD_ERROR("Invalid parameters"); + } + } else { + XLOGD_ERROR("Client is NULL"); + } + return(ret); +} + +bool ctrlm_thunder_plugin_t::call_plugin_string(std::string method, void *params, std::string *response) { + bool ret = false; + auto clientObject = (JSONRPC::LinkType*)this->plugin_client; + JsonObject *jsonParams = (JsonObject *)params; + if(clientObject) { + if(!method.empty() && jsonParams && response) { + Core::JSON::String jsonString; + uint32_t thunderRet = clientObject->Invoke(CALL_TIMEOUT, _T(method), *jsonParams, jsonString); + if(thunderRet != Core::ERROR_NONE) { + XLOGD_ERROR("Thunder call failed <%s> <%u>", method.c_str(), thunderRet); + } else { + *response = jsonString.Value(); + ret = true; + } + } else { + XLOGD_ERROR("Invalid parameters"); + } + } else { + XLOGD_ERROR("Client is NULL"); + } + return(ret); +} + + bool ctrlm_thunder_plugin_t::call_controller(std::string method, void *params, void *response) { bool ret = false; if(this->controller) { diff --git a/src/thunder/ctrlm_thunder_plugin.h b/src/thunder/ctrlm_thunder_plugin.h index f1a3944a..9bf6d780 100644 --- a/src/thunder/ctrlm_thunder_plugin.h +++ b/src/thunder/ctrlm_thunder_plugin.h @@ -105,9 +105,8 @@ class ctrlm_thunder_plugin_t { std::string callsign_with_api(); /** - * This functions is used to get a Thunder Plugin property. - * @param method The method in which the user wants to call. - * @param params The WPEFramework JsonObject containing the parameters for the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) + * This function is used to get a Thunder Plugin property. + * @param property The name of the property that the user wants to get * @param response The WPEFramework JsonObject containing the response from the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) * @param retries The number of retries if the call times out. * @return True if the call succeeded, otherwise False. @@ -115,7 +114,7 @@ class ctrlm_thunder_plugin_t { bool property_get(std::string property, void *response, unsigned int retries = 0); /** - * This functions is used to call a Thunder Plugin method. + * This function is used to call a Thunder Plugin method. * @param method The method in which the user wants to call. * @param params The WPEFramework JsonObject containing the parameters for the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) * @param response The WPEFramework JsonObject containing the response from the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) @@ -125,7 +124,25 @@ class ctrlm_thunder_plugin_t { bool call_plugin(std::string method, void *params, void *response, unsigned int retries = 0); /** - * This functions is used to call a Thunder Controller method. + * This function is used to call a Thunder Plugin method. + * @param method The method in which the user wants to call. + * @param params The WPEFramework JsonObject containing the parameters for the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) + * @param response The boolean pointer which will be assigned the response from the call + * @return True if the call succeeded, otherwise False. + */ + bool call_plugin_boolean(std::string method, void *params, bool *response); + + /** + * This function is used to call a Thunder Plugin method. + * @param method The method in which the user wants to call. + * @param params The WPEFramework JsonObject containing the parameters for the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) + * @param response The string pointer which will be assigned containing the response from the call. + * @return True if the call succeeded, otherwise False. + */ + bool call_plugin_string(std::string method, void *params, std::string *response); + + /** + * This function is used to call a Thunder Controller method. * @param method The method in which the user wants to call. * @param params The WPEFramework JsonObject containing the parameters for the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) * @param params The WPEFramework JsonObject containing the response from the call. (We can't include WPEFramework headers in controlMgr .h files as their logging macros clash) @@ -164,4 +181,4 @@ class ctrlm_thunder_plugin_t { }; }; -#endif \ No newline at end of file +#endif diff --git a/src/thunder/ctrlm_thunder_plugin_powermanager.cpp b/src/thunder/ctrlm_thunder_plugin_powermanager.cpp index 70c9c1d5..e84fa983 100755 --- a/src/thunder/ctrlm_thunder_plugin_powermanager.cpp +++ b/src/thunder/ctrlm_thunder_plugin_powermanager.cpp @@ -86,20 +86,18 @@ ctrlm_power_state_t ctrlm_thunder_plugin_powermanager_t::get_power_state() { } -#ifdef NETWORKED_STANDBY_MODE_ENABLED /* root@pioneer-uhd:~# curl --request POST --url http://127.0.0.1:9998/jsonrpc --header 'Content-Type: application/json' --data '{ "jsonrpc": "2.0", "id": 1234567890, "method": "org.rdk.PowerManager.1.getNetworkStandbyMode", "params": {} }' {"jsonrpc":"2.0","id":1234567890,"result":true} */ bool ctrlm_thunder_plugin_powermanager_t::get_networked_standby_mode() { - JsonObject params, response; + JsonObject params; params = {}; bool networked_standby_mode = false; - sem_wait(&this->semaphore); - if(this->call_plugin("getNetworkStandbyMode", (void *)¶ms, (void *)&response)) { - networked_standby_mode = response["result"].Boolean(); + sem_wait(&this->semaphore); + if(this->call_plugin_boolean("getNetworkStandbyMode", (void *)¶ms, &networked_standby_mode)) { XLOGD_DEBUG("networked_standby_mode is %s", networked_standby_mode?"TRUE":"FALSE"); } else { - XLOGD_ERROR("getNetworkedStandbyMode call failed"); + XLOGD_ERROR("getNetworkStandbyMode call failed"); } sem_post(&this->semaphore); @@ -109,22 +107,25 @@ bool ctrlm_thunder_plugin_powermanager_t::get_networked_standby_mode() { /* root@pioneer-uhd:~# curl --request POST --url http://127.0.0.1:9998/jsonrpc --header 'Content-Type: application/json' --data '{ "jsonrpc": "2.0", "id": 1234567890, "method": "org.rdk.PowerManager.1.getLastWakeupReason", "params": {} }' {"jsonrpc":"2.0","id":1234567890,"result":"COLDBOOT"} */ bool ctrlm_thunder_plugin_powermanager_t::get_wakeup_reason_voice() { - JsonObject params, response; + JsonObject params; + std::string response; params = {}; bool wakeup_reason_voice = false; sem_wait(&this->semaphore); - if(this->call_plugin("getLastWakeupReason", (void *)¶ms, (void *)&response)) { - wakeup_reason_voice = (0 == strncmp(response["result"].String().c_str(), "VOICE", 5)); - XLOGD_DEBUG("voice_wakeup is %s", wakeup_reason_voice?"TRUE":"FALSE"); + if(this->call_plugin_string("getLastWakeupReason", (void *)¶ms, &response)) { + if(response == "VOICE") { + wakeup_reason_voice = true; + } } else { XLOGD_ERROR("getLastWakeupReason call failed"); } sem_post(&this->semaphore); + XLOGD_DEBUG("voice_wakeup is %s", wakeup_reason_voice?"TRUE":"FALSE"); + return wakeup_reason_voice; } -#endif void ctrlm_thunder_plugin_powermanager_t::on_power_state_changed(const ctrlm_power_state_t ¤t_state, const ctrlm_power_state_t &new_state) { ctrlm_main_queue_power_state_change_t *msg = (ctrlm_main_queue_power_state_change_t *)g_malloc(sizeof(ctrlm_main_queue_power_state_change_t)); diff --git a/src/thunder/ctrlm_thunder_plugin_powermanager.h b/src/thunder/ctrlm_thunder_plugin_powermanager.h index ab6ae554..9d2c51a2 100755 --- a/src/thunder/ctrlm_thunder_plugin_powermanager.h +++ b/src/thunder/ctrlm_thunder_plugin_powermanager.h @@ -54,7 +54,6 @@ class ctrlm_thunder_plugin_powermanager_t : public Thunder::Plugin::ctrlm_thunde */ ctrlm_power_state_t get_power_state(); - #ifdef NETWORKED_STANDBY_MODE_ENABLED /** * This function is used to get the networked standby mode from PowerManager plugin. * @@ -67,7 +66,6 @@ class ctrlm_thunder_plugin_powermanager_t : public Thunder::Plugin::ctrlm_thunde * */ bool get_wakeup_reason_voice(); - #endif void on_power_state_changed(const ctrlm_power_state_t ¤t_state, const ctrlm_power_state_t &new_state); diff --git a/src/thunder/ctrlm_thunder_powermanager.cpp b/src/thunder/ctrlm_thunder_powermanager.cpp index ddc95499..8b80a4aa 100755 --- a/src/thunder/ctrlm_thunder_powermanager.cpp +++ b/src/thunder/ctrlm_thunder_powermanager.cpp @@ -36,14 +36,13 @@ ctrlm_power_state_t ctrlm_thunder_powermanager_t::get_power_state() { } } -#ifdef NETWORKED_STANDBY_MODE_ENABLED bool ctrlm_thunder_powermanager_t::get_networked_standby_mode() { if(this->plugin == NULL) { XLOGD_ERROR("plugin not yet available"); return false; } else { - return this->plugin->get_networked_standby_mode(networked_standby_mode); + return this->plugin->get_networked_standby_mode(); } } @@ -53,7 +52,6 @@ bool ctrlm_thunder_powermanager_t::get_wakeup_reason_voice() { XLOGD_WARN("plugin not yet available"); return false; } else { - return this->plugin->get_wakeup_reason_voice(wakeup_reason_voice); + return this->plugin->get_wakeup_reason_voice(); } } -#endif diff --git a/src/thunder/ctrlm_thunder_powermanager.h b/src/thunder/ctrlm_thunder_powermanager.h index 25dc45e9..b87ef19e 100755 --- a/src/thunder/ctrlm_thunder_powermanager.h +++ b/src/thunder/ctrlm_thunder_powermanager.h @@ -11,10 +11,8 @@ class ctrlm_thunder_powermanager_t : public ctrlm_powermanager_t { bool is_ready(); ctrlm_power_state_t get_power_state(); - #ifdef NETWORKED_STANDBY_MODE bool get_networked_standby_mode(); bool get_wakeup_reason_voice(); - #endif private: Thunder::PowerManager::ctrlm_thunder_plugin_powermanager_t *plugin; diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index 2491ae9a..12402a12 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -56,14 +56,18 @@ #define ADPCM_COMMAND_ID_MIN (0x20) ///< Minimum bound of command id as defined by XVP Spec. #define ADPCM_COMMAND_ID_MAX (0x3F) ///< Maximum bound of command id as defined by XVP Spec. +#define BEEP_ON_KWD_FILE_VD "/vendor/usr/share/keyword_beep.wav" +#define BEEP_ON_KWD_FILE_MW "/usr/share/keyword_beep.wav" + +#define BEEP_ON_KWD_SAP_VD "file://" BEEP_ON_KWD_FILE_VD +#define BEEP_ON_KWD_SAP_MW "file://" BEEP_ON_KWD_FILE_MW + static void ctrlm_voice_session_response_confirm(bool result, signed long long rsp_time, unsigned int rsp_window, const std::string &err_str, ctrlm_timestamp_t *timestamp, void *user_data); static void ctrlm_voice_data_post_processing_cb(int bytes_sent, void *user_data); static ctrlm_voice_session_group_t voice_device_to_session_group(ctrlm_voice_device_t device_type); -#ifdef BEEP_ON_KWD_ENABLED static void ctrlm_voice_system_audio_player_event_handler(system_audio_player_event_t event, void *user_data); -#endif static xrsr_power_mode_t voice_xrsr_power_map(ctrlm_power_state_t ctrlm_power_state); @@ -91,7 +95,7 @@ ctrlm_voice_t::ctrlm_voice_t() { session->session_active_controller = false; session->state_src = CTRLM_VOICE_STATE_SRC_INVALID; session->state_dst = CTRLM_VOICE_STATE_DST_INVALID; - session->end_reason = CTRLM_VOICE_SESSION_END_REASON_DONE; + session->end_reason_rcu = CTRLM_VOICE_SESSION_END_REASON_DONE; session->audio_pipe[PIPE_READ] = -1; session->audio_pipe[PIPE_WRITE] = -1; @@ -138,15 +142,38 @@ ctrlm_voice_t::ctrlm_voice_t() { this->session_id = 0; this->software_version = "N/A"; this->mask_pii = ctrlm_is_production_build() ? JSON_ARRAY_VAL_BOOL_CTRLM_GLOBAL_MASK_PII_0 : JSON_ARRAY_VAL_BOOL_CTRLM_GLOBAL_MASK_PII_1; + + xrsr_config_t xrsr_config; + if(xrsr_config_get(&xrsr_config)) { + this->local_mic = xrsr_config.local_mic; + this->local_mic_tap = xrsr_config.local_mic_tap; + this->local_mic_disable_via_privacy = this->local_mic; // set if local mic is present for now + } else { + XLOGD_ERROR("xrsr_config_get failed"); + this->local_mic = false; + this->local_mic_tap = false; + this->local_mic_disable_via_privacy = false; + + } + + if(ctrlm_file_exists(BEEP_ON_KWD_FILE_VD)) { + this->beep_on_kwd_file = BEEP_ON_KWD_SAP_VD; + this->beep_on_kwd_supported = true; + } else if(ctrlm_file_exists(BEEP_ON_KWD_FILE_MW)) { + this->beep_on_kwd_file = BEEP_ON_KWD_SAP_MW; + this->beep_on_kwd_supported = true; + } else { + this->beep_on_kwd_file = NULL; + this->beep_on_kwd_supported = false; + } + this->ocsp_verify_stapling = false; this->ocsp_verify_ca = false; this->capture_active = false; this->device_cert.type = CTRLM_VOICE_CERT_TYPE_NONE; this->prefs.server_url_src_ptt = JSON_STR_VALUE_VOICE_URL_SRC_PTT; this->prefs.server_url_src_ff = JSON_STR_VALUE_VOICE_URL_SRC_FF; - #ifdef CTRLM_LOCAL_MIC_TAP this->prefs.server_url_src_mic_tap = JSON_STR_VALUE_VOICE_URL_SRC_MIC_TAP; - #endif #ifdef JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_0 this->url_hostname_pattern_add(JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_0); #endif @@ -159,40 +186,40 @@ ctrlm_voice_t::ctrlm_voice_t() { #ifdef JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_3 this->url_hostname_pattern_add(JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_3); #endif - this->prefs.aspect_ratio = JSON_STR_VALUE_VOICE_ASPECT_RATIO; - this->prefs.guide_language = JSON_STR_VALUE_VOICE_LANGUAGE; - this->prefs.app_id_http = JSON_STR_VALUE_VOICE_APP_ID_HTTP; - this->prefs.app_id_ws = JSON_STR_VALUE_VOICE_APP_ID_WS; - this->prefs.timeout_vrex_connect = JSON_INT_VALUE_VOICE_VREX_REQUEST_TIMEOUT; - this->prefs.timeout_vrex_session = JSON_INT_VALUE_VOICE_VREX_RESPONSE_TIMEOUT; - this->prefs.timeout_stats = JSON_INT_VALUE_VOICE_TIMEOUT_STATS; - this->prefs.timeout_packet_initial = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_INITIAL; - this->prefs.timeout_packet_subsequent = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_SUBSEQUENT; - this->prefs.bitrate_minimum = JSON_INT_VALUE_VOICE_BITRATE_MINIMUM; - this->prefs.time_threshold = JSON_INT_VALUE_VOICE_TIME_THRESHOLD; - this->prefs.utterance_save = ctrlm_is_production_build() ? JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_0 : JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_1; - this->prefs.utterance_use_curtail = JSON_BOOL_VALUE_VOICE_UTTERANCE_USE_CURTAIL; - this->prefs.utterance_file_qty_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_QTY_MAX; - this->prefs.utterance_file_size_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_SIZE_MAX; - this->prefs.utterance_path = JSON_STR_VALUE_VOICE_UTTERANCE_PATH; - this->prefs.utterance_duration_min = JSON_INT_VALUE_VOICE_MINIMUM_DURATION; - this->prefs.ffv_leading_samples = JSON_INT_VALUE_VOICE_FFV_LEADING_SAMPLES; - this->prefs.force_voice_settings = JSON_BOOL_VALUE_VOICE_FORCE_VOICE_SETTINGS; - this->prefs.keyword_sensitivity = JSON_FLOAT_VALUE_VOICE_KEYWORD_DETECT_SENSITIVITY; - this->prefs.vrex_test_flag = JSON_BOOL_VALUE_VOICE_VREX_TEST_FLAG; - this->prefs.vrex_wuw_bypass_success_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG; - this->prefs.vrex_wuw_bypass_failure_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG; - this->prefs.force_toggle_fallback = JSON_BOOL_VALUE_VOICE_FORCE_TOGGLE_FALLBACK; - this->prefs.telemetry_session_stats = JSON_BOOL_VALUE_VOICE_TELEMETRY_SESSION_STATS; - this->prefs.par_voice_enabled = false; - this->prefs.par_voice_eos_method = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_METHOD; - this->prefs.par_voice_eos_timeout = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_TIMEOUT; + this->prefs.aspect_ratio = JSON_STR_VALUE_VOICE_ASPECT_RATIO; + this->prefs.guide_language = JSON_STR_VALUE_VOICE_LANGUAGE; + this->prefs.app_id_http = JSON_STR_VALUE_VOICE_APP_ID_HTTP; + this->prefs.app_id_ws = JSON_STR_VALUE_VOICE_APP_ID_WS; + this->prefs.timeout_vrex_connect = JSON_INT_VALUE_VOICE_VREX_REQUEST_TIMEOUT; + this->prefs.timeout_vrex_session = JSON_INT_VALUE_VOICE_VREX_RESPONSE_TIMEOUT; + this->prefs.timeout_stats = JSON_INT_VALUE_VOICE_TIMEOUT_STATS; + this->prefs.timeout_packet_initial = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_INITIAL; + this->prefs.timeout_packet_subsequent = JSON_INT_VALUE_VOICE_TIMEOUT_PACKET_SUBSEQUENT; + this->prefs.bitrate_minimum = JSON_INT_VALUE_VOICE_BITRATE_MINIMUM; + this->prefs.time_threshold = JSON_INT_VALUE_VOICE_TIME_THRESHOLD; + this->prefs.utterance_save = ctrlm_is_production_build() ? JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_0 : JSON_ARRAY_VAL_BOOL_VOICE_SAVE_LAST_UTTERANCE_1; + this->prefs.utterance_use_curtail = JSON_BOOL_VALUE_VOICE_UTTERANCE_USE_CURTAIL; + this->prefs.utterance_file_qty_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_QTY_MAX; + this->prefs.utterance_file_size_max = JSON_INT_VALUE_VOICE_UTTERANCE_FILE_SIZE_MAX; + this->prefs.utterance_path = JSON_STR_VALUE_VOICE_UTTERANCE_PATH; + this->prefs.utterance_duration_min = JSON_INT_VALUE_VOICE_MINIMUM_DURATION; + this->prefs.ffv_leading_samples = JSON_INT_VALUE_VOICE_FFV_LEADING_SAMPLES; + this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(JSON_STR_VALUE_VOICE_VOICE_ACTIVITY_DETECTION_MODE); + this->prefs.force_voice_settings = JSON_BOOL_VALUE_VOICE_FORCE_VOICE_SETTINGS; + this->prefs.vrex_test_flag = JSON_BOOL_VALUE_VOICE_VREX_TEST_FLAG; + this->prefs.vrex_wuw_bypass_success_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG; + this->prefs.vrex_wuw_bypass_failure_flag = JSON_BOOL_VALUE_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG; + this->prefs.force_toggle_fallback = JSON_BOOL_VALUE_VOICE_FORCE_TOGGLE_FALLBACK; + this->prefs.telemetry_session_stats = JSON_BOOL_VALUE_VOICE_TELEMETRY_SESSION_STATS; + this->prefs.par_voice_enabled = false; + this->prefs.par_voice_eos_method = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_METHOD; + this->prefs.par_voice_eos_timeout = JSON_INT_VALUE_VOICE_PAR_VOICE_EOS_TIMEOUT; this->voice_params_opus_encoder_default(); - this->xrsr_opened = false; - this->voice_ipc = NULL; - this->packet_loss_threshold = JSON_INT_VALUE_VOICE_PACKET_LOSS_THRESHOLD; - this->vsdk_config = NULL; - this->nsm_voice_session = false; + this->xrsr_opened = false; + this->voice_ipc = NULL; + this->packet_loss_threshold = JSON_INT_VALUE_VOICE_PACKET_LOSS_THRESHOLD; + this->vsdk_config = NULL; + this->nsm_voice_session = false; #ifndef TELEMETRY_SUPPORT XLOGD_WARN("telemetry is not enabled"); @@ -203,14 +230,12 @@ ctrlm_voice_t::ctrlm_voice_t() { } #endif - #ifdef NETWORKED_STANDBY_MODE_ENABLED this->prefs.dst_params_standby.connect_check_interval = JSON_INT_VALUE_VOICE_DST_PARAMS_STANDBY_CONNECT_CHECK_INTERVAL; this->prefs.dst_params_standby.timeout_connect = JSON_INT_VALUE_VOICE_DST_PARAMS_STANDBY_TIMEOUT_CONNECT; this->prefs.dst_params_standby.timeout_inactivity = JSON_INT_VALUE_VOICE_DST_PARAMS_STANDBY_TIMEOUT_INACTIVITY; this->prefs.dst_params_standby.timeout_session = JSON_INT_VALUE_VOICE_DST_PARAMS_STANDBY_TIMEOUT_SESSION; this->prefs.dst_params_standby.ipv4_fallback = JSON_BOOL_VALUE_VOICE_DST_PARAMS_STANDBY_IPV4_FALLBACK; this->prefs.dst_params_standby.backoff_delay = JSON_INT_VALUE_VOICE_DST_PARAMS_STANDBY_BACKOFF_DELAY; - #endif this->prefs.dst_params_low_latency.connect_check_interval = JSON_INT_VALUE_VOICE_DST_PARAMS_LOW_LATENCY_CONNECT_CHECK_INTERVAL; this->prefs.dst_params_low_latency.timeout_connect = JSON_INT_VALUE_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_CONNECT; @@ -225,14 +250,10 @@ ctrlm_voice_t::ctrlm_voice_t() { this->device_requires_stb_data[CTRLM_VOICE_DEVICE_PTT] = true; this->device_status[CTRLM_VOICE_DEVICE_FF] = CTRLM_VOICE_DEVICE_STATUS_NONE; this->device_requires_stb_data[CTRLM_VOICE_DEVICE_FF] = true; -#ifdef CTRLM_LOCAL_MIC this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] = CTRLM_VOICE_DEVICE_STATUS_NONE; this->device_requires_stb_data[CTRLM_VOICE_DEVICE_MICROPHONE] = true; -#endif -#ifdef CTRLM_LOCAL_MIC_TAP this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE_TAP] = CTRLM_VOICE_DEVICE_STATUS_NONE; this->device_requires_stb_data[CTRLM_VOICE_DEVICE_MICROPHONE_TAP] = true; -#endif this->device_status[CTRLM_VOICE_DEVICE_INVALID] = CTRLM_VOICE_DEVICE_STATUS_NOT_SUPPORTED; this->device_requires_stb_data[CTRLM_VOICE_DEVICE_INVALID] = true; @@ -241,20 +262,21 @@ ctrlm_voice_t::ctrlm_voice_t() { this->secure_url_required = JSON_BOOL_VALUE_VOICE_REQUIRE_SECURE_URL; XLOGD_TELEMETRY("require i_SAT <%s> i_MTLS <%s> i_secure_url <%s>", this->sat_token_required ? "YES" : "NO", this->mtls_required ? "YES" : "NO", this->secure_url_required ? "YES" : "NO"); + XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); errno_t safec_rc = memset_s(this->sat_token, sizeof(this->sat_token), 0, sizeof(this->sat_token)); ERR_CHK(safec_rc); // These semaphores are used to make sure we have all the data before calling the session begin callback sem_init(&this->vsr_semaphore, 0, 0); - #ifdef BEEP_ON_KWD_ENABLED - this->obj_sap = Thunder::SystemAudioPlayer::ctrlm_thunder_plugin_system_audio_player_t::getInstance(); - this->obj_sap->add_event_handler(ctrlm_voice_system_audio_player_event_handler, this); - this->sap_opened = this->obj_sap->open(SYSTEM_AUDIO_PLAYER_AUDIO_TYPE_WAV, SYSTEM_AUDIO_PLAYER_SOURCE_TYPE_FILE, SYSTEM_AUDIO_PLAYER_PLAY_MODE_SYSTEM); - if(!this->sap_opened) { - XLOGD_WARN("unable to open system audio player"); + if(this->beep_on_kwd_supported) { + this->obj_sap = Thunder::SystemAudioPlayer::ctrlm_thunder_plugin_system_audio_player_t::getInstance(); + this->obj_sap->add_event_handler(ctrlm_voice_system_audio_player_event_handler, this); + this->sap_opened = this->obj_sap->open(SYSTEM_AUDIO_PLAYER_AUDIO_TYPE_WAV, SYSTEM_AUDIO_PLAYER_SOURCE_TYPE_FILE, SYSTEM_AUDIO_PLAYER_PLAY_MODE_SYSTEM); + if(!this->sap_opened) { + XLOGD_WARN("unable to open system audio player"); + } } - #endif // Set audio mode to default ctrlm_voice_audio_settings_t settings = CTRLM_VOICE_AUDIO_SETTINGS_INITIALIZER; @@ -293,14 +315,12 @@ ctrlm_voice_t::~ctrlm_voice_t() { } } - #ifdef BEEP_ON_KWD_ENABLED - if(this->sap_opened) { + if(this->beep_on_kwd_supported && this->sap_opened) { if(!this->obj_sap->close()) { XLOGD_WARN("unable to close system audio player"); } this->sap_opened = false; } - #endif /* Close Voice SDK */ @@ -321,29 +341,12 @@ bool ctrlm_voice_t::vsdk_is_privacy_enabled(void) { return privacy; } -double ctrlm_voice_t::vsdk_keyword_sensitivity_limit_check(double sensitivity) { - float sensitivity_min; - float sensitivity_max; - - if(!xrsr_keyword_sensitivity_limits_get(&sensitivity_min, &sensitivity_max)) { - XLOGD_WARN("Unable to get keyword detector sensitivity limits. Using default sensitivity <%f>.", JSON_FLOAT_VALUE_VOICE_KEYWORD_DETECT_SENSITIVITY); - return(JSON_FLOAT_VALUE_VOICE_KEYWORD_DETECT_SENSITIVITY); - } else { - if(((float)(sensitivity) < sensitivity_min) || ((float)(sensitivity) > sensitivity_max)) { - XLOGD_WARN("Keyword detector sensitivity <%f> outside of range <%f to %f>. Using default sensitivity <%f>.", (float)(sensitivity), sensitivity_min, sensitivity_max, JSON_FLOAT_VALUE_VOICE_KEYWORD_DETECT_SENSITIVITY); - return(JSON_FLOAT_VALUE_VOICE_KEYWORD_DETECT_SENSITIVITY); - } - } - return(sensitivity); -} - void ctrlm_voice_t::voice_sdk_open(json_t *json_obj_vsdk) { if(this->xrsr_opened) { XLOGD_ERROR("already open"); return; } xrsr_route_t routes[1]; - xrsr_keyword_config_t kw_config; xrsr_capture_config_t capture_config = { .delete_files = !this->prefs.utterance_save, .enable = this->prefs.utterance_save, @@ -363,7 +366,6 @@ void ctrlm_voice_t::voice_sdk_open(json_t *json_obj_vsdk) { /* Open Voice SDK */ routes[0].src = XRSR_SRC_INVALID; routes[0].dst_qty = 0; - kw_config.sensitivity = this->prefs.keyword_sensitivity; char host_name[HOST_NAME_MAX]; host_name[0] = '\0'; @@ -379,7 +381,7 @@ void ctrlm_voice_t::voice_sdk_open(json_t *json_obj_vsdk) { ctrlm_power_state_t ctrlm_power_state = ctrlm_main_get_internal_power_state(); xrsr_power_mode_t xrsr_power_mode = voice_xrsr_power_map(ctrlm_power_state); - if(!xrsr_open(host_name, routes, &kw_config, &capture_config, xrsr_power_mode, privacy, this->mask_pii, json_obj_vsdk)) { + if(!xrsr_open(host_name, routes, NULL, &capture_config, xrsr_power_mode, privacy, this->mask_pii, json_obj_vsdk)) { XLOGD_ERROR("Failed to open speech router"); g_assert(0); } @@ -422,11 +424,10 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_BOOL_NAME_VOICE_ENABLE, enabled); conf.config_value_get(JSON_STR_NAME_VOICE_URL_SRC_PTT, this->prefs.server_url_src_ptt); conf.config_value_get(JSON_STR_NAME_VOICE_URL_SRC_FF, this->prefs.server_url_src_ff); - #ifdef CTRLM_LOCAL_MIC_TAP conf.config_value_get(JSON_STR_NAME_VOICE_URL_SRC_MIC_TAP, this->prefs.server_url_src_mic_tap); - #endif conf.config_value_get(JSON_STR_NAME_VOICE_LANGUAGE, this->prefs.guide_language); conf.config_value_get(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min); + if(conf.config_value_get(JSON_BOOL_NAME_VOICE_ENABLE_SAT, this->sat_token_required)) { ctrlm_sm_voice_sat_enable_write(this->sat_token_required); XLOGD_TELEMETRY("require c_SAT <%s>", this->sat_token_required ? "YES" : "NO"); @@ -434,6 +435,12 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_STR_NAME_VOICE_ASPECT_RATIO, this->prefs.aspect_ratio); } + std::string voice_activity_detection_mode; + if(conf.config_value_get(JSON_STR_NAME_VOICE_VOICE_ACTIVITY_DETECTION_MODE, voice_activity_detection_mode)) { + this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(voice_activity_detection_mode); + XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); + } + std::vector obj_server_hosts; if(conf.config_value_get(JSON_ARRAY_NAME_VOICE_SERVER_HOSTS, obj_server_hosts)) { this->url_hostname_patterns(obj_server_hosts); @@ -462,7 +469,6 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_INT_NAME_VOICE_FFV_LEADING_SAMPLES, this->prefs.ffv_leading_samples, 0); conf.config_value_get(JSON_STR_NAME_VOICE_APP_ID_HTTP, this->prefs.app_id_http); conf.config_value_get(JSON_STR_NAME_VOICE_APP_ID_WS, this->prefs.app_id_ws); - conf.config_value_get(JSON_FLOAT_NAME_VOICE_KEYWORD_DETECT_SENSITIVITY, this->prefs.keyword_sensitivity, 0.0, DBL_MAX); conf.config_value_get(JSON_BOOL_NAME_VOICE_VREX_TEST_FLAG, this->prefs.vrex_test_flag); conf.config_value_get(JSON_BOOL_NAME_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG,this->prefs.vrex_wuw_bypass_success_flag); conf.config_value_get(JSON_BOOL_NAME_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG,this->prefs.vrex_wuw_bypass_failure_flag); @@ -470,8 +476,9 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_BOOL_NAME_VOICE_TELEMETRY_SESSION_STATS, this->prefs.telemetry_session_stats); std::string opus_encoder_params_str; - conf.config_value_get(JSON_STR_NAME_VOICE_OPUS_ENCODER_PARAMS, opus_encoder_params_str); - this->voice_params_opus_encoder_validate(opus_encoder_params_str); + if(conf.config_value_get(JSON_STR_NAME_VOICE_OPUS_ENCODER_PARAMS, opus_encoder_params_str)) { + this->voice_params_opus_encoder_validate(opus_encoder_params_str); + } conf.config_value_get(JSON_INT_NAME_VOICE_PAR_VOICE_EOS_METHOD, this->prefs.par_voice_eos_method); conf.config_value_get(JSON_INT_NAME_VOICE_PAR_VOICE_EOS_TIMEOUT, this->prefs.par_voice_eos_timeout); @@ -483,14 +490,12 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * conf.config_value_get(JSON_FLOAT_NAME_VOICE_AUDIO_DUCKING_LEVEL, audio_settings.ducking_level, 0.0, 1.0); conf.config_value_get(JSON_BOOL_NAME_VOICE_AUDIO_DUCKING_BEEP, audio_settings.ducking_beep); - #ifdef NETWORKED_STANDBY_MODE_ENABLED conf.config_value_get(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_standby.connect_check_interval); conf.config_value_get(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_CONNECT, this->prefs.dst_params_standby.timeout_connect); conf.config_value_get(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_INACTIVITY, this->prefs.dst_params_standby.timeout_inactivity); conf.config_value_get(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_SESSION, this->prefs.dst_params_standby.timeout_session); conf.config_value_get(JSON_BOOL_NAME_VOICE_DST_PARAMS_STANDBY_IPV4_FALLBACK, this->prefs.dst_params_standby.ipv4_fallback); conf.config_value_get(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_BACKOFF_DELAY, this->prefs.dst_params_standby.backoff_delay); - #endif conf.config_value_get(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_low_latency.connect_check_interval); conf.config_value_get(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_CONNECT, this->prefs.dst_params_low_latency.timeout_connect); @@ -517,11 +522,9 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * for(int i = CTRLM_VOICE_DEVICE_PTT; i < CTRLM_VOICE_DEVICE_INVALID; i++) { uint8_t voice_device_status = CTRLM_VOICE_DEVICE_STATUS_NONE; ctrlm_db_voice_read_device_status(i, (int *)&voice_device_status); - #ifdef CTRLM_LOCAL_MIC if((i == CTRLM_VOICE_DEVICE_MICROPHONE) && (voice_device_status & CTRLM_VOICE_DEVICE_STATUS_PRIVACY)) { this->voice_privacy_enable(false); } - #endif if((voice_device_status & CTRLM_VOICE_DEVICE_STATUS_LEGACY) && (voice_device_status != CTRLM_VOICE_DEVICE_STATUS_DISABLED)) { // Convert from legacy to current (some value other than DISABLED set) XLOGD_INFO("Converting legacy device status value 0x%02X", voice_device_status); voice_device_status = CTRLM_VOICE_DEVICE_STATUS_NONE; @@ -533,9 +536,7 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * } ctrlm_sm_voice_url_ptt_read(this->prefs.server_url_src_ptt); ctrlm_sm_voice_url_ff_read(this->prefs.server_url_src_ff); - #ifdef CTRLM_LOCAL_MIC_TAP ctrlm_sm_voice_url_mic_tap_read(this->prefs.server_url_src_mic_tap); - #endif ctrlm_sm_voice_sat_enable_read(this->sat_token_required); ctrlm_sm_voice_mtls_enable_read(this->mtls_required); ctrlm_sm_voice_secure_url_required_read(this->secure_url_required); @@ -584,28 +585,17 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * // Update routes this->voice_sdk_update_routes(); - #ifdef CTRLM_LOCAL_MIC - // Read privacy mode state from the DB in case power cycle lost HW GPIO state - if(this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_DISABLED) { - XLOGD_INFO("voice is disabled, skip privacy"); - } else { - bool privacy_enabled = this->voice_is_privacy_enabled(); - if(privacy_enabled != this->vsdk_is_privacy_enabled()) { - privacy_enabled ? this->voice_privacy_enable(false) : this->voice_privacy_disable(false); - } - // Check keyword detector sensitivity value against limits; apply default if out of range. - double sensitivity_set = this->vsdk_keyword_sensitivity_limit_check(this->prefs.keyword_sensitivity); - if(sensitivity_set != this->prefs.keyword_sensitivity) { - xrsr_keyword_config_t kw_config; - kw_config.sensitivity = (float)sensitivity_set; - if(!xrsr_keyword_config_set(&kw_config)) { - XLOGD_ERROR("error updating keyword config"); - } else { - this->prefs.keyword_sensitivity = sensitivity_set; + if(this->local_mic) { + // Read privacy mode state from the DB in case power cycle lost HW GPIO state + if(this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_DISABLED) { + XLOGD_INFO("voice is disabled, skip privacy"); + } else { + bool privacy_enabled = this->voice_is_privacy_enabled(); + if(privacy_enabled != this->vsdk_is_privacy_enabled()) { + privacy_enabled ? this->voice_privacy_enable(false) : this->voice_privacy_disable(false); } } } - #endif // Set init message if read from DB if(!init.empty()) { @@ -726,7 +716,6 @@ bool ctrlm_voice_t::voice_configure(ctrlm_voice_iarm_call_settings_t *settings, ctrlm_sm_voice_url_ff_write(this->prefs.server_url_src_ff); } } - #ifdef CTRLM_LOCAL_MIC_TAP if(settings->available & CTRLM_VOICE_SETTINGS_MIC_TAP_SERVER_URL) { settings->server_url_src_mic_tap[CTRLM_VOICE_SERVER_URL_MAX_LENGTH - 1] = '\0'; XLOGD_INFO("Mic tap URL <%s>", this->mask_pii ? "***" : settings->server_url_src_mic_tap); @@ -736,7 +725,6 @@ bool ctrlm_voice_t::voice_configure(ctrlm_voice_iarm_call_settings_t *settings, ctrlm_sm_voice_url_mic_tap_write(this->prefs.server_url_src_mic_tap); } } - #endif if(update_routes && this->xrsr_opened) { this->voice_sdk_update_routes(); @@ -781,9 +769,7 @@ bool ctrlm_voice_t::voice_configure(json_t *settings, bool db_write) { } } if(conf.config_value_get("urlAll", url)) { - #ifdef CTRLM_LOCAL_MIC_TAP this->prefs.server_url_src_mic_tap = url; - #endif this->prefs.server_url_src_ptt = url; this->prefs.server_url_src_ff = std::move(url); update_routes = true; @@ -796,12 +782,10 @@ bool ctrlm_voice_t::voice_configure(json_t *settings, bool db_write) { this->prefs.server_url_src_ff = std::move(url); update_routes = true; } - #ifdef CTRLM_LOCAL_MIC_TAP if(conf.config_value_get("urlMicTap", url)) { this->prefs.server_url_src_mic_tap = std::move(url); update_routes = true; } - #endif if(conf.config_value_get("prv", prv_enabled)) { this->prefs.par_voice_enabled = prv_enabled; XLOGD_INFO("Press and Release Voice is <%s>", this->prefs.par_voice_enabled ? "ENABLED" : "DISABLED"); @@ -829,45 +813,43 @@ bool ctrlm_voice_t::voice_configure(json_t *settings, bool db_write) { } } } - #ifdef CTRLM_LOCAL_MIC - if(conf.config_object_get("mic", device_config)) { - if(device_config.config_value_get("enable", enable)) { - XLOGD_TELEMETRY("Voice Control MIC is <%s>", enable ? "ENABLED" : "DISABLED"); - #ifdef CTRLM_LOCAL_MIC_DISABLE_VIA_PRIVACY - bool privacy_enabled = this->voice_is_privacy_enabled(); - if(enable) { - if(privacy_enabled) { - this->voice_privacy_disable(true); + if(this->local_mic) { + if(conf.config_object_get("mic", device_config)) { + if(device_config.config_value_get("enable", enable)) { + XLOGD_TELEMETRY("Voice Control MIC is <%s>", enable ? "ENABLED" : "DISABLED"); + if(this->local_mic_disable_via_privacy) { + bool privacy_enabled = this->voice_is_privacy_enabled(); + if(enable) { + if(privacy_enabled) { + this->voice_privacy_disable(true); + } + } else if(!privacy_enabled) { + this->voice_privacy_enable(true); + } + } else { + if(enable) { + this->voice_device_enable(CTRLM_VOICE_DEVICE_MICROPHONE, db_write, &update_routes); + } else { + this->voice_device_disable(CTRLM_VOICE_DEVICE_MICROPHONE, db_write, &update_routes); + } } - } else if(!privacy_enabled) { - this->voice_privacy_enable(true); - } - #else - if(enable) { - this->voice_device_enable(CTRLM_VOICE_DEVICE_MICROPHONE, db_write, &update_routes); - } else { - this->voice_device_disable(CTRLM_VOICE_DEVICE_MICROPHONE, db_write, &update_routes); } - #endif } - } - #endif - if(conf.config_value_get("wwFeedback", enable)) { // This option will enable / disable the Wake Word feedback (typically an audible beep). - XLOGD_INFO("Voice Control kwd feedback is <%s>", enable ? "ENABLED" : "DISABLED"); - this->audio_ducking_beep_enabled = enable; + if(conf.config_value_get("wwFeedback", enable)) { // This option will enable / disable the Wake Word feedback (typically an audible beep). + XLOGD_INFO("Voice Control kwd feedback is <%s>", enable ? "ENABLED" : "DISABLED"); + this->audio_ducking_beep_enabled = enable; - if(db_write) { - ctrlm_db_voice_write_audio_ducking_beep_enable(enable); - } + if(db_write) { + ctrlm_db_voice_write_audio_ducking_beep_enable(enable); + } + } } if(update_routes && this->xrsr_opened) { this->voice_sdk_update_routes(); if(db_write) { ctrlm_sm_voice_url_ptt_write(this->prefs.server_url_src_ptt); ctrlm_sm_voice_url_ff_write(this->prefs.server_url_src_ff); - #ifdef CTRLM_LOCAL_MIC_TAP ctrlm_sm_voice_url_mic_tap_write(this->prefs.server_url_src_mic_tap); - #endif } } } @@ -914,11 +896,7 @@ bool ctrlm_voice_t::voice_status(ctrlm_voice_status_t *status) { */ ctrlm_voice_status_capabilities_t capabilities = { .prv = true, - #ifdef BEEP_ON_KWD_ENABLED - .wwFeedback = true, - #else - .wwFeedback = false, - #endif + .wwFeedback = this->beep_on_kwd_supported, }; if(!this->xrsr_opened) { @@ -928,9 +906,7 @@ bool ctrlm_voice_t::voice_status(ctrlm_voice_status_t *status) { } else { status->urlPtt = this->prefs.server_url_src_ptt; status->urlHf = this->prefs.server_url_src_ff; - #ifdef CTRLM_LOCAL_MIC_TAP status->urlMicTap = this->prefs.server_url_src_mic_tap; - #endif sem_wait(&this->device_status_semaphore); for(int i = CTRLM_VOICE_DEVICE_PTT; i < CTRLM_VOICE_DEVICE_INVALID; i++) { status->status[i] = this->device_status[i]; @@ -1014,12 +990,6 @@ void ctrlm_voice_t::process_xconf(json_t **json_obj_vsdk, bool local_conf) { // CTRLM_TR181_VOICE_PARAMS_AUDIO_DUCKING_BEEP doesn't exist because this is a user configurable setting via configureVoice thunder api - double keyword_sensitivity = 0.0; - result = ctrlm_tr181_real_get(CTRLM_TR181_VOICE_PARAMS_KEYWORD_SENSITIVITY, &keyword_sensitivity); - if(result == CTRLM_TR181_RESULT_SUCCESS) { - this->prefs.keyword_sensitivity = (keyword_sensitivity < 0.0) ? JSON_FLOAT_VALUE_VOICE_KEYWORD_DETECT_SENSITIVITY : keyword_sensitivity; - } - result = ctrlm_tr181_string_get(CTRLM_TR181_VOICE_PARAMS_VSDK_CONFIGURATION, &vsdk_config_str[0], CTRLM_RFC_MAX_PARAM_LEN); if(result == CTRLM_TR181_RESULT_SUCCESS) { json_error_t jerror; @@ -1128,6 +1098,17 @@ void ctrlm_voice_t::voice_params_opus_encoder_default(void) { this->voice_params_opus_samples_per_packet_set(); } +xrsr_stream_voice_activity_mode_t ctrlm_voice_t::voice_activity_detection_mode_to_xrsr(const std::string &mode) { + // Configure voice activity detection parameters based on mode + if(mode == "enabled") { // Voice activity detection will be used but not enforced + return(XRSR_STREAM_VOICE_ACTIVITY_MODE_ENABLED); + } else if(mode == "enforced") { // Voice session will only proceed if voice activity is detected + return(XRSR_STREAM_VOICE_ACTIVITY_MODE_ENFORCED); + } + // Voice activity detection is disabled (or invalid mode) + return(XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED); +} + void ctrlm_voice_t::voice_params_opus_samples_per_packet_set(void) { guchar fr_dur = (this->prefs.opus_encoder_params[3] >> 4) & 0xF; switch(fr_dur) { @@ -1347,7 +1328,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net ctrlm_voice_device_t device_type, ctrlm_voice_format_t format, voice_session_req_stream_params *stream_params, const char *controller_name, const char *sw_version, const char *hw_version, double voltage, bool command_status, - ctrlm_timestamp_t *timestamp, ctrlm_voice_session_rsp_confirm_t *cb_confirm, void **cb_confirm_param, bool use_external_data_pipe, bool press_and_hold, const char *l_transcription_in, const char *audio_file_in, const uuid_t *uuid, bool low_latency, bool low_cpu_util, int audio_fd) { + ctrlm_timestamp_t *timestamp, ctrlm_voice_session_rsp_confirm_t *cb_confirm, void **cb_confirm_param, bool use_external_data_pipe, bool press_and_hold, std::function cb_start_audio, ctrlm_voice_start_audio_params_t *cb_audio_start_params, const char *l_transcription_in, const char *audio_file_in, const uuid_t *uuid, bool low_latency, bool low_cpu_util, int audio_fd) { ctrlm_voice_session_t *session = &this->voice_session[voice_device_to_session_group(device_type)]; @@ -1379,6 +1360,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net // Cancel current speech router session XLOGD_INFO("Waiting on the results from previous session, aborting this and continuing.."); + pre_session_terminate(cb_start_audio, cb_audio_start_params, cb_confirm, cb_confirm_param); xrsr_session_terminate(voice_device_to_xrsr(session->voice_device)); // Synchronous - this will take a bit of time. Might need to revisit this down the road. } bool request_new_session = true; @@ -1390,6 +1372,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_new_session = false; } else { // Cancel current speech router session XLOGD_WARN("Session in progress with same controller - src <%s> dst <%s>, aborting this and continuing..", ctrlm_voice_state_src_str(session->state_src), ctrlm_voice_state_dst_str(session->state_dst)); + pre_session_terminate(cb_start_audio, cb_audio_start_params, cb_confirm, cb_confirm_param); xrsr_session_terminate(voice_device_to_xrsr(session->voice_device)); // Synchronous - this will take a bit of time. Might need to revisit this down the road. } } else { // session in progress with different controller @@ -1773,16 +1756,16 @@ void ctrlm_voice_t::voice_session_data_post_processing(int bytes_sent, const cha float rx_rate = (elapsed == 0) ? 0 : (session->audio_sent_samples * 2 * 8) / elapsed; session->timeout_packet_tag = g_timeout_add(timeout, ctrlm_voice_packet_timeout, NULL); - XLOGD_DEBUG("Audio %s bytes <%lu> samples <%lu> rate <%6.2f kbps> timeout <%lu ms>", action, session->audio_sent_bytes, session->audio_sent_samples, rx_rate, timeout); + XLOGD_AUTOMATION_DEBUG("Audio %s bytes <%lu> samples <%lu> rate <%6.2f kbps> timeout <%lu ms>", action, session->audio_sent_bytes, session->audio_sent_samples, rx_rate, timeout); } else { #ifdef VOICE_BUFFER_STATS if(voice_buffer_warning_triggered) { - XLOGD_DEBUG("Audio %s bytes <%lu> samples <%lu> pkt cnt <%3u> elapsed <%8llu ms> lag <%8lld ms> (%4.2f packets)", action, session->audio_sent_bytes, session->audio_sent_samples, packets_total, session_time / 1000, session_delta / 1000, (((float)session_delta) / this->voice_packet_interval)); + XLOGD_AUTOMATION_DEBUG("Audio %s bytes <%lu> samples <%lu> pkt cnt <%3u> elapsed <%8llu ms> lag <%8lld ms> (%4.2f packets)", action, session->audio_sent_bytes, session->audio_sent_samples, packets_total, session_time / 1000, session_delta / 1000, (((float)session_delta) / this->voice_packet_interval)); } else { - XLOGD_DEBUG("Audio %s bytes <%lu> samples <%lu>", action, session->audio_sent_bytes, session->audio_sent_samples); + XLOGD_AUTOMATION_DEBUG("Audio %s bytes <%lu> samples <%lu>", action, session->audio_sent_bytes, session->audio_sent_samples); } #else - XLOGD_DEBUG("Audio %s bytes <%lu> samples <%lu>", action, session->audio_sent_bytes, session->audio_sent_samples); + XLOGD_AUTOMATION_DEBUG("Audio %s bytes <%lu> samples <%lu>", action, session->audio_sent_bytes, session->audio_sent_samples); #endif } } @@ -1909,7 +1892,7 @@ void ctrlm_voice_t::voice_session_end(ctrlm_voice_session_t *session, ctrlm_voic return; } - session->end_reason = reason; + session->end_reason_rcu = reason; if(timestamp != NULL) { session->session_timing.ctrl_stop = *timestamp; @@ -2062,10 +2045,10 @@ void ctrlm_voice_t::voice_session_timeout() { signed long long elapsed = rdkx_timestamp_subtract_ms(session->session_timing.ctrl_audio_rxd_first, timestamp); float rx_rate = (elapsed == 0) ? 0 : (session->audio_sent_samples * 2 * 8) / elapsed; - XLOGD_INFO("elapsed time <%llu> ms rx samples <%u> rate <%6.1f> kbps", elapsed, session->audio_sent_samples, rx_rate); + XLOGD_AUTOMATION_INFO("elapsed time <%llu> ms rx samples <%u> rate <%6.1f> kbps", elapsed, session->audio_sent_samples, rx_rate); reason = CTRLM_VOICE_SESSION_END_REASON_MINIMUM_QOS; } - XLOGD_INFO("%s", ctrlm_voice_session_end_reason_str(reason)); + XLOGD_AUTOMATION_INFO("%s", ctrlm_voice_session_end_reason_str(reason)); this->voice_session_end(session, reason); } @@ -2289,18 +2272,6 @@ std::string ctrlm_voice_t::voice_stb_data_partner_id_get() const { return(this->partner_id); } -void ctrlm_voice_t::voice_stb_data_experience_set(std::string &experience) { - XLOGD_DEBUG("Experience Tag set to %s", experience.c_str()); - this->experience = experience; - for(const auto &itr : this->endpoints) { - itr->voice_stb_data_experience_set(experience); - } -} - -std::string ctrlm_voice_t::voice_stb_data_experience_get() const { - return(this->experience); -} - std::string ctrlm_voice_t::voice_stb_data_app_id_http_get() const { return(this->prefs.app_id_http); } @@ -2358,6 +2329,14 @@ bool ctrlm_voice_t::voice_stb_data_pii_mask_get() const { return(this->mask_pii); } +bool ctrlm_voice_t::voice_stb_data_local_mic_get() const { + return(this->local_mic); +} + +bool ctrlm_voice_t::voice_stb_data_local_mic_tap_get() const { + return(this->local_mic_tap); +} + bool ctrlm_voice_t::voice_stb_data_device_certificate_set(ctrlm_voice_cert_t &device_cert, bool &ocsp_verify_stapling, bool &ocsp_verify_ca) { this->ocsp_verify_stapling = ocsp_verify_stapling; this->ocsp_verify_ca = ocsp_verify_ca; @@ -2456,12 +2435,6 @@ bool ctrlm_voice_t::voice_session_has_stb_data() { return(false); } #endif -#ifdef AUTH_EXPERIENCE - if(this->experience == "") { - XLOGD_INFO("No experience tag"); - return(false); - } -#endif #ifdef AUTH_SAT_TOKEN if(this->sat_token_required && this->sat_token[0] == '\0') { XLOGD_INFO("No SAT token"); @@ -2551,7 +2524,7 @@ void ctrlm_voice_t::voice_session_begin_callback(ctrlm_voice_session_begin_cb_t session->ipc_common_data.voice_assistant = is_voice_assistant(session->voice_device); session->ipc_common_data.device_type = session->voice_device; session->endpoint_current = session_begin->endpoint; - session->end_reason = CTRLM_VOICE_SESSION_END_REASON_DONE; + session->end_reason_rcu = CTRLM_VOICE_SESSION_END_REASON_DONE; errno_t safec_rc = memset_s(&session->status, sizeof(session->status), 0 , sizeof(session->status)); ERR_CHK(safec_rc); @@ -2654,35 +2627,26 @@ void ctrlm_voice_t::voice_session_end_callback(ctrlm_voice_session_end_cb_t *ses if(command_status == VOICE_COMMAND_STATUS_PENDING) { switch(session->voice_device) { case CTRLM_VOICE_DEVICE_FF: { - session->status.status = (stats->reason == XRSR_SESSION_END_REASON_EOS) ? VOICE_COMMAND_STATUS_SUCCESS : VOICE_COMMAND_STATUS_FAILURE; + session->status.status = (stats->session_end_reason == XRSR_SESSION_END_REASON_EOS) ? VOICE_COMMAND_STATUS_SUCCESS : VOICE_COMMAND_STATUS_FAILURE; command_status = session->status.status; this->voice_status_set(session); break; } - #ifdef CTRLM_LOCAL_MIC case CTRLM_VOICE_DEVICE_MICROPHONE: { command_status = (session_end->success ? VOICE_COMMAND_STATUS_SUCCESS : VOICE_COMMAND_STATUS_FAILURE); // No need to set, as it's not a controller break; } - #endif default: { break; } } } - #ifdef CTRLM_LOCAL_MIC - #ifdef CTRLM_LOCAL_MIC_TAP if(session->voice_device == CTRLM_VOICE_DEVICE_MICROPHONE || session->voice_device == CTRLM_VOICE_DEVICE_MICROPHONE_TAP) { - #else - if(session->voice_device == CTRLM_VOICE_DEVICE_MICROPHONE) { - #endif - XLOGD_INFO("src <%s> reason <%s> voice command status <%s>", ctrlm_voice_device_str(session->voice_device), xrsr_session_end_reason_str(stats->reason), ctrlm_voice_command_status_str(command_status)); - } else - #endif - { - XLOGD_INFO("src <%s> audio sent bytes <%u> samples <%u> reason <%s> voice command status <%s>", ctrlm_voice_device_str(session->voice_device), session->audio_sent_bytes, session->audio_sent_samples, xrsr_session_end_reason_str(stats->reason), ctrlm_voice_command_status_str(command_status)); + XLOGD_INFO("src <%s> reason <%s> voice command status <%s>", ctrlm_voice_device_str(session->voice_device), xrsr_session_end_reason_str(stats->session_end_reason), ctrlm_voice_command_status_str(command_status)); + } else { + XLOGD_INFO("src <%s> audio sent bytes <%u> samples <%u> reason <%s> voice command status <%s>", ctrlm_voice_device_str(session->voice_device), session->audio_sent_bytes, session->audio_sent_samples, xrsr_session_end_reason_str(stats->session_end_reason), ctrlm_voice_command_status_str(command_status)); } // Update device status @@ -2695,18 +2659,24 @@ void ctrlm_voice_t::voice_session_end_callback(ctrlm_voice_session_end_cb_t *ses // Send Results IARM Event if(this->voice_ipc) { - if(stats->reason == XRSR_SESSION_END_REASON_ERROR_AUDIO_DURATION) { + if(stats->session_end_reason == XRSR_SESSION_END_REASON_ERROR_AUDIO_DURATION) { ctrlm_voice_ipc_event_session_end_t end; end.common = session->ipc_common_data; end.result = SESSION_END_SHORT_UTTERANCE; - end.reason = (int)session->end_reason; + end.reason = (int)session->end_reason_rcu; + this->voice_ipc->session_end(end); + } else if(stats->session_end_reason == XRSR_SESSION_END_REASON_ERROR_AUDIO_SILENT) { + ctrlm_voice_ipc_event_session_end_t end; + end.common = session->ipc_common_data; + end.result = SESSION_END_SILENT_UTTERANCE; + end.reason = (int)session->end_reason_rcu; this->voice_ipc->session_end(end); } else { ctrlm_voice_ipc_event_session_end_server_stats_t server_stats; ctrlm_voice_ipc_event_session_end_t end; end.common = session->ipc_common_data; end.result = (session_end->success ? SESSION_END_SUCCESS : SESSION_END_FAILURE); - end.reason = stats->reason; + end.reason = stats->session_end_reason; end.return_code_protocol = stats->ret_code_protocol; end.return_code_protocol_library = stats->ret_code_library; end.return_code_server = session->server_ret_code; @@ -2728,7 +2698,7 @@ void ctrlm_voice_t::voice_session_end_callback(ctrlm_voice_session_end_cb_t *ses // Update controller metrics ctrlm_main_queue_msg_controller_voice_metrics_t metrics = {0}; metrics.controller_id = session->controller_id; - metrics.short_utterance = (stats->reason == XRSR_SESSION_END_REASON_ERROR_AUDIO_DURATION ? 1 : 0); + metrics.short_utterance = (stats->session_end_reason == XRSR_SESSION_END_REASON_ERROR_AUDIO_DURATION ? 1 : 0); metrics.packets_total = session->packets_processed + session->packets_lost; metrics.packets_lost = session->packets_lost; @@ -2757,8 +2727,8 @@ void ctrlm_voice_t::voice_session_end_callback(ctrlm_voice_session_end_cb_t *ses if(telemetry) { ctrlm_telemetry_event_t vs_marker(MARKER_VOICE_SESSION_TOTAL, 1); ctrlm_telemetry_event_t vs_status_marker(session_end->success ? MARKER_VOICE_SESSION_SUCCESS : MARKER_VOICE_SESSION_FAILURE, 1); - ctrlm_telemetry_event_t vs_end_reason_marker(MARKER_VOICE_END_REASON_PREFIX + std::string(ctrlm_voice_session_end_reason_str(session->end_reason)), 1); - ctrlm_telemetry_event_t vs_xrsr_end_reason_marker(MARKER_VOICE_XRSR_END_REASON_PREFIX + std::string(xrsr_session_end_reason_str(stats->reason)), 1); + ctrlm_telemetry_event_t vs_end_reason_marker(MARKER_VOICE_END_REASON_PREFIX + std::string(ctrlm_voice_session_end_reason_str(session->end_reason_rcu)), 1); + ctrlm_telemetry_event_t vs_xrsr_end_reason_marker(MARKER_VOICE_XRSR_END_REASON_PREFIX + std::string(xrsr_session_end_reason_str(stats->session_end_reason)), 1); // Handle all VSRsp error telemetry if(session->current_vsr_err_string != "") { @@ -2783,7 +2753,7 @@ void ctrlm_voice_t::voice_session_end_callback(ctrlm_voice_session_end_cb_t *ses telemetry->event(ctrlm_telemetry_report_t::VOICE, vs_xrsr_end_reason_marker); if(this->prefs.telemetry_session_stats) { - if(!session->telemetry_session_stats.update_on_session_end(session_end->success, session->end_reason, stats->reason, session->server_ret_code, session->server_message, session->stats_session.voice_key_held_ms)) { + if(!session->telemetry_session_stats.update_on_session_end(session_end->success, session->end_reason_rcu, stats->session_end_reason, session->server_ret_code, session->server_message, session->stats_session.voice_key_held_ms, stats->ret_code_protocol, stats->stream_end_reason)) { XLOGD_ERROR("failed to generate session stats event"); } } @@ -2983,11 +2953,22 @@ void ctrlm_voice_t::voice_stream_end_callback(ctrlm_voice_stream_end_cb_t *strea stream_duration = (session->packets_processed * frame_duration_us) / 1000; samples_per_packet = (session->format.value.adpcm_frame.size_packet - session->format.value.adpcm_frame.size_header) * 2; // 2 samples per byte for ADPCM } - XLOGD_TELEMETRY("src <%s> Packets Lost/Total <%u/%u> %.02f%% duration <%u> ms", ctrlm_voice_device_str(session->voice_device), session->packets_lost, session->packets_lost + session->packets_processed, 100.0 * ((double)session->packets_lost / (double)(session->packets_lost + session->packets_processed)), stream_duration); + XLOGD_AUTOMATION_TELEMETRY("src <%s> Packets Lost/Total <%u/%u> %.02f%% duration <%u> ms", ctrlm_voice_device_str(session->voice_device), session->packets_lost, session->packets_lost + session->packets_processed, 100.0 * ((double)session->packets_lost / (double)(session->packets_lost + session->packets_processed)), stream_duration); #ifdef TELEMETRY_SUPPORT + uint32_t packets_total = session->packets_lost + session->packets_processed; + int32_t voice_detected = -1; + uint32_t peak_confidence = 0; + int32_t peak_rms_level = 0; + if(stats->audio_stats.vad_frames_processed > 0) { + voice_detected = (stats->audio_stats.vad_voice_detected) ? 1 : 0; + peak_confidence = (stats->audio_stats.vad_confidence_peak * 100); + peak_rms_level = stats->audio_stats.vad_rms_level_peak; + + XLOGD_INFO("VAD frames <%u> voice detected <%s> confidence peak <%u%%> rms level peak <%d dB>", stats->audio_stats.vad_frames_processed, voice_detected == 1 ? "YES" : "NO", peak_confidence, peak_rms_level); + } + if(this->prefs.telemetry_session_stats) { - uint32_t packets_total = session->packets_lost + session->packets_processed; - session->telemetry_session_stats.update_on_stream_end(stream_duration, packets_total, session->packets_lost, packets_total * samples_per_packet, session->packets_lost * samples_per_packet, decoder_failures, 0); + session->telemetry_session_stats.update_on_stream_end(stream_duration, packets_total, session->packets_lost, packets_total * samples_per_packet, session->packets_lost * samples_per_packet, decoder_failures, 0, voice_detected, peak_confidence, peak_rms_level); } #endif } else if(samples_processed > 0) { @@ -2995,7 +2976,7 @@ void ctrlm_voice_t::voice_stream_end_callback(ctrlm_voice_stream_end_cb_t *strea XLOGD_INFO("src <%s> Samples Lost/Total <%u/%u> %.02f%% buffered max <%u> duration <%u> ms", ctrlm_voice_device_str(session->voice_device), samples_lost, samples_lost + samples_processed, 100.0 * ((double)samples_lost / (double)(samples_lost + samples_processed)), samples_buffered_max, stream_duration); #ifdef TELEMETRY_SUPPORT if(this->prefs.telemetry_session_stats) { - session->telemetry_session_stats.update_on_stream_end(stream_duration, 0, 0, samples_lost + samples_processed, samples_lost, decoder_failures, samples_buffered_max); + session->telemetry_session_stats.update_on_stream_end(stream_duration, 0, 0, samples_lost + samples_processed, samples_lost, decoder_failures, samples_buffered_max, -1, 0, 0); } #endif } @@ -3018,7 +2999,7 @@ void ctrlm_voice_t::voice_stream_end_callback(ctrlm_voice_stream_end_cb_t *strea // This is a STREAM end.. ctrlm_voice_ipc_event_stream_end_t end; end.common = session->ipc_common_data; - end.reason = (int)session->end_reason; + end.reason = (int)session->end_reason_rcu; this->voice_ipc->stream_end(end); } @@ -3079,8 +3060,7 @@ void ctrlm_voice_t::voice_action_keyword_verification_callback(const uuid_t uuid } void ctrlm_voice_t::voice_keyword_verified_action(void) { - #ifdef BEEP_ON_KWD_ENABLED - if(this->audio_ducking_beep_enabled) { // play beep audio before ducking audio + if(this->beep_on_kwd_supported && (this->beep_on_kwd_file != NULL) && this->audio_ducking_beep_enabled) { // play beep audio before ducking audio if(this->audio_ducking_beep_in_progress) { XLOGD_WARN("audio ducking beep already in progress!"); this->obj_sap->close(); @@ -3102,8 +3082,8 @@ void ctrlm_voice_t::voice_keyword_verified_action(void) { } } - if(!this->obj_sap->play("file://" BEEP_ON_KWD_FILE)) { - XLOGD_WARN("unable to play beep file <%s>", BEEP_ON_KWD_FILE); + if(!this->obj_sap->play(this->beep_on_kwd_file)) { + XLOGD_WARN("unable to play beep file <%s>", this->beep_on_kwd_file); if(!this->obj_sap->close()) { XLOGD_WARN("unable to close system audio player"); } @@ -3120,11 +3100,9 @@ void ctrlm_voice_t::voice_keyword_verified_action(void) { return; } while(retry >= 0); } - #endif this->audio_state_set(true); } -#ifdef BEEP_ON_KWD_ENABLED void ctrlm_voice_t::voice_keyword_beep_completed_normal(void *data, int size) { this->voice_keyword_beep_completed_callback(false, false); } @@ -3159,7 +3137,6 @@ void ctrlm_voice_t::voice_keyword_beep_completed_callback(bool timeout, bool pla this->audio_state_set(true); } } -#endif void ctrlm_voice_t::voice_session_transcription_callback(const uuid_t uuid, const char *transcription) { // Get session based on uuid @@ -3241,12 +3218,8 @@ const char *ctrlm_voice_device_str(ctrlm_voice_device_t device) { switch(device) { case CTRLM_VOICE_DEVICE_PTT: return("PTT"); case CTRLM_VOICE_DEVICE_FF: return("FF"); - #ifdef CTRLM_LOCAL_MIC case CTRLM_VOICE_DEVICE_MICROPHONE: return("MICROPHONE"); - #endif - #ifdef CTRLM_LOCAL_MIC_TAP case CTRLM_VOICE_DEVICE_MICROPHONE_TAP: return("MICROPHONE_TAP"); - #endif case CTRLM_VOICE_DEVICE_INVALID: return("INVALID"); } return("UNKNOWN"); @@ -3307,18 +3280,14 @@ ctrlm_voice_device_t xrsr_to_voice_device(xrsr_src_t device) { ret = CTRLM_VOICE_DEVICE_FF; break; } - #ifdef CTRLM_LOCAL_MIC case XRSR_SRC_MICROPHONE: { ret = CTRLM_VOICE_DEVICE_MICROPHONE; break; } - #endif - #ifdef CTRLM_LOCAL_MIC_TAP case XRSR_SRC_MICROPHONE_TAP: { ret = CTRLM_VOICE_DEVICE_MICROPHONE_TAP; break; } - #endif default: { XLOGD_ERROR("unrecognized device type %d", device); break; @@ -3338,18 +3307,14 @@ xrsr_src_t voice_device_to_xrsr(ctrlm_voice_device_t device) { ret = XRSR_SRC_RCU_FF; break; } - #ifdef CTRLM_LOCAL_MIC case CTRLM_VOICE_DEVICE_MICROPHONE: { ret = XRSR_SRC_MICROPHONE; break; } - #endif - #ifdef CTRLM_LOCAL_MIC_TAP case CTRLM_VOICE_DEVICE_MICROPHONE_TAP: { ret = XRSR_SRC_MICROPHONE_TAP; break; } - #endif default: { XLOGD_ERROR("unrecognized device type %d", device); break; @@ -3359,11 +3324,9 @@ xrsr_src_t voice_device_to_xrsr(ctrlm_voice_device_t device) { } ctrlm_voice_session_group_t voice_device_to_session_group(ctrlm_voice_device_t device_type) { - #ifdef CTRLM_LOCAL_MIC_TAP if(device_type == CTRLM_VOICE_DEVICE_MICROPHONE_TAP) { return(VOICE_SESSION_GROUP_MIC_TAP); } - #endif return(VOICE_SESSION_GROUP_DEFAULT); } @@ -3463,16 +3426,12 @@ ctrlm_voice_format_t xrsr_to_voice_format(xrsr_audio_format_t format) { bool ctrlm_voice_t::is_voice_assistant(ctrlm_voice_device_t device) { bool voice_assistant = false; switch(device) { - #ifdef CTRLM_LOCAL_MIC case CTRLM_VOICE_DEVICE_MICROPHONE: - #endif case CTRLM_VOICE_DEVICE_FF: { voice_assistant = true; break; } - #ifdef CTRLM_LOCAL_MIC_TAP case CTRLM_VOICE_DEVICE_MICROPHONE_TAP: - #endif case CTRLM_VOICE_DEVICE_PTT: case CTRLM_VOICE_DEVICE_INVALID: default: { @@ -3590,12 +3549,10 @@ int ctrlm_voice_t::ctrlm_voice_controller_command_status_read_timeout(void *data return(false); } -#ifdef BEEP_ON_KWD_ENABLED int ctrlm_voice_t::ctrlm_voice_keyword_beep_end_timeout(void *data) { ctrlm_get_voice_obj()->voice_keyword_beep_completed_callback(true, false); return(false); } -#endif // Timeouts end @@ -3642,9 +3599,7 @@ const char *ctrlm_voice_session_response_status_str(ctrlm_voice_session_response const char *ctrlm_voice_session_group_str(ctrlm_voice_session_group_t group) { switch(group) { case VOICE_SESSION_GROUP_DEFAULT: return("DEFAULT"); - #ifdef CTRLM_LOCAL_MIC_TAP case VOICE_SESSION_GROUP_MIC_TAP: return("MIC_TAP"); - #endif case VOICE_SESSION_GROUP_QTY: break; // fall thru to return an invalid group } return(ctrlm_invalid_return(group)); @@ -3810,14 +3765,14 @@ void ctrlm_voice_t::voice_power_state_change(ctrlm_power_state_t power_state) { xrsr_power_mode_t xrsr_power_mode = voice_xrsr_power_map(power_state); - #ifdef CTRLM_LOCAL_MIC - if(power_state == CTRLM_POWER_STATE_ON) { - bool privacy_enabled = this->vsdk_is_privacy_enabled(); - if(privacy_enabled != this->voice_is_privacy_enabled()) { - privacy_enabled ? this->voice_privacy_enable(false) : this->voice_privacy_disable(false); + if(this->local_mic) { + if(power_state == CTRLM_POWER_STATE_ON) { + bool privacy_enabled = this->vsdk_is_privacy_enabled(); + if(privacy_enabled != this->voice_is_privacy_enabled()) { + privacy_enabled ? this->voice_privacy_enable(false) : this->voice_privacy_disable(false); + } } } - #endif if(!xrsr_power_mode_set(xrsr_power_mode)) { XLOGD_ERROR("failed to set xrsr to power state %s", ctrlm_power_state_str(power_state)); @@ -3851,57 +3806,56 @@ void ctrlm_voice_t::voice_session_set_inactive(ctrlm_voice_device_t device) { } bool ctrlm_voice_t::voice_is_privacy_enabled(void) { - #ifdef CTRLM_LOCAL_MIC - sem_wait(&this->device_status_semaphore); - bool value = (this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_PRIVACY) ? true : false; - sem_post(&this->device_status_semaphore); - return(value); - #else + if(this->local_mic) { + sem_wait(&this->device_status_semaphore); + bool value = (this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_PRIVACY) ? true : false; + sem_post(&this->device_status_semaphore); + return(value); + } return(false); - #endif } void ctrlm_voice_t::voice_privacy_enable(bool update_vsdk) { - #ifdef CTRLM_LOCAL_MIC - sem_wait(&this->device_status_semaphore); - if(this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_PRIVACY) { - sem_post(&this->device_status_semaphore); - XLOGD_WARN("already enabled"); - return; - } + if(this->local_mic) { + sem_wait(&this->device_status_semaphore); + if(this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_PRIVACY) { + sem_post(&this->device_status_semaphore); + XLOGD_WARN("already enabled"); + return; + } - this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] |= CTRLM_VOICE_DEVICE_STATUS_PRIVACY; - ctrlm_db_voice_write_device_status(CTRLM_VOICE_DEVICE_MICROPHONE, (this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_MASK_DB)); + this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] |= CTRLM_VOICE_DEVICE_STATUS_PRIVACY; + ctrlm_db_voice_write_device_status(CTRLM_VOICE_DEVICE_MICROPHONE, (this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_MASK_DB)); - #ifdef CTRLM_LOCAL_MIC_DISABLE_VIA_PRIVACY - if(update_vsdk && this->xrsr_opened && !xrsr_privacy_mode_set(true)) { - XLOGD_ERROR("xrsr_privacy_mode_set failed"); - } - #endif + if(this->local_mic_disable_via_privacy) { + if(update_vsdk && this->xrsr_opened && !xrsr_privacy_mode_set(true)) { + XLOGD_ERROR("xrsr_privacy_mode_set failed"); + } + } - sem_post(&this->device_status_semaphore); - #endif + sem_post(&this->device_status_semaphore); + } } void ctrlm_voice_t::voice_privacy_disable(bool update_vsdk) { - #ifdef CTRLM_LOCAL_MIC - sem_wait(&this->device_status_semaphore); - if(!(this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_PRIVACY)) { - sem_post(&this->device_status_semaphore); - XLOGD_WARN("already disabled"); - return; - } + if(this->local_mic) { + sem_wait(&this->device_status_semaphore); + if(!(this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_PRIVACY)) { + sem_post(&this->device_status_semaphore); + XLOGD_WARN("already disabled"); + return; + } - this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] &= ~CTRLM_VOICE_DEVICE_STATUS_PRIVACY; - ctrlm_db_voice_write_device_status(CTRLM_VOICE_DEVICE_MICROPHONE, (this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_MASK_DB)); + this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] &= ~CTRLM_VOICE_DEVICE_STATUS_PRIVACY; + ctrlm_db_voice_write_device_status(CTRLM_VOICE_DEVICE_MICROPHONE, (this->device_status[CTRLM_VOICE_DEVICE_MICROPHONE] & CTRLM_VOICE_DEVICE_STATUS_MASK_DB)); - #ifdef CTRLM_LOCAL_MIC_DISABLE_VIA_PRIVACY - if(update_vsdk && this->xrsr_opened && !xrsr_privacy_mode_set(false)) { - XLOGD_ERROR("xrsr_privacy_mode_set failed"); - } - #endif - sem_post(&this->device_status_semaphore); - #endif + if(this->local_mic_disable_via_privacy) { + if(update_vsdk && this->xrsr_opened && !xrsr_privacy_mode_set(false)) { + XLOGD_ERROR("xrsr_privacy_mode_set failed"); + } + } + sem_post(&this->device_status_semaphore); + } } void ctrlm_voice_t::voice_device_enable(ctrlm_voice_device_t device, bool db_write, bool *update_routes) { @@ -3940,7 +3894,6 @@ void ctrlm_voice_t::voice_device_disable(ctrlm_voice_device_t device, bool db_wr sem_post(&this->device_status_semaphore); } -#ifdef BEEP_ON_KWD_ENABLED void ctrlm_voice_system_audio_player_event_handler(system_audio_player_event_t event, void *user_data) { if(user_data == NULL) { return; @@ -3980,27 +3933,24 @@ void ctrlm_voice_system_audio_player_event_handler(system_audio_player_event_t e } } } -#endif -#ifdef NETWORKED_STANDBY_MODE_ENABLED void ctrlm_voice_t::voice_nsm_session_request(void) { ctrlm_network_id_t network_id = CTRLM_MAIN_NETWORK_ID_DSP; ctrlm_controller_id_t controller_id = CTRLM_MAIN_CONTROLLER_ID_DSP; ctrlm_voice_device_t device = CTRLM_VOICE_DEVICE_MICROPHONE; ctrlm_voice_format_t format = { .type = CTRLM_VOICE_FORMAT_PCM_32_BIT }; - #ifdef CTRLM_LOCAL_MIC_DISABLE_VIA_PRIVACY - //If the user un-muted the microphones in standby, we must un-mute our components - if(this->voice_is_privacy_enabled()) { - this->voice_privacy_disable(true); + if(this->local_mic_disable_via_privacy) { + //If the user un-muted the microphones in standby, we must un-mute our components + if(this->voice_is_privacy_enabled()) { + this->voice_privacy_disable(true); + } } - #endif this->nsm_voice_session = true; voice_session_req(network_id, controller_id, device, format, NULL, "NSM", "0.0.0.0", "0.0.0.0", 0.0); } -#endif int ctrlm_voice_t::packet_loss_threshold_get() const { return(this->packet_loss_threshold); @@ -4025,11 +3975,11 @@ xrsr_power_mode_t voice_xrsr_power_map(ctrlm_power_state_t ctrlm_power_state) { switch(ctrlm_power_state) { case CTRLM_POWER_STATE_DEEP_SLEEP: - #ifdef NETWORKED_STANDBY_MODE_ENABLED - xrsr_power_mode = ctrlm_main_get_networked_standby_mode() ? XRSR_POWER_MODE_LOW : XRSR_POWER_MODE_SLEEP; - #else - xrsr_power_mode = XRSR_POWER_MODE_SLEEP; - #endif + if(ctrlm_is_networked_standby_supported()) { + xrsr_power_mode = ctrlm_main_get_networked_standby_mode() ? XRSR_POWER_MODE_LOW : XRSR_POWER_MODE_SLEEP; + } else { + xrsr_power_mode = XRSR_POWER_MODE_SLEEP; + } break; case CTRLM_POWER_STATE_ON: case CTRLM_POWER_STATE_STANDBY: @@ -4046,7 +3996,8 @@ xrsr_power_mode_t voice_xrsr_power_map(ctrlm_power_state_t ctrlm_power_state) { void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { bool enabled = true; - bool reroute = false; + + XLOGD_INFO("processing RFC values"); attr.get_rfc_value(JSON_INT_NAME_VOICE_VREX_REQUEST_TIMEOUT, this->prefs.timeout_vrex_connect,0); attr.get_rfc_value(JSON_INT_NAME_VOICE_VREX_RESPONSE_TIMEOUT, this->prefs.timeout_vrex_session,0); @@ -4071,22 +4022,6 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { attr.get_rfc_value(JSON_STR_NAME_VOICE_APP_ID_WS, this->prefs.app_id_ws); attr.get_rfc_value(JSON_STR_NAME_VOICE_LANGUAGE, this->prefs.guide_language); - double keyword_sensitivity = 0.0; - if(attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_KEYWORD_DETECT_SENSITIVITY, keyword_sensitivity)) { - // Check keyword detector sensitivity value against limits; apply default if out of range. - double sensitivity_set = this->vsdk_keyword_sensitivity_limit_check(keyword_sensitivity); - if(sensitivity_set != keyword_sensitivity) { - XLOGD_ERROR("keyword sensitivity <%5.2f> out of limits", keyword_sensitivity); - } else { - this->prefs.keyword_sensitivity = keyword_sensitivity; - XLOGD_INFO("keyword sensitivity <%5.2f>", this->prefs.keyword_sensitivity); - xrsr_keyword_config_t kw_config; - kw_config.sensitivity = (float)this->prefs.keyword_sensitivity; - if(!xrsr_keyword_config_set(&kw_config)) { - XLOGD_ERROR("error updating keyword config"); - } - } - } attr.get_rfc_value(JSON_BOOL_NAME_VOICE_VREX_TEST_FLAG, this->prefs.vrex_test_flag); attr.get_rfc_value(JSON_BOOL_NAME_VOICE_VREX_WUW_BYPASS_SUCCESS_FLAG,this->prefs.vrex_wuw_bypass_success_flag); attr.get_rfc_value(JSON_BOOL_NAME_VOICE_VREX_WUW_BYPASS_FAILURE_FLAG,this->prefs.vrex_wuw_bypass_failure_flag); @@ -4110,22 +4045,34 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { } attr.get_rfc_value(JSON_INT_NAME_VOICE_PACKET_LOSS_THRESHOLD, this->packet_loss_threshold, 0); - if(attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_MODE, this->audio_mode) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_TIMING, this->audio_timing) | - attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_CONFIDENCE_THRESHOLD, this->audio_confidence_threshold, 0.0, 1.0) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_DUCKING_TYPE, this->audio_ducking_type, CTRLM_VOICE_AUDIO_DUCKING_TYPE_ABSOLUTE, CTRLM_VOICE_AUDIO_DUCKING_TYPE_RELATIVE) | - attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_DUCKING_LEVEL, this->audio_ducking_level, 0.0, 1.0) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_AUDIO_DUCKING_BEEP, this->audio_ducking_beep_enabled)) { + + bool audio_mode_updated = false; + audio_mode_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_MODE, this->audio_mode); + audio_mode_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_TIMING, this->audio_timing); + audio_mode_updated |= attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_CONFIDENCE_THRESHOLD, this->audio_confidence_threshold, 0.0, 1.0); + audio_mode_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_AUDIO_DUCKING_TYPE, this->audio_ducking_type, CTRLM_VOICE_AUDIO_DUCKING_TYPE_ABSOLUTE, CTRLM_VOICE_AUDIO_DUCKING_TYPE_RELATIVE); + audio_mode_updated |= attr.get_rfc_value(JSON_FLOAT_NAME_VOICE_AUDIO_DUCKING_LEVEL, this->audio_ducking_level, 0.0, 1.0); + + // JSON_BOOL_NAME_VOICE_AUDIO_DUCKING_BEEP is updated via Voice Control plugin so it cannot be overridden via RFC + + XLOGD_INFO("audio mode updated <%s>", audio_mode_updated ? "YES" : "NO"); + + if(audio_mode_updated) { ctrlm_voice_audio_settings_t audio_settings = {this->audio_mode, this->audio_timing, this->audio_confidence_threshold, this->audio_ducking_type, this->audio_ducking_level, this->audio_ducking_beep_enabled}; this->set_audio_mode(&audio_settings); } // All attributes that need capture configuration to be set - if(attr.get_rfc_value(JSON_ARRAY_NAME_VOICE_SAVE_LAST_UTTERANCE, this->prefs.utterance_save, ctrlm_is_production_build() ? CTRLM_JSON_ARRAY_INDEX_PRD : CTRLM_JSON_ARRAY_INDEX_DEV) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_UTTERANCE_USE_CURTAIL, this->prefs.utterance_use_curtail) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_QTY_MAX, this->prefs.utterance_file_qty_max, 1, 100000) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_SIZE_MAX, this->prefs.utterance_file_size_max, 4 * 1024) | - attr.get_rfc_value(JSON_STR_NAME_VOICE_UTTERANCE_PATH, this->prefs.utterance_path)) { + bool capture_config_updated = false; + capture_config_updated |= attr.get_rfc_value(JSON_ARRAY_NAME_VOICE_SAVE_LAST_UTTERANCE, this->prefs.utterance_save, ctrlm_is_production_build() ? CTRLM_JSON_ARRAY_INDEX_PRD : CTRLM_JSON_ARRAY_INDEX_DEV); + capture_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_UTTERANCE_USE_CURTAIL, this->prefs.utterance_use_curtail); + capture_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_QTY_MAX, this->prefs.utterance_file_qty_max, 1, 100000); + capture_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_UTTERANCE_FILE_SIZE_MAX, this->prefs.utterance_file_size_max, 4 * 1024); + capture_config_updated |= attr.get_rfc_value(JSON_STR_NAME_VOICE_UTTERANCE_PATH, this->prefs.utterance_path); + + XLOGD_INFO("capture config updated <%s>", capture_config_updated ? "YES" : "NO"); + + if(capture_config_updated) { xrsr_capture_config_t capture_config = { .delete_files = !this->prefs.utterance_save, .enable = this->prefs.utterance_save, @@ -4148,22 +4095,28 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { } // All attributes that need a re-route to apply - if(attr.get_rfc_value(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min) | - #ifdef NETWORKED_STANDBY_MODE_ENABLED - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_standby.connect_check_interval) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_CONNECT, this->prefs.dst_params_standby.timeout_connect) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_INACTIVITY, this->prefs.dst_params_standby.timeout_inactivity) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_SESSION, this->prefs.dst_params_standby.timeout_session) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_STANDBY_IPV4_FALLBACK, this->prefs.dst_params_standby.ipv4_fallback) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_BACKOFF_DELAY, this->prefs.dst_params_standby.backoff_delay) | - #endif - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_low_latency.connect_check_interval) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_CONNECT, this->prefs.dst_params_low_latency.timeout_connect) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_INACTIVITY, this->prefs.dst_params_low_latency.timeout_inactivity) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_SESSION, this->prefs.dst_params_low_latency.timeout_session) | - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_LOW_LATENCY_IPV4_FALLBACK, this->prefs.dst_params_low_latency.ipv4_fallback) | - attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_BACKOFF_DELAY, this->prefs.dst_params_low_latency.backoff_delay)) { - reroute = true; + bool routing_config_updated = false; + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_standby.connect_check_interval); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_CONNECT, this->prefs.dst_params_standby.timeout_connect); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_INACTIVITY, this->prefs.dst_params_standby.timeout_inactivity); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_TIMEOUT_SESSION, this->prefs.dst_params_standby.timeout_session); + routing_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_STANDBY_IPV4_FALLBACK, this->prefs.dst_params_standby.ipv4_fallback); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_STANDBY_BACKOFF_DELAY, this->prefs.dst_params_standby.backoff_delay); + + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_CONNECT_CHECK_INTERVAL, this->prefs.dst_params_low_latency.connect_check_interval); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_CONNECT, this->prefs.dst_params_low_latency.timeout_connect); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_INACTIVITY, this->prefs.dst_params_low_latency.timeout_inactivity); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_TIMEOUT_SESSION, this->prefs.dst_params_low_latency.timeout_session); + routing_config_updated |= attr.get_rfc_value(JSON_BOOL_NAME_VOICE_DST_PARAMS_LOW_LATENCY_IPV4_FALLBACK, this->prefs.dst_params_low_latency.ipv4_fallback); + routing_config_updated |= attr.get_rfc_value(JSON_INT_NAME_VOICE_DST_PARAMS_LOW_LATENCY_BACKOFF_DELAY, this->prefs.dst_params_low_latency.backoff_delay); + + + std::string voice_activity_detection_mode; + if(attr.get_rfc_value(JSON_STR_NAME_VOICE_VOICE_ACTIVITY_DETECTION_MODE, voice_activity_detection_mode)) { + this->prefs.voice_activity_detection_mode = this->voice_activity_detection_mode_to_xrsr(voice_activity_detection_mode); + XLOGD_INFO("voice activity detection mode <%s>", xrsr_stream_voice_activity_mode_str(this->prefs.voice_activity_detection_mode)); + routing_config_updated = true; } std::vector obj_server_hosts; @@ -4171,23 +4124,23 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { this->url_hostname_patterns(obj_server_hosts); } - attr.get_rfc_value(JSON_BOOL_NAME_VOICE_FORCE_VOICE_SETTINGS, this->prefs.force_voice_settings); if(attr.get_rfc_value(JSON_BOOL_NAME_VOICE_FORCE_VOICE_SETTINGS, this->prefs.force_voice_settings) && this->prefs.force_voice_settings) { attr.get_rfc_value(JSON_BOOL_NAME_VOICE_ENABLE, enabled); attr.get_rfc_value(JSON_STR_NAME_VOICE_URL_SRC_PTT, this->prefs.server_url_src_ptt); attr.get_rfc_value(JSON_STR_NAME_VOICE_URL_SRC_FF, this->prefs.server_url_src_ff); - #ifdef CTRLM_LOCAL_MIC_TAP attr.get_rfc_value(JSON_STR_NAME_VOICE_URL_SRC_MIC_TAP, this->prefs.server_url_src_mic_tap); - #endif // Check if enabled if(!enabled) { for(int i = CTRLM_VOICE_DEVICE_PTT; i < CTRLM_VOICE_DEVICE_INVALID; i++) { this->voice_device_disable((ctrlm_voice_device_t)i, true, NULL); } } - reroute = true; + routing_config_updated = true; } - if(reroute) { + + XLOGD_INFO("routing config updated <%s>", routing_config_updated ? "YES" : "NO"); + + if(routing_config_updated) { this->voice_sdk_update_routes(); } } @@ -4282,3 +4235,14 @@ void ctrlm_voice_t::url_hostname_patterns(const std::vector &obj_se this->url_hostname_pattern_add(itr.c_str()); } } + +void ctrlm_voice_t::pre_session_terminate(std::function cb_start_audio, ctrlm_voice_start_audio_params_t *cb_audio_start_params, ctrlm_voice_session_rsp_confirm_t *cb_confirm, void **cb_confirm_param) { + if (cb_start_audio != nullptr && cb_audio_start_params != nullptr) { + if(cb_confirm != NULL && cb_confirm_param != NULL) { + cb_audio_start_params->m_cb_confirm_voice_obj = ctrlm_voice_session_response_confirm; + cb_audio_start_params->m_cb_confirm_param = NULL; + cb_audio_start_params->m_status = (this->prefs.par_voice_enabled) ? VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE : VOICE_SESSION_RESPONSE_AVAILABLE; + } + cb_start_audio(cb_audio_start_params); + } +} diff --git a/src/voice/ctrlm_voice_obj.h b/src/voice/ctrlm_voice_obj.h index 70ddb4c7..9c0bc4be 100644 --- a/src/voice/ctrlm_voice_obj.h +++ b/src/voice/ctrlm_voice_obj.h @@ -37,21 +37,14 @@ #include "ctrlm_rfc.h" #include "xrsr.h" #include "ctrlm_voice_telemetry_events.h" - -#ifdef BEEP_ON_KWD_ENABLED #include "ctrlm_thunder_plugin_system_audio_player.h" -#endif #define VOICE_SESSION_REQ_DATA_LEN_MAX (33) typedef enum { VOICE_SESSION_GROUP_DEFAULT = 0, // Session index for regular voice sessions (PTT, FFV) - #ifdef CTRLM_LOCAL_MIC_TAP VOICE_SESSION_GROUP_MIC_TAP = 1, // Session index for microphone tap voice sessions VOICE_SESSION_GROUP_QTY = 2 - #else - VOICE_SESSION_GROUP_QTY = 1 - #endif } ctrlm_voice_session_group_t; #ifdef VOICE_BUFFER_STATS @@ -280,46 +273,42 @@ typedef struct { // End Event Callback Structures typedef struct { - std::string server_url_src_ptt; - std::string server_url_src_ff; - #ifdef CTRLM_LOCAL_MIC_TAP - std::string server_url_src_mic_tap; - #endif - std::vector server_hosts; - std::string aspect_ratio; - std::string guide_language; - std::string app_id_http; - std::string app_id_ws; - unsigned long timeout_vrex_connect; - unsigned long timeout_vrex_session; - unsigned long timeout_stats; - unsigned long timeout_packet_initial; - unsigned long timeout_packet_subsequent; - guchar bitrate_minimum; - guint16 time_threshold; - bool utterance_save; - bool utterance_use_curtail; - unsigned long utterance_file_qty_max; - unsigned long utterance_file_size_max; - std::string utterance_path; - unsigned long utterance_duration_min; - unsigned long ffv_leading_samples; - bool force_voice_settings; - double keyword_sensitivity; - bool vrex_test_flag; - bool vrex_wuw_bypass_success_flag; - bool vrex_wuw_bypass_failure_flag; - std::string opus_encoder_params_str; - uint8_t opus_encoder_params[CTRLM_RCU_RIB_ATTR_LEN_OPUS_ENCODING_PARAMS]; - bool force_toggle_fallback; - bool telemetry_session_stats; - #ifdef NETWORKED_STANDBY_MODE_ENABLED - xrsr_dst_params_t dst_params_standby; - #endif - xrsr_dst_params_t dst_params_low_latency; - bool par_voice_enabled; - uint8_t par_voice_eos_method; - uint16_t par_voice_eos_timeout; + std::string server_url_src_ptt; + std::string server_url_src_ff; + std::string server_url_src_mic_tap; + std::vector server_hosts; + std::string aspect_ratio; + std::string guide_language; + std::string app_id_http; + std::string app_id_ws; + unsigned long timeout_vrex_connect; + unsigned long timeout_vrex_session; + unsigned long timeout_stats; + unsigned long timeout_packet_initial; + unsigned long timeout_packet_subsequent; + guchar bitrate_minimum; + guint16 time_threshold; + bool utterance_save; + bool utterance_use_curtail; + unsigned long utterance_file_qty_max; + unsigned long utterance_file_size_max; + std::string utterance_path; + unsigned long utterance_duration_min; + unsigned long ffv_leading_samples; + xrsr_stream_voice_activity_mode_t voice_activity_detection_mode; + bool force_voice_settings; + bool vrex_test_flag; + bool vrex_wuw_bypass_success_flag; + bool vrex_wuw_bypass_failure_flag; + std::string opus_encoder_params_str; + uint8_t opus_encoder_params[CTRLM_RCU_RIB_ATTR_LEN_OPUS_ENCODING_PARAMS]; + bool force_toggle_fallback; + bool telemetry_session_stats; + xrsr_dst_params_t dst_params_standby; + xrsr_dst_params_t dst_params_low_latency; + bool par_voice_enabled; + uint8_t par_voice_eos_method; + uint16_t par_voice_eos_timeout; } voice_session_prefss_t; typedef struct { @@ -391,9 +380,7 @@ typedef struct { typedef struct { std::string urlPtt; std::string urlHf; - #ifdef CTRLM_LOCAL_MIC_TAP std::string urlMicTap; - #endif uint8_t status[CTRLM_VOICE_DEVICE_INVALID]; bool prv_enabled; bool wwFeedback; @@ -402,6 +389,29 @@ typedef struct { typedef void (*ctrlm_voice_session_rsp_confirm_t)(bool result, signed long long rsp_time, unsigned int rsp_window, const std::string &err_str, ctrlm_timestamp_t *timestamp, void *user_data); +class ctrlm_voice_start_audio_params_t +{ +public: + // Generic + ctrlm_voice_start_audio_params_t() = default; + virtual ~ctrlm_voice_start_audio_params_t() = default; + + ctrlm_controller_id_t m_controller_id = -1; + bool m_started = false; + + // BLE + int m_fd = -1; + + // RF4CE + bool m_use_stream_params = false; + int16_t m_offset = 0; + ctrlm_timestamp_t m_timestamp; + ctrlm_voice_session_rsp_confirm_t m_cb_confirm_voice_obj; + void * m_cb_confirm_param; + ctrlm_voice_session_response_status_t m_status; + ctrlm_voice_device_t m_device_type; +}; + typedef struct { ctrlm_network_id_t network_id; ctrlm_network_type_t network_type; @@ -434,7 +444,7 @@ typedef struct { uint32_t packets_lost; uint32_t lqi_total; - ctrlm_voice_session_end_reason_t end_reason; + ctrlm_voice_session_end_reason_t end_reason_rcu; bool is_press_and_release; bool is_session_by_text; @@ -484,7 +494,7 @@ class ctrlm_voice_t { ctrlm_voice_t(); virtual ~ctrlm_voice_t(); - ctrlm_voice_session_response_status_t voice_session_req(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, ctrlm_voice_device_t device_type, ctrlm_voice_format_t format, voice_session_req_stream_params *stream_params, const char *controller_name, const char *sw_version, const char *hw_version, double voltage, bool command_status=false, ctrlm_timestamp_t *timestamp=NULL, ctrlm_voice_session_rsp_confirm_t *cb_confirm=NULL, void **cb_confirm_param=NULL, bool use_external_data_pipe=false, bool press_and_hold=true, const char *transcription_in=NULL, const char *audio_file_in=NULL, const uuid_t *uuid = NULL, bool low_latency=false, bool low_cpu_util=false, int audio_fd = -1); + ctrlm_voice_session_response_status_t voice_session_req(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, ctrlm_voice_device_t device_type, ctrlm_voice_format_t format, voice_session_req_stream_params *stream_params, const char *controller_name, const char *sw_version, const char *hw_version, double voltage, bool command_status=false, ctrlm_timestamp_t *timestamp=NULL, ctrlm_voice_session_rsp_confirm_t *cb_confirm=NULL, void **cb_confirm_param=NULL, bool use_external_data_pipe=false, bool press_and_hold=true, std::function cb_start_audio=NULL, ctrlm_voice_start_audio_params_t *cb_audio_start_params=NULL, const char *transcription_in=NULL, const char *audio_file_in=NULL, const uuid_t *uuid = NULL, bool low_latency=false, bool low_cpu_util=false, int audio_fd = -1); void voice_session_rsp_confirm(bool result, signed long long rsp_time, unsigned int rsp_window, const std::string &err_str, ctrlm_timestamp_t *timestamp); bool voice_session_data(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, const char *buffer, long unsigned int length, ctrlm_timestamp_t *timestamp=NULL, uint8_t *lqi=NULL); bool voice_session_data(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, int fd, const uuid_t *uuid=NULL); @@ -516,8 +526,6 @@ class ctrlm_voice_t { ctrlm_device_type_t voice_stb_data_device_type_get() const; virtual void voice_stb_data_partner_id_set(std::string &partner_id); std::string voice_stb_data_partner_id_get() const; - virtual void voice_stb_data_experience_set(std::string &experience); - std::string voice_stb_data_experience_get() const; std::string voice_stb_data_app_id_http_get() const; std::string voice_stb_data_app_id_ws_get() const; virtual void voice_stb_data_guide_language_set(const char *language); @@ -529,6 +537,8 @@ class ctrlm_voice_t { bool voice_stb_data_bypass_wuw_verify_failure_get() const; virtual void voice_stb_data_pii_mask_set(bool mask_pii); bool voice_stb_data_pii_mask_get() const; + bool voice_stb_data_local_mic_get() const; + bool voice_stb_data_local_mic_tap_get() const; virtual bool voice_stb_data_device_certificate_set(ctrlm_voice_cert_t &device_cert, bool &ocsp_verify_stapling, bool &ocsp_verify_ca); virtual bool voice_stb_data_device_certificate_set(const char *p12_cert, const char *p12_pass); virtual bool voice_stb_data_device_certificate_set(const char *pem_cert, const char *pem_pkey, const char *pem_chain, const char *pem_passphrase); @@ -586,9 +596,7 @@ class ctrlm_voice_t { static int ctrlm_voice_packet_timeout(void *data); static int ctrlm_voice_controller_session_stats_rxd_timeout(void *data); static int ctrlm_voice_controller_command_status_read_timeout(void *data); - #ifdef BEEP_ON_KWD_ENABLED static int ctrlm_voice_keyword_beep_end_timeout(void *data); - #endif // End Static Callbacks // Event Interface @@ -609,15 +617,11 @@ class ctrlm_voice_t { virtual void voice_server_return_code_callback(const uuid_t uuid, const char *reason, long ret_code); virtual void voice_session_transcription_callback(const uuid_t uuid, const char *transcription); virtual void voice_power_state_change(ctrlm_power_state_t power_state); - #ifdef NETWORKED_STANDBY_MODE_ENABLED virtual void voice_nsm_session_request(void); - #endif virtual void voice_keyword_verified_action(void); - #ifdef BEEP_ON_KWD_ENABLED virtual void voice_keyword_beep_completed_normal(void *data, int size); virtual void voice_keyword_beep_completed_error(void *data, int size); virtual void voice_keyword_beep_completed_callback(bool timeout, bool playback_error); - #endif // End Event Interface protected: @@ -634,6 +638,8 @@ class ctrlm_voice_t { bool voice_params_opus_encoder_validate(std::string &opus_encoder_params_str); void voice_params_opus_samples_per_packet_set(void); bool voice_params_hex_str_to_bytes(std::string hex_string, guchar *data, guint32 length); + + xrsr_stream_voice_activity_mode_t voice_activity_detection_mode_to_xrsr(const std::string &mode); bool voice_session_can_request(ctrlm_voice_device_t device); void voice_session_set_active(ctrlm_voice_device_t device); @@ -657,12 +663,16 @@ class ctrlm_voice_t { std::string device_id; ctrlm_device_type_t device_type; std::string partner_id; - std::string experience; char sat_token[XRSR_SAT_TOKEN_LEN_MAX]; bool sat_token_required; bool mtls_required; bool secure_url_required; bool mask_pii; + bool local_mic; + bool local_mic_tap; + bool local_mic_disable_via_privacy; + const char * beep_on_kwd_file; + bool beep_on_kwd_supported; bool ocsp_verify_stapling; bool ocsp_verify_ca; bool capture_active; @@ -685,11 +695,9 @@ class ctrlm_voice_t { bool xrsr_opened; ctrlm_voice_ipc_t *voice_ipc; - #ifdef BEEP_ON_KWD_ENABLED Thunder::SystemAudioPlayer::ctrlm_thunder_plugin_system_audio_player_t *obj_sap; bool sap_opened; ctrlm_timestamp_t sap_play_timestamp; - #endif // Current Session Data unsigned long opus_samples_per_packet; @@ -736,7 +744,10 @@ class ctrlm_voice_t { void set_audio_mode(ctrlm_voice_audio_settings_t *settings); void audio_state_set(bool session); bool vsdk_is_privacy_enabled(void); - double vsdk_keyword_sensitivity_limit_check(double sensitivity); + void pre_session_terminate(std::function cb_start_audio, + ctrlm_voice_start_audio_params_t *cb_audio_start_params, + ctrlm_voice_session_rsp_confirm_t *cb_confirm, + void **cb_confirm_param); }; // Helper Functions @@ -759,27 +770,11 @@ xrsr_src_t voice_device_to_xrsr(ctrlm_voice_device_t device); __inline bool ctrlm_voice_device_is_mic(ctrlm_voice_device_t device) { - #ifdef CTRLM_LOCAL_MIC - #ifdef CTRLM_LOCAL_MIC_TAP return(device == CTRLM_VOICE_DEVICE_MICROPHONE || device == CTRLM_VOICE_DEVICE_MICROPHONE_TAP); - #else - return(device == CTRLM_VOICE_DEVICE_MICROPHONE); - #endif - #else - return(false); - #endif } __inline bool ctrlm_voice_xrsr_src_is_mic(xrsr_src_t src) { - #ifdef CTRLM_LOCAL_MIC - #ifdef CTRLM_LOCAL_MIC_TAP return(src == XRSR_SRC_MICROPHONE || src == XRSR_SRC_MICROPHONE_TAP); - #else - return(src == XRSR_SRC_MICROPHONE); - #endif - #else - return(false); - #endif } #endif diff --git a/src/voice/ctrlm_voice_obj_generic.cpp b/src/voice/ctrlm_voice_obj_generic.cpp index 340b295d..948d6039 100644 --- a/src/voice/ctrlm_voice_obj_generic.cpp +++ b/src/voice/ctrlm_voice_obj_generic.cpp @@ -165,32 +165,35 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { errno_t safec_rc = memset_s(&routes, sizeof(routes), 0, sizeof(routes)); ERR_CHK(safec_rc); + bool networked_standby_supported = ctrlm_is_networked_standby_supported(); + // iterate over source to url mapping for(int j = 0; j < XRSR_SRC_INVALID; j++) { xrsr_src_t src = (xrsr_src_t)j; ctrlm_voice_device_t src_device = xrsr_to_voice_device(src); std::string *url = NULL; + + xrsr_stream_voice_activity_mode_t stream_vad_mode = XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED; sem_wait(&this->device_status_semaphore); if(this->device_status[src_device] != CTRLM_VOICE_DEVICE_STATUS_DISABLED && this->device_status[src_device] != CTRLM_VOICE_DEVICE_STATUS_NOT_SUPPORTED) { switch(src_device) { case CTRLM_VOICE_DEVICE_PTT: { url = &this->prefs.server_url_src_ptt; + + // For PTT source, use the configured VAD mode which may be disabled, enabled, or enforced + stream_vad_mode = this->prefs.voice_activity_detection_mode; break; } - #ifdef CTRLM_LOCAL_MIC case CTRLM_VOICE_DEVICE_MICROPHONE: - #endif case CTRLM_VOICE_DEVICE_FF: { url = &this->prefs.server_url_src_ff; break; } - #ifdef CTRLM_LOCAL_MIC_TAP case CTRLM_VOICE_DEVICE_MICROPHONE_TAP: { url = &this->prefs.server_url_src_mic_tap; break; } - #endif default: { break; } @@ -234,20 +237,19 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { if(!this->obj_ws_nextgen->get_handlers(&handlers_xrsr)) { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; - #ifdef NETWORKED_STANDBY_MODE_ENABLED - if(src == XRSR_SRC_MICROPHONE) { + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; + if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } - #endif i++; XLOGD_INFO("url translation from %s to %s", url->c_str(), urls_translated[translated_index].c_str()); } @@ -268,20 +270,19 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { if(!this->obj_ws_nsp->get_handlers(&handlers_xrsr)) { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_PCM_32_BIT | XRSR_AUDIO_FORMAT_PCM_32_BIT_MULTI | XRSR_AUDIO_FORMAT_PCM_RAW; - routes[i].dsts[0].stream_time_min = 0; - routes[i].dsts[0].stream_from = XRSR_STREAM_FROM_LIVE; - routes[i].dsts[0].stream_offset = 0; - routes[i].dsts[0].stream_until = XRSR_STREAM_UNTIL_END_OF_STREAM; - #ifdef NETWORKED_STANDBY_MODE_ENABLED - if(src == XRSR_SRC_MICROPHONE) { + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_PCM_32_BIT | XRSR_AUDIO_FORMAT_PCM_32_BIT_MULTI | XRSR_AUDIO_FORMAT_PCM_RAW; + routes[i].dsts[0].stream_time_min = 0; + routes[i].dsts[0].stream_vad_mode = XRSR_STREAM_VOICE_ACTIVITY_MODE_DISABLED; + routes[i].dsts[0].stream_from = XRSR_STREAM_FROM_LIVE; + routes[i].dsts[0].stream_offset = 0; + routes[i].dsts[0].stream_until = XRSR_STREAM_UNTIL_END_OF_STREAM; + if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } - #endif // Set low latency websocket parameters routes[i].dsts[0].params[XRSR_POWER_MODE_FULL] = &this->prefs.dst_params_low_latency; @@ -311,24 +312,23 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { url->append("speech?"); } - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = url->c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = url->c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; #ifdef AUDIO_DECODE - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; #else - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_ADPCM; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM | XRSR_AUDIO_FORMAT_ADPCM; #endif - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; - #ifdef NETWORKED_STANDBY_MODE_ENABLED - if(src == XRSR_SRC_MICROPHONE) { + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; + if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } - #endif i++; } } @@ -342,20 +342,19 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = url->c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; - #ifdef NETWORKED_STANDBY_MODE_ENABLED - if(src == XRSR_SRC_MICROPHONE) { + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = url->c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; + if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } - #endif i++; } } else if(url->rfind("avs", 0) == 0) { @@ -367,20 +366,19 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { XLOGD_ERROR("failed to get handlers ws"); } else { - routes[i].src = src; - routes[i].dst_qty = 1; - routes[i].dsts[0].url = urls_translated[translated_index].c_str(); - routes[i].dsts[0].handlers = handlers_xrsr; - routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; - routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; - routes[i].dsts[0].stream_from = stream_from; - routes[i].dsts[0].stream_offset = stream_offset; - routes[i].dsts[0].stream_until = stream_until; - #ifdef NETWORKED_STANDBY_MODE_ENABLED - if(src == XRSR_SRC_MICROPHONE) { + routes[i].src = src; + routes[i].dst_qty = 1; + routes[i].dsts[0].url = urls_translated[translated_index].c_str(); + routes[i].dsts[0].handlers = handlers_xrsr; + routes[i].dsts[0].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[0].stream_time_min = this->prefs.utterance_duration_min; + routes[i].dsts[0].stream_vad_mode = stream_vad_mode; + routes[i].dsts[0].stream_from = stream_from; + routes[i].dsts[0].stream_offset = stream_offset; + routes[i].dsts[0].stream_until = stream_until; + if(networked_standby_supported && (src == XRSR_SRC_MICROPHONE)) { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } - #endif i++; } } diff --git a/src/voice/ctrlm_voice_types.h b/src/voice/ctrlm_voice_types.h index e8f86fe6..e65a49ba 100644 --- a/src/voice/ctrlm_voice_types.h +++ b/src/voice/ctrlm_voice_types.h @@ -31,12 +31,8 @@ typedef enum { CTRLM_VOICE_DEVICE_PTT, CTRLM_VOICE_DEVICE_FF, - #ifdef CTRLM_LOCAL_MIC CTRLM_VOICE_DEVICE_MICROPHONE, - #ifdef CTRLM_LOCAL_MIC_TAP CTRLM_VOICE_DEVICE_MICROPHONE_TAP, - #endif - #endif CTRLM_VOICE_DEVICE_INVALID, } ctrlm_voice_device_t; diff --git a/src/voice/endpoints/ctrlm_voice_endpoint.cpp b/src/voice/endpoints/ctrlm_voice_endpoint.cpp index 7815cd27..1aed8d46 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint.cpp +++ b/src/voice/endpoints/ctrlm_voice_endpoint.cpp @@ -63,7 +63,6 @@ void ctrlm_voice_endpoint_t::voice_stb_data_account_number_set(std::string &acco void ctrlm_voice_endpoint_t::voice_stb_data_device_id_set(std::string &device_id) {} void ctrlm_voice_endpoint_t::voice_stb_data_device_type_set(ctrlm_device_type_t device_type) {} void ctrlm_voice_endpoint_t::voice_stb_data_partner_id_set(std::string &partner_id) {} -void ctrlm_voice_endpoint_t::voice_stb_data_experience_set(std::string &experience) {} void ctrlm_voice_endpoint_t::voice_stb_data_guide_language_set(const char *language) {} void ctrlm_voice_endpoint_t::voice_stb_data_mask_pii_set(bool enable) {} diff --git a/src/voice/endpoints/ctrlm_voice_endpoint.h b/src/voice/endpoints/ctrlm_voice_endpoint.h index ac881880..3298348f 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint.h +++ b/src/voice/endpoints/ctrlm_voice_endpoint.h @@ -50,7 +50,6 @@ class ctrlm_voice_endpoint_t { virtual void voice_stb_data_device_id_set(std::string &device_id); virtual void voice_stb_data_device_type_set(ctrlm_device_type_t device_type); virtual void voice_stb_data_partner_id_set(std::string &partner_id); - virtual void voice_stb_data_experience_set(std::string &experience); virtual void voice_stb_data_guide_language_set(const char *language); virtual void voice_stb_data_mask_pii_set(bool enable); // End Data Setters diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_http.cpp b/src/voice/endpoints/ctrlm_voice_endpoint_http.cpp index 653e2260..c119ee43 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_http.cpp +++ b/src/voice/endpoints/ctrlm_voice_endpoint_http.cpp @@ -72,14 +72,12 @@ bool ctrlm_voice_endpoint_http_t::open() { std::string device_id = this->voice_obj->voice_stb_data_device_id_get(); std::string partner_id = this->voice_obj->voice_stb_data_partner_id_get(); - std::string experience = this->voice_obj->voice_stb_data_experience_get(); std::string app_id = this->voice_obj->voice_stb_data_app_id_http_get(); std::string language = this->voice_obj->voice_stb_data_guide_language_get().c_str(); xrsv_http_params_t params_http = { .device_id = device_id.c_str(), .partner_id = partner_id.c_str(), - .experience = experience.c_str(), .app_id = app_id.c_str(), .language = language.c_str(), .test_flag = this->voice_obj->voice_stb_data_test_get(), @@ -138,12 +136,6 @@ void ctrlm_voice_endpoint_http_t::voice_stb_data_partner_id_set(std::string &par } } -void ctrlm_voice_endpoint_http_t::voice_stb_data_experience_set(std::string &experience) { - if(this->xrsv_obj_http) { - xrsv_http_update_experience(this->xrsv_obj_http, experience.c_str()); - } -} - void ctrlm_voice_endpoint_http_t::voice_stb_data_guide_language_set(const char *language) { if(this->xrsv_obj_http) { xrsv_http_update_language(this->xrsv_obj_http, language); @@ -194,7 +186,7 @@ void ctrlm_voice_endpoint_http_t::voice_session_begin_callback_http(void *data, has_sat = true; } - XLOGD_TELEMETRY("session begin - src <%s> h_SAT <%s> h_MTLS <%s> h_OCSPst <%s> h_OCSPca <%s>", ctrlm_voice_device_str(source), has_sat ? "YES" : "NO", use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO"); + XLOGD_AUTOMATION_TELEMETRY("session begin - src <%s> h_SAT <%s> h_MTLS <%s> h_OCSPst <%s> h_OCSPca <%s>", ctrlm_voice_device_str(source), has_sat ? "YES" : "NO", use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO"); errno_t safec_rc = strcpy_s(this->user_agent, sizeof(this->user_agent), user_agent.str().c_str()); ERR_CHK(safec_rc); @@ -286,7 +278,7 @@ void ctrlm_voice_endpoint_http_t::voice_session_end_callback_http(void *data, in } // Check if HTTP was successful - if((stats->reason != XRSR_SESSION_END_REASON_EOS && stats->reason != XRSR_SESSION_END_REASON_TERMINATE && stats->reason != XRSR_SESSION_END_REASON_EOT) + if((stats->session_end_reason != XRSR_SESSION_END_REASON_EOS && stats->session_end_reason != XRSR_SESSION_END_REASON_TERMINATE && stats->session_end_reason != XRSR_SESSION_END_REASON_EOT) || (stats->ret_code_library != 0) || (stats->ret_code_protocol != 200) || (this->server_ret_code != 0)) { success = false; } diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_http.h b/src/voice/endpoints/ctrlm_voice_endpoint_http.h index 9338e20d..79a0d50d 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_http.h +++ b/src/voice/endpoints/ctrlm_voice_endpoint_http.h @@ -32,7 +32,6 @@ class ctrlm_voice_endpoint_http_t : public ctrlm_voice_endpoint_t { public: void voice_stb_data_device_id_set(std::string &device_id); void voice_stb_data_partner_id_set(std::string &partner_id); - void voice_stb_data_experience_set(std::string &experience); void voice_stb_data_guide_language_set(const char *language); void voice_stb_data_pii_mask_set(bool enable); diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_sdt.cpp b/src/voice/endpoints/ctrlm_voice_endpoint_sdt.cpp index ca4fe980..2324878c 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_sdt.cpp +++ b/src/voice/endpoints/ctrlm_voice_endpoint_sdt.cpp @@ -250,7 +250,7 @@ void ctrlm_voice_endpoint_sdt_t::voice_session_begin_callback_sdt(void *data, in keyword_verification = true; } } - XLOGD_INFO("session begin - ptt <%s> keyword begin <%u> end <%u> doa <%u> gain <%4.1f> db", (stream_params->push_to_talk ? "TRUE" : "FALSE"), stream_params->keyword_sample_begin, stream_params->keyword_sample_end, stream_params->keyword_doa, stream_params->dynamic_gain); + XLOGD_AUTOMATION_INFO("session begin - ptt <%s> keyword begin <%u> end <%u> doa <%u> gain <%4.1f> db", (stream_params->push_to_talk ? "TRUE" : "FALSE"), stream_params->keyword_sample_begin, stream_params->keyword_sample_end, stream_params->keyword_doa, stream_params->dynamic_gain); } // End handle stream parameters diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp index 248832cc..9abc27db 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp +++ b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp @@ -86,7 +86,6 @@ bool ctrlm_voice_endpoint_ws_nextgen_t::open() { } std::string device_id = this->voice_obj->voice_stb_data_device_id_get(); std::string partner_id = this->voice_obj->voice_stb_data_partner_id_get(); - std::string experience = this->voice_obj->voice_stb_data_experience_get(); std::string language = this->voice_obj->voice_stb_data_guide_language_get().c_str(); std::string account_number = this->voice_obj->voice_stb_data_account_number_get(); std::string device_mac = ctrlm_device_mac_get(); @@ -96,7 +95,6 @@ bool ctrlm_voice_endpoint_ws_nextgen_t::open() { .device_id = (device_id.empty() == false ? device_id.c_str() : NULL), .account_id = (account_number.empty() == false ? account_number.c_str() : NULL), .partner_id = (partner_id.empty() == false ? partner_id.c_str() : NULL), - .experience = (experience.empty() == false ? experience.c_str() : NULL), .audio_profile = controller_name_to_audio_profile(""), .audio_model = controller_name_to_audio_model(""), .language = language.c_str(), @@ -235,12 +233,6 @@ void ctrlm_voice_endpoint_ws_nextgen_t::voice_stb_data_partner_id_set(std::strin } } -void ctrlm_voice_endpoint_ws_nextgen_t::voice_stb_data_experience_set(std::string &experience) { - if(this->xrsv_obj_ws_nextgen) { - xrsv_ws_nextgen_update_experience(this->xrsv_obj_ws_nextgen, experience.c_str()); - } -} - void ctrlm_voice_endpoint_ws_nextgen_t::voice_stb_data_guide_language_set(const char *language) { if(this->xrsv_obj_ws_nextgen) { xrsv_ws_nextgen_update_language(this->xrsv_obj_ws_nextgen, language); @@ -365,7 +357,7 @@ void ctrlm_voice_endpoint_ws_nextgen_t::voice_session_begin_callback_ws_nextgen( } config_in.ws.app_config = stream_params_out; - XLOGD_TELEMETRY("session begin - src <%s> ptt <%s> w_SAT <%s> w_MTLS <%s> w_OCSPst <%s> w_OCSPca <%s> keyword begin <%u> end <%u> doa <%u> gain <%4.1f> db", ctrlm_voice_device_str(source), (stream_params->push_to_talk ? "TRUE" : "FALSE"), has_sat ? "YES" : "NO", use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO", stream_params->keyword_sample_begin, stream_params->keyword_sample_end, stream_params->keyword_doa, stream_params->dynamic_gain); + XLOGD_AUTOMATION_TELEMETRY("session begin - src <%s> ptt <%s> w_SAT <%s> w_MTLS <%s> w_OCSPst <%s> w_OCSPca <%s> keyword begin <%u> end <%u> doa <%u> gain <%4.1f> db", ctrlm_voice_device_str(source), (stream_params->push_to_talk ? "TRUE" : "FALSE"), has_sat ? "YES" : "NO", use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO", stream_params->keyword_sample_begin, stream_params->keyword_sample_end, stream_params->keyword_doa, stream_params->dynamic_gain); } else if(!is_mic || dqm->configuration.user_initiated) { xrsv_ws_nextgen_stream_params_t *stream_params = (xrsv_ws_nextgen_stream_params_t *)malloc(sizeof(xrsv_ws_nextgen_stream_params_t)); @@ -378,7 +370,7 @@ void ctrlm_voice_endpoint_ws_nextgen_t::voice_session_begin_callback_ws_nextgen( } config_in.ws.app_config = stream_params; - XLOGD_TELEMETRY("session begin - src <%s> ptt w_SAT <%s> w_MTLS <%s> w_OCSPst <%s> w_OCSPca <%s>", ctrlm_voice_device_str(source), has_sat ? "YES" : "NO", use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO"); + XLOGD_AUTOMATION_TELEMETRY("session begin - src <%s> ptt w_SAT <%s> w_MTLS <%s> w_OCSPst <%s> w_OCSPca <%s>", ctrlm_voice_device_str(source), has_sat ? "YES" : "NO", use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO"); } else { XLOGD_ERROR("session begin - invalid params - src <%s>", ctrlm_voice_device_str(source)); } @@ -460,7 +452,7 @@ void ctrlm_voice_endpoint_ws_nextgen_t::voice_session_end_callback_ws_nextgen(vo } // Check if WS was successful - if((stats->reason != XRSR_SESSION_END_REASON_EOS && stats->reason != XRSR_SESSION_END_REASON_EOT) || (this->server_ret_code != 0 && this->server_ret_code != 200)) { + if((stats->session_end_reason != XRSR_SESSION_END_REASON_EOS && stats->session_end_reason != XRSR_SESSION_END_REASON_EOT) || (this->server_ret_code != 0 && this->server_ret_code != 200)) { success = false; } diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h index 16667f2f..218e1d39 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h +++ b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h @@ -37,7 +37,6 @@ class ctrlm_voice_endpoint_ws_nextgen_t : public ctrlm_voice_endpoint_t { void voice_stb_data_device_id_set(std::string &device_id); void voice_stb_data_device_type_set(ctrlm_device_type_t device_type); void voice_stb_data_partner_id_set(std::string &partner_id); - void voice_stb_data_experience_set(std::string &experience); void voice_stb_data_guide_language_set(const char *language); void voice_stb_data_mask_pii_set(bool enable); diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nsp.cpp b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nsp.cpp index 45ad9714..a123c0d6 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nsp.cpp +++ b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nsp.cpp @@ -133,7 +133,7 @@ void ctrlm_voice_endpoint_ws_nsp_t::voice_session_begin_callback_ws_nsp(void *da config_in.ws.cert_revoked_allow = false; config_in.ws.ocsp_expired_allow = false; - XLOGD_TELEMETRY("session begin - src <%s> x_MTLS <%s> x_OCSPst <%s> x_OCSPca <%s>", ctrlm_voice_device_str(source), use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO"); + XLOGD_AUTOMATION_TELEMETRY("session begin - src <%s> x_MTLS <%s> x_OCSPst <%s> x_OCSPca <%s>", ctrlm_voice_device_str(source), use_mtls ? "YES" : "NO", ocsp_verify_stapling ? "YES" : "NO", ocsp_verify_ca ? "YES" : "NO"); ctrlm_voice_session_begin_cb_t session_begin; uuid_copy(session_begin.header.uuid, dqm->uuid); @@ -203,7 +203,7 @@ void ctrlm_voice_endpoint_ws_nsp_t::voice_session_end_callback_ws_nsp(void *data } // Check if WS was successful - if((stats->reason != XRSR_SESSION_END_REASON_DISCONNECT_REMOTE) || (this->server_ret_code != 0 && this->server_ret_code != 200)) { + if((stats->session_end_reason != XRSR_SESSION_END_REASON_DISCONNECT_REMOTE) || (this->server_ret_code != 0 && this->server_ret_code != 200)) { success = false; } diff --git a/src/voice/ipc/ctrlm_voice_ipc.h b/src/voice/ipc/ctrlm_voice_ipc.h index 969a8380..db8dc582 100644 --- a/src/voice/ipc/ctrlm_voice_ipc.h +++ b/src/voice/ipc/ctrlm_voice_ipc.h @@ -92,7 +92,8 @@ typedef enum { SESSION_END_SUCCESS, SESSION_END_FAILURE, SESSION_END_ABORT, - SESSION_END_SHORT_UTTERANCE + SESSION_END_SHORT_UTTERANCE, + SESSION_END_SILENT_UTTERANCE } ctrlm_voice_ipc_event_session_end_result_t; typedef struct { diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp index 1121e795..bdcc85d8 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp @@ -158,6 +158,18 @@ bool ctrlm_voice_ipc_iarm_legacy_t::session_end(const ctrlm_voice_ipc_event_sess ret = broadcast_iarm_event_legacy(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_SESSION_SHORT, &event, sizeof(event)); break; } + case SESSION_END_SILENT_UTTERANCE: { + ctrlm_voice_iarm_event_session_silent_t event = {0}; + event.api_revision = CTRLM_VOICE_IARM_BUS_API_REVISION; + event.network_id = session_end.common.network_id; + event.network_type = session_end.common.network_type; + event.controller_id = session_end.common.controller_id; + event.session_id = session_end.common.session_id_ctrlm; + event.reason = (ctrlm_voice_session_end_reason_t)session_end.reason; + event.return_code_internal = session_end.return_code_internal; + ret = broadcast_iarm_event_legacy(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_SESSION_SILENT, &event, sizeof(event)); + break; + } } } // Reset state @@ -228,4 +240,4 @@ IARM_Result_t update_settings(void *arg) { } return(IARM_RESULT_SUCCESS); -} \ No newline at end of file +} diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp index ad9cc5db..b148dda5 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp @@ -62,6 +62,7 @@ #define JSON_SESSION_END_RESULT_ERROR "error" #define JSON_SESSION_END_RESULT_ABORT "abort" #define JSON_SESSION_END_RESULT_SHORT "shortUtterance" +#define JSON_SESSION_END_RESULT_SILENT "silentUtterance" #define JSON_SESSION_END_TRANSCRIPTION "transcription" #define JSON_SESSION_END_PROTOCOL_ERROR "protocolErrorCode" #define JSON_SESSION_END_PROTOCOL_LIBRARY_ERROR "protocolLibraryErrorCode" @@ -71,6 +72,7 @@ #define JSON_SESSION_END_ERROR_REASON "reason" #define JSON_SESSION_END_ABORT_REASON "reason" #define JSON_SESSION_END_SHORT_REASON "reason" +#define JSON_SESSION_END_SILENT_REASON "reason" #define JSON_SESSION_END_STB_STATS "stbStats" #define JSON_SESSION_END_STB_STATS_TYPE "type" #define JSON_SESSION_END_STB_STATS_FIRMWARE "firmware" @@ -87,7 +89,7 @@ static const char *voice_device_str(ctrlm_voice_device_t device); static const char *voice_device_status_str(uint8_t status); ctrlm_voice_ipc_iarm_thunder_t::ctrlm_voice_ipc_iarm_thunder_t(ctrlm_voice_t *obj_voice): ctrlm_voice_ipc_t(obj_voice) { - + set_api_revision(CTRLM_VOICE_IARM_BUS_API_REVISION); } bool ctrlm_voice_ipc_iarm_thunder_t::register_ipc() const { @@ -98,24 +100,31 @@ bool ctrlm_voice_ipc_iarm_thunder_t::register_ipc() const { if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_STATUS, &ctrlm_voice_ipc_iarm_thunder_t::status)) { ret = false; } + if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_CONFIGURE_VOICE, &ctrlm_voice_ipc_iarm_thunder_t::configure_voice)) { ret = false; } + if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_SET_VOICE_INIT, &ctrlm_voice_ipc_iarm_thunder_t::set_voice_init)) { ret = false; } + if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_SEND_VOICE_MESSAGE, &ctrlm_voice_ipc_iarm_thunder_t::send_voice_message)) { ret = false; } + if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_SESSION_TYPES, &ctrlm_voice_ipc_iarm_thunder_t::voice_session_types)) { ret = false; } + if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_SESSION_REQUEST, &ctrlm_voice_ipc_iarm_thunder_t::voice_session_request)) { ret = false; } + if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_SESSION_TERMINATE, &ctrlm_voice_ipc_iarm_thunder_t::voice_session_terminate)) { ret = false; } + if(!register_iarm_call(CTRLM_VOICE_IARM_CALL_SESSION_AUDIO_STREAM_START, &ctrlm_voice_ipc_iarm_thunder_t::voice_session_audio_stream_start)) { ret = false; } @@ -142,7 +151,7 @@ bool ctrlm_voice_ipc_iarm_thunder_t::session_begin(const ctrlm_voice_ipc_event_s if(json_str) { //TODO: surface the event through IARM XLOGD_INFO("%s", json_str); - ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_BUS_API_REVISION, CTRLM_VOICE_IARM_EVENT_JSON_SESSION_BEGIN, json_str); + ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_JSON_SESSION_BEGIN, json_str); free(json_str); } else { XLOGD_ERROR("Failed to encode JSON string"); @@ -171,7 +180,7 @@ bool ctrlm_voice_ipc_iarm_thunder_t::stream_begin(const ctrlm_voice_ipc_event_st if(json_str) { //TODO: surface the event through IARM XLOGD_INFO("%s", json_str); - ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_BUS_API_REVISION, CTRLM_VOICE_IARM_EVENT_JSON_STREAM_BEGIN, json_str); + ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_JSON_STREAM_BEGIN, json_str); free(json_str); } else { XLOGD_ERROR("Failed to encode JSON string"); @@ -201,7 +210,7 @@ bool ctrlm_voice_ipc_iarm_thunder_t::stream_end(const ctrlm_voice_ipc_event_stre if(json_str) { //TODO: surface the event through IARM XLOGD_INFO("%s", json_str); - ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_BUS_API_REVISION, CTRLM_VOICE_IARM_EVENT_JSON_STREAM_END, json_str); + ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_JSON_STREAM_END, json_str); free(json_str); } else { XLOGD_ERROR("Failed to encode JSON string"); @@ -284,6 +293,20 @@ bool ctrlm_voice_ipc_iarm_thunder_t::session_end(const ctrlm_voice_ipc_event_ses } break; } + case SESSION_END_SILENT_UTTERANCE: { + int rc_silent; + rc |= json_object_set_new_nocheck(event_data, JSON_SESSION_END_RESULT, json_string(JSON_SESSION_END_RESULT_SILENT)); + + // Add Audio Silent Data to result object + rc_silent = json_object_set_new_nocheck(event_result, JSON_SESSION_END_SILENT_REASON, json_integer(session_end.reason)); + if(0 != rc_silent) { + XLOGD_ERROR("Error creating audio silent JSON subobject"); + JSON_DEREFERENCE(event_result); + } else { + rc |= json_object_set_new_nocheck(event_data, JSON_SESSION_END_RESULT_SILENT, event_result); + } + break; + } } if(session_end.stb_stats) { int stats_rc; @@ -324,8 +347,8 @@ bool ctrlm_voice_ipc_iarm_thunder_t::session_end(const ctrlm_voice_ipc_event_ses char *json_str = json_dumps(event_data, JSON_ENCODE_FLAGS); if(json_str) { //TODO: surface the event through IARM - XLOGD_INFO("<%s>", this->obj_voice->voice_stb_data_pii_mask_get() ? "***" : json_str); - ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_BUS_API_REVISION, CTRLM_VOICE_IARM_EVENT_JSON_SESSION_END, json_str); + XLOGD_AUTOMATION_INFO("<%s>", this->obj_voice->voice_stb_data_pii_mask_get() ? "***" : json_str); + ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_JSON_SESSION_END, json_str); free(json_str); } else { XLOGD_ERROR("Failed to encode JSON string"); @@ -340,8 +363,8 @@ bool ctrlm_voice_ipc_iarm_thunder_t::session_end(const ctrlm_voice_ipc_event_ses bool ctrlm_voice_ipc_iarm_thunder_t::server_message(const char *message, unsigned long size) { bool ret = false; if(message) { - XLOGD_INFO("%ul : <%s>", size, this->obj_voice->voice_stb_data_pii_mask_get() ? "***" : message); //CID -160950 - Printargs - ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_BUS_API_REVISION, CTRLM_VOICE_IARM_EVENT_JSON_SERVER_MESSAGE, message); + XLOGD_AUTOMATION_INFO("%ul : <%s>", size, this->obj_voice->voice_stb_data_pii_mask_get() ? "***" : message); //CID -160950 - Printargs + ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_JSON_SERVER_MESSAGE, message); } return(ret); } @@ -364,7 +387,7 @@ bool ctrlm_voice_ipc_iarm_thunder_t::keyword_verification(const ctrlm_voice_ipc_ if(json_str) { //TODO: surface the event through IARM XLOGD_INFO("%s", json_str); - ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_BUS_API_REVISION, CTRLM_VOICE_IARM_EVENT_JSON_KEYWORD_VERIFICATION, json_str); + ret = broadcast_iarm_event(CTRLM_MAIN_IARM_BUS_NAME, CTRLM_VOICE_IARM_EVENT_JSON_KEYWORD_VERIFICATION, json_str); free(json_str); } else { XLOGD_ERROR("Failed to encode JSON string"); @@ -411,9 +434,7 @@ IARM_Result_t ctrlm_voice_ipc_iarm_thunder_t::status(void *data) { rc |= json_object_set_new_nocheck(obj, JSON_URL_PTT, json_string(status.urlPtt.c_str())); rc |= json_object_set_new_nocheck(obj, JSON_URL_HF, json_string(status.urlHf.c_str())); - #ifdef CTRLM_LOCAL_MIC_TAP rc |= json_object_set_new_nocheck(obj, JSON_URL_MIC_TAP, json_string(status.urlMicTap.c_str())); - #endif rc |= json_object_set_new_nocheck(obj, JSON_WW_FEEDBACK, status.wwFeedback ? json_true() : json_false()); rc |= json_object_set_new_nocheck(obj, JSON_PRV, status.prv_enabled ? json_true() : json_false()); rc |= json_object_set_new_nocheck(obj, JSON_THUNDER_RESULT, json_true()); @@ -553,14 +574,16 @@ IARM_Result_t ctrlm_voice_ipc_iarm_thunder_t::voice_session_types(void *data) { int rc = json_array_append_new(obj_types, json_string("ptt_transcription")); rc |= json_array_append_new(obj_types, json_string("ptt_audio_file")); - #ifdef CTRLM_LOCAL_MIC - rc |= json_array_append_new(obj_types, json_string("mic_audio_file")); - rc |= json_array_append_new(obj_types, json_string("mic_stream_single")); - rc |= json_array_append_new(obj_types, json_string("mic_stream_multi")); - rc |= json_array_append_new(obj_types, json_string("mic_tap_stream_single")); - rc |= json_array_append_new(obj_types, json_string("mic_tap_stream_multi")); - rc |= json_array_append_new(obj_types, json_string("mic_factory_test")); - #endif + if(voice_obj->voice_stb_data_local_mic_get()) { + rc |= json_array_append_new(obj_types, json_string("mic_audio_file")); + rc |= json_array_append_new(obj_types, json_string("mic_stream_single")); + rc |= json_array_append_new(obj_types, json_string("mic_stream_multi")); + if(voice_obj->voice_stb_data_local_mic_tap_get()) { + rc |= json_array_append_new(obj_types, json_string("mic_tap_stream_single")); + rc |= json_array_append_new(obj_types, json_string("mic_tap_stream_multi")); + } + rc |= json_array_append_new(obj_types, json_string("mic_factory_test")); + } rc |= json_object_set_new_nocheck(obj_result, JSON_TYPES, obj_types); rc |= json_object_set_new_nocheck(obj_result, JSON_THUNDER_RESULT, json_true()); @@ -760,7 +783,8 @@ IARM_Result_t ctrlm_voice_ipc_iarm_thunder_t::voice_session_request(void *data) ctrlm_voice_session_response_status_t voice_status = voice_obj->voice_session_req( CTRLM_MAIN_NETWORK_ID_INVALID, CTRLM_MAIN_CONTROLLER_ID_INVALID, request_config.device, request_config.format, NULL, str_name_of_source.c_str(), "0.0.0.0", "0.0.0.0", 0.0, - false, NULL, NULL, NULL, (fd >= 0) ? true : false, true, str_transcription.empty() ? NULL : str_transcription.c_str(), str_audio_file.empty() ? NULL : str_audio_file.c_str(), &request_uuid, request_config.low_latency, request_config.low_cpu_util, fd); + false, NULL, NULL, NULL, (fd >= 0) ? true : false, true, NULL, NULL, + str_transcription.empty() ? NULL : str_transcription.c_str(), str_audio_file.empty() ? NULL : str_audio_file.c_str(), &request_uuid, request_config.low_latency, request_config.low_cpu_util, fd); if (voice_status != VOICE_SESSION_RESPONSE_AVAILABLE && voice_status != VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE) { XLOGD_ERROR("Failed opening voice session <%s>", ctrlm_voice_session_response_status_str(voice_status)); @@ -931,12 +955,8 @@ const char *voice_device_str(ctrlm_voice_device_t device) { switch(device) { case CTRLM_VOICE_DEVICE_PTT: return("ptt"); case CTRLM_VOICE_DEVICE_FF: return("ff"); - #ifdef CTRLM_LOCAL_MIC case CTRLM_VOICE_DEVICE_MICROPHONE: return("mic"); - #ifdef CTRLM_LOCAL_MIC_TAP case CTRLM_VOICE_DEVICE_MICROPHONE_TAP: return("mic_tap"); - #endif - #endif default: break; } return("invalid"); @@ -989,9 +1009,9 @@ bool ctrlm_voice_ipc_request_supported_mic_transcription(ctrlm_voice_ipc_request } bool ctrlm_voice_ipc_request_supported_mic_audio_file(ctrlm_voice_ipc_request_config_t *config) { - #ifndef CTRLM_LOCAL_MIC - return(false); - #else + if(!ctrlm_get_voice_obj()->voice_stb_data_local_mic_get()) { + return(false); + } config->requires_transcription = false; config->requires_audio_file = true; config->supports_named_pipe = false; @@ -1000,7 +1020,6 @@ bool ctrlm_voice_ipc_request_supported_mic_audio_file(ctrlm_voice_ipc_request_co config->low_latency = false; config->low_cpu_util = false; return(true); - #endif } bool ctrlm_voice_ipc_request_supported_mic_stream_default(ctrlm_voice_ipc_request_config_t *config) { @@ -1019,9 +1038,9 @@ bool ctrlm_voice_ipc_request_supported_mic_stream_default(ctrlm_voice_ipc_reques } bool ctrlm_voice_ipc_request_supported_mic_stream_single(ctrlm_voice_ipc_request_config_t *config) { - #ifndef CTRLM_LOCAL_MIC - return(false); - #else + if(!ctrlm_get_voice_obj()->voice_stb_data_local_mic_get()) { + return(false); + } config->requires_transcription = false; config->requires_audio_file = false; config->supports_named_pipe = false; @@ -1030,13 +1049,12 @@ bool ctrlm_voice_ipc_request_supported_mic_stream_single(ctrlm_voice_ipc_request config->low_latency = true; config->low_cpu_util = false; return(true); - #endif } bool ctrlm_voice_ipc_request_supported_mic_stream_multi(ctrlm_voice_ipc_request_config_t *config) { - #ifndef CTRLM_LOCAL_MIC - return(false); - #else + if(!ctrlm_get_voice_obj()->voice_stb_data_local_mic_get()) { + return(false); + } config->requires_transcription = false; config->requires_audio_file = false; config->supports_named_pipe = false; @@ -1045,13 +1063,12 @@ bool ctrlm_voice_ipc_request_supported_mic_stream_multi(ctrlm_voice_ipc_request_ config->low_latency = true; config->low_cpu_util = false; return(true); - #endif } bool ctrlm_voice_ipc_request_supported_mic_tap_stream_single(ctrlm_voice_ipc_request_config_t *config) { - #ifndef CTRLM_LOCAL_MIC_TAP - return(false); - #else + if(!ctrlm_get_voice_obj()->voice_stb_data_local_mic_tap_get()) { + return(false); + } config->requires_transcription = false; config->requires_audio_file = false; config->supports_named_pipe = false; @@ -1060,13 +1077,12 @@ bool ctrlm_voice_ipc_request_supported_mic_tap_stream_single(ctrlm_voice_ipc_req config->low_latency = true; config->low_cpu_util = true; return(true); - #endif } bool ctrlm_voice_ipc_request_supported_mic_tap_stream_multi(ctrlm_voice_ipc_request_config_t *config) { - #ifndef CTRLM_LOCAL_MIC_TAP - return(false); - #else + if(!ctrlm_get_voice_obj()->voice_stb_data_local_mic_tap_get()) { + return(false); + } config->requires_transcription = false; config->requires_audio_file = false; config->supports_named_pipe = false; @@ -1075,20 +1091,22 @@ bool ctrlm_voice_ipc_request_supported_mic_tap_stream_multi(ctrlm_voice_ipc_requ config->low_latency = true; config->low_cpu_util = true; return(true); - #endif } bool ctrlm_voice_ipc_request_supported_mic_factory_test(ctrlm_voice_ipc_request_config_t *config) { - #ifdef CTRLM_LOCAL_MIC_TAP - config->requires_transcription = false; - config->requires_audio_file = false; - config->supports_named_pipe = false; - config->device = CTRLM_VOICE_DEVICE_MICROPHONE_TAP; - config->format = { .type = CTRLM_VOICE_FORMAT_PCM_RAW }; - config->low_latency = true; - config->low_cpu_util = true; - return(true); - #elif defined(CTRLM_LOCAL_MIC) + if(ctrlm_get_voice_obj()->voice_stb_data_local_mic_tap_get()) { + config->requires_transcription = false; + config->requires_audio_file = false; + config->supports_named_pipe = false; + config->device = CTRLM_VOICE_DEVICE_MICROPHONE_TAP; + config->format = { .type = CTRLM_VOICE_FORMAT_PCM_RAW }; + config->low_latency = true; + config->low_cpu_util = true; + return(true); + } + if(!ctrlm_get_voice_obj()->voice_stb_data_local_mic_get()) { + return(false); + } config->requires_transcription = false; config->requires_audio_file = false; config->supports_named_pipe = false; @@ -1097,7 +1115,4 @@ bool ctrlm_voice_ipc_request_supported_mic_factory_test(ctrlm_voice_ipc_request_ config->low_latency = true; config->low_cpu_util = false; return(true); - #else - return(false); - #endif } diff --git a/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp b/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp index e7ac6e71..041d420a 100644 --- a/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp +++ b/src/voice/telemetry/ctrlm_voice_telemetry_events.cpp @@ -206,11 +206,16 @@ bool ctrlm_voice_telemetry_session_t::event() { ss << m_samples_lost << ","; ss << m_decoder_failures << ","; ss << m_samples_buffered_max << ","; - ss << m_end_reason_stream << ","; - ss << m_end_reason_protocol << ","; + ss << m_end_reason_rcu << ","; + ss << m_end_reason_session << ","; ss << m_end_reason_server << ","; ss << "\"" << m_server_message << "\","; - ss << m_result << "]]"; + ss << m_result << ","; + ss << m_end_reason_stream << ","; + ss << m_ret_code_protocol << ","; + ss << m_voice_detected << ","; + ss << m_peak_confidence << ","; + ss << m_peak_rms_level << "]]"; if(m_event_list.length() + ss.str().length() > m_event_list_max_size) { // Maximum data size exceeded XLOGD_WARN("telemetry event exceeds max size <%s,%s>", val_marker.c_str(), ss.str().c_str()); @@ -265,7 +270,7 @@ void ctrlm_voice_telemetry_session_t::update_on_key_release(int32_t time_start_l m_has_key_release = true; } -void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_len_act, uint32_t packets_total, uint32_t packets_lost, uint32_t samples_total, uint32_t samples_lost, uint32_t decoder_failures, uint32_t samples_buffered_max) { +void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_len_act, uint32_t packets_total, uint32_t packets_lost, uint32_t samples_total, uint32_t samples_lost, uint32_t decoder_failures, uint32_t samples_buffered_max, int32_t voice_detected, uint32_t peak_confidence, int32_t peak_rms_level) { m_time_stream_len_act = time_stream_len_act; m_packets_total = packets_total; m_packets_lost = packets_lost; @@ -274,17 +279,25 @@ void ctrlm_voice_telemetry_session_t::update_on_stream_end(uint32_t time_stream_ m_decoder_failures = decoder_failures; m_samples_buffered_max = samples_buffered_max; + if(voice_detected >= 0) { + m_voice_detected = voice_detected; + m_peak_confidence = peak_confidence; + m_peak_rms_level = peak_rms_level; + } + if(m_has_key_release) { m_time_stream_delta = m_time_stream_len_act - m_time_stream_len_exp; } } -bool ctrlm_voice_telemetry_session_t::update_on_session_end(bool result, int32_t end_reason_stream, int32_t end_reason_protocol, int32_t end_reason_server, const std::string &server_message, int32_t time_stream_len_exp) { +bool ctrlm_voice_telemetry_session_t::update_on_session_end(bool result, int32_t end_reason_rcu, int32_t end_reason_session, int32_t end_reason_server, const std::string &server_message, int32_t time_stream_len_exp, int32_t ret_code_protocol, int32_t stream_end_reason) { m_result = result; - m_end_reason_stream = end_reason_stream; - m_end_reason_protocol = end_reason_protocol; + m_end_reason_rcu = end_reason_rcu; + m_end_reason_session = end_reason_session; m_end_reason_server = end_reason_server; m_server_message = server_message; + m_ret_code_protocol = ret_code_protocol; + m_end_reason_stream = stream_end_reason; if(!m_has_key_release) { // if there is no key release, the start time and end time are not known rdkx_timestamp_get(&m_time_prev_session_end); @@ -310,10 +323,15 @@ void ctrlm_voice_telemetry_session_t::reset_stats() { m_decoder_failures = 0; m_samples_buffered_max = 0; - m_end_reason_stream = 0; - m_end_reason_protocol = 0; + m_end_reason_rcu = 0; + m_end_reason_session = 0; m_end_reason_server = 0; m_result = false; + m_ret_code_protocol = 0; + + m_voice_detected = -1; + m_peak_confidence = 0; + m_peak_rms_level = 0; m_server_message.clear(); m_device_type.clear(); diff --git a/src/voice/telemetry/ctrlm_voice_telemetry_events.h b/src/voice/telemetry/ctrlm_voice_telemetry_events.h index d8bf90dd..18fcab29 100644 --- a/src/voice/telemetry/ctrlm_voice_telemetry_events.h +++ b/src/voice/telemetry/ctrlm_voice_telemetry_events.h @@ -75,8 +75,8 @@ class ctrlm_voice_telemetry_session_t : public ctrlm_telemetry_event_t