# KioskLauncher + TMS â€” Full Architecture & Implementation Plan

> **Scope:** MoreFun Kiosk device management via the existing TMS backend â€” covering Marketplace (app distribution) and Debug Mode (screen share + log streaming). This document covers both the Android (KioskLauncher) and the Phoenix (tmsuat_apps) sides.

---

## 1. What Exists Today

### 1.1 KioskLauncher (Android â€” `com.shukria.kiosklauncher`)

| Feature | State | Notes |
|---|---|---|
| Kiosk lock-task mode | âœ… Working | Device Admin enforced |
| App launcher grid | âœ… Working | All installed apps in 4-col grid |
| WebSocket telemetry | âœ… Working | `wss://healthcare.ctrmv.com:9443/` â€” 5 s heartbeat |
| USB thermal printer | âœ… Working | ESC/POS SDK |
| Boot auto-start | âœ… Working | `BOOT_COMPLETED` receiver |
| MQTT client | âŒ Missing | No Tortoise/Paho on Android side |
| App download/install | âŒ Missing | MoreFun SDK API available but unused |
| Log push | âŒ Missing | LogRecorder SDK available but unused |
| Screen share | âŒ Missing | Android MediaProjection available |

### 1.2 TMS Backend (Phoenix â€” `tmsuat_apps`)

| Feature | State | Notes |
|---|---|---|
| MQTT broker integration | âœ… Working | Tortoise client â†’ `demo.ctrmv.com:1883` |
| MF919 MQTT command builder | âœ… Working | `UPDATE_PARAMS`, `UPDATE_L3_CONFIG`, `LOAD_KEYS`, `UPDATE_APPLICATION` |
| SR600 log streaming | âœ… Working | `RemoteLogService` GenServer â€” MQTT logpush topic |
| Log viewer LiveView | âœ… Working | `terminal_live/index.ex` with REALTIME/WITH_DELAY modes |
| OTA / app upgrade | âœ… Working | `AppPackage`, `AppUpgradeConfig`, `AutoPushService` |
| App package storage | âœ… Working | `priv/static/uploads/app_packages/` |
| Device registry | âœ… Working | `TmsTerminal` schema (vendor, model, serial_numberâ€¦) |
| Screen share receiver | âŒ Missing | New Phoenix Channel needed |
| Marketplace catalog API | âŒ Missing | REST endpoint for device to fetch app list |

---

## 2. Key Insight: Most Backend Infrastructure Is Already Built

The TMS backend already handles **MF919 devices** in `MQTTCommandBuilder` and `AutoPushService`. The same MQTT topic scheme used for SR600 log streaming works for MoreFun â€” we just need to:

1. Wire the Android app to speak the same MQTT protocol.
2. Extend `RemoteLogService` to accept MoreFun-format log payloads (minor).
3. Add screen share as a new channel (no existing backend equivalent).
4. Expose a marketplace catalog endpoint (app list JSON + APK download URLs).

---

## 3. Full System Architecture

