# Parameter Management

This document covers how application parameters are categorised, where they are stored, how they are initialised at startup, and how MQTT-triggered remote updates flow through the system.

---

## 1. Storage Architecture

The app uses three distinct storage layers depending on the data type:

```
┌────────────────────────────────────────────────────────────┐
│  SharedPreferences  (ParamsUtils wrapper)                  │
│  All scalar params: booleans, strings, ints, longs         │
│  File: /data/data/<pkg>/shared_prefs/*.xml                 │
├────────────────────────────────────────────────────────────┤
│  Room SQLite DB  (MerchantService → MerchantDao)           │
│  Table: T_MERCHANT — one row per card organisation         │
├────────────────────────────────────────────────────────────┤
│  JSON config file  (ConfigFileManager)                     │
│  File: /data/data/<pkg>/files/tms_config.json              │
│  TMS version tracking only (param, EMV, keys, app)         │
└────────────────────────────────────────────────────────────┘
```

### SharedPreferences — `ParamsUtils`

`base/.../utils/ParamsUtils.java` wraps `PreferenceManager.getDefaultSharedPreferences()`.
All values are stored as `String` regardless of type.

| Method | Notes |
|--------|-------|
| `getString(key, default)` / `setString(key, value)` | Raw string |
| `getBoolean(key, default)` / `setBoolean(key, value)` | Stored as `"1"` / `"0"` |
| `getInt(key, default)` / `setInt(key, value)` | Parsed from string |
| `getLong(key, default)` / `setLong(key, value)` | Parsed from string |
| `setObject(key, value)` | Auto-detects `Boolean / Integer / Long / String` |
| `save(Map<String,String>)` | Batch write, uses `commit()` (synchronous) |
| `get()` | Returns full param map as `Map<String, String>` |
| `registerChangeListener(listener)` | Observes any key change |

Sensitive keys containing `PASSWORD`, `PIN`, `TOKEN`, `SECRET`, `KEY`, `AUTH`, or `HMAC` are redacted to `"***REDACTED***"` in logs.

### Room Database — `MerchantService`

`database/.../model/Merchant.java` — table `T_MERCHANT`:

| Column | Type | Description |
|--------|------|-------------|
| `ID` | Int (PK, auto) | Internal row ID |
| `MID` | String | Merchant ID |
| `TID` | String | Terminal ID |
| `CARD_ORGANIZATION` | String | `Visa`, `MasterCard`, etc. — also used as lookup key |
| `BATCH_NO` | String | Current batch number |
| `SETTLE_EQUAL` | Boolean | Settlement balanced flag |
| `SETTLE_STEP` | Int | Settlement progress indicator |
| `SETTLE_DATE` / `SETTLE_TIME` | String | Last settlement timestamp (`yyyyMMdd` / `HHmmss`) |
| `LAST_RECEIPT` | String | Most recent receipt data blob |

Access is always through `MerchantService` (never the DAO directly). Key methods:

```java
MerchantService.find(String cardOrg)          // by card organisation
MerchantService.find(String mid, String tid)  // by MID+TID pair
MerchantService.add(Merchant)                 // insert (skips if duplicate)
MerchantService.update(Merchant)              // update, auto-resolves ID
MerchantService.deleteAll()                   // wipe before re-import
MerchantService.clearHalt()                   // reset settleStep → 0 on all
```

### JSON Config File — `ConfigFileManager`

`core/.../config/ConfigFileManager.java` manages `tms_config.json`:

```json
{
  "versions": {
    "parameter_config": "1.0.0",
    "emv_config":       "1.2.3",
    "keys_config":      "2.0.0",
    "application":      "1.5.0"
  },
  "device_info":    { "serial_number": "<auto>" },
  "mqtt_settings":  { "broker_url": "", "port": 1883, "enabled": true },
  "last_updated":   "2025-04-21 10:30:45",
  "last_sync_time": "2025-04-21 10:25:00"
}
```

Version keys (`parameter_config`, `emv_config`, `keys_config`, `application`) are compared against incoming MQTT payloads to decide whether a download is needed. A matching version means the update is skipped.

---

## 2. Parameter Categories (`ParamsConst`)

`core/.../constant/ParamsConst.java` defines every key used with `ParamsUtils`. Keys are grouped below by domain.

### MQTT / TMS

