diff --git a/CHANGELOG.md b/CHANGELOG.md index 7979a87ca..a04f17b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,35 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.3.9](https://github.com/rdkcentral/tr69hostif/compare/1.3.8...1.3.9) + +- Add rrd enable default value to false [`#443`](https://github.com/rdkcentral/tr69hostif/pull/443) +- tr69hostif: Add Document for L2 Coverage and Thunder Plugin details [`#442`](https://github.com/rdkcentral/tr69hostif/pull/442) +- RDKEMW-15382 Crash observed in hostif [`#427`](https://github.com/rdkcentral/tr69hostif/pull/427) +- tr69hostif - Updated Runtime Dependencies and JSON usage [`#437`](https://github.com/rdkcentral/tr69hostif/pull/437) +- Merge tag '1.3.8' into develop [`1b5fe07`](https://github.com/rdkcentral/tr69hostif/commit/1b5fe07477da9823ec145a667ec7b3029f019961) + +#### [1.3.8](https://github.com/rdkcentral/tr69hostif/compare/1.3.7...1.3.8) + +> 20 March 2026 + +- tr69hostif - Detailed Documentation for the Component Modules [`#432`](https://github.com/rdkcentral/tr69hostif/pull/432) +- RDKEMW-15684 : Updated Hotel related handlers to match plugin output. [`#431`](https://github.com/rdkcentral/tr69hostif/pull/431) +- RDKEMW-14971 : Bring Data Model Parameters Missing in RDKE Stack [`#383`](https://github.com/rdkcentral/tr69hostif/pull/383) +- RDKEMW-14825: WifiReset DataModel Params missing on RDKE Builds [`#397`](https://github.com/rdkcentral/tr69hostif/pull/397) +- tr69hostif 1.3.7 release changelog updates [`#424`](https://github.com/rdkcentral/tr69hostif/pull/424) +- tr69hostif 1.3.7 release changelog updates [`#423`](https://github.com/rdkcentral/tr69hostif/pull/423) +- tr69hostif 1.3.8 release changelog updates [`78bbbe0`](https://github.com/rdkcentral/tr69hostif/commit/78bbbe04b80f2cd4c94fa497cd7ac7a4e650b094) +- Potential fix for pull request finding [`8fc7daa`](https://github.com/rdkcentral/tr69hostif/commit/8fc7daa294bba9eee4e1a11c8a03b4492d6daacf) +- Merge tag '1.3.7' into develop [`8e69c43`](https://github.com/rdkcentral/tr69hostif/commit/8e69c43f6bfe0327ca858a002c3ea7f810ce3c78) + #### [1.3.7](https://github.com/rdkcentral/tr69hostif/compare/1.3.6...1.3.7) +> 13 March 2026 + - RDKEMW-14686: Fix the wifi signal strength api calls [`#416`](https://github.com/rdkcentral/tr69hostif/pull/416) - tr69hostif 1.3.6 release changelog updates [`#418`](https://github.com/rdkcentral/tr69hostif/pull/418) +- tr69hostif 1.3.7 release changelog updates [`4db557f`](https://github.com/rdkcentral/tr69hostif/commit/4db557f97f312720e1dc64abd0a1c22b70ed4814) - Merge tag '1.3.6' into develop [`635237a`](https://github.com/rdkcentral/tr69hostif/commit/635237a63734e7c2f917850cc36cc0bde1b30ef0) #### [1.3.6](https://github.com/rdkcentral/tr69hostif/compare/1.3.5...1.3.6) diff --git a/docs/README.md b/docs/README.md index 62ee25d27..73b17bc1b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ This directory contains implementation-oriented documentation for the tr69hostif ### Architecture - [System Overview](architecture/overview.md) describes the daemon's major components, startup sequence, and runtime boundaries. +- [JSON Usage](architecture/json-usage.md) maps the module's JSON request, config, notification, and JSON-RPC paths and records the current robustness gaps. - [Threading Model](architecture/threading-model.md) documents worker threads, synchronization primitives, and shutdown behavior. - [Data Flow](architecture/data-flow.md) traces request routing, event propagation, and RFC/bootstrap precedence. @@ -27,6 +28,101 @@ This directory contains implementation-oriented documentation for the tr69hostif The pages in this directory are intentionally implementation-specific. They reference the current source layout under `src/hostif/`, the shipped config files under `conf/`, and the repo-maintained validation scripts such as `run_ut.sh` and `run_l2.sh`. +## Dependent Components + +The `tr69hostif` module depends on a mix of middleware services, platform-facing components, parser and transport libraries, and runtime data sources. The list below is intended as a module-wide dependency map for readers navigating the rest of the documentation. + +### Core Middleware And IPC Dependencies + +| Component | Role in tr69hostif | +|-----------|--------------------| +| IARM Bus (`libIARMBus`) | Primary local IPC path for TR-181 get/set requests, notifications, and manager integration | +| RBUS (`librbus`) | Optional data-model provider path and fallback routing for selected parameter flows | +| Parodus (`libparodus`) | WebPA message transport, request ingress, and outbound notification delivery | +| WRP-C (`libwrp-c`) | WebPA and Parodus message envelope handling | +| WDMP-C (`libwdmp-c`) | WDMP request parsing and response formatting for the newer HTTP JSON flow | + +### Core Runtime Libraries + +| Component | Role in tr69hostif | +|-----------|--------------------| +| GLib / GThread | Main loop integration, worker threads, async queues, and utility containers | +| libsoup 3 | Local HTTP server implementation for the current JSON request path | +| YAJL | Legacy local JSON request parsing in the older handler thread | +| cJSON | JSON parsing and serialization for HTTP, Parodus, config files, notifications, and JSON-RPC consumers | +| libcurl | Thunder JSON-RPC requests and other HTTP-based helper flows | +| libtinyxml2 | Data-model and XML-related helper parsing used by the module build and runtime flows | +| libsecure_wrapper | Safe file, process, and string helper wrappers used across the daemon | + +### External Services And Platform Components + +| Component | Role in tr69hostif | +|-----------|--------------------| +| ACS / CWMP stack | Remote management plane that reaches the daemon through local IPC paths | +| WebPA gateway | Remote management plane that uses the Parodus integration path | +| Parodus daemon | Local broker service required for WebPA request and notification exchange | +| Device Settings / DS HAL | Backing implementation for `STBService` and selected device state queries | +| WiFi HAL / WiFi manager | Backing implementation for the `Device.WiFi.*` profile | +| MoCA HAL | Backing implementation for the `Device.MoCA.*` profile when enabled | +| SNMP stack | Used by the SNMP adapter to expose mapped TR-181 values through OIDs | +| systemd notify | Optional readiness signaling for service startup integration | + +### Thunder JSON-RPC Runtime Dependencies + +The JSON-RPC helper in `tr69hostif` posts to the local Thunder endpoint at `http://127.0.0.1:9998/jsonrpc` and expects specific plugin callsigns and methods to be available at runtime. These are not link-time dependencies of the daemon binary, but they are runtime service dependencies for JSON-RPC-backed parameter retrieval. + +The most direct runtime repository dependency in this path is `rdkcentral/networkmanager`, because `tr69hostif` actively calls the `org.rdk.NetworkManager` Thunder plugin for interface enumeration, primary-interface selection, IP settings lookup, interface enable or disable flows, and connected-SSID retrieval. + +| JSON-RPC plugin or API family | Used by tr69hostif for | Repository relationship | +|--------------------------------|------------------------|-------------------------| +| `org.rdk.NetworkManager` | `GetPrimaryInterface`, `GetIPSettings`, `GetAvailableInterfaces`, `EnableInterface`, `DisableInterface`, `GetConnectedSSID` | Primary runtime implementation is provided by the `rdkcentral/networkmanager` Thunder plugin repository, which exposes `org.rdk.NetworkManager` over JSON-RPC and COM-RPC | +| `org.rdk.System` | system-level data such as privacy mode and other device state helpers | API contract is documented in `rdkcentral/entservices-apis`; the deployed runtime plugin is provided through the SystemServices plugin line, currently tracked in `rdkcentral/entservices-systemservices` | +| `org.rdk.Account` | account-related values such as checkout reset time | API contract is documented in `rdkcentral/entservices-apis`; device images must include the corresponding Thunder plugin implementation | +| `org.rdk.AuthService` | service account and experience-related data | API contract is documented in `rdkcentral/entservices-apis`; device images must include the corresponding Thunder plugin implementation | +| `org.rdk.MigrationPreparer` | migration readiness and component-readiness state | device images must include the corresponding Thunder plugin implementation for migration-preparer flows | +| `rdkcentral/entservices-apis` | source of Ent Services API definitions used by the Thunder JSON-RPC ecosystem | API-definition repository, not by itself a runtime plugin implementation | + +In practice, the JSON-RPC path depends on two layers being present on the device image: + +- the Thunder or WPEFramework service host +- the specific plugin implementations that back the callsigns used by `tr69hostif` + +For the current source tree, the known JSON-RPC method usage is concentrated in DeviceInfo and WiFi profile code and includes these method families: + +- `org.rdk.NetworkManager.*` +- `org.rdk.System.*` +- `org.rdk.Account.*` +- `org.rdk.AuthService.*` +- `org.rdk.MigrationPreparer.*` + +For `org.rdk.NetworkManager.*`, the dependency should be read as a concrete runtime dependency on the `rdkcentral/networkmanager` plugin repository rather than only as an Ent Services API reference. That repository owns the Thunder method surface used by `tr69hostif`, including `GetAvailableInterfaces`, `GetPrimaryInterface`, `GetIPSettings`, `SetInterfaceState`, and `GetConnectedSSID`. + +When these plugins are absent, disabled, renamed, or version-mismatched, the affected `tr69hostif` parameters fall back to empty or failed JSON-RPC responses. + +### Runtime Data And Configuration Dependencies + +| Component | Role in tr69hostif | +|-----------|--------------------| +| `mgrlist.conf` / `tr69hostIf.conf` | Manager-to-parameter routing map and runtime defaults | +| TR-181 data-model XML files | Source model fragments merged into the runtime data model | +| `partners_defaults.json` | Partner-specific bootstrap defaults consumed by the bootstrap store | +| `/opt/secure/RFC/` store | RFC override and bootstrap persistence area | +| `webpa_cfg.json` | WebPA and Parodus runtime configuration | +| `notify_webpa_cfg.json` | Initial WebPA notification subscription list | +| `tr181_snmpOID.conf` | SNMP OID to TR-181 mapping for the SNMP adapter | + +### Feature-Scoped Internal Components + +| Internal component | Dependency focus | +|--------------------|------------------| +| Request handlers under `src/hostif/handlers/` | IARM, RBUS, common dispatcher, notification routing | +| Profiles under `src/hostif/profiles/` | Platform HALs, sysfs, process utilities, JSON-RPC helpers, bootstrap stores | +| Parodus client under `src/hostif/parodusClient/` | Parodus, WRP-C, WDMP-C, notification config, data-model lookup | +| HTTP server under `src/hostif/httpserver/` | libsoup 3, WDMP-C, cJSON, WAL DB support | +| SNMP adapter under `src/hostif/snmpAdapter/` | SNMP OID map, local TR-181 parameter access, GLib threading support | + +For build-time package expectations and runtime file prerequisites, see [Build Setup](integration/build-setup.md). For component-specific dependency details, use the documentation under `src/hostif/**/docs/`. + ## Maintenance Rules - Update the relevant page when thread ownership, feature flags, or request routing changes. diff --git a/docs/api/thunder-plugin-interfaces.md b/docs/api/thunder-plugin-interfaces.md new file mode 100644 index 000000000..7768605bb --- /dev/null +++ b/docs/api/thunder-plugin-interfaces.md @@ -0,0 +1,655 @@ +# Thunder Plugin Interfaces via curl + +## Overview + +tr69hostif communicates with the Thunder (WPEFramework) runtime over a local JSON-RPC HTTP +endpoint using `libcurl`. All TR-181 parameter handlers that require live device state—network, +Wi-Fi, authentication, privacy—issue JSON-RPC 2.0 POST requests to Thunder and parse the +JSON response before returning the parameter value to the TR-069/CWMP stack. + +**At a glance:** 5 Thunder plugins · 13 methods · 21 TR-181 parameters + +## Architecture + +```mermaid +flowchart LR + A[TR-181 Profile Handler] --> B[getJsonRPCData\nhostIf_utils.cpp] + B --> C[libcurl\ncurl_easy_perform] + C --> D[Thunder JSON-RPC\nhttp://127.0.0.1:9998/jsonrpc] + D --> E[org.rdk.* Plugin] + E --> D + D --> C + C --> B + B --> F[cJSON parse] + F --> A +``` + +## Current Handler Workflow and Parse Logic + +The current implementation centralizes only the HTTP transport in `getJsonRPCData()`. Each +handler still constructs its own JSON-RPC request body, parses the raw response with `cJSON`, +walks the response tree, validates result fields, and maps those fields into `HOSTIF_MsgData_t`. + +```mermaid +flowchart TD + A[TR-181 GET or SET handler] --> B[Build JSON-RPC request string inline] + B --> C[getJsonRPCData in hostIf_utils.cpp] + C --> D[get_security_token] + D --> E[WPEFrameworkSecurityUtility] + C --> F[libcurl POST to /jsonrpc] + F --> G[Thunder plugin org.rdk.*] + G --> H[Raw JSON response string] + H --> I[cJSON_Parse inside handler] + I --> J[result lookup] + J --> K[field lookup and type checks] + K --> L[Convert to TR-181 output type] + L --> M[Populate HOSTIF_MsgData_t] + + I -. duplicated across handlers .-> N[Repeated parse/validation code] + K -. inconsistent checks .-> N +``` + +### Parse Flow Seen in Current Code + +Representative handlers follow the same pattern: + +1. Build a JSON string inline for a specific method call. +2. Call `getJsonRPCData()` to get a raw response buffer. +3. Parse the response with `cJSON_Parse(response.c_str())`. +4. Read `result` and then one or more nested keys such as `interfaces`, `enabled`, `ssid`, `strength`, `ipaddress`, or `success`. +5. Convert the extracted field into TR-181 output storage. + +This pattern is present in multiple places, including: + +- [src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) +- [src/hostif/profiles/wifi/Device_WiFi.cpp](../../src/hostif/profiles/wifi/Device_WiFi.cpp) +- [src/hostif/profiles/wifi/Device_WiFi_EndPoint.cpp](../../src/hostif/profiles/wifi/Device_WiFi_EndPoint.cpp) +- [src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp](../../src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp) +- [src/hostif/profiles/wifi/Device_WiFi_SSID.cpp](../../src/hostif/profiles/wifi/Device_WiFi_SSID.cpp) + +### Review of Current Implementation + +The refactor proposal is valid and should be pursued. The code already shows that the problem is +not the transport alone, but the handler-local parsing contract. + +Key observations from the current implementation: + +- `getJsonRPCData()` already centralizes token retrieval, headers, timeout setup, and `curl_easy_perform()`. +- The current curl write callback is also part of the transport contract and should be normalized during the refactor, so the common helper owns response buffering with the expected libcurl callback shape. +- Response parsing is duplicated per handler, so fixes to JSON validation have to be repeated in many files. +- Some handlers use weak response checks such as `if(response.c_str())`, which is always non-null for a `std::string`; the real intent should be an emptiness check. +- Field validation is inconsistent. Some handlers validate array/object/string types carefully, while others dereference `cJSON` members with minimal checking. +- JSON-RPC error payload handling is not centralized. Callers mostly look only for `result`, with no shared handling for an `error` object or malformed schema. +- Request construction is duplicated as raw string concatenation, which makes method-specific bugs harder to audit. + +### Recommended Common Helper Direction + +The next step should be to extend [src/hostif/src/hostIf_utils.cpp](../../src/hostif/src/hostIf_utils.cpp) with a common Thunder helper layer that owns both transport and response validation. + +Suggested split: + +- `invokeThunderJsonRpc(method, params, responseRoot)` + Returns parsed root JSON after curl, HTTP, and top-level JSON-RPC validation. +- `getThunderResultObject(root)` + Returns validated `result` object or reports JSON-RPC `error` details. +- Typed extractors such as `readThunderString`, `readThunderBool`, `readThunderInt`, `readThunderArrayItemByKey` + Eliminate repeated field/type checks in handlers. + +```mermaid +flowchart TD + A[TR-181 handler] --> B[Common Thunder helper API] + B --> C[Build request object] + C --> D[getJsonRPCData or successor transport helper] + D --> E[libcurl + token + timeouts] + E --> F[Thunder JSON-RPC endpoint] + F --> G[Raw response] + G --> H[Central cJSON_Parse] + H --> I[Central JSON-RPC validation] + I --> J[Central result extraction] + J --> K[Typed field extractor] + K --> L[Handler receives validated value] + L --> M[Populate HOSTIF_MsgData_t] + + I --> N[Shared error logging] + K --> O[Consistent type checks] +``` + +### Expected Benefits of Centralizing Parse Logic + +- One implementation of timeout, HTTP status, JSON parse failure, and JSON-RPC error handling. +- Consistent empty-response and missing-field behavior across all Thunder-backed TR-181 parameters. +- Less duplicate code in handlers, especially for Wi-Fi and DeviceInfo parameters. +- Easier unit testing of success, malformed JSON, missing `result`, missing field, and wrong-type scenarios. +- Lower risk of handler-specific parsing bugs when new Thunder methods are added. + +### Recommended Refactor Scope + +Prioritize the highest-duplication handlers first: + +1. `org.rdk.NetworkManager.GetAvailableInterfaces` +2. `org.rdk.NetworkManager.GetConnectedSSID` +3. `org.rdk.NetworkManager.GetIPSettings` +4. `org.rdk.Account.getLastCheckoutResetTime` +5. `org.rdk.AuthService.*` + +These methods account for most of the repeated request/parse logic in the current codebase. + +## Request/Response Infrastructure + +### Endpoint + +``` +http://127.0.0.1:9998/jsonrpc +``` + +Defined as `JSONRPC_URL` in [src/hostif/include/hostIf_utils.h](../../src/hostif/include/hostIf_utils.h). + +### Request Format + +All calls follow JSON-RPC 2.0: + +```json +{ + "jsonrpc": "2.0", + "id": "", + "method": ".", + "params": { "": "" } +} +``` + +### Authentication + +Every request carries a Bearer token in the `Authorization` header: + +``` +Authorization: Bearer +Content-Type: application/json +``` + +The token is fetched at call time via `get_security_token()` (same file). + +### Core Helper Function + +```cpp +// src/hostif/include/hostIf_utils.h +string getJsonRPCData(std::string postData); +``` + +**Behaviour:** + +1. Calls `get_security_token()` and builds the Authorization header. +2. Initialises a `CURL` handle via `curl_easy_init()`. +3. Sets `CURLOPT_POST`, `CURLOPT_POSTFIELDS`, `CURLOPT_HTTPHEADER`, + `CURLOPT_WRITEFUNCTION` / `CURLOPT_WRITEDATA`. +4. Sets `CURLOPT_CONNECTTIMEOUT = 5 s`, `CURLOPT_TIMEOUT = 10 s`. +5. Calls `curl_easy_perform()` and returns the raw response string. +6. On failure returns an empty string; callers must check before parsing. + +**Thread Safety:** Not thread-safe; each call allocates and frees its own `CURL` handle. + +**Memory:** The returned `string` is owned by the caller. No persistent allocation. + +--- + +## Plugin Interfaces + +### org.rdk.NetworkManager + +**Used in:** [Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp), +[Device_WiFi.cpp](../../src/hostif/profiles/wifi/Device_WiFi.cpp), +[Device_WiFi_EndPoint.cpp](../../src/hostif/profiles/wifi/Device_WiFi_EndPoint.cpp), +[Device_WiFi_EndPoint_Security.cpp](../../src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp), +[Device_WiFi_SSID.cpp](../../src/hostif/profiles/wifi/Device_WiFi_SSID.cpp) + +> **Build flags:** WiFi Thunder paths are active only when `RDKV_NM` is **not** defined. +> The DeviceInfo IP path requires `MEDIA_CLIENT` defined and `RDKV_TR69` **not** defined. + +#### TR-181 Parameters — org.rdk.NetworkManager + +| TR-181 Parameter | Dir | Handler Function | Thunder Method | Response Field | +|------------------|-----|-----------------|----------------|----------------| +| `Device.DeviceInfo.X_COMCAST-COM_STB_IP` | GET | `get_Device_DeviceInfo_X_COMCAST_COM_STB_IP()` | `GetPrimaryInterface` → `GetIPSettings` | `result.interface` → `result.ipaddress` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshArgs` | SET | `set_xOpsReverseSshArgs()` | `GetPrimaryInterface` → `GetIPSettings` | `result.ipaddress` | +| `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` | GET | `get_Device_WiFi_EnableWiFi()` | `GetAvailableInterfaces` | `interfaces[WIFI].enabled` | +| `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` | SET | `set_Device_WiFi_EnableWiFi()` | `EnableInterface` / `DisableInterface` | `result.success` | +| `Device.WiFi.SSID.{i}.BSSID` | GET | `get_Device_WiFi_SSID_BSSID()` | `GetConnectedSSID` | `result.bssid` | +| `Device.WiFi.SSID.{i}.SSID` | GET | `get_Device_WiFi_SSID_SSID()` | `GetConnectedSSID` | `result.ssid` | +| `Device.WiFi.SSID.{i}.Name` | GET | `get_Device_WiFi_SSID_Name()` | `GetConnectedSSID` | `result.ssid` | +| `Device.WiFi.SSID.{i}.Enable` | GET | `get_Device_WiFi_SSID_Enable()` | `GetAvailableInterfaces` | `interfaces[WIFI].enabled` | +| `Device.WiFi.SSID.{i}.MACAddress` | GET | `get_Device_WiFi_SSID_MACAddress()` | `GetAvailableInterfaces` | `interfaces[WIFI].mac` | +| `Device.WiFi.SSID.{i}.Status` | GET | `get_Device_WiFi_SSID_Status()` | `GetWifiState` | `result.state` (mapped to string) | +| `Device.WiFi.Endpoint.{i}.Enable` | GET | `get_Device_WiFi_EndPoint_Enable()` | `GetAvailableInterfaces` ¹ | `interfaces[WIFI].enabled` | +| `Device.WiFi.Endpoint.{i}.Status` | GET | `get_Device_WiFi_EndPoint_Status()` | `GetAvailableInterfaces` ¹ | derived from `enabled` | +| `Device.WiFi.Endpoint.{i}.SSIDReference` | GET | `get_Device_WiFi_EndPoint_SSIDReference()` | `GetConnectedSSID` | `result.ssid` | +| `Device.WiFi.Endpoint.{i}.Stats.SignalStrength` | GET | `get_Device_WiFi_EndPoint_Stats_SignalStrength()` | `GetConnectedSSID` | `result.strength` | +| `Device.WiFi.Endpoint.{i}.Security.ModesEnabled` | GET | `get_hostIf_WiFi_EndPoint_Security_ModesEnabled()` | `GetConnectedSSID` | `result.securityMode` | + +> ¹ `Device_WiFi_EndPoint.cpp` calls the versioned form `org.rdk.NetworkManager.1.GetAvailableInterfaces`. + +#### GetPrimaryInterface + +Returns the name of the currently active network interface. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "42", + "method": "org.rdk.NetworkManager.GetPrimaryInterface" +} +``` + +**Response fields used:** `result.interface` (string) + +**TR-181 use:** Intermediate step — resolves interface name before querying `GetIPSettings`. + +--- + +#### GetIPSettings + +Returns IP configuration for a named interface. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "42", + "method": "org.rdk.NetworkManager.GetIPSettings", + "params": { "interface": "" } +} +``` + +**Response fields used:** `result.ipaddress` (string) + +**TR-181 use:** +- `Device.DeviceInfo.X_COMCAST-COM_STB_IP` GET +- `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshArgs` SET (IP lookup) + +--- + +#### GetAvailableInterfaces + +Returns all network interfaces with type, MAC, and enabled state. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "42", + "method": "org.rdk.NetworkManager.GetAvailableInterfaces" +} +``` + +**Response fields used (WIFI array element):** + +| Field | Type | Description | +|-------|------|-------------| +| `type` | string | Interface type — match on `"WIFI"` | +| `mac` | string | MAC address | +| `enabled` | bool/int | Whether the interface is active | + +**TR-181 use:** +- `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` GET +- `Device.WiFi.SSID.{i}.Enable`, `Device.WiFi.SSID.{i}.MACAddress` +- `Device.WiFi.Endpoint.{i}.Enable`, `Device.WiFi.Endpoint.{i}.Status` + +--- + +#### GetConnectedSSID + +Returns details of the currently associated Wi-Fi network. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "42", + "method": "org.rdk.NetworkManager.GetConnectedSSID" +} +``` + +**Response fields used:** + +| Field | Type | Description | +|-------|------|-------------| +| `ssid` | string | Connected SSID name | +| `bssid` | string | Access point BSSID | +| `strength` | number | Signal strength | +| `securityMode` | string | Security mode (e.g. `"WPA2"`) | + +**TR-181 use:** +- `Device.WiFi.SSID.{i}.SSID`, `Device.WiFi.SSID.{i}.BSSID`, `Device.WiFi.SSID.{i}.Name` +- `Device.WiFi.Endpoint.{i}.SSIDReference`, `Device.WiFi.Endpoint.{i}.Stats.SignalStrength` +- `Device.WiFi.Endpoint.{i}.Security.ModesEnabled` + +--- + +#### GetWifiState + +Returns an integer state code for the Wi-Fi subsystem. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "42", + "method": "org.rdk.NetworkManager.GetWifiState" +} +``` + +**Response fields used:** `result.state` (number — mapped to string status) + +**TR-181 use:** `Device.WiFi.SSID.{i}.Status` + +--- + +#### EnableInterface / DisableInterface + +Enables or disables the Wi-Fi interface. + +**Request (enable):** +```json +{ + "jsonrpc": "2.0", + "id": "42", + "method": "org.rdk.NetworkManager.EnableInterface", + "params": { "type": "WIFI" } +} +``` + +**Request (disable):** same with `"DisableInterface"`. + +**Response fields used:** `result.success` (bool) + +**TR-181 use:** `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` SET handler. + +--- + +### org.rdk.AuthService + +**Used in:** [Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) + +#### TR-181 Parameters — org.rdk.AuthService + +| TR-181 Parameter | Dir | Handler Function | Thunder Method | Transport | +|------------------|-----|-----------------|----------------|-----------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_Syndication.PartnerId` | SET | `set_Device_DeviceInfo_X_RDKCENTRAL_COM_Syndication_PartnerId()` | `setPartnerId` | Direct `curl_easy_perform` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AccountInfo.AccountID` | GET | `get_xRDKCentralComRFCAccountId()` | `getServiceAccountId` | `getJsonRPCData()` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_Experience` | GET | `get_X_RDKCENTRAL_COM_experience()` | `getExperience` | `getJsonRPCData()` | + +#### setPartnerId + +Updates the partner ID on the device. This is the **only** interface that bypasses +`getJsonRPCData()` and constructs its own `CURL` handle directly (fire-and-forget SET; +HTTP 200 is all that is checked — no JSON body is consumed). + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "3", + "method": "org.rdk.AuthService.setPartnerId", + "params": { "partnerId": "" } +} +``` + +**Response:** HTTP 200 OK only — no JSON fields read. + +**TR-181 use:** `Device.DeviceInfo.X_RDKCENTRAL-COM_Syndication.PartnerId` SET. + +--- + +#### getServiceAccountId + +Returns the service account identifier for this device. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "3", + "method": "org.rdk.AuthService.getServiceAccountId" +} +``` + +**Response fields used:** `result` — account ID string. + +**TR-181 use:** `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AccountInfo.AccountID` GET +(called only when the locally stored value is empty or `"unknown"`). + +--- + +#### getExperience + +Returns the UX experience type provisioned on the device. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "3", + "method": "org.rdk.AuthService.getExperience" +} +``` + +**Response fields used:** `result` — experience string (e.g. `"X1"`, `"XiOne"`). + +**TR-181 use:** `Device.DeviceInfo.X_RDKCENTRAL-COM_Experience` GET. + +--- + +### org.rdk.System + +**Used in:** [Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) + +> **Build flag:** The `getPrivacyMode` call is compiled only when `PRIVACYMODES_CONTROL` is defined. + +#### TR-181 Parameters — org.rdk.System + +| TR-181 Parameter | Dir | Handler Function | Thunder Method | Notes | +|------------------|-----|-----------------|----------------|-------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshTrigger` | SET | `set_xOpsReverseSshTrigger()` | `getPrivacyMode` | Gate check only — returns `NOK` if `privacyMode == "DO_NOT_SHARE"` | + +#### getPrivacyMode + +Returns the current privacy mode setting. Used as a **pre-condition gate** — the SSH +trigger is blocked if the device is in `DO_NOT_SHARE` mode. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "3", + "method": "org.rdk.System.getPrivacyMode" +} +``` + +**Response fields used:** `result.privacyMode` (string) + +| Value | Meaning | +|-------|---------| +| `"SHARE"` | Privacy sharing enabled — SSH trigger proceeds | +| `"DO_NOT_SHARE"` | Privacy restricted — SSH trigger blocked, returns `NOK` | + +**TR-181 use:** `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshTrigger` SET. + +--- + +### org.rdk.MigrationPreparer + +**Used in:** [Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) + +#### TR-181 Parameters — org.rdk.MigrationPreparer + +| TR-181 Parameter | Dir | Handler Function | Thunder Method | Response Field | +|------------------|-----|-----------------|----------------|----------------| +| `Device.DeviceInfo.MigrationPreparer.MigrationReady` | GET | `get_Device_DeviceInfo_MigrationPreparer_MigrationReady()` | `getComponentReadiness` | `result.ComponentList` | + +#### getComponentReadiness + +Returns a list of system components and their migration readiness state. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "3", + "method": "org.rdk.MigrationPreparer.getComponentReadiness" +} +``` + +**Response fields used:** `result.ComponentList` (array) + +**TR-181 use:** `Device.DeviceInfo.MigrationPreparer.MigrationReady` GET. + +--- + +### org.rdk.Account + +**Used in:** [Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) + +#### TR-181 Parameters — org.rdk.Account + +Both parameters call the **same** Thunder method; they differ only in how `resetTime` is interpreted. + +| TR-181 Parameter | Dir | Handler Function | Thunder Method | Response Field | Return Type | +|------------------|-----|-----------------|----------------|----------------|-------------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.LastResetTime` | GET | `get_HotelCheckoutLastResetTime()` | `getLastCheckoutResetTime` | `result.resetTime` | `UnsignedLong` (epoch) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.Status` | GET | `get_HotelCheckoutStatus()` | `getLastCheckoutResetTime` | `result.resetTime` | `String` (`"success"` if >0, else `"unknown"`) | + +#### getLastCheckoutResetTime + +Returns the UNIX timestamp of the last hotel checkout or factory reset event. + +**Request:** +```json +{ + "jsonrpc": "2.0", + "id": "3", + "method": "org.rdk.Account.getLastCheckoutResetTime" +} +``` + +**Response fields used:** `result.resetTime` (number — stored as `unsigned long`) + +| Handler | Interpretation | +|---------|----------------| +| `get_HotelCheckoutLastResetTime()` | Returns raw epoch timestamp as `UnsignedLong` | +| `get_HotelCheckoutStatus()` | Returns `"success"` if `resetTime > 0`, otherwise `"unknown"` | + +**TR-181 use:** +- `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.LastResetTime` GET +- `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.Status` GET + +--- + +## Call Flow Sequence + +```mermaid +sequenceDiagram + participant Profile as TR-181 Profile Handler + participant Utils as getJsonRPCData() + participant Curl as libcurl + participant Thunder as Thunder :9998/jsonrpc + participant Plugin as org.rdk.* Plugin + + Profile->>Utils: postData JSON string + Utils->>Utils: get_security_token() + Utils->>Curl: curl_easy_init() + Utils->>Curl: setopt (URL, POST, headers, timeout) + Curl->>Thunder: HTTP POST /jsonrpc + Thunder->>Plugin: dispatch method + Plugin-->>Thunder: JSON result + Thunder-->>Curl: HTTP 200 + JSON body + Curl-->>Utils: writeCurlResponse() callback + Utils-->>Profile: response string + Profile->>Profile: cJSON_Parse → extract field +``` + +--- + +## Timeout and Error Handling + +| Setting | Value | Notes | +|---------|-------|-------| +| `CURLOPT_CONNECTTIMEOUT` | 5 s | Connection establishment | +| `CURLOPT_TIMEOUT` | 10 s | Total request time | +| On `curl_easy_init()` failure | Returns `""` | Logged at `RDK_LOG_ERROR` | +| On `curl_easy_setopt()` failure | Returns `""` early | Each option checked individually | +| On empty / NULL response | Caller checks `response.empty()` | Logs error and returns `NOK` | +| HTTP status code | Checked via `CURLINFO_RESPONSE_CODE` | Only `setPartnerId` enforces HTTP 200 | + +--- + +## Summary Table + +### By Plugin and Method (13 methods) + +| Plugin | Method | TR-181 Parameter(s) | Dir | +|--------|--------|---------------------|-----| +| `org.rdk.NetworkManager` | `GetPrimaryInterface` | `Device.DeviceInfo.X_COMCAST-COM_STB_IP` *(intermediate)* | GET | +| `org.rdk.NetworkManager` | `GetIPSettings` | `Device.DeviceInfo.X_COMCAST-COM_STB_IP`
`…xOpsReverseSshArgs` | GET | +| `org.rdk.NetworkManager` | `GetAvailableInterfaces` | `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable`
`Device.WiFi.SSID.{i}.Enable`
`Device.WiFi.SSID.{i}.MACAddress`
`Device.WiFi.Endpoint.{i}.Enable`
`Device.WiFi.Endpoint.{i}.Status` | GET | +| `org.rdk.NetworkManager` | `GetConnectedSSID` | `Device.WiFi.SSID.{i}.SSID`
`Device.WiFi.SSID.{i}.BSSID`
`Device.WiFi.SSID.{i}.Name`
`Device.WiFi.Endpoint.{i}.SSIDReference`
`Device.WiFi.Endpoint.{i}.Stats.SignalStrength`
`Device.WiFi.Endpoint.{i}.Security.ModesEnabled` | GET | +| `org.rdk.NetworkManager` | `GetWifiState` | `Device.WiFi.SSID.{i}.Status` | GET | +| `org.rdk.NetworkManager` | `EnableInterface` | `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` | SET | +| `org.rdk.NetworkManager` | `DisableInterface` | `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` | SET | +| `org.rdk.AuthService` | `setPartnerId` | `Device.DeviceInfo.X_RDKCENTRAL-COM_Syndication.PartnerId` | SET | +| `org.rdk.AuthService` | `getServiceAccountId` | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AccountInfo.AccountID` | GET | +| `org.rdk.AuthService` | `getExperience` | `Device.DeviceInfo.X_RDKCENTRAL-COM_Experience` | GET | +| `org.rdk.System` | `getPrivacyMode` | `Device.DeviceInfo.…ReverseSSH.xOpsReverseSshTrigger` *(gate)* | SET | +| `org.rdk.MigrationPreparer` | `getComponentReadiness` | `Device.DeviceInfo.MigrationPreparer.MigrationReady` | GET | +| `org.rdk.Account` | `getLastCheckoutResetTime` | `Device.DeviceInfo.…HotelCheckout.LastResetTime`
`Device.DeviceInfo.…HotelCheckout.Status` | GET | + +### By TR-181 Parameter (21 parameters) + +| # | TR-181 Parameter | Plugin | Method | Dir | Build Flag | +|---|-----------------|--------|--------|-----|------------| +| 1 | `Device.DeviceInfo.X_COMCAST-COM_STB_IP` | NetworkManager | GetPrimaryInterface + GetIPSettings | GET | `MEDIA_CLIENT` + `!RDKV_TR69` | +| 2 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshArgs` | NetworkManager | GetPrimaryInterface + GetIPSettings | SET | `MEDIA_CLIENT` + `!RDKV_TR69` | +| 3 | `Device.DeviceInfo.X_RDKCENTRAL-COM_Syndication.PartnerId` | AuthService | setPartnerId | SET | — | +| 4 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AccountInfo.AccountID` | AuthService | getServiceAccountId | GET | — | +| 5 | `Device.DeviceInfo.X_RDKCENTRAL-COM_Experience` | AuthService | getExperience | GET | — | +| 6 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshTrigger` | System | getPrivacyMode | SET | `PRIVACYMODES_CONTROL` | +| 7 | `Device.DeviceInfo.MigrationPreparer.MigrationReady` | MigrationPreparer | getComponentReadiness | GET | — | +| 8 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.LastResetTime` | Account | getLastCheckoutResetTime | GET | — | +| 9 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.Status` | Account | getLastCheckoutResetTime | GET | — | +| 10 | `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` | NetworkManager | GetAvailableInterfaces / Enable\|DisableInterface | GET+SET | `!RDKV_NM` | +| 11 | `Device.WiFi.SSID.{i}.BSSID` | NetworkManager | GetConnectedSSID | GET | `!RDKV_NM` | +| 12 | `Device.WiFi.SSID.{i}.SSID` | NetworkManager | GetConnectedSSID | GET | `!RDKV_NM` | +| 13 | `Device.WiFi.SSID.{i}.Name` | NetworkManager | GetConnectedSSID | GET | `!RDKV_NM` | +| 14 | `Device.WiFi.SSID.{i}.Enable` | NetworkManager | GetAvailableInterfaces | GET | `!RDKV_NM` | +| 15 | `Device.WiFi.SSID.{i}.MACAddress` | NetworkManager | GetAvailableInterfaces | GET | `!RDKV_NM` | +| 16 | `Device.WiFi.SSID.{i}.Status` | NetworkManager | GetWifiState | GET | `!RDKV_NM` | +| 17 | `Device.WiFi.Endpoint.{i}.Enable` | NetworkManager | GetAvailableInterfaces ¹ | GET | `!RDKV_NM` | +| 18 | `Device.WiFi.Endpoint.{i}.Status` | NetworkManager | GetAvailableInterfaces ¹ | GET | `!RDKV_NM` | +| 19 | `Device.WiFi.Endpoint.{i}.SSIDReference` | NetworkManager | GetConnectedSSID | GET | `!RDKV_NM` | +| 20 | `Device.WiFi.Endpoint.{i}.Stats.SignalStrength` | NetworkManager | GetConnectedSSID | GET | `!RDKV_NM` | +| 21 | `Device.WiFi.Endpoint.{i}.Security.ModesEnabled` | NetworkManager | GetConnectedSSID | GET | `!RDKV_NM` | + +> ¹ Uses versioned method `org.rdk.NetworkManager.1.GetAvailableInterfaces`. + +### Count by Plugin + +| Plugin | Methods | TR-181 Parameters | +|--------|---------|-------------------| +| `org.rdk.NetworkManager` | 7 | **15** (2 DeviceInfo + 13 WiFi) | +| `org.rdk.AuthService` | 3 | **3** | +| `org.rdk.Account` | 1 | **2** | +| `org.rdk.System` | 1 | **1** | +| `org.rdk.MigrationPreparer` | 1 | **1** | +| **Total** | **13** | **21** | + +--- + +## See Also + +- [hostIf_utils.h](../../src/hostif/include/hostIf_utils.h) — `getJsonRPCData()` and `JSONRPC_URL` +- [hostIf_utils.cpp](../../src/hostif/src/hostIf_utils.cpp) — curl implementation +- [Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) — DeviceInfo profile handlers +- [Device_WiFi.cpp](../../src/hostif/profiles/wifi/Device_WiFi.cpp) — WiFi enable/disable +- [Device_WiFi_SSID.cpp](../../src/hostif/profiles/wifi/Device_WiFi_SSID.cpp) — SSID profile +- [Device_WiFi_EndPoint.cpp](../../src/hostif/profiles/wifi/Device_WiFi_EndPoint.cpp) — EndPoint profile +- [Device_WiFi_EndPoint_Security.cpp](../../src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp) — Security profile +- [public-api.md](public-api.md) — Overall public API reference +- [data-flow.md](../architecture/data-flow.md) — System-level data flow diff --git a/docs/architecture/json-usage.md b/docs/architecture/json-usage.md new file mode 100644 index 000000000..1176f5599 --- /dev/null +++ b/docs/architecture/json-usage.md @@ -0,0 +1,402 @@ +# JSON Usage In tr69hostif + +## Overview + +`tr69hostif` uses JSON in multiple independent paths rather than through a single shared abstraction. The current codebase mixes three patterns: + +- request ingress over local HTTP interfaces +- configuration and state ingestion from JSON files on disk +- outbound and internal service integration through JSON notifications and JSON-RPC payloads + +The implementation is also split across two parser stacks: + +- `cJSON` for most production JSON parsing and serialization +- `YAJL` for the legacy local JSON request thread in `src/hostif/handlers/` + +This document maps the active JSON contracts, the source files that own them, and the robustness gaps that matter for the planned user story: robust handling of JSON objects in `tr69hostif`. + +## Architecture + +### JSON Boundary Diagram + +```mermaid +flowchart LR + subgraph Inputs[JSON Inputs] + LEGACY[Legacy local JSON HTTP] + WDMP[WDMP HTTP request body] + CFG[Config files] + RPC[Thunder JSON-RPC responses] + end + + subgraph Core[tr69hostif] + YAJL[Legacy YAJL parser] + CJSON[cJSON-based handlers] + DISPATCH[hostif request dispatcher] + PROFILE[Profile handlers] + NOTIFY[Notification builder] + end + + subgraph Outputs[JSON Outputs] + HTTPRESP[HTTP JSON response] + PARODUS[Parodus/WebPA event payload] + FILTERED[Generated filtered JSON] + end + + LEGACY --> YAJL + WDMP --> CJSON + CFG --> CJSON + RPC --> CJSON + YAJL --> DISPATCH + CJSON --> DISPATCH + DISPATCH --> PROFILE + PROFILE --> CJSON + CJSON --> HTTPRESP + NOTIFY --> PARODUS + PROFILE --> FILTERED +``` + +### JSON Usage Categories + +| Category | Primary modules | Library | Direction | +|----------|-----------------|---------|-----------| +| Legacy local HTTP requests | `src/hostif/handlers/src/hostIf_jsonReqHandlerThread.cpp` | `YAJL` | inbound + outbound | +| Current WDMP HTTP server | `src/hostif/httpserver/src/http_server.cpp` | `cJSON` | inbound + outbound | +| Parodus and startup config files | `src/hostif/parodusClient/startParodus/startParodus.cpp`, `src/hostif/parodusClient/pal/libpd.cpp`, `src/hostif/parodusClient/pal/webpa_notification.cpp` | `cJSON` | inbound | +| Device defaults and bootstrap data | `src/hostif/profiles/DeviceInfo/XrdkCentralComBSStore.cpp` | `cJSON` | inbound | +| Thunder JSON-RPC consumers | `src/hostif/src/hostIf_utils.cpp`, `src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp`, `src/hostif/profiles/wifi/*.cpp` | `cJSON` | outbound request + inbound response | +| Parodus notifications | `src/hostif/handlers/src/hostIf_NotificationHandler.cpp` | `cJSON` | outbound | + +## Request And Response Contracts + +### 1. Legacy Local JSON HTTP Path + +**Owner:** `src/hostif/handlers/src/hostIf_jsonReqHandlerThread.cpp` + +This is the older local HTTP interface started by the JSON handler thread. It uses YAJL callbacks instead of `cJSON`. + +**Accepted request shape:** + +```json +{ + "paramList": [ + { "name": "Device.DeviceInfo.Manufacturer" }, + { "name": "Device.DeviceInfo.ModelName" } + ] +} +``` + +**Returned response shape:** + +```json +{ + "paramList": [ + { + "name": "Device.DeviceInfo.Manufacturer", + "value": "ExampleVendor" + } + ] +} +``` + +**Behavior notes:** + +- only `paramList[].name` is extracted from the request +- other fields are ignored by the parser state machine +- `DateTime` values are serialized as the literal string `"Unknown"` +- there is no explicit schema error payload beyond the HTTP status code + +### 2. Current WDMP HTTP JSON Path + +**Owner:** `src/hostif/httpserver/src/http_server.cpp` + +This is the newer local HTTP interface. It accepts JSON request bodies, converts them into `req_struct`, routes them through the common dispatcher, then rebuilds a WDMP-style JSON response. + +**GET request pattern:** + +```json +{ + "names": [ + "Device.DeviceInfo.Manufacturer", + "Device.DeviceInfo.SerialNumber" + ] +} +``` + +**POST request pattern:** + +```json +{ + "parameters": [ + { + "name": "Device.Time.NTPServer1", + "value": "time.example.net", + "dataType": 0 + } + ] +} +``` + +**Response pattern:** + +```json +{ + "statusCode": 0, + "parameters": [ + { + "name": "Device.DeviceInfo.Manufacturer", + "value": "ExampleVendor", + "message": "Success" + } + ] +} +``` + +**Behavior notes:** + +- the top-level `statusCode` is post-processed after WDMP response generation +- field-level schema validation is largely delegated to the WDMP helper layer +- malformed JSON returns HTTP `400 Bad Request` +- missing `CallerID` is tolerated for GET and rejected for POST + +### 3. Thunder JSON-RPC Path + +**Owners:** + +- `src/hostif/src/hostIf_utils.cpp` +- `src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` +- `src/hostif/profiles/wifi/Device_WiFi.cpp` +- `src/hostif/profiles/wifi/Device_WiFi_SSID.cpp` +- `src/hostif/profiles/wifi/Device_WiFi_EndPoint.cpp` +- `src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp` + +`getJsonRPCData()` sends JSON-RPC POST bodies to the Thunder endpoint and returns a response string which is then parsed by profile code. + +**Representative request pattern:** + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "DeviceInfo.1.getPrivacyMode" +} +``` + +**Representative response pattern:** + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "privacyMode": "Disabled" + } +} +``` + +**Observed response fields currently consumed by profiles:** + +| Consumer | Expected JSON path | +|----------|--------------------| +| DeviceInfo primary interface | `result.interface` | +| DeviceInfo IP settings | `result.ipaddress` | +| DeviceInfo privacy mode | `result.privacyMode` | +| DeviceInfo component readiness | `result.ComponentList[]` | +| DeviceInfo service account | `result.serviceAccountId` | +| DeviceInfo checkout reset time | `result` as number | +| DeviceInfo experience | `result.experience` | +| WiFi interface list | `result.interfaces[]` | +| WiFi endpoint security | `result.securityMode` | +| WiFi enable or disable result | `result.success` | + +### 4. JSON File Inputs + +#### WebPA and Parodus runtime config + +**Owners:** + +- `src/hostif/parodusClient/startParodus/startParodus.cpp` +- `src/hostif/parodusClient/pal/libpd.cpp` + +**Observed keys:** + +```json +{ + "ServerIP": "https://example.endpoint", + "acquire-jwt": 1, + "DeviceNetworkInterface": "erouter0", + "ServerPort": 6666, + "MaxPingWaitTimeInSec": 30, + "ParodusURL": "tcp://127.0.0.1:6666", + "ParodusClientURL": "tcp://127.0.0.1:6667" +} +``` + +#### Notify-on config + +**Owner:** `src/hostif/parodusClient/pal/webpa_notification.cpp` + +**Observed shape:** + +```json +{ + "Notify": [ + "Device.DeviceInfo.X_RDKCENTRAL-COM_RFCExtensions.Enable", + "Device.Time.NTPServer1" + ] +} +``` + +#### Partner defaults and bootstrap data + +**Owner:** `src/hostif/profiles/DeviceInfo/XrdkCentralComBSStore.cpp` + +**Observed shape:** + +```json +{ + "default": { + "Device.Time.NTPServer1": "time.example.net" + }, + "partnerA": { + "Device.Time.NTPServer1": "time.partner.example.net" + } +} +``` + +#### Reboot reason file + +**Owners:** + +- `src/hostif/parodusClient/startParodus/startParodus.cpp` +- `src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +**Observed shape:** + +```json +{ + "reason": "software-reset" +} +``` + +#### Generated filtered JSON + +**Owner:** `src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +This path reads a local JSON object-of-objects and emits a compact JSON object whose values are arrays of field names. It is a transformation step rather than an external contract. + +### 5. Outbound Notification Payloads + +**Owner:** `src/hostif/handlers/src/hostIf_NotificationHandler.cpp` + +The notification layer uses `cJSON_CreateObject()` and `cJSON_PrintUnformatted()` to build WebPA or Parodus event payloads. + +**Representative device status payload:** + +```json +{ + "device_id": "mac:112233445566", + "status": "reboot-pending", + "boot-time": 1710000000, + "reboot-reason": "software-reset", + "delay": 30 +} +``` + +## Threading And Ownership Notes + +### Threading Model + +| Path | Thread context | +|------|----------------| +| Legacy JSON server | JSON handler thread created from `hostIf_main.cpp` | +| New HTTP JSON server | dedicated HTTP server thread | +| Parodus config and notify config | startup and Parodus-related worker paths | +| Thunder JSON-RPC parsing | caller thread inside profile GET or SET execution | +| Notification payload generation | update and notification execution paths | + +### Memory Ownership Rules In Current Code + +| Object type | Expected owner action | +|-------------|-----------------------| +| `cJSON_Parse()` return value | must be released with `cJSON_Delete()` | +| `cJSON_CreateObject()` or `cJSON_CreateArray()` return value | must be released with `cJSON_Delete()` | +| `cJSON_Print()` or `cJSON_PrintUnformatted()` return value | must be released with `free()` | +| YAJL parser or generator handles | must be released with `yajl_free()` or `yajl_gen_free()` | + +Current code does not consistently honor these ownership rules across all JSON paths. + +## Current Gaps And Issues + +The following items are the main input for the planned robustness story. + +### High Priority Gaps + +| Gap | Affected files | Why it matters | +|-----|----------------|----------------| +| `getJsonRPCData()` does not accumulate the HTTP response body because the curl write callback takes the output string by value | `src/hostif/src/hostIf_utils.cpp` | Most Thunder JSON-RPC consumers effectively parse an empty string, which breaks the central JSON-RPC integration path | +| Nested JSON members are dereferenced without consistent null and type checks | `src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp`, `src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp` | Malformed or changed JSON can cause crashes or invalid reads | +| Parsed JSON roots are not deleted on many success and error paths | `src/hostif/parodusClient/startParodus/startParodus.cpp`, `src/hostif/parodusClient/pal/libpd.cpp`, `src/hostif/parodusClient/pal/webpa_notification.cpp`, `src/hostif/profiles/DeviceInfo/XrdkCentralComBSStore.cpp` | Long-running service code accumulates avoidable leaks | +| The notify config parser dereferences `notify_cfg` before verifying parse success | `src/hostif/parodusClient/pal/webpa_notification.cpp` | Invalid JSON can turn into null dereference or inconsistent startup behavior | + +### Medium Priority Gaps + +| Gap | Affected files | Why it matters | +|-----|----------------|----------------| +| The legacy YAJL path only extracts `paramList[].name` and silently ignores unexpected structure | `src/hostif/handlers/src/hostIf_jsonReqHandlerThread.cpp` | Callers receive weak feedback and malformed requests can appear valid | +| HTTP server error reporting is mostly transport-level and depends on lower layers for schema detail | `src/hostif/httpserver/src/http_server.cpp` | Operational debugging is harder when request shape is wrong | +| File-backed JSON readers do not consistently distinguish file I/O failure, empty file, parse failure, and schema failure | startup, Parodus, DeviceInfo bootstrap files | Error handling is not precise enough for quick triage | +| Several config readers assume strings or numbers without validating the exact JSON type | Parodus config, bootstrap config, reboot reason parsing | Schema drift produces undefined behavior instead of explicit rejection | + +### Low Priority Gaps + +| Gap | Affected files | Why it matters | +|-----|----------------|----------------| +| The legacy JSON response path emits `"Unknown"` for `DateTime` values | `src/hostif/handlers/src/hostIf_jsonReqHandlerThread.cpp` | Response semantics are inconsistent with the rest of the module | +| JSON handling is spread across YAJL, `cJSON`, and WDMP helpers without a shared validation helper layer | multiple modules | Maintenance cost is high and behavior differs by path | + +## Testing Gaps + +| Area | Current state | Gap | +|------|---------------|-----| +| WDMP HTTP server | basic unit coverage exists | response schema and error-body assertions are thin | +| Thunder JSON-RPC | smoke coverage exists | current tests do not reliably catch the broken response accumulation path | +| DeviceInfo JSON contracts | a few response-shape assumptions are implied in tests | field-by-field schema validation coverage is missing | +| WiFi JSON-RPC contracts | partial coverage | malformed-response cases are largely untested | +| Notify config parsing | shape is covered in Parodus tests | malformed JSON, mixed array types, and cleanup failure paths are not well covered | + +## Recommended Scope For The Robust JSON User Story + +### Functional Hardening Goals + +1. Normalize parse and validation behavior for all `cJSON` inputs. +2. Reject malformed or schema-invalid JSON with explicit logs and deterministic return codes. +3. Eliminate parse-tree and printed-string ownership leaks. +4. Protect all nested-object access with null and type checks. +5. Add contract-focused unit tests for every external JSON shape the daemon accepts or emits. + +### Suggested Acceptance Criteria + +1. All production `cJSON_Parse()` call sites check for parse failure before dereferencing the root. +2. All accessed JSON members are validated with the correct `cJSON_Is*()` predicate before use. +3. All parsed or constructed `cJSON` trees are deleted on every success and failure path. +4. `getJsonRPCData()` returns the full HTTP response body and has a regression test. +5. Invalid `webpa_cfg.json`, `notify_webpa_cfg.json`, `partners_defaults.json`, or reboot reason JSON produces actionable error logs and safe failure behavior. +6. Legacy JSON and WDMP HTTP interfaces document and enforce their accepted request schema. + +## Existing Documentation To Reuse + +- `src/hostif/httpserver/docs/README.md` documents the newer WDMP HTTP JSON flow. +- `src/hostif/handlers/docs/README.md` documents the legacy JSON request handler and notification flow. +- `src/hostif/parodusClient/docs/README.md` documents WebPA orchestration and JSON config files. +- `src/hostif/docs/README.md` already records the `getJsonRPCData()` response handling defect. +- `src/hostif/profiles/wifi/docs/README.md` documents the non-RDKV WiFi JSON-RPC path. +- `src/hostif/profiles/DeviceInfo/docs/README.md` documents partner-default JSON and bootstrap behavior. + +## See Also + +- [System Overview](overview.md) +- [Threading Model](threading-model.md) +- [Data Flow](data-flow.md) +- [Public API](../api/public-api.md) +- [Build Setup](../integration/build-setup.md) +- [Common Errors](../troubleshooting/common-errors.md) \ No newline at end of file diff --git a/docs/architecture/threading-model.md b/docs/architecture/threading-model.md index 5855a29c3..dd6ce763b 100644 --- a/docs/architecture/threading-model.md +++ b/docs/architecture/threading-model.md @@ -2,33 +2,65 @@ ## Overview -`tr69hostif` mixes GLib-managed threads, POSIX threads, and one standard C++ thread in the bootstrap store. The design keeps long-running I/O and polling work off the main loop while preserving a single shared request contract for all front ends. +`tr69hostif` mixes GLib-managed threads, POSIX threads, and C++ `std::thread` across the codebase. The design keeps long-running I/O and polling work off the main loop while preserving a single shared request contract for all front ends. The threading model has grown organically and contains several undocumented detached threads, an uninitialized mutex on the critical shutdown path, and missed-signal races that represent the highest operational risk areas. ## Thread Inventory -| Thread | Creation site | Type | Purpose | Shutdown behavior | -|--------|---------------|------|---------|-------------------| -| Main thread | process start | OS main thread | Initializes services and runs `g_main_loop_run()` | Exits through `exit_gracefully()` | -| Shutdown thread | `hostIf_main.cpp` | `pthread_create()` | Waits on `shutdown_thread_sem` and triggers graceful exit on signal | Woken by signal handler path | -| JSON handler thread | `hostIf_main.cpp` | `g_thread_try_new()` | Handles JSON request traffic on configured socket | Stops during daemon shutdown | -| HTTP server thread | `hostIf_main.cpp` | `g_thread_try_new()` | Serves optional legacy HTTP RFC endpoint | Controlled by runtime and feature gating | -| Update handler | `updateHandler::Init()` | `g_thread_new()` | Polls profiles for changes and emits add/remove/value-changed events | Stops when `updateHandler::stopped` becomes true | -| Parodus init/receive thread | `pthread_create()` into `libpd_client_mgr()` | POSIX thread | Connects to Parodus and stays in receive/send loop | Self-detaches in `connect_parodus()` | -| WebConfig thread | `hostIf_main.cpp` | `pthread_create()` | Handles WebConfig Lite processing when enabled | Feature-gated | -| Partner ID worker | `XBSStore` | `std::thread` | Resolves bootstrap partner identity asynchronously | Store-specific lifecycle | +### Daemon-level threads + +| Thread | Creation site | Type | ID stored? | Join / Detach at shutdown | +|--------|---------------|------|-----------|--------------------------| +| Main thread | process start | OS main thread | n/a | `g_main_loop_quit()` unblocks it | +| Shutdown thread | [hostIf_main.cpp:338](../../src/hostif/src/hostIf_main.cpp) | `pthread_create()` | `shutdown_thread` (static) | **Never joined, never detached.** Exits when `exit_gracefully()` calls process exit. | +| JSON handler thread | [hostIf_main.cpp:442](../../src/hostif/src/hostIf_main.cpp) | `g_thread_try_new()` | `hostIf_JsonIfThread` | `g_thread_join()` in `main()` after loop returns | +| HTTP server thread | [hostIf_main.cpp:451](../../src/hostif/src/hostIf_main.cpp) | `g_thread_try_new()` | `HTTPServerThread` | `g_thread_join()` in `main()` after loop returns | +| Parodus init/receive thread | [hostIf_main.cpp:482](../../src/hostif/src/hostIf_main.cpp) | `pthread_create()` | `parodus_init_tid` | **⚠ Never joined.** Self-detaches via `pthread_detach(pthread_self())` inside `connect_parodus()` | +| WebConfig thread | [hostIf_main.cpp:495](../../src/hostif/src/hostIf_main.cpp) | `pthread_create()` | `webconfig_threadId` | **Never joined in cleanup** | +| Update handler | [hostIf_updateHandler.cpp:102](../../src/hostif/handlers/src/hostIf_updateHandler.cpp) | `g_thread_new()` | `updateHandler::thread` | **Not joined, not detached.** `stop()` only sets `stopped=true`; waits up to 60 s for sleep to complete | + +### Ad-hoc and profile-level detached threads + +These threads are created at request time or at profile initialization and are immediately detached. None are tracked or joined at shutdown. + +| Thread function | Creation site | Detach mechanism | Shutdown tracking | +|----------------|---------------|-----------------|------------------| +| `getPwrContInterface` | [hostIf_IARM_ReqHandler.cpp:161](../../src/hostif/handlers/src/hostIf_IARM_ReqHandler.cpp) | `pwrThread.detach()` at line 164 | None | +| `ResetFunc` | [Device_DeviceInfo.cpp:2515](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) | `PTHREAD_CREATE_DETACHED` attr | None | +| `executeRfcMgr` | [Device_DeviceInfo.cpp:4907](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) | `.detach()` at line 4908 | None | +| `triggerRPCReboot` | [Device_DeviceInfo.cpp:5385](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) | `.detach()` at line 5386 | None | +| `systemMgmtTimePathMonitorThr` | [Device_DeviceInfo.cpp:5622](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) | `.detach()` at line 5623 | None | +| `getAuthServicePartnerID` | [XrdkCentralComBSStore.cpp:832](../../src/hostif/profiles/DeviceInfo/XrdkCentralComBSStore.cpp) | `.detach()` immediately | None | ## Synchronization Primitives -| Primitive | Location | Role | -|-----------|----------|------| -| `pthread_mutex_t graceful_exit_mutex` | `hostIf_main.cpp` | Serializes graceful shutdown path | -| `sem_t shutdown_thread_sem` | `hostIf_main.cpp` | Wakes the dedicated shutdown thread | -| `std::mutex get_handler_mutex` | `hostIf_msgHandler.cpp` | Serializes synchronous GET dispatch | -| `std::mutex set_handler_mutex` | `hostIf_msgHandler.cpp` | Serializes synchronous SET dispatch | -| `std::mutex mtx_httpServerThreadDone` + `std::condition_variable cv_httpServerThreadDone` | `hostIf_main.cpp` | Coordinates HTTP server startup completion | -| `pthread_mutex_t parodus_lock` + `pthread_cond_t parodus_cond` | `libpd.cpp` | Implements timed wait/retry behavior in Parodus receive loop | -| `GAsyncQueue* notificationQueue` | notification handler | Asynchronous queue for outbound change notifications | -| bootstrap store mutexes and condition variable | `XBSStore` | Guard bootstrap dictionaries and stop notifications | +### Daemon-level primitives + +| Primitive | Location | Type | Init | Destroy | Notes | +|-----------|----------|------|------|---------|-------| +| `graceful_exit_mutex` | [hostIf_main.cpp:145](../../src/hostif/src/hostIf_main.cpp) | `pthread_mutex_t` | **⚠ Never initialized** — no `PTHREAD_MUTEX_INITIALIZER` and no `pthread_mutex_init()` call | Never | Used on the critical shutdown path; undefined behavior | +| `shutdown_thread_sem` | [hostIf_main.cpp:143](../../src/hostif/src/hostIf_main.cpp) | `sem_t` | `sem_init(…, 0, 0)` at line 332 | Never explicitly destroyed | Wakes shutdown thread from signal handler | +| `mtx_httpServerThreadDone` | [hostIf_main.cpp:120](../../src/hostif/src/hostIf_main.cpp) | `std::mutex` | Default-constructed | Never | Guards `httpServerThreadDone` flag | +| `cv_httpServerThreadDone` | [hostIf_main.cpp:121](../../src/hostif/src/hostIf_main.cpp) | `std::condition_variable` | Default-constructed | Never | Wait uses lambda predicate against `httpServerThreadDone` — spurious-wake-safe | +| `get_handler_mutex` | [hostIf_msgHandler.cpp](../../src/hostif/handlers/src/hostIf_msgHandler.cpp) | `std::mutex` | Default-constructed | Never | Serializes all synchronous GET dispatches | +| `set_handler_mutex` | [hostIf_msgHandler.cpp](../../src/hostif/handlers/src/hostIf_msgHandler.cpp) | `std::mutex` | Default-constructed | Never | Serializes all synchronous SET dispatches | + +### Parodus primitives + +| Primitive | Location | Type | Init | Notes | +|-----------|----------|------|------|-------| +| `parodus_lock` | [libpd.cpp:71](../../src/hostif/parodusClient/pal/libpd.cpp) | `pthread_mutex_t` | `PTHREAD_MUTEX_INITIALIZER` | Guards `pthread_cond_timedwait` path | +| `parodus_cond` | [libpd.cpp:70](../../src/hostif/parodusClient/pal/libpd.cpp) | `pthread_cond_t` | `PTHREAD_COND_INITIALIZER` | **⚠ Signaled without holding `parodus_lock`** — missed-signal risk | + +### Profile and component primitives + +| Primitive | Location | Type | Init | Notes | +|-----------|----------|------|------|-------| +| `hostIf_DeviceInfo::m_mutex` | [Device_DeviceInfo.cpp:142](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) | `pthread_mutex_t` | Initialized as `PTHREAD_MUTEX_ERRORCHECK` via `pthread_once` at line 290 | Re-initialized after static-init; intent is correct | +| `XBSStore::g_instance_mutex` | [XrdkCentralComBSStore.cpp:63](../../src/hostif/profiles/DeviceInfo/XrdkCentralComBSStore.cpp) | `std::mutex` | Default-constructed | Guards singleton creation | +| `XBSStore::mtx_stopped` + `cv` | [XrdkCentralComBSStore.cpp:61–62](../../src/hostif/profiles/DeviceInfo/XrdkCentralComBSStore.cpp) | `std::mutex` + `std::condition_variable` | Default-constructed | Guards `m_stopped` flag | +| `IPClientReqHandler::m_mutex` | [hostIf_IPClient_ReqHandler.cpp:55](../../src/hostif/handlers/src/hostIf_IPClient_ReqHandler.cpp) | `std::mutex` | Default-constructed | Guards singleton | +| `g_db_mutex` | [waldb.cpp:65](../../src/hostif/parodusClient/waldb/waldb.cpp) | `std::mutex` | Default-constructed | Guards WAL DB access | +| Profile `GMutex` instances (per-object) | Time, IP, Ethernet, InterfaceStack, STBService profiles | `GMutex` | `g_mutex_init()` | Generally `g_mutex_clear()` in destructor where present | ## Concurrency Rules @@ -41,11 +73,17 @@ ### Update monitoring -The update handler is a single polling thread. It calls the profile-specific `checkForUpdates()` hooks in sequence and sleeps for 60 seconds between polling passes. This keeps notification generation predictable, but also means update latency is polling-based rather than interrupt-driven for most profiles. +The update handler is a single polling thread. It calls the profile-specific `checkForUpdates()` hooks in sequence, then calls `sleep(60)`. This is not a condition variable wait, so the thread cannot respond to a stop signal until the full 60-second sleep completes. A stop signal issued while the thread is sleeping will take up to 60 seconds to take effect. There is no mutex protecting the profile iteration sequence inside `run()`. ### Parodus behavior -The Parodus worker thread calls `pthread_detach(pthread_self())` inside `connect_parodus()`. That makes it explicitly non-joinable and means shutdown logic must signal it to exit rather than attempt a `pthread_join()`. +The Parodus worker thread calls `pthread_detach(pthread_self())` inside `connect_parodus()`. That makes it explicitly non-joinable. Shutdown logic must signal it via `stop_parodus_recv_wait()` rather than attempting a `pthread_join()`. The actual exit signal is sent by setting `exit_parodus_recv = true` and calling `pthread_cond_signal()`, both without holding `parodus_lock`. + +### Lock ordering + +No confirmed AB/BA (lock-inversion) deadlock patterns are present in current production paths. Notable proximity: + +- Inside `exit_gracefully()`: `graceful_exit_mutex` is held while `XBSStore::stop()` is called, which internally takes `mtx_stopped`. These are distinct mutex instances on different objects, so no inversion exists. However, if this pattern is extended, the ordering rule must be: acquire `graceful_exit_mutex` before `mtx_stopped`. ## Lifecycle Diagram @@ -55,7 +93,7 @@ stateDiagram-v2 Boot --> Init: parse config and start IPC Init --> Running: main loop active Running --> Polling: updateHandler iteration - Polling --> Running: sleep 60s + Polling --> Running: sleep(60) completes Running --> Receiving: Parodus request loop Receiving --> Running: request processed Running --> ShutdownRequested: signal or fatal stop path @@ -63,6 +101,35 @@ stateDiagram-v2 Cleanup --> [*] ``` +## Shutdown Sequence + +Signal path: `SIGINT / SIGTERM / SIGHUP` → `quit_handler()` → `sem_post(&shutdown_thread_sem)` → `shutdown_thread_entry` wakes from `sem_wait` → calls `exit_gracefully(sig)`. + +`exit_gracefully()` operations in order: + +1. Non-atomic read of `static int isShutdownTriggered` (no fence, no atomic) +2. `pthread_mutex_trylock(&graceful_exit_mutex)` — **mutex is never initialized; this is undefined behavior** +3. Set `isShutdownTriggered = 1` +4. `t2_uninit()` (conditional on `T2_EVENT_ENABLED`) +5. `WiFiDevice::shutdown()` (conditional on `USE_WIFI_PROFILE`) +6. `stop_parodus_recv_wait()` — sets `exit_parodus_recv = true` and calls `pthread_cond_signal()` **without holding `parodus_lock`** +7. `hostIf_HttpServerStop()` — stops HTTP and JSON handler threads +8. `updateHandler::stop()` — sets `stopped = true` only; thread is not joined; may still be sleeping +9. `XBSStore::getInstance()->stop()` — sets `m_stopped = true` and calls `cv.notify_one()` +10. `fclose(logfile)` +11. `g_hash_table_destroy(paramMgrhash)` — destroyed while handler threads are possibly still live +12. `hostIf_IARM_IF_Stop()` +13. `g_main_loop_quit(main_loop)` — unblocks `g_main_loop_run()` in `main()` +14. `HttpServerStop()` (conditional, legacy HTTP) +15. `pthread_mutex_unlock(&graceful_exit_mutex)` + +Back in `main()` after `g_main_loop_run` returns: + +16. `g_thread_join(hostIf_JsonIfThread)` — if non-NULL +17. `g_thread_join(HTTPServerThread)` — if non-NULL + +**Threads not joined at process exit:** `shutdown_thread`, `parodus_init_tid`, `webconfig_threadId`, `updateHandler::thread`, and all ad-hoc detached threads listed in the thread inventory. + ## Notification Path ```mermaid @@ -81,21 +148,175 @@ sequenceDiagram end ``` -## Shutdown Notes +## Signal Handling + +| Location | Signal | Handler | Safety | +|----------|--------|---------|--------| +| [hostIf_main.cpp:349](../../src/hostif/src/hostIf_main.cpp) | `SIGINT` | `quit_handler` | Safe — writes `int`, calls `sem_post` (async-signal-safe) | +| [hostIf_main.cpp:350](../../src/hostif/src/hostIf_main.cpp) | `SIGTERM` | `quit_handler` | Safe | +| [hostIf_main.cpp:351](../../src/hostif/src/hostIf_main.cpp) | `SIGHUP` | `quit_handler` | Safe | +| [hostIf_main.cpp:352](../../src/hostif/src/hostIf_main.cpp) | `SIGPIPE` | `SIG_IGN` | Safe | +| [startParodus.cpp:300](../../src/hostif/parodusClient/startParodus/startParodus.cpp) | `SIGTERM` | `processExit` | **⚠ Unsafe** — calls `printf()` which is not async-signal-safe | +| [startParodus.cpp:301](../../src/hostif/parodusClient/startParodus/startParodus.cpp) | `SIGKILL` | `processExit` | **⚠ Invalid** — `SIGKILL` cannot be caught; this `signal()` call is silently ignored | +| [startParodus.cpp:302](../../src/hostif/parodusClient/startParodus/startParodus.cpp) | `SIGABRT` | `processExit` | **⚠ Unsafe** — `printf()` in handler | + +## Gaps and High-Risk Areas + +This section documents specific defects, undocumented behaviors, and patterns that present risk of crashes, hangs, or undefined behavior. Items are rated by severity. + +### Risk Summary + +| ID | Area | Severity | Risk type | +|----|------|----------|-----------| +| T-1 | `graceful_exit_mutex` uninitialized | **Critical** | Undefined behavior / crash | +| T-2 | Parodus missed-signal race | **High** | Thread hang / incorrect exit | +| T-3 | `paramMgrhash` destroyed while threads live | **High** | Use-after-free / crash | +| T-4 | `updateHandler::stopped` not atomic | **High** | Stale read / thread never stops | +| T-5 | `isShutdownTriggered` not atomic | **Medium** | Stale read / double shutdown | +| T-6 | `httpServerThreadDone` pre-read without lock | **Medium** | Race condition | +| T-7 | `startParodus.cpp` signal handler `printf` | **Medium** | Signal-handler safety violation | +| T-8 | `SIGKILL` registered but cannot be caught | **Medium** | Programmer error, misleading code | +| T-9 | `libparodus_instance` unguarded | **Medium** | Data race | +| T-10 | `updateHandler` uses `sleep(60)` not cond-wait | **Medium** | Slow shutdown response | +| T-11 | Multiple ad-hoc threads without shutdown tracking | **Medium** | Resource leak, undefined teardown | +| T-12 | Static cache buffers in DeviceInfo without locks | **Low** | Stale read / torn write | + +--- + +### T-1 — `graceful_exit_mutex` is never initialized (Critical) + +**File:** [hostIf_main.cpp:145](../../src/hostif/src/hostIf_main.cpp) + +`graceful_exit_mutex` is declared as a `pthread_mutex_t` but is neither assigned `PTHREAD_MUTEX_INITIALIZER` nor passed to `pthread_mutex_init()`. Using it via `pthread_mutex_trylock()` and `pthread_mutex_unlock()` in `exit_gracefully()` is undefined behavior on all POSIX platforms and can cause a crash or silent no-op depending on the memory contents at startup. + +**Required fix:** Add `= PTHREAD_MUTEX_INITIALIZER` at the declaration, or call `pthread_mutex_init(&graceful_exit_mutex, NULL)` at daemon startup before any signal can arrive. + +--- + +### T-2 — Parodus missed-signal race (High) + +**File:** [libpd.cpp:87–88](../../src/hostif/parodusClient/pal/libpd.cpp) + +`stop_parodus_recv_wait()` sets `exit_parodus_recv = true` and immediately calls `pthread_cond_signal(&parodus_cond)` without holding `parodus_lock`. The receiving thread checks `exit_parodus_recv` at the top of the loop, then enters `pthread_cond_timedwait` — if the signal arrives in the window between the flag check and the wait entry, it is lost. The thread then blocks for the full 5-second timeout before rechecking. + +``` +Thread A (stop) Thread B (receiver loop) +───────────────────── ──────────────────────────── +exit_parodus_recv = true /* passes while(!exit_parodus_recv) */ +pthread_cond_signal(...) /* signal arrives here, lost */ + pthread_cond_timedwait(...) ← blocks 5s +``` + +**Required fix:** Acquire `parodus_lock` before setting the flag and before calling `pthread_cond_signal()`, matching the standard condition variable pattern. + +--- + +### T-3 — `paramMgrhash` destroyed while handler threads remain live (High) + +**File:** [hostIf_main.cpp — exit_gracefully() step 11](../../src/hostif/src/hostIf_main.cpp) + +`updateHandler::stop()` only sets `stopped = true`. The update thread is not joined before `g_hash_table_destroy(paramMgrhash)` is called. If the update thread is mid-iteration calling profile `checkForUpdates()` handlers that dereference `paramMgrhash`, the result is use-after-free. + +**Required fix:** Either join the update thread (or wait on a completion semaphore) before destroying the hash table, or ensure `paramMgrhash` is not dereferenced from the update thread path after the stop signal. + +--- + +### T-4 — `updateHandler::stopped` is a plain `bool`, not `std::atomic` (High) + +**File:** [hostIf_updateHandler.cpp:68](../../src/hostif/handlers/src/hostIf_updateHandler.cpp) + +`stopped` is written on the shutdown thread and read on the update thread without any synchronization fence. The C++ memory model does not guarantee the update thread will ever observe a write to a plain `bool` from another thread. The compiler is also permitted to hoist the read outside the loop. + +**Required fix:** Change `static bool stopped` to `static std::atomic stopped{false}` and replace `stopped = true` with `stopped.store(true, std::memory_order_release)`. + +--- + +### T-5 — `isShutdownTriggered` is a plain `static int`, not atomic (Medium) + +**File:** [hostIf_main.cpp:106](../../src/hostif/src/hostIf_main.cpp) + +Written on the shutdown thread, read on the same thread. Risk is low in practice because `exit_gracefully()` runs only on the dedicated shutdown thread. However, if a second signal fires before shutdown completes, `isShutdownTriggered` could be read stale on a re-entry. Using `std::atomic` or `volatile sig_atomic_t` would make the intent explicit. + +--- + +### T-6 — `httpServerThreadDone` read outside lock before `wait_for` (Medium) + +**File:** [hostIf_main.cpp:508 vs 511–514](../../src/hostif/src/hostIf_main.cpp) + +`httpServerThreadDone` is checked at line 508 without holding `mtx_httpServerThreadDone`, then the mutex is acquired and `wait_for` is called. Although `wait_for` uses a lambda predicate that rechecks the flag safely under the lock, the pre-read at line 508 is a data race against the write in `http_server.cpp` under the lock. The race is benign in practice because the fast path is only taken when the daemon starts, but it is technically undefined behavior. + +**Recommended fix:** Remove the pre-lock check and rely solely on the `wait_for` predicate. + +--- + +### T-7 — Signal handler in `startParodus.cpp` calls `printf()` (Medium) + +**File:** [startParodus.cpp:300–302](../../src/hostif/parodusClient/startParodus/startParodus.cpp) + +`processExit`, registered for `SIGTERM` and `SIGABRT`, calls `printf()`. `printf()` is not async-signal-safe (POSIX.1-2017 §2.4.3). If the signal fires while the process is inside `malloc`, `printf`, or any other non-reentrant function, the result is undefined behavior, commonly a deadlock on the internal `flockfile()` mutex. + +**Required fix:** Replace `printf()` in `processExit` with `write(STDOUT_FILENO, …)` or remove the output entirely. + +--- + +### T-8 — `SIGKILL` cannot be caught (Medium) + +**File:** [startParodus.cpp:301](../../src/hostif/parodusClient/startParodus/startParodus.cpp) + +`signal(SIGKILL, processExit)` is silently ignored by the kernel. The intent (run cleanup before a forced kill) cannot be achieved. The call gives a false impression that cleanup will run on `SIGKILL` and should be removed to avoid confusing future readers. + +--- + +### T-9 — `libparodus_instance` accessed from multiple threads without a lock (Medium) + +**File:** [libpd.cpp:67](../../src/hostif/parodusClient/pal/libpd.cpp) + +The handle is written in `connect_parodus()` (Parodus thread) and read in `parodus_receive_wait()` and `sendNotification()` which can be called from the main loop context. No mutex guards concurrent access. In practice, `connect_parodus()` completes before the receive loop is used, but the absence of any memory fence means the compiler or CPU is free to reorder the write such that readers see a stale or partial value. + +--- + +### T-10 — `updateHandler` uses `sleep(60)`, not a timed condition wait (Medium) + +**File:** [hostIf_updateHandler.cpp:188](../../src/hostif/handlers/src/hostIf_updateHandler.cpp) + +The thread calls `sleep(60)` between profile polls. A stop signal issued while the thread is in `sleep()` will not interrupt it; the thread will exit only after the current sleep period completes, delaying clean shutdown by up to 60 seconds. Additionally, there is no lock protecting the profile iteration sequence inside `run()`. + +**Recommended fix:** Replace `sleep(60)` with: + +```cpp +std::unique_lock lk(stop_mutex); +stop_cv.wait_for(lk, std::chrono::seconds(60), []{ return stopped.load(); }); +``` + +--- + +### T-11 — Ad-hoc detached threads have no shutdown tracking (Medium) + +Six `std::thread` or `pthread_t` instances in `Device_DeviceInfo.cpp` and `XrdkCentralComBSStore.cpp` are detached immediately after creation and are not tracked anywhere in the daemon. If the daemon shuts down while these threads are active they continue running against deallocated or freed resources (profile objects, IPC handles, curl handles). + +**Recommended fix:** For long-running threads, store the `std::thread` and call `.join()` in the owning object's destructor. For truly fire-and-forget operations, ensure any shared resources they touch are either reference-counted or outlive the thread lifetime. + +--- + +### T-12 — Static cache buffers in DeviceInfo accessed without locks (Low) + +**File:** [Device_DeviceInfo.cpp:~155](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) + +Several `static char[]` buffers (e.g., `stbMacCache`) are written and read inside functions such as `get_Device_DeviceInfo_X_COMCAST_COM_STB_MAC()` that can be called concurrently during GET handling. No lock is held. In practice, concurrent MAC queries are rare, but a torn write to the static buffer produces a corrupted string without any error indication. -- Signals are converted into a semaphore wakeup for the dedicated shutdown thread. -- The update thread is cooperative and stops on a shared boolean flag. -- The Parodus receive loop exits when `exit_parodus_recv` is set and the condition variable is signaled. -- Detached workers must be shut down by signaling and resource cleanup, not by thread joining. +--- -## Operational Risks +## Operational Risks (Summary) -- Because update polling is single-threaded and sequential, a slow profile `checkForUpdates()` implementation can delay notifications for every other profile. -- The top-level GET/SET serialization simplifies safety but limits request concurrency under heavy management traffic. -- The Parodus path depends on external service availability and deliberately retries with exponential backoff. +- An uninitialized mutex on the shutdown path (`graceful_exit_mutex`) is the highest single-point defect. +- The Parodus missed-signal race can cause the Parodus thread to linger active for up to 5 seconds after the daemon has destroyed shared resources. +- The update handler's use of `sleep()` for polling and absence of a join in shutdown allows up to 60 seconds of post-shutdown execution and potential access to freed data. +- GET/SET serialization at the top level simplifies safety but limits request concurrency under heavy management traffic. +- Six untracked detached threads in `Device_DeviceInfo.cpp` can outlive the daemon's structured teardown. ## See Also - [System Overview](overview.md) - [Data Flow](data-flow.md) +- [JSON Usage](json-usage.md) - [Common Errors](../troubleshooting/common-errors.md) \ No newline at end of file diff --git a/src/hostif/parodusClient/waldb/data-model-generic.xml b/src/hostif/parodusClient/waldb/data-model-generic.xml index 8813aefe1..fce647473 100644 --- a/src/hostif/parodusClient/waldb/data-model-generic.xml +++ b/src/hostif/parodusClient/waldb/data-model-generic.xml @@ -89,14 +89,6 @@ - - - - - - - - @@ -145,13 +137,6 @@ - - - - - - - diff --git a/src/hostif/parodusClient/waldb/data-model/data-model-generic.xml b/src/hostif/parodusClient/waldb/data-model/data-model-generic.xml index 87a7770f5..1ef1a86dd 100644 --- a/src/hostif/parodusClient/waldb/data-model/data-model-generic.xml +++ b/src/hostif/parodusClient/waldb/data-model/data-model-generic.xml @@ -3604,7 +3604,7 @@ - + @@ -4189,6 +4189,13 @@ + + + + + + + diff --git a/src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp b/src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp index 5630d7e64..030659a96 100644 --- a/src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp +++ b/src/hostif/profiles/wifi/Device_WiFi_EndPoint_Security.cpp @@ -172,17 +172,23 @@ int hostIf_WiFi_EndPoint_Security::get_hostIf_WiFi_EndPoint_Security_ModesEnable if (jsonObj) { - cJSON *securityModeObj = cJSON_GetObjectItem(jsonObj, "securityMode"); + cJSON *securityObj = cJSON_GetObjectItem(jsonObj, "security"); //ASSIGN TO OP HERE - strncpy(stMsgData->paramValue,securityModeObj->valuestring,sizeof(stMsgData->paramValue) -1); - stMsgData->paramValue[sizeof(stMsgData->paramValue) - 1] = '\0'; - stMsgData->paramtype = hostIf_StringType; - stMsgData->paramLen = strlen(stMsgData->paramValue); - - RDK_LOG(RDK_LOG_INFO, LOG_TR69HOSTIF, "[%s] WiFi Security Mode : %s\n",__FUNCTION__, stMsgData->paramValue); - RDK_LOG(RDK_LOG_INFO, LOG_TR69HOSTIF, "[%s] WiFi Security Mode : %s\n",__FUNCTION__, securityModeObj->valuestring); - retVal = OK; + if (securityObj && cJSON_IsNumber(securityObj)) + { + put_int(stMsgData->paramValue,securityObj->valueint); + stMsgData->paramtype = hostIf_IntegerType; + stMsgData->paramLen = sizeof(int); + + RDK_LOG(RDK_LOG_INFO, LOG_TR69HOSTIF, "[%s] WiFi Security Mode : %d\n",__FUNCTION__, securityObj->valueint); + retVal = OK; + } + else + { + RDK_LOG (RDK_LOG_ERROR, LOG_TR69HOSTIF, "[%s] json parse error, missing/invalid \"security\" in result\n", __FUNCTION__); + retVal = NOK; + } } else diff --git a/test/docs/L2_Test_Coverage.md b/test/docs/L2_Test_Coverage.md new file mode 100644 index 000000000..08d6d7f5b --- /dev/null +++ b/test/docs/L2_Test_Coverage.md @@ -0,0 +1,1232 @@ +# L2 Functional Test Coverage + +## Overview + +This document maps the current L2 functional tests in `test/functional-tests/` against +the full tr69hostif module surface. It identifies what is covered, what is not, and +precisely quantifies the tests needed to reach 100% functional coverage. + +> Last analysed: March 2026 +> Test suite: `test/functional-tests/` — 4 feature files, **45 ordered pytest functions** +> Module surface: **708 parameter handlers** + **38 behavioral scenarios** = **746 testable items** +> **Tests needed for 100% coverage: ~761** +> **Current effective coverage: ~52 tests (~6.8%)** +> **Tests still required: ~709** + +--- + +## Test Suite Layout + +``` +test/functional-tests/ +├── features/ # BDD scenario descriptions (not wired to pytest) +│ ├── tr69hostif_bootup_sequence.feature +│ ├── tr69hostif_deviceip.feature +│ ├── tr69hostif_handlers_communications.feature +│ └── tr69hostif_webpa.feature +└── tests/ # Runnable pytest functions + ├── test_bootup_sequence.py # orders 1–18 + ├── test_handlers_communications.py # orders 19–24 + ├── tr69hostif_deviceip.py # orders 25–28 + ├── tr69hostif_webpa.py # orders 29–45 + ├── helper_functions.py # shell/log helpers + ├── basic_constants.py # shared constants + └── profile_helper_functions.py # ⚠ stub — broken (NameError at runtime) +``` + +**Test runner:** `pytest` with `@pytest.mark.run(order=N)`, executed sequentially. +**Interfaces exercised:** `rbuscli` (rbus DML), mock `parodus` binary (WebPA), log scraping. + +--- + +## Infrastructure Notes + +| Component | Status | Notes | +|-----------|--------|-------| +| `conftest.py` / fixtures | **Missing** | No setup/teardown; no parameter rollback between tests | +| BDD wiring | **Missing** | `.feature` files are documentation only — no `@given/@when/@then` implementations | +| `profile_helper_functions.py` | **Broken** | `GREP_STRING` undefined → `NameError` at runtime | +| HTTP server test helper | **Dead code** | `profile_init_run_command()` builds a `curl` command against `:11999` but is never called | +| Log isolation | **Absent** | Log cleared once at suite start; grep spans entire boot log | +| Test state isolation | **Absent** | SET operations persist; later tests may see values from earlier tests | +| Hardcoded expected values | `"DOCKER"`, `"99.99.15.07"`, etc. | Tests are tied to one specific container image | + +--- + +## Current Coverage + +### Bootup Sequence (orders 1–18) + +All tests are **log-scrape checks** — they verify messages appear (or are absent) after +daemon startup. No parameter values are read or written. + +| Order | Area Tested | Method | +|-------|-------------|--------| +| 1–2 | HTTP/JSON server thread start | Log: `"SERVER: Started server successfully."` | +| 3 | Parodus connection init | Log: `"Initiating Connection with PARODUS success.."` | +| 4 | Thread creation success | Log absence: `"pthread_create() failed"` | +| 5 | rbus DML registration | Log: `"rbus_regDataElements registered successfully"` | +| 6 | Config manager init | Log absence: `"Failed to hostIf_initalize_ConfigManger()"` | +| 7–8 | IARM bus init + `getPwrContInterface` thread | Log positive | +| 9 | Data model XML merge pipeline | Log: `"Successfully merged Data Model"` | +| 10 | Data model load | Log: `"Successfully initialize Data Model"` | +| 11 | Ethernet client thread start | Log: `"checkForUpdates] Got lock.."` | +| 12 | Bootstrap config file load | Log: `"/opt/secure/RFC/bootstrap.ini"` | +| 13 | Device manager (dsClient) init | Log: `"Device manager Initialized success"` | +| 14 | WebPA/parodus thread start | Log: `"Starting WEBPA Parodus Connections"` | +| 15–16 | PowerController start + callback register | Log positive | +| 17 | No fatal errors in full log | Negative sweep: no `FATAL`/`CRITICAL` | +| 18 | RFC default store | File `/tmp/rfcdefaults.ini` + rbus GET of `…RFC.Feature.Airplay.Enable` | + +--- + +### RFC / Handler Parameters (orders 19–24) + +All via **`rbuscli` SET + GET roundtrip** (rbus DML path). + +| Order | TR-181 Parameter | Dir | Type | +|-------|-----------------|-----|------| +| 19 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Telemetry.Version` | SET+GET | string | +| 20 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.DHCPv6Client.Enable` | SET+GET | boolean | +| 20 | `Device.Time.NTPServer1` | SET+GET | string | +| 21 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.HdmiCecSink.CECVersion` | SET+GET | string | +| 21 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SWDLSpLimit.Enable` | SET+GET | boolean | +| 21 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SWDLSpLimit.TopSpeed` | SET+GET | int | +| 21 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.eMMCFirmware.Version` | SET+GET | string | +| 21 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.IncrementalCDL.Enable` | SET+GET | boolean | +| 22 | `Device.DeviceInfo.X_RDKCENTRAL-COM_IPRemoteSupport.Enable` | SET+GET | boolean | +| 22 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ForwardSSH.Enable` | SET+GET | boolean | +| 22 | `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadDeferReboot` | SET+GET | boolean | +| 22 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.RPC.FirmwareDownloadCompletedNotification` | SET+GET | boolean | +| 23 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Bootstrap.PartnerProductName` | SET+GET | string | +| 23 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Bootstrap.NetflixESNprefix` | SET+GET | string | +| 23 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Bootstrap.PartnerName` | SET+GET | string | +| 23 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Bootstrap.SsrUrl` | SET+GET | string | +| 24 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Bootstrap.PartnerProductName` + file persistence | SET+GET+file | string | + +--- + +### DeviceInfo / IP Parameters (orders 25–28) + +| Order | TR-181 Parameter | Dir | Expected Value | +|-------|-----------------|-----|----------------| +| 25 | `Device.DeviceInfo.SoftwareVersion` | GET | `"99.99.15.07"` | +| 25 | `Device.DeviceInfo.ModelName` | GET | `"DOCKER"` | +| 25 | `Device.DeviceInfo.X_COMCAST-COM_FirmwareFilename` | GET | `"Platform_Cotainer_1.0.0"` | +| 25 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.MEMSWAP.Enable` | SET+GET | `"true"` | +| 26 | `Device.IP.Interface.1.IPv4Address.1.Enable` | GET | `"true"` | +| 26 | `Device.IP.Interface.1.IPv6Enable` | GET | `"true"` | +| 26 | `Device.IP.Interface.1.IPv6Address.1.Enable` | GET | `"true"` | +| 26 | `Device.IP.Interface.1.IPv6Address.1.Anycast` | GET | `"false"` | +| 26 | `Device.IP.Interface.1.IPv6Address.1.Origin` | GET | `"WellKnown"` | +| 26 | `Device.IP.Interface.1.IPv6Address.1.PreferredLifetime` | GET | `"0001-01-01T00:00:00Z"` | +| 26 | `Device.IP.Interface.1.IPv6Prefix.1.Autonomous` | GET | `"false"` | +| 26 | `Device.IP.Interface.1.IPv6Prefix.1.StaticType` | GET | `"Inapplicable"` | +| 26 | `Device.IP.Interface.1.IPv6Prefix.1.PrefixStatus` | GET | `"Preferred"` | +| 26 | `Device.IP.Interface.1.IPv6Prefix.1.ValidLifetime` | GET | `"0001-01-01T00:00:00Z"` | +| 26 | `Device.IP.Interface.1.IPv6AddressNumberOfEntries` | GET | `"1"` | +| 27 | `Device.Services.STBServiceNumberOfEntries` | GET | `"1"` | +| 28 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshStatus` | GET | `"INACTIVE"` | +| 28 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshTrigger` | SET | `"start shorts"` | +| 28 | `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshArgs` | SET | SSH args string | + +--- + +### WebPA / Parodus (orders 29–45) + +Via **mock `parodus` binary** with JSON payloads. Validation reads `/opt/logs/parodus.log`. + +| Order | TR-181 Parameter | Op | Verification | +|-------|-----------------|-----|-------------| +| 29–30 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Control.XconfUrl` | SET→GET | statusCode 200, value roundtrip | +| 31–32 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.FWUpdate.AutoExcluded.Enable` | SET→GET | statusCode 200, `"false"` | +| 33–34 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.LogUpload.LogServerUrl` | SET→GET | statusCode 200, `"logs.mock.tv"` | +| 35 | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.SWDLSpLimit.LowSpeed` | GET | `"12800"` | +| 36 | `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadProtocol` | GET | `"http"` | +| 37 | `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadStatus` | GET | presence only | +| 38 | `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadURL` | GET | `"https://mockserver.tv/Images"` | +| 39 | `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareToDownload` | GET | `"TESTIMAGE_DEV.bin"` | +| 40 | `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareUpdateState` | GET | `"Download complete"` | +| 41 | `Device.DeviceInfo.` (wildcard) | GET | statusCode 200, `"Success"` | +| 42–44 | FW upgrade: Protocol, URL, Image | SET × 3 | statusCode 200 each | +| 45 | `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadNow` (DownloadNow) | SET | statusCode 200 + log `"Triggered Download"` | + +--- + +## Coverage Heat Map + +```mermaid +graph TD + A[tr69hostif Module] --> B[Bootup Lifecycle] + A --> C[rbus/DML Handler] + A --> D[HTTP Server] + A --> E[WebPA/Parodus] + A --> F[Thunder Plugins] + A --> G[RFC Store] + A --> H[Device.WiFi] + A --> I[Device.IP] + A --> J[Device.Ethernet] + A --> K[Device.DHCPv4] + + style B fill:#2d7a2d,color:#fff + style C fill:#2d7a2d,color:#fff + style E fill:#2d7a2d,color:#fff + style G fill:#d4a017,color:#000 + style I fill:#2d7a2d,color:#fff + style D fill:#c0392b,color:#fff + style F fill:#c0392b,color:#fff + style H fill:#c0392b,color:#fff + style J fill:#d4a017,color:#000 + style K fill:#c0392b,color:#fff +``` + +| Colour | Meaning | +|--------|---------| +| Green | Covered | +| Amber | Partially covered | +| Red | Not covered | + +--- + +## Coverage Gaps + +### Priority 1 — Thunder Plugin Calls (0% covered) + +**All 5 Thunder plugins and all 21 TR-181 parameters that use them have zero test coverage.** +This is the largest gap because Thunder calls are synchronous blocking operations with a +10-second timeout; any regression silently returns empty/NOK with no daemon crash. + +| Plugin | Method | TR-181 Parameter | Gap | +|--------|--------|-----------------|-----| +| `org.rdk.NetworkManager` | `GetPrimaryInterface` + `GetIPSettings` | `Device.DeviceInfo.X_COMCAST-COM_STB_IP` | No GET test | +| `org.rdk.NetworkManager` | `GetAvailableInterfaces` | `Device.WiFi.SSID.{i}.Enable` / `MACAddress` | No GET test | +| `org.rdk.NetworkManager` | `GetConnectedSSID` | `Device.WiFi.SSID.{i}.SSID` / `BSSID` / `Name` | No GET test | +| `org.rdk.NetworkManager` | `GetConnectedSSID` | `Device.WiFi.Endpoint.{i}.SSIDReference` / `Stats.SignalStrength` | No GET test | +| `org.rdk.NetworkManager` | `GetConnectedSSID` | `Device.WiFi.Endpoint.{i}.Security.ModesEnabled` | No GET test | +| `org.rdk.NetworkManager` | `GetWifiState` | `Device.WiFi.SSID.{i}.Status` | No GET test | +| `org.rdk.NetworkManager` | `Enable/DisableInterface` | `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` | No SET test | +| `org.rdk.AuthService` | `setPartnerId` | `Device.DeviceInfo.X_RDKCENTRAL-COM_Syndication.PartnerId` | No SET test | +| `org.rdk.AuthService` | `getServiceAccountId` | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AccountInfo.AccountID` | No GET test | +| `org.rdk.AuthService` | `getExperience` | `Device.DeviceInfo.X_RDKCENTRAL-COM_Experience` | No GET test | +| `org.rdk.System` | `getPrivacyMode` | `Device.DeviceInfo.…ReverseSSH.xOpsReverseSshTrigger` gate | No privacy-mode gate test | +| `org.rdk.MigrationPreparer` | `getComponentReadiness` | `Device.DeviceInfo.MigrationPreparer.MigrationReady` | No GET test | +| `org.rdk.Account` | `getLastCheckoutResetTime` | `…HotelCheckout.LastResetTime` / `Status` | No GET test | + +**Recommended test approach:** +- Deploy a mock Thunder JSON-RPC responder on `127.0.0.1:9998` in the test container +- Stub each `org.rdk.*` method to return a known JSON payload +- Verify the TR-181 parameter GET returns the expected mapped value + +--- + +### Priority 2 — HTTP Server (0% functional coverage) + +The libsoup-based HTTP server (`/`) accepting WDMP-C JSON is completely untested at the +protocol level. The only evidence of intent is dead code in `test_bootup_sequence.py`: + +```python +# Dead code — never called from any test function +def profile_init_run_command(): + cmd = f"curl -s -X GET http://127.0.0.1:11999/ ..." +``` + +**Required tests:** + +| Test | Method | Request | Expected | +|------|--------|---------|----------| +| GET single parameter | HTTP GET | `{"names":["Device.DeviceInfo.ModelName"]}` | `{"statusCode":200,...}` | +| GET multiple parameters | HTTP GET | `{"names":["param1","param2"]}` | Multi-value response | +| GET wildcard | HTTP GET | `{"names":["Device.DeviceInfo."]}` | All DeviceInfo params | +| SET parameter | HTTP POST with CallerID | `{"parameters":[{"name":...,"value":...}]}` | `{"statusCode":200}` | +| SET without CallerID | HTTP POST no header | — | `500 POST Not Allowed without CallerID` | +| Malformed JSON body | HTTP GET | `{bad json}` | `400 Bad Request` | +| Unknown parameter | HTTP GET | nonexistent param | Non-zero statusCode | +| Empty body | HTTP GET | no body | `400 No request data.` | + +--- + +### Priority 3 — WiFi TR-181 Subtree (0% covered) + +`Device.WiFi.*` has 13 TR-181 parameters mapped to Thunder — none are tested. + +| Parameter | Dir | Needs | +|-----------|-----|-------| +| `Device.WiFi.X_RDKCENTRAL-COM_WiFiEnable` | GET+SET | Positive GET; SET enable/disable roundtrip | +| `Device.WiFi.SSID.{i}.BSSID` | GET | GET with mock Thunder response | +| `Device.WiFi.SSID.{i}.SSID` | GET | GET with mock Thunder response | +| `Device.WiFi.SSID.{i}.Name` | GET | GET with mock Thunder response | +| `Device.WiFi.SSID.{i}.Enable` | GET | GET with mock Thunder response | +| `Device.WiFi.SSID.{i}.MACAddress` | GET | GET with mock Thunder response | +| `Device.WiFi.SSID.{i}.Status` | GET | GET with mock Thunder response | +| `Device.WiFi.Endpoint.{i}.Enable` | GET | GET with mock Thunder response | +| `Device.WiFi.Endpoint.{i}.Status` | GET | GET with mock Thunder response | +| `Device.WiFi.Endpoint.{i}.SSIDReference` | GET | GET with mock Thunder response | +| `Device.WiFi.Endpoint.{i}.Stats.SignalStrength` | GET | GET with mock Thunder response | +| `Device.WiFi.Endpoint.{i}.Security.ModesEnabled` | GET | GET with mock Thunder response | + +--- + +### Priority 4 — RFC Variable Store (partial) + +| Scenario | Status | +|----------|--------| +| `rfcdefaults.ini` file read + rbus GET | Covered (order 18) | +| `bootstrap.ini` persistence + `.journal` file | Covered (order 24) | +| `rfcVariable.ini` read-back | **Not covered** | +| RFC override precedence (`rfcVariable` overrides `rfcdefaults`) | **Not covered** | +| `XRFCVarStore` consistency after daemon restart | **Not covered** | +| `RFC_CONTROL_RELOADCACHE` trigger (via HTTP server POST) | **Not covered** | + +--- + +### Priority 5 — Negative / Edge Cases (0% covered) + +No negative test exists in the current suite. + +| Missing Test | Description | +|-------------|-------------| +| SET wrong data type | SET a string param with an integer value | +| SET out-of-range value | SET an integer param beyond valid range | +| GET nonexistent parameter | GET a param that does not exist in data model | +| Malformed WebPA JSON | Send malformed JSON to parodus mock | +| Thunder timeout simulation | Kill mock Thunder server mid-request; verify NOK returned | +| Thunder empty response | Return `{}` from mock; verify handler returns NOK, no crash | +| HTTP server POST without CallerID | Expect `500` response | +| WebPA REPLACE command | Currently only GET/SET tested | + +--- + +### Priority 6 — Untested Module Areas + +| Module / Profile | Status | Notes | +|-----------------|--------|-------| +| `Device.Ethernet.*` | Thread start logged only | No parameter GET/SET | +| `Device.DHCPv4.*` | **Zero** | No thread log, no parameter test | +| `Device.InterfaceStack.*` | **Zero** | No test | +| `Device.MoCA.*` | **Zero** | No test | +| `Device.X_RDKCENTRAL-COM_T2.*` | **Zero** | Constants defined but `check_Rbus_data()` never called | +| `Device.StorageService.*` | **Zero** | No test | +| STB Service profile | `STBServiceNumberOfEntries` GET only (order 27) | Internal params untested | + +--- + +## Tests Needed — Prioritised Backlog + +```mermaid +flowchart TD + P1[P1: Thunder Plugin Mock Tests\n13 methods × GET/SET] --> P2 + P2[P2: HTTP Server Protocol Tests\nGET · POST · errors] --> P3 + P3[P3: WiFi Parameter Tests\n12 params via Thunder mock] --> P4 + P4[P4: RFC Store Override Tests\nrfcVariable precedence] --> P5 + P5[P5: Negative / Edge Case Tests\nbad input · timeout · malformed] + P5 --> P6 + P6[P6: Missing Profile Tests\nEthernet · DHCPv4 · MoCA · T2] +``` + +| Priority | Area | Estimated Tests | Blocking? | +|----------|------|-----------------|-----------| +| P1 | Thunder plugin mock tests | ~26 | Yes — zero coverage of live path | +| P2 | HTTP server protocol tests | ~8 | Yes — dead code in current suite | +| P3 | WiFi TR-181 parameter tests | ~12 | Yes — zero coverage | +| P4 | RFC variable store override | ~4 | No | +| P5 | Negative / edge cases | ~8 | No | +| P6 | Ethernet, DHCPv4, MoCA, T2 | ~10 | No | + +--- + +## Infrastructure Fixes Required + +Before new tests can be added reliably, the following infrastructure issues must be resolved: + +| Issue | Fix | +|-------|-----| +| No `conftest.py` | Add `conftest.py` with `@pytest.fixture(autouse=True)` that records and restores any SET parameters after each test | +| BDD feature files not wired | Either wire them with `pytest-bdd` step implementations or drop them and document test intent in docstrings | +| `profile_helper_functions.py` broken | Fix `GREP_STRING` undefined reference or remove the file | +| HTTP server dead code | Move `profile_init_run_command()` into actual test functions | +| Hardcoded expected values | Extract to `basic_constants.py` with a comment that they are image-specific | +| Log isolation | Call `clear_tr69hostiflogs()` at the start of each test (the function exists but is commented out) | + +--- + +--- + +## Complete Coverage Count Analysis + +### Counting Methodology + +- Each **GET handler** = 1 required test (positive GET, verify value returned) +- Each **SET handler** = 1 required test (positive SET + GET roundtrip) +- Each **behavioral scenario** = 1 required test +- Negative/edge case tests are counted separately (~16 total) +- Internal helpers, dispatcher delegates, and duplicated `#ifdef` branches excluded + +--- + +### Per-Profile Handler Counts and Coverage Status + +| # | Profile Area | TR-181 Namespace | GET | SET | Tests Needed | Covered | Gap | Coverage | +|---|-------------|-----------------|:---:|:---:|:---:|:---:|:---:|:---:| +| 1 | **DeviceInfo** | `Device.DeviceInfo.*` | 111 | 61 | **172** | ~20 | ~152 | ~12% | +| 2 | **Ethernet** | `Device.Ethernet.*` | 25 | 5 | **30** | 0 | 30 | 0% | +| 3 | **IP** | `Device.IP.*` | 73 | 33 | **106** | ~12 | ~94 | ~11% | +| 4 | **DHCPv4** | `Device.DHCPv4.*` | 4 | 0 | **4** | 0 | 4 | 0% | +| 5 | **InterfaceStack** | `Device.InterfaceStack.*` | 2 | 0 | **2** | 0 | 2 | 0% | +| 6 | **MoCA** | `Device.MoCA.*` | 89 | 10 | **99** | 0 | 99 | 0% | +| 7 | **STBService** | `Device.Services.STBService.*` | 71 | 14 | **85** | ~1 | ~84 | ~1% | +| 8 | **StorageService** | `Device.StorageService.*` | 15 | 0 | **15** | 0 | 15 | 0% | +| 9 | **Time** | `Device.Time.*` | 20 | 17 | **37** | ~1 | ~36 | ~3% | +| 10 | **WiFi** | `Device.WiFi.*` | 132 | 21 | **153** | 0 | 153 | 0% | +| 11 | **Device** | `Device.*` (WebPA URLs) | 3 | 1 | **4** | 0 | 4 | 0% | +| | **Parameter subtotal** | | **545** | **163** | **707** | **~34** | **~673** | **~5%** | + +### DeviceInfo Profile — Per-File Breakdown + +DeviceInfo is the largest single profile area (24% of all handler tests needed). + +| Source File | GET | SET | Tests Needed | Notes | +|-------------|:---:|:---:|:---:|-------| +| [Device_DeviceInfo.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp) | 70 | 59 | 129 | Largest file; all Thunder-backed paths live here | +| [Device_DeviceInfo_Processor.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo_Processor.cpp) | 1 | 0 | 1 | `Processor.Architecture` | +| [Device_DeviceInfo_ProcessStatus.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo_ProcessStatus.cpp) | 1 | 0 | 1 | `ProcessStatus.CPUUsage` | +| [Device_DeviceInfo_ProcessStatus_Process.cpp](../../src/hostif/profiles/DeviceInfo/Device_DeviceInfo_ProcessStatus_Process.cpp) | 6 | 0 | 6 | PID, Command, Size, Priority, CPUTime, State | +| [XrdkBlueTooth.cpp](../../src/hostif/profiles/DeviceInfo/XrdkBlueTooth.cpp) | 32 | 2 | 34 | `BLE_TILE_PROFILE` compile guard | +| [XrdkCentralComRFC.cpp](../../src/hostif/profiles/DeviceInfo/XrdkCentralComRFC.cpp) | 1 | 0 | 1 | `XRFCStorage::getValue` | +| **DeviceInfo TOTAL** | **111** | **61** | **172** | | + +### WiFi Profile — Sub-Object Breakdown + +WiFi is the most handler-diverse profile with 15 distinct sub-object types and **0% current coverage**. + +| Sub-Object | GET | SET | Tests Needed | +|-----------|:---:|:---:|:---:| +| WiFi top-level | 5 | 0 | 5 | +| Radio | 27 | 0 | 27 | +| Radio.Stats | 9 | 0 | 9 | +| SSID | 7 | 0 | 7 | +| SSID.Stats | 15 | 0 | 15 | +| AccessPoint | 11 | 8 | 19 | +| AccessPoint.AssociatedDevice | 7 | 0 | 7 | +| AccessPoint.Security | 9 | 6 | 15 | +| AccessPoint.WPS | 3 | 0 | 3 | +| EndPoint | 10 | 5 | 15 | +| EndPoint.Profile | 6 | 0 | 6 | +| EndPoint.Profile.Security | 4 | 2 | 6 | +| EndPoint.Security | 2 | 0 | 2 | +| EndPoint.WPS | 3 | 0 | 3 | +| X_RDKCENTRAL.ClientRoaming | 13 | 0 | 13 | +| **WiFi TOTAL** | **132** | **21** | **153** | + +### Non-Parameter Behavioral Scenarios + +| Category | Needed | Covered | Gap | +|----------|:---:|:---:|:---:| +| HTTP Server (GET, POST, errors, missing CallerID, malformed JSON, empty body) | 8 | 0 | 8 | +| WebPA / Parodus (GET, SET, REPLACE, ADD, attributes, wildcard, FW upgrade) | 10 | ~5 | ~5 | +| RFC Store (read, override precedence, reload trigger, restart consistency) | 10 | ~3 | ~7 | +| Daemon lifecycle (start, stop, SIGTERM, re-init, PID file, sd_notify) | 10 | ~10 | 0 | +| **Behavioral subtotal** | **38** | **~18** | **~20** | + +### Grand Total + +| Category | Tests Needed | Currently Covered | Still Required | +|----------|:---:|:---:|:---:| +| Parameter handlers (GET + SET across all 11 profiles) | 707 | ~34 | ~673 | +| Behavioral scenarios (HTTP, WebPA, RFC, lifecycle) | 38 | ~18 | ~20 | +| Negative / edge case tests | ~16 | 0 | ~16 | +| **TOTAL** | **~761** | **~52** | **~709** | + +> **Current L2 coverage: ~6.8% of module surface.** +> **709 additional test cases are required to reach 100%.** + +--- + +### Where We Are NOT — Profile Gap Summary + +| Profile | Tests Needed | Have | Missing | Primary Gap Areas | +|---------|:---:|:---:|:---:|-------------------| +| `Device.WiFi.*` | 153 | 0 | **153** | Entire profile untested — Radio (36), AccessPoint (41), SSID (22), EndPoint (32), ClientRoaming (13) | +| `Device.MoCA.*` | 99 | 0 | **99** | Interface (43), AssociatedDevice (17), Stats (15), QoS (10), MeshTable (4) | +| `Device.DeviceInfo.*` | 172 | ~20 | **~152** | Thunder-backed (21), BT (34), ProcessStatus (8), firmware (10), SSH/privacy (3), remaining ~76 params | +| `Device.IP.*` | 106 | ~12 | **~94** | IPv4 SETs (6), all IPv6Address/Prefix (23), Interface.Stats (9), IP-level SETs (10) | +| `Device.Services.STBService.*` | 85 | ~1 | **~84** | AudioOutput SET/GET (25), eMMC (14), SPDIF (11), SDCard (10), Security (9) | +| `Device.Ethernet.*` | 30 | 0 | **30** | Interface GET+SET (15), Interface.Stats GET (15) | +| `Device.Time.*` | 37 | ~1 | **~36** | NTPServer2–5 (8), NTP directives (5), all 17 SET handlers | +| `Device.StorageService.*` | 15 | 0 | **15** | PhysicalMedium GET-only (14) + service entry (1) | +| Thunder Plugin endpoints | 21 params | 0 | **21** | All 5 plugins, 13 methods; requires mock JSON-RPC server on :9998 | +| HTTP Server protocol | 8 | 0 | **8** | GET/POST/errors — only dead code exists in current suite | +| `Device.DHCPv4.*` | 4 | 0 | **4** | Client params; all GET-only | +| `Device.InterfaceStack.*` | 2 | 0 | **2** | HigherLayer, LowerLayer | +| Negative / edge cases | ~16 | 0 | **~16** | Wrong type, nonexistent param, malformed JSON, timeout simulation | + +--- + +## Complete TR-181 Parameter Inventory + +This is the exhaustive flat list of every testable TR-181 parameter, non-parameter +functional behaviour, and lifecycle path discovered by reading every profile source +file. Use this table as the master checklist to calculate 100% test coverage. + +**Columns:** `Parameter` | `Dir` (GET / SET / GET+SET) | `Source File` | `Handler Function` + +--- + +### 1. Device.DeviceInfo — Standard Parameters +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` / `.h` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.Manufacturer` | GET | `get_Device_DeviceInfo_Manufacturer` | +| `Device.DeviceInfo.ManufacturerOUI` | GET | `get_Device_DeviceInfo_ManufacturerOUI` | +| `Device.DeviceInfo.ModelName` | GET | `get_Device_DeviceInfo_ModelName` | +| `Device.DeviceInfo.Description` | GET | `get_Device_DeviceInfo_Description` | +| `Device.DeviceInfo.ProductClass` | GET | `get_Device_DeviceInfo_ProductClass` | +| `Device.DeviceInfo.SerialNumber` | GET | `get_Device_DeviceInfo_SerialNumber` | +| `Device.DeviceInfo.HardwareVersion` | GET | `get_Device_DeviceInfo_HardwareVersion` | +| `Device.DeviceInfo.SoftwareVersion` | GET | `get_Device_DeviceInfo_SoftwareVersion` | +| `Device.DeviceInfo.AdditionalHardwareVersion` | GET | `get_Device_DeviceInfo_AdditionalHardwareVersion` | +| `Device.DeviceInfo.AdditionalSoftwareVersion` | GET | `get_Device_DeviceInfo_AdditionalSoftwareVersion` | +| `Device.DeviceInfo.ProvisioningCode` | GET | `get_Device_DeviceInfo_ProvisioningCode` | +| `Device.DeviceInfo.UpTime` | GET | `get_Device_DeviceInfo_UpTime` | +| `Device.DeviceInfo.FirstUseDate` | GET | `get_Device_DeviceInfo_FirstUseDate` | +| `Device.DeviceInfo.VendorConfigFileNumberOfEntries` | GET | `get_Device_DeviceInfo_VendorConfigFileNumberOfEntries` | +| `Device.DeviceInfo.SupportedDataModelNumberOfEntries` | GET | `get_Device_DeviceInfo_SupportedDataModelNumberOfEntries` | +| `Device.DeviceInfo.ProcessorNumberOfEntries` | GET | `get_Device_DeviceInfo_ProcessorNumberOfEntries` | +| `Device.DeviceInfo.VendorLogFileNumberOfEntries` | GET | `get_Device_DeviceInfo_VendorLogFileNumberOfEntries` | +| `Device.DeviceInfo.MemoryStatus.Total` | GET | `get_Device_DeviceInfo_MemoryStatus_Total` | +| `Device.DeviceInfo.MemoryStatus.Free` | GET | `get_Device_DeviceInfo_MemoryStatus_Free` | + +--- + +### 2. Device.DeviceInfo — Processor / ProcessStatus +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo_Processor.cpp` +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo_ProcessStatus_Process.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.Processor.{i}.Architecture` | GET | `get_Device_DeviceInfo_Processor_Architecture` | +| `Device.DeviceInfo.ProcessStatus.Process.{i}.PID` | GET | `getProcessFields(eProcessPid)` | +| `Device.DeviceInfo.ProcessStatus.Process.{i}.Command` | GET | `getProcessFields(eProcessCmd)` | +| `Device.DeviceInfo.ProcessStatus.Process.{i}.Size` | GET | `getProcessFields(eProcessSize)` | +| `Device.DeviceInfo.ProcessStatus.Process.{i}.Priority` | GET | `getProcessFields(eProcessPriority)` | +| `Device.DeviceInfo.ProcessStatus.Process.{i}.CPUTime` | GET | `getProcessFields(eProcessCPUTime)` | +| `Device.DeviceInfo.ProcessStatus.Process.{i}.State` | GET | `getProcessFields(eProcessState)` | +| `Device.DeviceInfo.ProcessStatus.ProcessNumberOfEntries` | GET | `get_Device_DeviceInfo_ProcessStatus_ProcessNumberOfEntries` | + +--- + +### 3. Device.DeviceInfo — Comcast/RDK Custom Parameters +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_COMCAST-COM_STB_MAC` | GET | `get_Device_DeviceInfo_X_COMCAST_COM_STB_MAC` | +| `Device.DeviceInfo.X_COMCAST-COM_STB_IP` | GET | `get_Device_DeviceInfo_X_COMCAST_COM_STB_IP` | +| `Device.DeviceInfo.X_COMCAST-COM_PowerStatus` | GET | `get_Device_DeviceInfo_X_COMCAST_COM_PowerStatus` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareFilename` | GET | `get_Device_DeviceInfo_X_RDKCENTRAL_COM_FirmwareFilename` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareToDownload` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_FirmwareToDownload` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadStatus` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_FirmwareDownloadStatus` | +| `Device.DeviceInfo.X_COMCAST-COM_FirmwareDownloadProtocol` | GET+SET | `get/set_Device_DeviceInfo_X_COMCAST_COM_FirmwareDownloadProtocol` | +| `Device.DeviceInfo.X_COMCAST-COM_FirmwareDownloadURL` | GET+SET | `get/set_Device_DeviceInfo_X_COMCAST_COM_FirmwareDownloadURL` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadUseCodebig` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_FirmwareDownloadUseCodebig` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadDeferReboot` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_FirmwareDownloadDeferReboot` | +| `Device.DeviceInfo.X_COMCAST-COM_FirmwareDownloadPercent` | GET | `get_Device_DeviceInfo_X_COMCAST_COM_FirmwareDownloadPercent` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareUpdateState` | GET | `get_Device_DeviceInfo_X_RDKCENTRAL_COM_FirmwareUpdateState` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_FirmwareDownloadNow` | SET | `set_xFirmwareDownloadNow` (triggers download) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_Reset` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_Reset` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_BootStatus` | GET | `get_Device_DeviceInfo_X_RDKCENTRAL_COM_BootStatus` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_BootTime` | GET | `get_X_RDKCENTRAL_COM_BootTime` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_CPUTemp` | GET | `get_Device_DeviceInfo_X_RDKCENTRAL_COM_CPUTemp` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_LastRebootReason` | GET | `get_X_RDKCENTRAL_COM_LastRebootReason` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_Experience` | GET | (Thunder `org.rdk.AuthService.getExperience`) | +| `Device.DeviceInfo.X_RDK_FirmwareName` | GET | `get_X_RDK_FirmwareName` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_PreferredGatewayType` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_PreferredGatewayType` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_MigrationPreparer.MigrationReady` | GET | `get_Device_DeviceInfo_MigrationPreparer_MigrationReady` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_Migration.MigrationStatus` | GET | `get_Device_DeviceInfo_Migration_MigrationStatus` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM.IUI.Version` | GET+SET | `get/set_Device_DeviceInfo_IUI_Version` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM.IUI.AppsVersion` | GET+SET | `get/set_Device_DeviceInfo_IUI_AppsVersion` | + +--- + +### 4. Device.DeviceInfo — xOpsDeviceMgmt Logging +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.Logging.xOpsDMUploadLogsNow` | GET+SET | `get/set_xOpsDMUploadLogsNow` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.Logging.xOpsDMLogsUploadStatus` | GET | `get_xOpsDMLogsUploadStatus` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.Logging.xOpsDMMoCALogEnabled` | GET+SET | `get/set_xOpsDMMoCALogEnabled` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.Logging.xOpsDMMoCALogPeriod` | GET+SET | `get/set_xOpsDMMoCALogPeriod` | + +--- + +### 5. Device.DeviceInfo — xOpsDeviceMgmt ReverseSSH / ForwardSSH +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshTrigger` | SET | `set_xOpsReverseSshTrigger` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshArgs` | GET+SET | `get/set_xOpsReverseSshArgs` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshStatus` | GET | `get_xOpsReverseSshStatus` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ForwardSSH.Enable` | GET+SET | `get/set_xOpsDeviceMgmtForwardSSHEnable` | + +--- + +### 6. Device.DeviceInfo — xOpsDeviceMgmt RPC +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.RPC.RebootNow` | SET | `set_xOpsDeviceMgmtRPCRebootNow` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.RPC.DeviceManageableNotification` | GET+SET | `get/set_xOpsRPCDevManageableNotification` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.RPC.FirmwareDownloadStartedNotification` | GET+SET | `get/set_xOpsRPCFwDwldStartedNotification` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.RPC.FirmwareDownloadCompletedNotification` | GET+SET | `get/set_xOpsRPCFwDwldCompletedNotification` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.RPC.RebootPendingNotification` | GET+SET | `get/set_xOpsRPCRebootPendingNotification` | + +--- + +### 7. Device.DeviceInfo — xOpsDeviceMgmt hwHealthTest *(USE_HWSELFTEST_PROFILE)* +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.Enable` | SET | `set_xOpsDeviceMgmt_hwHealthTest_Enable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.ExecuteTest` | SET | `set_xOpsDeviceMgmt_hwHealthTest_ExecuteTest` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.Results` | GET | `get_xOpsDeviceMgmt_hwHealthTest_Results` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.SetTuneType` | SET | `set_xOpsDeviceMgmt_hwHealthTest_SetTuneType` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.ExecuteTuneTest` | SET | `set_xOpsDeviceMgmt_hwHealthTest_ExecuteTuneTest` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTestTune.TuneResults` | GET | `get_xOpsDeviceMgmt_hwHealthTestTune_TuneResults` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.EnablePeriodicRun` | SET | `set_xOpsDeviceMgmt_hwHealthTest_EnablePeriodicRun` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.PeriodicRunFrequency` | SET | `set_xOpsDeviceMgmt_hwHealthTest_PeriodicRunFrequency` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.cpuThreshold` | SET | `set_xOpsDeviceMgmt_hwHealthTest_CpuThreshold` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTest.dramThreshold` | SET | `set_xOpsDeviceMgmt_hwHealthTest_DramThreshold` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.hwHealthTestWAN.WANTestEndPointURL` | SET | `set_RFC_hwHealthTestWAN_WANEndPointURL` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.hwHealthTest.ResultFilter.Enable` | SET | `set_xRDKCentralComRFC_hwHealthTest_ResultFilter_Enable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.hwHealthTest.ResultFilter.QueueDepth` | SET | `set_xRDKCentralComRFC_hwHealthTest_ResultFilter_QueueDepth` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.hwHealthTest.ResultFilter.FilterParams` | SET | `set_xRDKCentralComRFC_hwHealthTest_ResultFilter_FilterParams` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.hwHealthTest.ResultFilter.ResultsFiltered` | SET | `set_xRDKCentralComRFC_hwHealthTest_ResultFilter_ResultsFiltered` | + +--- + +### 8. Device.DeviceInfo — RFC Store Parameters +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp`, `XrdkCentralComRFC.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Control.ClearDB` | SET | `set_xRDKCentralComRFC` → `m_rfcStore->clearAll()` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Control.ClearDBEnd` | SET | `set_xRDKCentralComRFC` → `m_rfcStorage.clearAll()` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Control.RetrieveNow` | SET | `set_xRDKCentralComRFCRetrieveNow` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.DolbyVision.Enable` | SET | `set_xRDKCentralComRFC` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RoamTrigger` | SET | `set_xRDKCentralComRFCRoamTrigger` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.MS12.DAPv2_Enable` | SET | `set_xRDKCentralComRFC` (dsMS12FEATURE_DAPV2) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.MS12.DE_Enable` | SET | `set_xRDKCentralComRFC` (dsMS12FEATURE_DE) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.LoudnessEquivalence.Enable` | SET | `set_xRDKCentralComRFCLoudnessEquivalenceEnable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.DAB.Enable` | SET | `set_xRDKCentralComDABRFCEnable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.LXC.XRE.Enable` | SET | `set_xRDKCentralComXREContainerRFCEnable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AutoReboot.Enable` | SET | `set_xRDKCentralComRFCAutoRebootEnable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.ManageableNotification.Enable` | GET+SET | `get/set_xRDKCentralComRFC` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Tr069DoSLimit.Threshold` | SET | `validate_ParamValue` (range 0–30) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.VideoTelemetry.FrequncyMinutes` | SET | `set_xRDKCentralComRFCVideoTelFreq` *(ENABLE_VIDEO_TELEMETRY)* | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.newNTP.Enable` | SET | `set_xRDKCentralComNewNtpEnable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RebootStop.Enable` | SET | `set_Device_DeviceInfo_X_RDKCENTRAL_COM_RebootStopEnable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Canary.wakeUpStart` | SET | `set_Device_DeviceInfo_X_RDKCENTRAL_COM_Canary_wakeUpStart` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.Canary.wakeUpEnd` | SET | `set_Device_DeviceInfo_X_RDKCENTRAL_COM_Canary_wakeUpEnd` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.xMemInsight.Enable` | SET | `set_Device_DeviceInfo_X_RDKCENTRAL_COM_XMemInsight_Enable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.NonRootSupport.ApparmorBlocklist` | GET+SET | `get_ApparmorBlockListStatus` / `set_xRDKCentralComApparmorBlocklist` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.AccountInfo.AccountID` | GET+SET | `get/set_xRDKCentralComRFC` (Thunder `org.rdk.AuthService`) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.*` (any key) | GET+SET | `get/set_xRDKCentralComRFC` (generic pass-through to rfcStore) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Bootstrap.*` (any key) | GET+SET | `get/set_xRDKCentralComBootstrap` (XBSStore) | + +--- + +### 9. Device.DeviceInfo — IPRemoteSupport / Syndication / XRPolling +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_IPRemoteSupport.Enable` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_IPRemoteSupportEnable` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_IPRemoteSupport.IPAddr` | GET | `get_Device_DeviceInfo_X_RDKCENTRAL_COM_IPRemoteSupportIpaddress` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_IPRemoteSupport.MACAddr` | GET | `get_Device_DeviceInfo_X_RDKCENTRAL_COM_IPRemoteSupportMACaddress` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_Syndication.PartnerId` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_Syndication_PartnerId` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_XRPolling.Action` | GET+SET | `get/set_Device_DeviceInfo_X_RDKCENTRAL_COM_XRPollingAction` | + +--- + +### 10. Device.DeviceInfo — RDKDownloadManager +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RDKDownloadManager.InstallPackage` | SET | `set_xRDKDownloadManager_InstallPackage` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RDKDownloadManager.DownloadStatus` | SET | `set_xRDKDownloadManager_DownloadStatus` | + +--- + +### 11. Device.DeviceInfo — RDKRemoteDebugger *(USE_REMOTE_DEBUGGER)* +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.Enable` | SET | `set_xRDKCentralComRFC` (rfcStore pass-through) | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType` | SET | `set_Device_DeviceInfo_X_RDKCENTRAL_COM_RDKRemoteDebuggerIssueType` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.WebCfgData` | SET | `set_Device_DeviceInfo_X_RDKCENTRAL_COM_RDKRemoteDebuggerWebCfgData` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.ProfileData` | GET | `get_Device_DeviceInfo_X_RDKCENTRAL_COM_RDKRemoteDebuggergetProfileData` | + +--- + +### 12. Device.DeviceInfo — HotelCheckout / Account *(Thunder)* +`src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.LastResetTime` | GET | Thunder `org.rdk.Account.getLastCheckoutResetTime` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xAccount.HotelCheckout.Status` | GET | Thunder `org.rdk.Account` | + +--- + +### 13. Device.DeviceInfo — xBlueTooth +`src/hostif/profiles/DeviceInfo/XrdkBlueTooth.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xBlueTooth.Enable` | GET+SET | `isEnabled` / `setDeviceInfo` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xBlueTooth.DeviceInfo` | GET+SET | `getDeviceInfo` / `setDeviceInfo` | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xBlueTooth.LimitBeaconDetection` | SET | `setLimitBeaconDetection` *(BLE_TILE_PROFILE)* | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xBlueTooth.TileId` | SET | inline *(BLE_TILE_PROFILE)* | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xBlueTooth.SessionId` | SET | inline *(BLE_TILE_PROFILE)* | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xBlueTooth.TileStatus.Trigger` | SET | `do_Ring_A_Tile` *(BLE_TILE_PROFILE)* | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_xBlueTooth.TileStatus.CmdRequest` | SET | `process_TileCmdRequest` *(BLE_TILE_PROFILE)* | + +--- + +### 14. Device — X_RDK_WebPA Profile +`src/hostif/profiles/Device/x_rdk_profile.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.X_RDK_WebPA_Server.URL` | GET | `get_WebPA_Server_URL` | +| `Device.X_RDK_WebPA_TokenServer.URL` | GET | `get_WebPA_TokenServer_URL` | +| `Device.X_RDK_WebPA_DNSText.URL` | GET+SET | `get/set_WebPA_DNSText_URL` | + +--- + +### 15. Device.Ethernet +`src/hostif/profiles/Ethernet/Device_Ethernet_Interface.cpp` / `Device_Ethernet_Interface_Stats.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.Ethernet.InterfaceNumberOfEntries` | GET | `get_Device_Ethernet_InterfaceNumberOfEntries` | +| `Device.Ethernet.Interface.{i}.Enable` | GET+SET | `get/set_Device_Ethernet_Interface_Enable` | +| `Device.Ethernet.Interface.{i}.Status` | GET | `get_Device_Ethernet_Interface_Status` | +| `Device.Ethernet.Interface.{i}.Alias` | GET+SET | `get/set_Device_Ethernet_Interface_Alias` | +| `Device.Ethernet.Interface.{i}.Name` | GET | `get_Device_Ethernet_Interface_Name` | +| `Device.Ethernet.Interface.{i}.LastChange` | GET | `get_Device_Ethernet_Interface_LastChange` | +| `Device.Ethernet.Interface.{i}.LowerLayers` | GET+SET | `get/set_Device_Ethernet_Interface_LowerLayers` | +| `Device.Ethernet.Interface.{i}.Upstream` | GET | `get_Device_Ethernet_Interface_Upstream` | +| `Device.Ethernet.Interface.{i}.MACAddress` | GET | `get_Device_Ethernet_Interface_MACAddress` | +| `Device.Ethernet.Interface.{i}.MaxBitRate` | GET+SET | `get/set_Device_Ethernet_Interface_MaxBitRate` | +| `Device.Ethernet.Interface.{i}.DuplexMode` | GET+SET | `get/set_Device_Ethernet_Interface_DuplexMode` | +| `Device.Ethernet.Interface.{i}.Stats.BytesSent` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.BytesReceived` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.PacketsSent` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.PacketsReceived` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.ErrorsSent` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.ErrorsReceived` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.UnicastPacketsSent` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.UnicastPacketsReceived` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.DiscardPacketsSent` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.DiscardPacketsReceived` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.MulticastPacketsSent` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.MulticastPacketsReceived` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.BroadcastPacketsSent` | GET | Stats handler | +| `Device.Ethernet.Interface.{i}.Stats.BroadcastPacketsReceived` | GET | Stats handler | + +--- + +### 16. Device.IP +`src/hostif/profiles/IP/Device_IP.cpp`, `Device_IP_Interface.cpp`, `Device_IP_Interface_IPv4Address.cpp`, +`Device_IP_Interface_IPv6Address.cpp`, `Device_IP_Interface_Stats.cpp`, `Device_IP_ActivePort.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.IP.InterfaceNumberOfEntries` | GET | `get_Device_IP_InterfaceNumberOfEntries` | +| `Device.IP.ActivePortNumberOfEntries` | GET | `get_Device_IP_ActivePortNumberOfEntries` | +| `Device.IP.Interface.{i}.Enable` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.IPv4Enable` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.IPv6Enable` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.ULAEnable` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.Status` | GET | `handleGetMsg` | +| `Device.IP.Interface.{i}.Alias` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.Name` | GET | `handleGetMsg` | +| `Device.IP.Interface.{i}.LastChange` | GET | `handleGetMsg` | +| `Device.IP.Interface.{i}.LowerLayers` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.Router` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.Type` | GET | `handleGetMsg` | +| `Device.IP.Interface.{i}.Loopback` | GET+SET | `handleGetMsg/handleSetMsg` | +| `Device.IP.Interface.{i}.IPv4AddressNumberOfEntries` | GET | `handleGetMsg` | +| `Device.IP.Interface.{i}.IPv4Address.{j}.Enable` | GET+SET | IPv4Address handler | +| `Device.IP.Interface.{i}.IPv4Address.{j}.Status` | GET | IPv4Address handler | +| `Device.IP.Interface.{i}.IPv4Address.{j}.Alias` | GET+SET | IPv4Address handler | +| `Device.IP.Interface.{i}.IPv4Address.{j}.IPAddress` | GET+SET | IPv4Address handler | +| `Device.IP.Interface.{i}.IPv4Address.{j}.SubnetMask` | GET+SET | IPv4Address handler | +| `Device.IP.Interface.{i}.IPv4Address.{j}.AddressingType` | GET | IPv4Address handler | +| `Device.IP.Interface.{i}.IPv6AddressNumberOfEntries` | GET | `handleGetMsg` | +| `Device.IP.Interface.{i}.IPv6Address.{j}.Enable` | GET+SET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Address.{j}.Status` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Address.{j}.IPAddress` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Address.{j}.Prefix` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Address.{j}.Origin` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Address.{j}.Anycast` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Address.{j}.PreferredLifetime` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Address.{j}.ValidLifetime` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Prefix.{j}.Autonomous` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Prefix.{j}.StaticType` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Prefix.{j}.PrefixStatus` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.IPv6Prefix.{j}.ValidLifetime` | GET | IPv6Address handler | +| `Device.IP.Interface.{i}.Stats.BytesSent` | GET | `get_Device_IP_Interface_Stats_BytesSent` | +| `Device.IP.Interface.{i}.Stats.BytesReceived` | GET | `get_Device_IP_Interface_Stats_BytesReceived` | +| `Device.IP.Interface.{i}.Stats.PacketsSent` | GET | `get_Device_IP_Interface_Stats_PacketsSent` | +| `Device.IP.Interface.{i}.Stats.PacketsReceived` | GET | `get_Device_IP_Interface_Stats_PacketsReceived` | +| `Device.IP.Interface.{i}.Stats.ErrorsSent` | GET | `get_Device_IP_Interface_Stats_ErrorsSent` | +| `Device.IP.Interface.{i}.Stats.ErrorsReceived` | GET | `get_Device_IP_Interface_Stats_ErrorsReceived` | +| `Device.IP.Interface.{i}.Stats.UnicastPacketsSent` | GET | `get_Device_IP_Interface_Stats_UnicastPacketsSent` | +| `Device.IP.Interface.{i}.Stats.UnicastPacketsReceived` | GET | `get_Device_IP_Interface_Stats_UnicastPacketsReceived` | +| `Device.IP.Interface.{i}.Stats.DiscardPacketsSent` | GET | `get_Device_IP_Interface_Stats_DiscardPacketsSent` | +| `Device.IP.Interface.{i}.Stats.DiscardPacketsReceived` | GET | `get_Device_IP_Interface_Stats_DiscardPacketsReceived` | +| `Device.IP.Interface.{i}.Stats.MulticastPacketsSent` | GET | `get_Device_IP_Interface_Stats_MulticastPacketsSent` | +| `Device.IP.Interface.{i}.Stats.MulticastPacketsReceived` | GET | `get_Device_IP_Interface_Stats_MulticastPacketsReceived` | +| `Device.IP.Interface.{i}.Stats.BroadcastPacketsSent` | GET | `get_Device_IP_Interface_Stats_BroadcastPacketsSent` | +| `Device.IP.Interface.{i}.Stats.BroadcastPacketsReceived` | GET | `get_Device_IP_Interface_Stats_BroadcastPacketsReceived` | +| `Device.IP.Interface.{i}.Stats.UnknownProtoPacketsReceived` | GET | `get_Device_IP_Interface_Stats_UnknownProtoPacketsReceived` | +| `Device.IP.ActivePort.{i}.LocalIPAddress` | GET | `get_Device_IP_ActivePort_LocalIPAddress` | +| `Device.IP.ActivePort.{i}.LocalPort` | GET | `get_Device_IP_ActivePort_LocalPort` | +| `Device.IP.ActivePort.{i}.RemoteIPAddress` | GET | `get_Device_IP_ActivePort_RemoteIPAddress` | +| `Device.IP.ActivePort.{i}.RemotePort` | GET | `get_Device_IP_ActivePort_RemotePort` | +| `Device.IP.ActivePort.{i}.Status` | GET | `get_Device_IP_ActivePort_Status` | + +--- + +### 17. Device.DHCPv4 +`src/hostif/profiles/DHCPv4/Device_DHCPv4_Client.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.DHCPv4.ClientNumberOfEntries` | GET | `get_Device_DHCPv4_ClientNumberOfEntries` | +| `Device.DHCPv4.Client.{i}.InterfaceReference` | GET | `get_Device_DHCPv4_Client_InterfaceReference` | +| `Device.DHCPv4.Client.{i}.DnsServer` | GET | `get_Device_DHCPv4_Client_DnsServer` | +| `Device.DHCPv4.Client.{i}.IPRouters` | GET | `get_Device_DHCPv4_Client_IPRouters` | + +--- + +### 18. Device.InterfaceStack +`src/hostif/profiles/InterfaceStack/Device_InterfaceStack.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.InterfaceStackNumberOfEntries` | GET | `get_Device_InterfaceStackNumberOfEntries` | +| `Device.InterfaceStack.{i}.HigherLayer` | GET | `get_Device_InterfaceStack_HigherLayer` | +| `Device.InterfaceStack.{i}.LowerLayer` | GET | `get_Device_InterfaceStack_LowerLayer` | + +--- + +### 19. Device.MoCA +`src/hostif/profiles/moca/Device_MoCA_Interface.cpp`, `Device_MoCA_Interface_Stats.cpp`, +`Device_MoCA_Interface_QoS.cpp`, `Device_MoCA_Interface_QoS_FlowStats.cpp`, +`Device_MoCA_Interface_X_RDKCENTRAL_COM_MeshTable.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.MoCA.InterfaceNumberOfEntries` | GET | `get_InterfaceNumberOfEntries` | +| `Device.MoCA.Interface.{i}.Enable` | GET+SET | `get_Enable` / `set_Enable` | +| `Device.MoCA.Interface.{i}.Status` | GET | `get_Status` | +| `Device.MoCA.Interface.{i}.Alias` | GET+SET | `get_Alias` / `set_Alias` | +| `Device.MoCA.Interface.{i}.Name` | GET | `get_Name` | +| `Device.MoCA.Interface.{i}.LastChange` | GET | `get_LastChange` | +| `Device.MoCA.Interface.{i}.LowerLayers` | GET+SET | `get_LowerLayers` / `set_LowerLayers` | +| `Device.MoCA.Interface.{i}.Upstream` | GET | `get_Upstream` | +| `Device.MoCA.Interface.{i}.MACAddress` | GET | `get_MACAddress` | +| `Device.MoCA.Interface.{i}.FirmwareVersion` | GET | `get_FirmwareVersion` | +| `Device.MoCA.Interface.{i}.MaxBitRate` | GET | `get_MaxBitRate` | +| `Device.MoCA.Interface.{i}.MaxIngressBW` | GET | `get_MaxIngressBW` | +| `Device.MoCA.Interface.{i}.MaxEgressBW` | GET | `get_MaxEgressBW` | +| `Device.MoCA.Interface.{i}.HighestVersion` | GET | `get_HighestVersion` | +| `Device.MoCA.Interface.{i}.CurrentVersion` | GET | `get_CurrentVersion` | +| `Device.MoCA.Interface.{i}.NetworkCoordinator` | GET | `get_NetworkCoordinator` | +| `Device.MoCA.Interface.{i}.NodeID` | GET | `get_NodeID` | +| `Device.MoCA.Interface.{i}.MaxNodes` | GET | `get_MaxNodes` | +| `Device.MoCA.Interface.{i}.PreferredNC` | GET | `get_PreferredNC` | +| `Device.MoCA.Interface.{i}.BackupNC` | GET | `get_BackupNC` | +| `Device.MoCA.Interface.{i}.PrivacyEnabledSetting` | GET | `get_PrivacyEnabledSetting` | +| `Device.MoCA.Interface.{i}.FreqCapabilityMask` | GET | `get_FreqCapabilityMask` | +| `Device.MoCA.Interface.{i}.FreqCurrentMaskSetting` | GET | `get_FreqCurrentMaskSetting` | +| `Device.MoCA.Interface.{i}.FreqCurrentMask` | GET | `get_FreqCurrentMask` | +| `Device.MoCA.Interface.{i}.TxBcastRate` | GET | `get_TxBcastRate` | +| `Device.MoCA.Interface.{i}.PowerCntlPhyTarget` | GET | `get_PowerCntlPhyTarget` | +| `Device.MoCA.Interface.{i}.TxBcastPowerReduction` | GET | `get_TxBcastPowerReduction` | +| `Device.MoCA.Interface.{i}.QAM256Capable` | GET | `get_QAM256Capable` | +| `Device.MoCA.Interface.{i}.PacketAggregationCapability` | GET | `get_PacketAggregationCapability` | +| `Device.MoCA.Interface.{i}.AssociatedDeviceNumberOfEntries` | GET | `get_AssociatedDeviceNumberOfEntries` | +| `Device.MoCA.Interface.{i}.Stats.BytesSent` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.BytesReceived` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.PacketsSent` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.PacketsReceived` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.ErrorsSent` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.ErrorsReceived` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.UnicastPacketsSent` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.UnicastPacketsReceived` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.DiscardPacketsSent` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.DiscardPacketsReceived` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.MulticastPacketsSent` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.Stats.X_RDKCENTRAL-COM_RxMapPhyRate` | GET | Stats handler | +| `Device.MoCA.Interface.{i}.QoS.EgressNumFlows` | GET | QoS handler | +| `Device.MoCA.Interface.{i}.QoS.IngressNumFlows` | GET | QoS handler | +| `Device.MoCA.Interface.{i}.QoS.FlowStats.{j}.FlowID` | GET | QoS FlowStats handler | +| `Device.MoCA.Interface.{i}.QoS.FlowStats.{j}.PacketDA` | GET | QoS FlowStats handler | +| `Device.MoCA.Interface.{i}.QoS.FlowStats.{j}.MaxRate` | GET | QoS FlowStats handler | +| `Device.MoCA.Interface.{i}.X_RDKCENTRAL-COM.MeshTable.{j}.MeshTxNodeId` | GET | MeshTable handler | +| `Device.MoCA.Interface.{i}.X_RDKCENTRAL-COM.MeshTable.{j}.MeshRxNodeId` | GET | MeshTable handler | +| `Device.MoCA.Interface.{i}.X_RDKCENTRAL-COM.MeshTable.{j}.MeshPHYTxRate` | GET | MeshTable handler | + +--- + +### 20. Device.Services.STBService — Components +`src/hostif/profiles/STBService/` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.Services.STBServiceNumberOfEntries` | GET | Top-level handler | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.Status` | GET | `getStatus` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.Enable` | GET | `getEnable` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.CancelMute` | GET+SET | `getCancelMute` / `setCancelMute` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.Name` | GET | `getName` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.AudioLevel` | GET+SET | `getAudioLevel` / `setAudioLevel` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioFormat` | GET | `getX_COMCAST_COM_AudioFormat` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioOptimalLevel` | GET | `getX_COMCAST_COM_AudioOptimalLevel` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_MinAudioDB` | GET | `getX_COMCAST_COM_MinAudioDB` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_MaxAudioDB` | GET | `getX_COMCAST_COM_MaxAudioDB` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioDB` | GET+SET | `getX_COMCAST_COM_AudioDB` / `setX_COMCAST_COM_AudioDB` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioStereoMode` | GET+SET | `getX_COMCAST_COM_AudioStereoMode` / `setX_COMCAST_COM_AudioStereoMode` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioLoopThru` | GET+SET | `getX_COMCAST_COM_AudioLoopThru` / `setX_COMCAST_COM_AudioLoopThru` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioEncoding` | GET+SET | `getX_COMCAST_COM_AudioEncoding` / `setAudioEncoding` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioCompression` | GET+SET | `getX_COMCAST_COM_AudioCompression` / `setX_COMCAST_COM_AudioCompression` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_AudioGain` | GET | `getX_COMCAST_COM_AudioGain` | +| `Device.Services.STBService.1.Components.AudioOutput.{i}.X_COMCAST-COM_DialogEnhancement` | GET+SET | `getX_COMCAST_COM_DialogEnhancement` / `setX_COMCAST_COM_DialogEnhancement` | +| `Device.Services.STBService.1.Components.HDMI.{i}.Enable` | GET+SET | `getEnable` / `setEnableVideoPort` | +| `Device.Services.STBService.1.Components.HDMI.{i}.Status` | GET | `getStatus` | +| `Device.Services.STBService.1.Components.HDMI.{i}.Name` | GET | `getName` | +| `Device.Services.STBService.1.Components.HDMI.{i}.ResolutionMode` | GET+SET | inline / `setHDMIResolutionMode` | +| `Device.Services.STBService.1.Components.HDMI.{i}.ResolutionValue` | GET+SET | `getResolutionValue` / `setResolution` | +| `Device.Services.STBService.1.Components.HDMI.{i}.DisplayDevice.Status` | GET | `getStatus` | +| `Device.Services.STBService.1.Components.HDMI.{i}.DisplayDevice.EDID` | GET | DisplayDevice handler | +| `Device.Services.STBService.1.Components.HDMI.{i}.DisplayDevice.SupportedResolutions` | GET | DisplayDevice handler | +| `Device.Services.STBService.1.Components.HDMI.{i}.DisplayDevice.PreferredResolution` | GET | DisplayDevice handler | +| `Device.Services.STBService.1.Components.VideoOutput.{i}.Status` | GET | `getStatus` | +| `Device.Services.STBService.1.Components.VideoOutput.{i}.DisplayFormat` | GET | VideoOutput handler | +| `Device.Services.STBService.1.Components.VideoOutput.{i}.VideoFormat` | GET | VideoOutput handler | +| `Device.Services.STBService.1.Components.VideoOutput.{i}.AspectRatio` | GET | VideoOutput handler | +| `Device.Services.STBService.1.Components.VideoOutput.{i}.HDCP` | GET | VideoOutput handler | +| `Device.Services.STBService.1.Components.VideoDecoder.{i}.Status` | GET | `getStatus` | +| `Device.Services.STBService.1.Components.VideoDecoder.{i}.ContentAspectRatio` | GET | VideoDecoder handler | +| `Device.Services.STBService.1.Components.VideoDecoder.{i}.Name` | GET | `getName` | +| `Device.Services.STBService.1.Components.VideoDecoder.{i}.X_COMCAST-COM_Standby` | GET+SET | VideoDecoder handler / `setX_COMCAST_COM_Standby` | +| `Device.Services.STBService.1.Components.SPDIF.{i}.Enable` | GET | SPDIF handler | +| `Device.Services.STBService.1.Components.SPDIF.{i}.Status` | GET | `getStatus` | +| `Device.Services.STBService.1.Components.SPDIF.{i}.Alias` | GET | SPDIF handler | +| `Device.Services.STBService.1.Components.SPDIF.{i}.Name` | GET | SPDIF handler | +| `Device.Services.STBService.1.Components.SPDIF.{i}.ForcePCM` | GET+SET | SPDIF handler / `setForcePCM` | +| `Device.Services.STBService.1.Components.SPDIF.{i}.PassThrough` | GET | SPDIF handler | +| `Device.Services.STBService.1.Components.SPDIF.{i}.AudioDelay` | GET | SPDIF handler | +| `Device.Services.STBService.1.Components.X_RDKCENTRAL-COM_eMMC.*` | GET | `handleGetMsg` (Components_XrdkEMMC.cpp) | +| `Device.Services.STBService.1.Components.X_RDKCENTRAL-COM_SDCard.*` | GET | `handleGetMsg` (Components_XrdkSDCard.cpp) | +| `Device.Services.STBService.1.Capabilities.*` | GET | `handleGetMsg` (Capabilities.cpp) | + +--- + +### 21. Device.Services.StorageService +`src/hostif/profiles/StorageService/Service_Storage.cpp`, `Service_Storage_PhyMedium.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.Services.StorageServiceNumberOfEntries` | GET | `get_Device_StorageSrvc_ClientNumberOfEntries` | +| `Device.Services.StorageService.{i}.PhysicalMediumNumberOfEntries` | GET | `get_Device_Service_StorageMedium_ClientNumberOfEntries` | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Name` | GET | `get_Device_Service_StorageMedium_Name` | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.SmartCapable` | GET | `get_Device_Service_StorageMedium_SMARTCapable` | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Health` | GET | `get_Device_Service_StorageMedium_Health` | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Alias` | GET | `get_Device_Service_StorageMedium_Alias` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Vendor` | GET | `get_Device_Service_StorageMedium_Vendor` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Model` | GET | `get_Device_Service_StorageMedium_Model` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.SerialNumber` | GET | `get_Device_Service_StorageMedium_SerialNumber` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.FirmwareVersion` | GET | `get_Device_Service_StorageMedium_FirmwareVersion` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.ConnectionType` | GET | `get_Device_Service_StorageMedium_ConnectionType` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Removable` | GET | `get_Device_Service_StorageMedium_Removable` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Status` | GET | `get_Device_Service_StorageMedium_Status` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.Uptime` | GET | `get_Device_Service_StorageMedium_Uptime` *(stub — returns NOK)* | +| `Device.Services.StorageService.{i}.PhysicalMedium.{j}.HotSwappable` | GET | `get_Device_Service_StorageMedium_HotSwappable` *(stub — returns NOK)* | + +--- + +### 22. Device.Time +`src/hostif/profiles/Time/Device_Time.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.Time.Enable` | GET+SET | `get/set_Device_Time_Enable` | +| `Device.Time.Status` | GET | `get_Device_Time_Status` | +| `Device.Time.NTPServer1` | GET+SET | `get/set_Device_Time_NTPServer1` | +| `Device.Time.NTPServer2` | GET+SET | `get/set_Device_Time_NTPServer2` | +| `Device.Time.NTPServer3` | GET+SET | `get/set_Device_Time_NTPServer3` | +| `Device.Time.NTPServer4` | GET+SET | `get/set_Device_Time_NTPServer4` | +| `Device.Time.NTPServer5` | GET+SET | `get/set_Device_Time_NTPServer5` | +| `Device.Time.CurrentLocalTime` | GET | `get_Device_Time_CurrentLocalTime` | +| `Device.Time.LocalTimeZone` | GET+SET | `get/set_Device_Time_LocalTimeZone` | +| `Device.Time.X_RDKCENTRAL-COM_Chrony.Enable` | GET+SET | `get/set_Device_Time_Chrony_Enable` | +| `Device.Time.X_RDKCENTRAL-COM_NTPMinpoll` | GET+SET | `get/set_Device_Time_NTPMinpoll` | +| `Device.Time.X_RDKCENTRAL-COM_NTPMaxpoll` | GET+SET | `get/set_Device_Time_NTPMaxpoll` | +| `Device.Time.X_RDKCENTRAL-COM_NTPMaxstep` | GET+SET | `get/set_Device_Time_NTPMaxstep` | +| `Device.Time.X_RDKCENTRAL-COM_NTPServer1Directive` | GET+SET | `get/set_Device_Time_NTPServer1Directive` | +| `Device.Time.X_RDKCENTRAL-COM_NTPServer2Directive` | GET+SET | `get/set_Device_Time_NTPServer2Directive` | +| `Device.Time.X_RDKCENTRAL-COM_NTPServer3Directive` | GET+SET | `get/set_Device_Time_NTPServer3Directive` | +| `Device.Time.X_RDKCENTRAL-COM_NTPServer4Directive` | GET+SET | `get/set_Device_Time_NTPServer4Directive` | +| `Device.Time.X_RDKCENTRAL-COM_NTPServer5Directive` | GET+SET | `get/set_Device_Time_NTPServer5Directive` | + +--- + +### 23. Device.WiFi — Top-level / Radio +`src/hostif/profiles/wifi/Device_WiFi.cpp`, `Device_WiFi_Radio.cpp`, `Device_WiFi_Radio_Stats.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.WiFi.RadioNumberOfEntries` | GET | `get_Device_WiFi_RadioNumberOfEntries` | +| `Device.WiFi.SSIDNumberOfEntries` | GET | `get_Device_WiFi_SSIDNumberOfEntries` | +| `Device.WiFi.AccessPointNumberOfEntries` | GET | `get_Device_WiFi_AccessPointNumberOfEntries` | +| `Device.WiFi.EndPointNumberOfEntries` | GET | `get_Device_WiFi_EndPointNumberOfEntries` | +| `Device.WiFi.Enable` | GET+SET | `get/set_Device_WiFi_EnableWiFi` (Thunder `org.rdk.NetworkManager`) | +| `Device.WiFi.Radio.{i}.Enable` | GET+SET | `get/set_Device_WiFi_Radio_Enable` | +| `Device.WiFi.Radio.{i}.Status` | GET | `get_Device_WiFi_Radio_Status` | +| `Device.WiFi.Radio.{i}.Alias` | GET+SET | `get/set_Device_WiFi_Radio_Alias` | +| `Device.WiFi.Radio.{i}.Name` | GET | `get_Device_WiFi_Radio_Name` | +| `Device.WiFi.Radio.{i}.LastChange` | GET | `get_Device_WiFi_Radio_LastChange` | +| `Device.WiFi.Radio.{i}.LowerLayers` | GET+SET | `get/set_Device_WiFi_Radio_LowerLayers` | +| `Device.WiFi.Radio.{i}.Upstream` | GET | `get_Device_WiFi_Radio_Upstream` | +| `Device.WiFi.Radio.{i}.MaxBitRate` | GET | Radio handler | +| `Device.WiFi.Radio.{i}.SupportedFrequencyBands` | GET | Radio handler | +| `Device.WiFi.Radio.{i}.OperatingFrequencyBand` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.SupportedStandards` | GET | Radio handler | +| `Device.WiFi.Radio.{i}.OperatingStandards` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.PossibleChannels` | GET | Radio handler | +| `Device.WiFi.Radio.{i}.ChannelsInUse` | GET | Radio handler | +| `Device.WiFi.Radio.{i}.Channel` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.AutoChannelEnable` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.OperatingChannelBandwidth` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.ExtensionChannel` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.GuardInterval` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.TransmitPowerSupported` | GET | Radio handler | +| `Device.WiFi.Radio.{i}.TransmitPower` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.IEEE80211hSupported` | GET | Radio handler | +| `Device.WiFi.Radio.{i}.IEEE80211hEnabled` | GET+SET | Radio handler | +| `Device.WiFi.Radio.{i}.Stats.BytesSent` | GET | `get_Device_WiFi_Radio_Stats_BytesSent` | +| `Device.WiFi.Radio.{i}.Stats.BytesReceived` | GET | `get_Device_WiFi_Radio_Stats_BytesReceived` | +| `Device.WiFi.Radio.{i}.Stats.PacketsSent` | GET | `get_Device_WiFi_Radio_Stats_PacketsSent` | +| `Device.WiFi.Radio.{i}.Stats.PacketsReceived` | GET | `get_Device_WiFi_Radio_Stats_PacketsReceived` | +| `Device.WiFi.Radio.{i}.Stats.ErrorsSent` | GET | `get_Device_WiFi_Radio_Stats_ErrorsSent` | +| `Device.WiFi.Radio.{i}.Stats.ErrorsReceived` | GET | `get_Device_WiFi_Radio_Stats_ErrorsReceived` | +| `Device.WiFi.Radio.{i}.Stats.DiscardPacketsSent` | GET | `get_Device_WiFi_Radio_Stats_DiscardPacketsSent` | +| `Device.WiFi.Radio.{i}.Stats.DiscardPacketsReceived` | GET | `get_Device_WiFi_Radio_Stats_DiscardPacketsReceived` | +| `Device.WiFi.Radio.{i}.Stats.NoiseFloor` | GET | `get_Device_WiFi_Radio_Stats_NoiseFloor` | + +--- + +### 24. Device.WiFi — SSID +`src/hostif/profiles/wifi/Device_WiFi_SSID.cpp`, `Device_WiFi_SSID_Stats.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.WiFi.SSID.{i}.Enable` | GET+SET | `get/set_Device_WiFi_SSID_Enable` | +| `Device.WiFi.SSID.{i}.Status` | GET | `get_Device_WiFi_SSID_Status` (Thunder `org.rdk.NetworkManager`) | +| `Device.WiFi.SSID.{i}.Alias` | GET+SET | `get/set_Device_WiFi_SSID_Alias` | +| `Device.WiFi.SSID.{i}.Name` | GET | `get_Device_WiFi_SSID_Name` | +| `Device.WiFi.SSID.{i}.BSSID` | GET | `get_Device_WiFi_SSID_BSSID` (Thunder) | +| `Device.WiFi.SSID.{i}.MACAddress` | GET | `get_Device_WiFi_SSID_MACAddress` (Thunder) | +| `Device.WiFi.SSID.{i}.SSID` | GET+SET | `get/set_Device_WiFi_SSID_SSID` (Thunder) | +| `Device.WiFi.SSID.{i}.Stats.BytesSent` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.BytesReceived` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.PacketsSent` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.PacketsReceived` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.ErrorsSent` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.ErrorsReceived` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.UnicastPacketsSent` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.UnicastPacketsReceived` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.DiscardPacketsSent` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.DiscardPacketsReceived` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.MulticastPacketsSent` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.MulticastPacketsReceived` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.BroadcastPacketsSent` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.BroadcastPacketsReceived` | GET | SSID Stats handler | +| `Device.WiFi.SSID.{i}.Stats.UnknownProtoPacketsReceived` | GET | SSID Stats handler | + +--- + +### 25. Device.WiFi — EndPoint +`src/hostif/profiles/wifi/Device_WiFi_EndPoint.cpp`, `Device_WiFi_EndPoint_WPS.cpp`, +`Device_WiFi_EndPoint_Profile.cpp`, `Device_WiFi_EndPoint_Security.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.WiFi.EndPoint.{i}.Enable` | GET+SET | `get/set_Device_WiFi_EndPoint_Enable` | +| `Device.WiFi.EndPoint.{i}.Status` | GET | `get_Device_WiFi_EndPoint_Status` | +| `Device.WiFi.EndPoint.{i}.Alias` | GET+SET | `get/set_Device_WiFi_EndPoint_Alias` | +| `Device.WiFi.EndPoint.{i}.ProfileReference` | GET+SET | `get/set_Device_WiFi_EndPoint_ProfileReference` | +| `Device.WiFi.EndPoint.{i}.SSIDReference` | GET | `get_Device_WiFi_EndPoint_SSIDReference` | +| `Device.WiFi.EndPoint.{i}.ProfileNumberOfEntries` | GET | `get_Device_WiFi_EndPoint_ProfileNumberOfEntries` | +| `Device.WiFi.EndPoint.{i}.Stats.LastDataDownlinkRate` | GET | `get_Device_WiFi_EndPoint_Stats_LastDataDownlinkRate` | +| `Device.WiFi.EndPoint.{i}.Stats.LastDataUplinkRate` | GET | `get_Device_WiFi_EndPoint_Stats_LastDataUplinkRate` | +| `Device.WiFi.EndPoint.{i}.Stats.SignalStrength` | GET | `get_Device_WiFi_EndPoint_Stats_SignalStrength` | +| `Device.WiFi.EndPoint.{i}.Stats.Retransmissions` | GET | `get_Device_WiFi_EndPoint_Stats_Retransmissions` | +| `Device.WiFi.EndPoint.{i}.WPS.Enable` | GET | `get_Device_WiFi_EndPoint_WPS_Enable` | +| `Device.WiFi.EndPoint.{i}.WPS.ConfigMethodsSupported` | GET | `get_Device_WiFi_EndPoint_WPS_ConfigMethodsSupported` | +| `Device.WiFi.EndPoint.{i}.WPS.ConfigMethodsEnabled` | GET | `get_Device_WiFi_EndPoint_WPS_ConfigMethodsEnabled` | +| `Device.WiFi.EndPoint.{i}.Security.ModesEnabled` | GET | EndPoint Security handler (Thunder) | +| `Device.WiFi.EndPoint.{i}.Profile.{j}.*` | GET | Profile handler | + +--- + +### 26. Device.WiFi — X_RDKCENTRAL-COM_ClientRoaming +`src/hostif/profiles/wifi/Device_WiFi_X_RDKCENTRAL_COM_ClientRoaming.cpp` + +| Parameter | Dir | Handler | +|-----------|-----|---------| +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.Enable` | GET+SET | `get/set_Device_WiFi_X_Rdkcentral_clientRoaming_Enable` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PreAssn.ProbeRetryCnt` | GET+SET | `get/set_…_PreAssn_ProbeRetryCnt` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PreAssn.BestThresholdLevel` | GET+SET | `get/set_…_PreAssn_BestThresholdLevel` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PreAssn.BestDeltaLevel` | GET+SET | `get/set_…_PreAssn_BestDeltaLevel` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.SelfSteerOverride` | GET+SET | `get/set_…_SelfSteerOverride` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PostAssn.BestDeltaLevelConnected` | GET+SET | `get/set_…_PostAssn_BestDeltaLevelConnected` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PostAssn.BestDeltaLevelDisconnected` | GET+SET | `get/set_…_PostAssn_BestDeltaLevelDisconnected` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PostAssn.SelfSteerThreshold` | GET+SET | `get/set_…_PostAssn_SelfSteerThreshold` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PostAssn.SelfSteerTimeframe` | GET+SET | `get/set_…_PostAssn_SelfSteerTimeframe` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PostAssn.APcontrolThresholdLevel` | GET+SET | `get/set_…_PostAssn_APcontrolThresholdLevel` | +| `Device.WiFi.X_RDKCENTRAL-COM_ClientRoaming.PostAssn.APcontrolTimeframe` | GET+SET | `get/set_…_PostAssn_APcontrolTimeframe` | + +--- + +### 27. Non-Parameter Behaviours — HTTP Server +`src/hostif/httpserver/src/http_server.cpp`, `request_handler.cpp` + +| Behaviour | Trigger | Expected Response | +|-----------|---------|-------------------| +| GET single parameter | `HTTP GET` body `{"names":["param"]}` + CallerID header | `200 OK {"statusCode":0,"parameters":[...]}` | +| GET multiple parameters | `HTTP GET` body with 2+ names | `200 OK` multi-value response | +| GET wildcard subtree | `HTTP GET` body `{"names":["Device.DeviceInfo."]}` | `200 OK` all sub-params | +| GET unknown parameter | `HTTP GET` with nonexistent name | `200 OK {"statusCode":non-zero}` | +| GET missing CallerID | `HTTP GET` no `CallerID` header | Allowed — defaults to `"Unknown"` | +| POST SET parameter | `HTTP POST` + CallerID + `{"parameters":[...]}` | `200 OK {"statusCode":0}` | +| POST missing CallerID | `HTTP POST` no `CallerID` header | `500 POST Not Allowed without CallerID` | +| Empty body | `HTTP GET` or `POST` zero-length body | `400 No request data.` | +| Malformed JSON | `HTTP GET` with `{broken json}` | `cJSON_Parse` returns NULL → `500 Invalid request format` | +| Unknown HTTP method (PUT/DELETE) | Any unsupported method | `501 Not Implemented` | +| Valid request → `handleRequest` returns NULL | Corner case | `500 Invalid request format` | + +--- + +### 28. Non-Parameter Behaviours — WebPA / Parodus +`src/hostif/parodusClient/pal/webpa_adapter.cpp`, `webpa_parameter.cpp` + +| Behaviour | WDMP Request Type | Handler | +|-----------|-------------------|---------| +| GET single parameter | `GET` | `getValues()` | +| GET multiple parameters | `GET` (multi-name) | `getValues()` | +| GET wildcard — rejected | `GET_ATTRIBUTES` with trailing `.` | Returns `WDMP_ERR_WILDCARD_NOT_SUPPORTED` | +| GET attributes | `GET_ATTRIBUTES` | `getAttributes()` | +| SET parameter (WebPA source) | `SET` | `setValues()` with `WEBPA_SET` | +| SET attributes | `SET_ATTRIBUTES` | `setAttributes()` | +| TEST_AND_SET | `TEST_AND_SET` | No-op (break) | +| REPLACE_ROWS | `REPLACE_ROWS` | No-op (break) | +| ADD_ROWS | `ADD_ROWS` | No-op (break) | +| DELETE_ROW | `DELETE_ROW` | No-op (break) | +| NULL request object | `reqObj == NULL` | Skips all processing, returns empty response | + +--- + +### 29. Non-Parameter Behaviours — RFC Store +`src/hostif/profiles/DeviceInfo/XrdkCentralComRFCStore.cpp`, `XrdkCentralComBSStore.cpp` + +| Behaviour | Trigger | Expected | +|-----------|---------|----------| +| Read RFC default | `getValue` with `rfcdefaults.ini` key | Returns file value | +| RFC override via SET (`HOSTIF_SRC_RFC`) | `setValue` with RFC requestor | Written to persistent ini | +| Local override via SET (`HOSTIF_SRC_WEBPA`) | `setValue` with WEBPA requestor | Written to local store layer | +| GET after local override | `getValue` | Returns local override (higher precedence) | +| Clear all RFC data | SET `Control.ClearDB = true` | `clearAll()` wipes store | +| Clear single param | SET `RFC.ClearParam = ` | Removes one key from local store | +| Bootstrap store GET | `XBSStore::getValue` | Returns partner-specific value | +| Bootstrap store override | `XBSStore::overrideValue` | Writes to bootstrap override layer | +| Reload RFC cache | SET `Control.RetrieveNow` | Triggers RFC refresh | + +--- + +### 30. Non-Parameter Behaviours — Daemon Lifecycle +`src/hostif/src/hostIf_main.cpp` + +| Behaviour | Mechanism | Testable Via | +|-----------|-----------|--------------| +| Daemon start | `main()` init chain | Log: `"tr69HostIf starting up"` | +| Handler registration (all profiles) | `hostIf_initalize_ConfigManger()` | Log: `"Registered handler"` / rbus GET any param | +| rbus DML registration | `rbus_regDataElements()` | Log: `"rbus_regDataElements registered successfully"` | +| HTTP server thread start | `g_thread_create(HTTPServerStartThread)` | Log: `"SERVER: Started server successfully."` | +| HTTP server thread join on stop | `g_thread_join` on `HTTPServerThread` | `HttpServerStop()` + join | +| Parodus/libpd thread start (detached) | `pthread_create(…libpd_client_mgr…)` | Log: `"Starting WEBPA Parodus Connections"` | +| Parodus connects | `connect_parodus()` | Log: `"Initiating Connection with PARODUS success.."` | +| SIGTERM graceful exit | `signal(SIGTERM, …)` | Send SIGTERM → daemon exits cleanly | +| SIGINT handler | `signal(SIGINT, …)` | Send SIGINT → daemon exits cleanly | +| No fatal errors in log | Post-init log scan | Absence of `FATAL`/`CRITICAL` strings | + +--- + +## Parameter Count Summary + +| Profile Area | GET-only | SET-only | GET+SET | Total Params | +|--------------|----------|----------|---------|--------------| +| DeviceInfo Standard | 19 | 0 | 0 | 19 | +| DeviceInfo Custom/RDK | 10 | 3 | 12 | 25 | +| DeviceInfo xOpsMgmt Logging | 2 | 0 | 2 | 4 | +| DeviceInfo ReverseSSH/ForwardSSH | 1 | 1 | 2 | 4 | +| DeviceInfo xOpsRPC | 0 | 1 | 4 | 5 | +| DeviceInfo hwHealthTest | 2 | 13 | 0 | 15 | +| DeviceInfo RFC Store | 0 | 17 | 6 | 23 | +| DeviceInfo IPRemote/Syndication | 2 | 0 | 3 | 5 | +| DeviceInfo RDKDownloadMgr | 0 | 2 | 0 | 2 | +| DeviceInfo RDKRemoteDebugger | 1 | 2 | 0 | 3 | +| DeviceInfo HotelCheckout | 2 | 0 | 0 | 2 | +| DeviceInfo Processor/ProcessStatus | 8 | 0 | 0 | 8 | +| DeviceInfo xBlueTooth | 1 | 3 | 3 | 7 | +| Device X_RDK_WebPA | 2 | 0 | 1 | 3 | +| Ethernet Interface | 6 | 0 | 5 | 11 | +| Ethernet Stats | 14 | 0 | 0 | 14 | +| IP Interface + Sub-objects | 14 | 0 | 10 | 24 | +| IP Interface Stats | 14 | 0 | 0 | 14 | +| IP ActivePort | 5 | 0 | 0 | 5 | +| DHCPv4 | 4 | 0 | 0 | 4 | +| InterfaceStack | 3 | 0 | 0 | 3 | +| MoCA Interface + sub-tables | 33 | 0 | 5 | 38 | +| STBService Components | 10 | 0 | 17 | 27 | +| StorageService | 15 | 0 | 0 | 15 | +| Time | 3 | 0 | 15 | 18 | +| WiFi Top-level + Radio | 12 | 0 | 22 | 34 | +| WiFi SSID | 5 | 0 | 9 | 14 (standard) + 16 (Stats) | +| WiFi EndPoint | 5 | 0 | 8 | 13 | +| WiFi ClientRoaming | 0 | 0 | 11 | 11 | +| **Total TR-181 Parameters** | | | | **≈ 370** | +| HTTP Server behaviours | — | — | — | 11 | +| WebPA behaviours | — | — | — | 10 | +| RFC Store behaviours | — | — | — | 9 | +| Daemon Lifecycle behaviours | — | — | — | 10 | +| **Grand Total Testable Items** | | | | **≈ 410** | + +--- + +## See Also + +- [thunder-plugin-interfaces.md](../api/thunder-plugin-interfaces.md) — Complete list of Thunder plugin calls and TR-181 parameters +- [testing.md](testing.md) — Test environment setup and run instructions +- [common-errors.md](../troubleshooting/common-errors.md) — Runtime error reference +- [data-flow.md](../architecture/data-flow.md) — System data flow architecture