```
â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
â”‚                           TMS BACKEND (Phoenix)                              â”‚
â”‚                                                                              â”‚
â”‚  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”    â”‚
â”‚  â”‚                    platform_web  (LiveView UI)                       â”‚    â”‚
â”‚  â”‚                                                                      â”‚    â”‚
â”‚  â”‚  terminal_live/index.ex       â† existing log viewer, OTA push       â”‚    â”‚
â”‚  â”‚  marketplace_live/index.ex    â† NEW: app catalog CRUD               â”‚    â”‚
â”‚  â”‚  screen_share_live/index.ex   â† NEW: live screen viewer             â”‚    â”‚
â”‚  â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜    â”‚
â”‚                                 â”‚ Phoenix.PubSub                            â”‚
â”‚  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â–¼â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”    â”‚
â”‚  â”‚                       tms_core (Business Logic)                      â”‚    â”‚
â”‚  â”‚                                                                      â”‚    â”‚
â”‚  â”‚  RemoteLogService        â† extend for MoreFun log format             â”‚    â”‚
â”‚  â”‚  MQTTCommandBuilder      â† extend: add log/screen-share commands     â”‚    â”‚
â”‚  â”‚  AutoPushService         â† MF919 path already exists                 â”‚    â”‚
â”‚  â”‚  AppCatalogService       â† NEW: serves marketplace catalog JSON      â”‚    â”‚
â”‚  â”‚  ScreenShareChannel      â† NEW: Phoenix Channel for frame streaming  â”‚    â”‚
â”‚  â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜    â”‚
â”‚                                 â”‚                                            â”‚
â”‚           â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”                   â”‚
â”‚           â”‚ MQTT (Tortoise)     â”‚                       â”‚ HTTP              â”‚
â”‚           â”‚                     â”‚                       â”‚                   â”‚
â”‚  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â–¼â”€â”€â”€â”€â”€â”€â”   â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â–¼â”€â”€â”€â”€â”€â”€â”€â”   â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â–¼â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”      â”‚
â”‚  â”‚ MQTT Broker   â”‚   â”‚ Phoenix.PubSub   â”‚   â”‚ HTTP File Server      â”‚      â”‚
â”‚  â”‚ demo.ctrmv    â”‚   â”‚ (in-process)     â”‚   â”‚ /ota/{sn}/...         â”‚      â”‚
â”‚  â”‚ :1883         â”‚   â”‚                  â”‚   â”‚ /marketplace/apks/... â”‚      â”‚
â”‚  â””â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”˜   â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜   â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜      â”‚
â”‚           â”‚                                                                  â”‚
â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
            â”‚ MQTT  /ota/{product_key}/{serial_number}/...
            â”‚
â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â–¼â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
â”‚                        MoreFun Kiosk Device (Android)                        â”‚
â”‚                         com.shukria.kiosklauncher                              â”‚
â”‚                                                                              â”‚
â”‚  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”   â”‚
â”‚  â”‚                        MQTTManager (NEW)                              â”‚   â”‚
â”‚  â”‚  - Eclipse Paho Android client                                        â”‚   â”‚
â”‚  â”‚  - Topics: /ota/{pk}/{sn}/update  (receive commands)                  â”‚   â”‚
â”‚  â”‚           /ota/{pk}/{sn}/logpush  (send logs)                         â”‚   â”‚
â”‚  â”‚           /ota/{pk}/{sn}/status   (send heartbeat)                    â”‚   â”‚
â”‚  â”‚           /ota/{pk}/{sn}/screenpush (send screen frames)              â”‚   â”‚
â”‚  â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜   â”‚
â”‚                 â”‚                                                            â”‚
â”‚     â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”                      â”‚
â”‚     â”‚           â”‚                                   â”‚                      â”‚
â”‚  â”Œâ”€â”€â–¼â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â–¼â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â–¼â”€â”€â”€â”€â”€â”€â”              â”‚
â”‚  â”‚ OTA /  â”‚  â”‚  LogPushService â”‚  â”‚ ScreenShareService    â”‚              â”‚
â”‚  â”‚Marketplaceâ”‚  â”‚  (NEW)          â”‚  â”‚ (NEW)                 â”‚              â”‚
â”‚  â”‚ Feature   â”‚  â”‚                 â”‚  â”‚                       â”‚              â”‚
â”‚  â”‚  (NEW)    â”‚  â”‚ On command:     â”‚  â”‚ MediaProjection +     â”‚              â”‚
â”‚  â”‚           â”‚  â”‚ - run logcat    â”‚  â”‚ VirtualDisplay +      â”‚              â”‚
â”‚  â”‚ - Fetch   â”‚  â”‚ - collect MF    â”‚  â”‚ ImageReader â†’         â”‚              â”‚
â”‚  â”‚   catalog â”‚  â”‚   LogRecorder   â”‚  â”‚ JPEG compress â†’       â”‚              â”‚
â”‚  â”‚ - Downloadâ”‚  â”‚ - MQTT publish  â”‚  â”‚ MQTT publish          â”‚              â”‚
â”‚  â”‚   APK     â”‚  â”‚   logpush topic â”‚  â”‚ screenpush topic      â”‚              â”‚
â”‚  â”‚ - Install â”‚  â”‚                 â”‚  â”‚                       â”‚              â”‚
â”‚  â”‚   via MF  â”‚  â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜  â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜             â”‚
â”‚  â”‚   SDK     â”‚                                                              â”‚
â”‚  â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜                                                              â”‚
â”‚                                                                              â”‚
â”‚  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”    â”‚
â”‚  â”‚  Existing: KioskUtil â”‚ AppsListFragment â”‚ WebSocketManager â”‚ Printerâ”‚    â”‚
â”‚  â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜    â”‚
â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
```