| Constant | Description | Default |
|----------|-------------|---------|
| `PARAMS_KEY_MQTT_BROKER_ADDRESS` | MQTT broker hostname | `demo.ctrmv.com` |
| `PARAMS_KEY_MQTT_BROKER_PORT` | MQTT broker port | `1883` |
| `PARAMS_KEY_MQTT_CONFIG_VERSION` | Stored param config version | — |
| `PARAMS_KEY_MQTT_EMV_VERSION` | Stored EMV config version | — |
| `PARAMS_KEY_MQTT_KEYS_VERSION` | Stored keys version | — |
| `PARAMS_KEY_MQTT_APP_VERSION` | Stored application version | — |

### Communication (ISO 8583 Host)

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_COMM_SERVER_ADDRESS` | Host server IP / hostname |
| `PARAMS_KEY_COMM_PORT` | Host server port |
| `PARAMS_KEY_COMM_USE_SSL` | Enable TLS (boolean) |
| `PARAMS_KEY_COMM_TIMEOUT` | Network timeout in seconds |
| `PARAMS_KEY_COMM_TPDU` | Terminal Protocol Data Unit |
| `PARAMS_KEY_COMM_NII` | Network Identifier Index |

### Merchant / Base

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_BASE_MERCHANT_NAME` | Display name on receipts |
| `PARAMS_KEY_BASE_TRACE_NO` | Current transaction trace number |
| `PARAMS_KEY_BASE_MAX_REFUND_AMOUNT` | Max refund value in cents (long) |
| `PARAMS_KEY_BASE_CURRENCY_CODE` | ISO 4217 (840 = USD, 156 = CNY) |
| `PARAMS_KEY_BASE_MAX_TRANS_COUNT` | Transaction history retention limit |

### Transaction Types (all boolean)

| Constant | Transaction |
|----------|-------------|
| `PARAMS_KEY_TRANS_SALE` | Sale |
| `PARAMS_KEY_TRANS_VOID` | Void |
| `PARAMS_KEY_TRANS_REFUND` | Refund |
| `PARAMS_KEY_TRANS_BALANCE` | Balance inquiry |
| `PARAMS_KEY_TRANS_TOPUP` | Top-up |
| `PARAMS_KEY_TRANS_PREAUTH` | Pre-authorisation |
| `PARAMS_KEY_TRANS_MOBILE_PAY` | Mobile / QR payment |
| `PARAMS_KEY_TRANS_INSTALLMENT` | Instalment |
| `PARAMS_KEY_TRANS_PAY_LINK` | Pay link |
| `PARAMS_KEY_TRANS_SETTLE` | Settlement |

### PIN Pad

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_PINPAD_MASTER_KEY_INDEX` | Hardware key slot index |
| `PARAMS_KEY_PINPAD_TIMEOUT` | PIN entry timeout (seconds) |
| `PARAMS_KEY_PINPAD_ALGORITHM_TYPE` | `0x00` = DUKPT, `0x01` = MKSK |
| `PARAMS_KEY_EXTERNAL_PINPAD` | Use external PIN pad (boolean) |
| `PARAMS_KEY_EXTERNAL_PINPAD_CONNECT_MODE` | `ConnectMode` enum value |
| `PARAMS_KEY_YSDK_HARDWARE` | Use YSDK hardware mode (boolean) |

### Printing

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_PRINT_COUNT` | Number of receipt copies |
| `PARAMS_KEY_PRINT_REMARKS` | Footer text on receipt |
| `PARAMS_KEY_PRINT_EXTERNAL` | Use external printer (boolean) |
| `PARAMS_KEY_PRINT_EXTERNAL_CONNECT_MODE` | `ConnectMode` for external printer |
| `PARAMS_KEY_PRINT_EXTERNAL_SERIAL_BAUDRATE` | Baud rate (e.g. `115200`) |

### Scanner

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_SCAN_PRIORITY_SCANNER` | Scanner enum preference |
| `PARAMS_KEY_SCAN_EXTERN_CONNECT_MODE` | External scanner connection mode |
| `PARAMS_KEY_SCAN_EXTERN_USB_WAIT_TIME` | Wait time after USB scan (ms) |
| `PARAMS_KEY_SCAN_EXTERN_SERIAL_BAUDRATE` | Serial baud rate |

### QR Payments

| Constant | Description | Default |
|----------|-------------|---------|
| `PARAMS_KEY_QR_SERVER_ADDRESS` | QR server hostname | `demo.ctrmv.com` |
| `PARAMS_KEY_QR_SERVER_PORT` | QR server port | `4001` |
| `PARAMS_KEY_QR_USE_SSL` | Use HTTPS (boolean) | — |
| `PARAMS_KEY_QR_API_ENDPOINT` | API path | `/api/Iotmsgtest/createQrMf` |
| `TRANSTYPE_QR_AANI` | Provider code `"10"` | — |
| `TRANSTYPE_QR_ALIPAY` | Provider code `"11"` | — |
| `TRANSTYPE_QR_WECHAT` | Provider code `"12"` | — |
| `TRANSTYPE_QR_UPI` | Provider code `"13"` | — |

### Receipt / Signature

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_ELECSIGN_IS_SUPPORT` | Electronic signature support (boolean) |
| `PARAMS_KEY_NFC_RECEIPT` | Deliver receipt via NFC tag (boolean) |
| `PARAMS_KEY_CLOUD_RECEIPT` | Cloud receipt mode — use field 63 format (boolean) |
| `PARAMS_KEY_CLOUD_RECEIPT_BUTTON_LABEL` | Button label text for cloud receipt |