---

## 4. MQTT Topic Contract

All topics follow the existing TMS pattern:

| Direction | Topic | Payload type | Purpose |
|---|---|---|---|
| Server â†’ Device | `/ota/{pk}/{sn}/update` | JSON | OTA, log start/stop, screen share commands |
| Device â†’ Server | `/ota/{pk}/{sn}/logpush` | JSON | Log lines (same as SR600) |
| Device â†’ Server | `/ota/{pk}/{sn}/status` | JSON | Heartbeat / device telemetry |
| Device â†’ Server | `/ota/{pk}/{sn}/screenpush` | Binary / JSON+base64 | Screen frames |
| Device â†’ Server | `/ota/{pk}/{sn}/ack` | JSON | Command acknowledgements |

`{pk}` = product key (e.g., `pFppbioOCKlo5c8E`)  
`{sn}` = device serial number

### 4.1 Command Payloads (Server â†’ Device)

```json
// Start log streaming (same pattern as SR600)
{
  "type": "tms_command",
  "command": "debug_log_upload_start",
  "requestId": "req-1746436800-abc123",
  "mode": "REALTIME",
  "logLevel": "DEBUG",
  "lastLinesCount": 200
}

// Stop log streaming
{
  "type": "tms_command",
  "command": "debug_log_upload_stop",
  "requestId": "req-1746436801-def456"
}

// Start screen share
{
  "type": "tms_command",
  "command": "screen_share_start",
  "requestId": "req-1746436802-ghi789",
  "fps": 1,
  "quality": 60
}

// Stop screen share
{
  "type": "tms_command",
  "command": "screen_share_stop",
  "requestId": "req-1746436803-jkl012"
}

// OTA / app install (already implemented on backend)
{
  "type": "tms_command",
  "command": "UPDATE_APPLICATION",
  "requestId": "req-1746436804-mno345",
  "downloadUrl": "http://demo.ctrmv.com:14005/ota/{sn}/app.apk",
  "version": "1.2.0",
  "packageName": "com.example.payapp",
  "autoStart": false
}
```

### 4.2 Device â†’ Server Payloads

```json
// Log push (same format as SR600 â€” RemoteLogService already parses this)
{
  "logs": "2026-05-05 10:00:01 D/Tag: message\n2026-05-05 10:00:02 D/Tag: ...",
  "session_info": {
    "timestamp": "2026-05-05T10:00:01Z",
    "session_id": "req-1746436800-abc123",
    "status": "streaming"
  }
}

// Screen frame
{
  "type": "screen_frame",
  "serial_number": "MF12345678",
  "timestamp": 1746436810000,
  "sequence": 42,
  "frame": "<base64-encoded JPEG>"
}

// Device status heartbeat
{
  "type": "device_status",
  "serial_number": "MF12345678",
  "app_version": "1.2.0",
  "android_version": "10",
  "ram_free_mb": 512,
  "storage_free_mb": 4096,
  "battery_pct": 85,
  "network_type": "WIFI",
  "kiosk_active": true,
  "timestamp": "2026-05-05T10:00:00Z"
}

// ACK
{
  "type": "ack",
  "requestId": "req-1746436800-abc123",
  "status": "success",
  "message": "Log streaming started"
}
```

---

## 5. Marketplace Architecture

### 5.1 Data Flow

```
TMS Admin UI
  â””â”€ upload APK â†’ AppPackage schema (already exists)
  â””â”€ create catalog entry â†’ AppCatalogItem (NEW schema, extends AppPackage)

Device polls (HTTPS GET):
  /api/v1/marketplace/apps?vendor=morefun&model=kiosk
  â† returns catalog JSON

Device downloads APK (HTTPS GET):
  /ota/{sn}/marketplace/{packageName}-{version}.apk

Device installs:
  DeviceHelper.installApp(apkPath, null, packageName)  â† MoreFun SDK

Device ACKs install result via MQTT:
  /ota/{pk}/{sn}/ack  { command: "INSTALL_APP", status: "success", ... }
```

### 5.2 Catalog JSON Response

```json
{
  "catalog_version": "2026-05-05T10:00:00Z",
  "apps": [
    {
      "id": "com.example.payapp",
      "name": "Pay App",
      "version": "1.2.0",
      "version_code": 12,
      "apk_url": "http://demo.ctrmv.com:14005/ota/marketplace/payapp-1.2.0.apk",
      "icon_url": "http://demo.ctrmv.com:14005/marketplace/icons/payapp.png",
      "description": "POS payment application",
      "size_bytes": 4500000,
      "checksum_sha256": "abc123...",
      "min_android_sdk": 23,
      "force_update": false,
      "category": "payment"
    }
  ]
}
```

### 5.3 New Backend Schema: `AppCatalogItem`

```elixir
schema "app_catalog_items" do
  field :package_name, :string         # unique identifier
  field :display_name, :string
  field :description, :string
  field :category, :string
  field :icon_url, :string
  field :is_published, :boolean, default: false
  field :target_vendors, {:array, :string}   # ["morefun", "sr600"]
  field :target_models, {:array, :string}    # ["kiosk", "MF919"]
  field :force_update, :boolean, default: false
  belongs_to :app_package, AppPackage       # links to existing APK version
  timestamps()
end
```

---

## 6. Screen Share Architecture

### 6.1 Approach: MQTT frames â†’ Phoenix Channel â†’ LiveView

Since the device already has an MQTT connection and the TMS already handles binary messaging via Tortoise, the simplest path is:

1. Device publishes JPEG frames to `/ota/{pk}/{sn}/screenpush`
2. Tortoise handler receives binary/JSON, broadcasts via `Phoenix.PubSub` on topic `screen_share:{serial_number}`
3. `ScreenShareLive` subscribes and pushes frames to browser via `push_event`
4. Browser renders frames in `<img>` tag via JS hook updating `src`

```
Device
  MediaProjection â†’ VirtualDisplay â†’ ImageReader
  â†’ JPEG compress (quality 40-60, scale 50%)
  â†’ base64 encode
  â†’ MQTT publish /ota/{pk}/{sn}/screenpush

Backend (Tortoise handler)
  â†’ decode JSON
  â†’ Phoenix.PubSub.broadcast("screen_share:{sn}", {:frame, base64_jpeg})

ScreenShareLive (LiveView)
  â†’ handle_info {:frame, data}
  â†’ push_event(socket, "screen_frame", %{data: data})

Browser (JS Hook)
  â†’ handleEvent("screen_frame", ({data}) => img.src = "data:image/jpeg;base64," + data)
```

### 6.2 Frame Rate / Bandwidth Estimate

| FPS | JPEG quality | ~Frame size | Bandwidth |
|---|---|---|---|
| 0.5 fps | 40% | ~30 KB | ~15 KB/s |
| 1 fps | 50% | ~50 KB | ~50 KB/s |
| 2 fps | 60% | ~80 KB | ~160 KB/s |

**Recommendation:** Default 1 fps, quality 50% â€” adequate for monitoring, minimal bandwidth.

---

## 7. Implementation Plan

### Phase 1 â€” Android: MQTT Foundation (Week 1)

**Goal:** Device connects to TMS MQTT broker and sends heartbeat.

- [ ] Add Eclipse Paho MQTT dependency to `build.gradle`
  ```gradle
  implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
  implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
  ```
- [ ] Create `MQTTManager.kt` singleton (mirrors `WebSocketManager.kt` pattern)
  - Connect to `demo.ctrmv.com:1883` with `clientId = "morefun-{serial_number}"`
  - Subscribe to `/ota/{product_key}/{serial_number}/update`
  - Publish heartbeat to `/ota/{product_key}/{serial_number}/status` every 30 s
  - Dispatch incoming commands via `CommandDispatcher`
- [ ] Create `CommandDispatcher.kt` â€” routes `command` field to correct handler
- [ ] Remove/keep existing WebSocket telemetry (keep for now, migrate later)
- [ ] Add `FOREGROUND_SERVICE` permission, run MQTT in `MQTTService` (ForegroundService)