### UI / Display

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_LOGO_DYNAMIC` | Enable dynamic logo loading (boolean) |
| `PARAMS_KEY_LOGO_URL` | URL for main display logo |
| `PARAMS_KEY_AUX_LCD_LOGO_URL` | URL for secondary display logo |
| `PARAMS_KEY_AUX_SCREEN_ENABLED` | Show transaction info on aux screen (boolean) |
| `PARAMS_KEY_OTHER_SECOND_SCREEN_TOP` | Aux screen overlay mode (boolean) |
| `PARAMS_KEY_OTHER_VOID_CARD` | Void requires card present (boolean) |
| `PARAMS_KEY_OTHER_VOID_PIN` | Void requires PIN (boolean) |
| `PARAMS_KEY_OTHER_TIP_INPUT` | Enable tip input step (boolean) |
| `PARAMS_KEY_OLD_LAYOUT` | Use legacy UI layout (boolean) |
| `PARAMS_KEY_AUTO_START` | Auto-start on device power-on (boolean) |

### EMV / Card

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_EMV_AID_CAPK` | AID/CAPK loaded into hardware flag (boolean) |
| `PARAMS_CHIP_MSR_FALLBACK` | Allow chip → magnetic fallback (boolean) |
| `PARAMS_CHIP_MSR_SCHEME_FALLBACK` | Scheme fallback (boolean) |
| `PARAMS_CHIP_MSR_TECH_FALLBACK` | Technology fallback (boolean) |
| `PARAMS_PIN_FOR_MANUAL` | Require PIN for manually-keyed entry (boolean) |
| `PARAMS_TTQ_CTLS` | Contactless TTQ bitmask |

### Passwords

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_PASSWORD_ADMIN` | Transaction-level password |
| `PARAMS_KEY_PASSWORD_SYSTEM_ADMIN` | System/vendor-level password |
| `PARAMS_KEY_PASSWORD_SECURITY` | Security/safe password |

### TOMS / Fly Services

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_TOMS_FLY_PARAMETERS` | Enable Fly Parameters (TMS param download) |
| `PARAMS_KEY_TOMS_FLY_RECEIPT` | Enable Fly Receipt (cloud receipt upload) |

### Lifecycle

| Constant | Description |
|----------|-------------|
| `PARAMS_KEY_FIRST_RUN` | Set `false` after first-run initialisation |
| `PARAMS_KEY_MORE_FUN_DEVICE` | Device type identifier |
| `PARAMS_OFFLINE_REFUND` | Allow offline refund (boolean) |
| `PARAMS_KEY_INJECT_YSP_KEK` | Dev/test flag: inject YSP KEK |

---

## 3. Startup Initialisation Flow

```
App.onCreate()  [background thread]
  └─ SelfCheckHelper.initAppConfig(context)
        │
        ├─ ParamsUtils.getBoolean(PARAMS_KEY_FIRST_RUN, true)
        │       │
        │  true ┤  AppParamsImporter.initDefaultAppParams()
        │       │    └─ assets/defaultparams.properties
        │       │         → [PARAMS] section key=value pairs
        │       │         → ParamsUtils.save(map)  [batch commit]
        │       │
        │       ├─ AppParamsImporter.initDefaultMerchants()
        │       │    └─ assets/defaultmerchants.properties
        │       │         → [Visa], [MasterCard] … sections
        │       │         → MerchantService.deleteAll()
        │       │         → MerchantService.add(merchant) per org
        │       │
        │       └─ ParamsUtils.setBoolean(PARAMS_KEY_FIRST_RUN, false)
        │
        └─ AppParamsImporter.ensureQrMqttDefaults()
               → Sets QR_SERVER_ADDRESS, QR_SERVER_PORT, QR_API_ENDPOINT,
                 MQTT_BROKER_ADDRESS, MQTT_BROKER_PORT if not already present

MainActivity.onCreate()  [background thread]
  └─ SelfCheckHelper.initDevice(context)
        ├─ Init NSDK / ExtServiceHelper for PIN pad
        ├─ loadEmvConfig()
        │    ├─ Skip if PARAMS_KEY_EMV_AID_CAPK == true
        │    └─ Otherwise: parse assets/EMV_CONFIG XML → hardware loader
        │         → set PARAMS_KEY_EMV_AID_CAPK = true on success
        └─ If PARAMS_KEY_TOMS_FLY_PARAMETERS:
             FlyParameterHelper.bind() + register RemoteParamsUpdater as watcher
```