**Backend work (minimal):**
- Verify `TmsTerminal` can register a MoreFun Kiosk device (vendor=`morefun`, model=`kiosk`)
- Confirm existing `terminal_live/index.ex` shows the device in device list

---

### Phase 2 â€” Android: Log Streaming (Week 1-2)

**Goal:** Admin can start/stop real-time log viewing from TMS UI â€” same UX as SR600.

- [ ] Create `LogPushService.kt`
  - Listens for `debug_log_upload_start` command from `CommandDispatcher`
  - Runs `Runtime.exec("logcat -d -v time *:D")` and collects output
  - Optionally integrates MoreFun `LogRecorder` SDK
  - Packages logs in the same JSON format as SR600 (`logs`, `session_info`)
  - Publishes to `/ota/{pk}/{sn}/logpush` via `MQTTManager`
  - For REALTIME mode: streams continuously until `debug_log_upload_stop`
  - For WITH_DELAY: buffers and flushes on interval
- [ ] ACK the command before starting log collection
- [ ] Handle `debug_log_upload_stop`: flush remaining buffer, send final ack

**Backend work (minimal):**
- `RemoteLogService` already handles this topic and payload format â€” **no changes needed**
- The existing `terminal_live/index.ex` log viewer will work immediately
- Test end-to-end: trigger log session from LiveView â†’ see device logs in UI

---

### Phase 3 â€” Android: Marketplace (Week 2-3)

**Goal:** Admin publishes apps to catalog; device shows them in a "Store" tab; user can install.

**Android:**
- [ ] Add Retrofit + Gson dependency
- [ ] Create `AppCatalogService.kt` (Retrofit) â€” `GET /api/v1/marketplace/apps`
- [ ] Create `AppCatalogItem.kt` data class
- [ ] Create `MarketplaceFragment.kt`
  - RecyclerView with card: icon, name, version, size, install/update/uninstall button
  - Button state: NOT_INSTALLED / DOWNLOADING / INSTALLING / INSTALLED / UPDATE_AVAILABLE
  - Download via Android `DownloadManager` to `getExternalFilesDir()/marketplace/`
  - Install via `DeviceHelper.installApp(path, null, packageName)` (MoreFun SDK)
- [ ] Create `PackageStateReceiver.kt` â€” `ACTION_PACKAGE_ADDED/REPLACED/REMOVED` â†’ refresh UI
- [ ] Handle OTA `UPDATE_APPLICATION` MQTT command: download + install + ACK
- [ ] Add "Store" button to `HomeFragment` alongside existing "Apps" button

**Backend:**
- [ ] Create `AppCatalogItem` Ecto schema (extends `AppPackage`, adds catalog metadata)
- [ ] Create `AppCatalogContext` â€” `list_published_apps/1`, `publish_app/2`, `unpublish_app/1`
- [ ] Create `GET /api/v1/marketplace/apps` controller (JSON API, filterable by vendor/model)
- [ ] Serve APKs from `/ota/marketplace/:filename` (already have file server in place)
- [ ] Create `marketplace_live/index.ex` â€” admin UI to publish/unpublish apps
- [ ] Wire `AppUpgradeConfig` + `AutoPushService` to trigger `UPDATE_APPLICATION` MQTT command to target devices

---

### Phase 4 â€” Android: Screen Share (Week 3-4)

**Goal:** Admin can see live device screen in TMS LiveView.

**Android:**
- [ ] Add `FOREGROUND_SERVICE` and `MEDIA_PROJECTION` handling
- [ ] Create `ScreenShareService.kt` (ForegroundService)
  - On `screen_share_start` command: request `MediaProjection` permission (if not device owner), or auto-grant if device owner
  - Create `VirtualDisplay` â†’ `ImageReader` pipeline
  - On each frame: scale to 50%, compress to JPEG (quality 50%), base64 encode
  - Publish to `/ota/{pk}/{sn}/screenpush`
  - Respect `fps` from command (default 1)
  - On `screen_share_stop`: release `MediaProjection`, stop service

**Backend:**
- [ ] Create Tortoise handler for `/ota/{pk}/{sn}/screenpush` topic
- [ ] Broadcast via PubSub: `Phoenix.PubSub.broadcast("screen_share:{sn}", {:frame, data})`
- [ ] Create `ScreenShareLive` LiveView
  - Subscribe to `screen_share:{sn}` on mount
  - `handle_info({:frame, data})` â†’ `push_event(socket, "screen_frame", %{data: data})`
  - Show live `<img>` updated by JS hook
  - "Start" / "Stop" buttons send MQTT commands via `MQTTCommandBuilder`
  - Display FPS counter, last-frame timestamp, connection status
- [ ] Add JS hook `ScreenFrameHook` to `app.js`:
  ```js
  ScreenFrameHook: {
    mounted() {
      this.handleEvent("screen_frame", ({ data }) => {
        this.el.src = "data:image/jpeg;base64," + data;
      });
    }
  }
  ```
- [ ] Integrate screen share button into existing `terminal_live/index.ex` (alongside log viewer)

---

### Phase 5 â€” Polish & Production Hardening (Week 4-5)

- [ ] Remove hardcoded credentials (device_id, user_id) â€” fetch from TMS registration API on first boot
- [ ] Fix duplicate boot receivers (`StartupOnBootUpReceiver.kt` + `startupOnBootUpReceiver.java`)
- [ ] Fix mock CPU usage â€” read from `/proc/stat` properly
- [ ] SSL certificate pinning for MQTT and HTTP connections
- [ ] MQTT reconnect with exponential backoff in `MQTTManager`
- [ ] Rate-limit screen share (server-side: max 1 concurrent session per device)
- [ ] Log rotation â€” cap `logcat` output, send in chunks if > 100 KB
- [ ] Graceful kiosk mode interaction â€” screen share requires starting outside lock task mode or with `MediaProjection` exemption

---

## 8. File-by-File Changes

### Android New Files

| File | Purpose |
|---|---|
| `util/MQTTManager.kt` | MQTT client singleton (Paho), subscribes/publishes |
| `service/MQTTService.kt` | ForegroundService housing MQTTManager |
| `util/CommandDispatcher.kt` | Routes incoming MQTT commands to handlers |
| `service/LogPushService.kt` | Collects logcat + MoreFun LogRecorder, streams via MQTT |
| `service/ScreenShareService.kt` | MediaProjection capture â†’ JPEG â†’ MQTT |
| `ui/MarketplaceFragment.kt` | App store grid UI |
| `ui/AppDetailBottomSheet.kt` | App detail + install/uninstall |
| `adapter/MarketplaceAdapter.kt` | RecyclerView adapter for catalog cards |
| `model/AppCatalogItem.kt` | Catalog data class (from API) |
| `network/AppCatalogService.kt` | Retrofit interface |
| `network/AppDownloader.kt` | DownloadManager wrapper |
| `receiver/PackageStateReceiver.kt` | Package add/remove broadcast â†’ UI refresh |

### Android Modified Files

| File | Change |
|---|---|
| `ui/MainActivity.kt` | Initialize `MQTTManager`, start `MQTTService` |
| `ui/HomeFragment.kt` | Add "Store" button |
| `AndroidManifest.xml` | Add MQTT service, screen share service, package receiver, new permissions |
| `build.gradle` | Add Paho MQTT, Retrofit, Gson dependencies |

### Backend New Files

| File | Purpose |
|---|---|
| `tms_core/marketplace/app_catalog_item.ex` | Ecto schema |
| `tms_core/marketplace/app_catalog_context.ex` | CRUD + publish/unpublish |
| `tms_core/marketplace/app_catalog_service.ex` | Serve catalog JSON, build APK URLs |
| `tms_core/terminal_management/screen_share_handler.ex` | Tortoise message handler for screenpush topic |
| `platform_web/live/marketplace_live/index.ex` | Admin LiveView â€” manage catalog |
| `platform_web/live/screen_share_live/index.ex` | Admin LiveView â€” view device screen |
| `platform_web/controllers/marketplace_controller.ex` | `GET /api/v1/marketplace/apps` |

### Backend Modified Files