**External file override at first run:** If `defaultparams.properties` or `defaultmerchants.properties` exist at `/sdcard/Android/data/<pkg>/SHARE_PATH/`, they take precedence over the bundled assets and are deleted after import.

---

## 4. MQTT-Triggered Parameter Update

### Connection Setup — `MqttIntegration`

`app/.../mqtt/MqttIntegration.java` connects to the MQTT broker on startup using the device serial number as the client identity.

```
MqttIntegration.initialize()
  ├─ BDevice.getSn() → device serial number
  ├─ MqttManager.connect(broker, port, username, password)
  └─ Registers event listener callbacks
```

### Inbound Message Structure — `MqttInboundPayload`

```json
{
  "type":        "tms_command",
  "command":     "UPDATE_PARAMS | UPDATE_L3_CONFIG | UPDATE_APPLICATION | LOAD_KEYS",
  "requestId":   "<tms-tracking-id>",
  "downloadUrl": "https://...",
  "version":     "2.1.0"
}
```

For transaction-trigger messages (`type: "card"` or `"qr"`), the payload contains `amount`, `phone`, `terminalid`, and `request_id` instead.

### Command Routing — `TmsCommandHandler`

`core/.../tms/TmsCommandHandler.java` receives the parsed payload and branches by `command`:

```
handleCommand(topic, payload)
  │
  ├─ "UPDATE_PARAMS"       → handleUpdateParams()
  ├─ "UPDATE_L3_CONFIG"    → handleUpdateL3Config()
  ├─ "UPDATE_APPLICATION"  → handleUpdateApplication()
  ├─ "LOAD_KEYS"           → handleLoadKeys()
  └─ unknown               → MQTT response "failed: Unknown command"
```

### `UPDATE_PARAMS` — Full Update Flow

```
handleUpdateParams(requestId, payload)
  │
  ├─ Validate downloadUrl present
  ├─ ConfigFileManager.getVersion("parameter_config")
  │    ├─ version MATCHES payload.version → MQTT "success" (already current)
  │    └─ version DIFFERS  ─────────────────────────────────────────────┐
  │                                                                      │
  ├─ Publish MQTT "initiated"                                            │
  │                                                                      ▼
  └─ RemoteParamsUpdater.updateParams(ctx, downloadUrl, version, "parameter_config")
          │
          ├─ [If Activity context] Show confirmation dialog → user confirms
          │
          ├─ Show ProgressDialog
          │
          ├─ FlyParameterHelper.fetchParametersIgnoreSsl(downloadUrl)
          │    └─ HTTP GET → response type check:
          │         ├─ application/json → parse JSON → Map<String, Object>
          │         └─ (default) ZIP → extract entries:
          │              ├─ default_params.properties   → Map<String, String>
          │              ├─ default_merchants.properties → merchant structure
          │              └─ *configuration*.xml         → InputStream (for EMV)
          │
          └─ For each entry in downloaded map:
               │
               ├─ key == "Merchants"
               │    └─ setMerchant(list)
               │         ├─ Parse: { cardOrg → [{ MERCHANT_ID, TERMINAL_ID, BATCH_NO }] }
               │         ├─ MerchantService.find(cardOrg)
               │         │    ├─ exists → MerchantService.update(merchant)
               │         │    └─ new    → MerchantService.add(merchant)
               │         └─ [Room DB committed]
               │
               ├─ key == "Newland_L3_configuration"
               │    └─ parseEmv(inputStream)
               │         ├─ Select loader: BExtEmvParamLoader | YsdkEmvParamLoader | BEmvParamLoader
               │         ├─ EmvConfigXmlParser.parseXml(stream, loader) → hardware
               │         └─ ParamsUtils.setBoolean(PARAMS_KEY_EMV_AID_CAPK, true)
               │
               └─ any other key
                    ├─ isValidSettingKey(key)
                    │    └─ Reflection scan of ParamsConst static String fields
                    ├─ valid   → ParamsUtils.setObject(key, value)  [SharedPreferences]
                    └─ invalid → log error, skip

          ├─ ConfigFileManager.setVersion("parameter_config", newVersion)
          ├─ MQTT response "success" (or "failed" on error)
          └─ Dismiss ProgressDialog
```