| File | Change |
|---|---|
| `tms_core/terminal_management/mqtt_command_builder.ex` | Add `screen_share_start/stop`, `debug_log_*` for MF Kiosk device type |
| `tms_core/terminal_management/remote_log_service.ex` | Accept MoreFun log format (minor: same JSON structure already parsed) |
| `platform_web/live/terminal_live/index.ex` | Add screen share button, wire new commands |
| `da_product_app/lib/mqtt_handler.ex` | Subscribe to `/ota/{pk}/+/screenpush` wildcard topic |
| `config/config.exs` | Add marketplace file storage path config |

---

## 9. Dependencies to Add

### Android (`app/build.gradle`)

```gradle
// MQTT
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'

// HTTP / Marketplace catalog
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'

// Image loading for marketplace icons
implementation 'io.coil-kt:coil:2.6.0'
```

### Backend (`tms_core/mix.exs`) â€” no new deps needed
- `tortoise` already present
- `req` (HTTP) already present
- `jason` already present
- `phoenix_pubsub` already present

---

## 10. Risks and Mitigations

| Risk | Mitigation |
|---|---|
| `MediaProjection` requires user dialog â€” kiosk blocks interaction | Use Device Owner `grantRuntimePermission` or show dialog before kiosk mode starts; admin can trigger remotely |
| MQTT broker at `demo.ctrmv.com` â€” is this prod or dev? | Confirm endpoint; use env config, not hardcoded |
| MoreFun `installApp()` requires system-level privileges | Confirm device is provisioned with MoreFun OEM permissions before shipping |
| Screen share bandwidth on cellular | Rate-limit at server (max 1 session/device), and cap at 1 fps by default |
| Logcat permission on Android 8+ | Must request `READ_LOGS` (only grantable via ADB or device owner) â€” use MoreFun LogRecorder SDK as fallback |
| APK download + install requires `REQUEST_INSTALL_PACKAGES` | Add to manifest; verify MoreFun SDK bypasses user confirmation dialog |
| Existing WebSocket hardcoded to `healthcare.ctrmv.com` | Decide: migrate to MQTT only, or keep both. Recommend MQTT as primary, remove WS after Phase 2. |

---

## 11. Recommended Delivery Order

```
Week 1:  Phase 1 (MQTT) + Phase 2 (Log streaming)
         â†’ Immediate value: MoreFun devices appear in TMS, log viewer works same as SR600

Week 2-3: Phase 3 (Marketplace)
         â†’ Admin can push apps to fleet via TMS UI

Week 3-4: Phase 4 (Screen share)
         â†’ Remote debugging screen visible in TMS

Week 4-5: Phase 5 (Polish)
         â†’ Production-ready
```

---

## 12. Decisions Confirmed

| # | Question | Decision |
|---|---|---|
| 1 | MQTT endpoint | âœ… `demo.ctrmv.com:1883` confirmed |
| 2 | Product key | âœ… Same key as SR600: `pFppbioOCKlo5c8E` |
| 3 | Device registration | Kiosk registers via existing `com.newland.template` payment app flow. TMS UI: add a **"Kiosk" sub-tab** under the existing terminal view for kiosk-specific data. Full self-registration logic planned for a later phase. |
| 4 | Screen share permission | âœ… Device Owner â€” `MediaProjection` can be auto-granted programmatically |
| 5 | APK hosting | âœ… Same TMS file server: `demo.ctrmv.com:14005/ota/marketplace/` |
| 6 | WebSocket | âœ… Keep WebSocket as-is. Use as fallback if MQTT screen share has broker packet-size issues in testing. |

---

## 13. MQTT Screen Share Feasibility â€” Confirmed

Analysis of broker and Tortoise client configuration confirms MQTT is viable for JPEG frame streaming:

- **Tortoise 0.10 default max packet size:** 256 KB
- **Typical JPEG frame at 1 fps, quality 50%, 50% scale:** 20â€“45 KB â†’ well within limit
- **Existing binary handler pattern** (`ota/+/+/logpush`) already broadcasts raw binary payloads â€” same pattern used for `screenpush`
- **Design:** Device publishes **raw JPEG bytes** (not base64-in-JSON) at **QoS 0** on topic `/ota/{pk}/{sn}/screenpush`. Backend converts to base64 only at the LiveView â†’ browser step.
- **Fallback trigger:** If broker rejects frames (>256 KB) in testing, switch `ScreenShareService` to use `WebSocketManager` â€” same JPEG bytes, different transport, no other changes needed.