### `UPDATE_L3_CONFIG` — EMV Config Update

Same version-check pattern as `UPDATE_PARAMS`. If a new version is present, calls `RemoteParamsUpdater.updateParams()` with version type `"emv_config"`. The downloaded package contains only the `Newland_L3_configuration` XML entry; the AID/CAPK are pushed directly into the NSDK hardware loader.

### `UPDATE_APPLICATION` — App Version Tracking

Does **not** download or install an APK. Records the incoming version string in `ConfigFileManager` and returns `"success"`. Actual OTA install is handled externally (MDM / device management layer).

### `LOAD_KEYS` — Remote Key Injection

```
handleLoadKeys(requestId, payload)
  │
  ├─ FlyParameterHelper.fetchParametersIgnoreSsl(downloadUrl)
  │    └─ Returns Map<String, Object> with a "keys" array
  │
  └─ FlyKeyHelper.loadCustomKeys(context, map)
        ├─ Extract "keys" array: [{ index: int, value: hex }]
        ├─ For each key:
        │    ├─ Convert hex → byte[]
        │    ├─ Build SymmetricKey (KeyUsage.KEK, KeyType.DES)
        │    └─ KeyManager.generateKey(CLEAR, null, null, symmetricKey) → hardware HSM
        ├─ MQTT "success" with count of installed keys
        └─ MQTT "failed" on error
```

### MQTT Response Publishing

All responses are published to:
```
Topic: /ota/pFppbioOCKlo5c8E/{deviceSN}/response
```

Payload — `MqttOutboundResponse`:
```json
{
  "type":      "tms_response",
  "requestId": "<from request>",
  "status":    "initiated | success | failed",
  "message":   "<detail>"
}
```

### Heartbeat / Config Push

Non-command messages that contain a `heartbeat_interval` field update the MQTT keep-alive interval in `MqttManager` at runtime. No storage write occurs for this type.

---

## 5. Key Validation — `isValidSettingKey`

Before any remote value is written to `SharedPreferences`, `RemoteParamsUpdater` validates the key against the full set of constants declared in `ParamsConst` using Java reflection:

```java
// Scans all public static final String fields of ParamsConst
for (Field field : ParamsConst.class.getDeclaredFields()) {
    if (String.class == field.getType() && isPublicStaticFinal(field)) {
        settingsKeys.add((String) field.get(null));
    }
}
return settingsKeys.contains(incomingKey);
```

Unknown keys are silently dropped. This means adding a new parameter requires declaring a constant in `ParamsConst` before it can be accepted from a remote push.

---

## 6. Settings UI

The `settings` module exposes a subset of `ParamsConst` keys through `BaseSettingFragment` subclasses. Changes write back immediately via `ParamsUtils.setString/setBoolean/…`. Notable fragments:

| Fragment | Params Exposed |
|----------|---------------|
| `SettingUpdateFragment` | TOMS Fly Parameters toggle + manual update trigger |
| `SettingExternalPinpadFragment` | External PIN pad type and connection mode |
| `SettingKeyManageFragment` | Key management / injection UI |
| `SettingMerchantFragment` | Merchant MID / TID / batch management |
| `SettingSystemFragment` | System-level params (server address, timeouts, etc.) |

Password-protected settings require entry of the vendor password (`201003`) or admin password (`000000`) before the settings screen is shown. Parameters that affect hardware state (EMV, PIN pad algorithm) take effect on next device init, not immediately.

---

## 7. Relevant Source Files

| File | Role |
|------|------|
| `core/.../constant/ParamsConst.java` | All parameter key constants |
| `base/.../utils/ParamsUtils.java` | SharedPreferences wrapper |
| `database/.../model/Merchant.java` | Room entity |
| `database/.../service/impl/MerchantServiceImpl.java` | Merchant CRUD |
| `app/.../mqtt/MqttIntegration.java` | MQTT connection and message routing |
| `core/.../tms/TmsCommandHandler.java` | Command dispatch and version checking |
| `core/.../tools/RemoteParamsUpdater.java` | Download, parse, and write params |
| `sdk_helper/.../FlyParameterHelper.java` | HTTP download (JSON / ZIP) |
| `sdk_helper/.../FlyKeyHelper.java` | Remote key injection |
| `core/.../config/ConfigFileManager.java` | TMS version persistence |
| `core/.../tools/AppParamsImporter.java` | Asset-file import at first run |
| `core/.../tools/SelfCheckHelper.java` | Startup initialisation orchestration |
