# QSJNIC API Reference

## McuClient
- `getInstance()` → McuClient (单例)
- `init(Context)` / `release()`
- `isReady()` / `addReadyListener(ReadyListener)` / `removeReadyListener(ReadyListener)`
- `getAidlVersion()` / `isLinkAlive()`
- `getSdkVersion()` → String (本地常量, 无 IPC)
- `getRemoteServiceVersion()` → String (经 Binder 取 Service 端版本)
- `vehicle()` → McuVehicleClient
- `key()` → McuKeyClient
- `radio()` → McuRadioClient
- `upgrade()` → McuUpgradeClient
- `sendCommand(msgType, cmd, body, timeoutMs)` → int
- `postCommand(msgType, cmd, body)`
- `addFrameListener(IMcuFrameListener)` / `removeFrameListener(IMcuFrameListener)`

### ReadyListener
- `onReady()` — Service 连接就绪回调

## McuVehicleClient
- `requestInitInfo()` → int
- `setBacklight(duty1, duty2)` → int
- `setMute(on)` → int
- `setAmpPower(on)` → int
- `setTimeInfo(y,m,d,h,m,s)` → int
- `requestSleep()` → int
- `requestTypeInfo(type)` → int
- `requestArmReset()` → int
- `setTftOnOff(on)` → int
- `factoryRestore()` → int
- `setKeyBacklightDutyCycle(duty)` → int
- `setSubMute(on)` → int
- `setKeyPwrStatus(status)` → int
- `setReverse(on)` → int
- `setSpeedRatio(ratio)` → int
- `requestTripClear()` → int
- `requestTripClear(type)` → int (1=长期小计, 2=总里程, 3=短期小计)
- `setSwitchStatus(switchBitmap)` → int
- `setBtErrorCode(code)` → int
- `setGpsInfo(latE6, lonE6, speed, heading)` → int
- `setGpsInfoFull(moduleStatus, lonPpm, lonStatus, latPpm, latStatus)` → int
- `setTotalMiles(tenthKm)` → int
- `setWorkMode(mode)` → int
- `setFuelCal(data)` → int
- `setVehicleCfg(data)` → int
- `requestVehicleCfg()` → int
- `setArmSwVersion(v)` → int
- `setArmHwVersion(v)` → int
- `setAutoClearMeterType(type)` → int
- `responseFactoryCmd(data)` → int
- `responseTestCmd(data)` → int
- `requestArmForceUpgrade()` → int 【危险】
- `getLatestSnapshot()` → VehicleSnapshot (纯内存读取, 可能为 null)
- `getMcuVersion()` → McuVersion (纯内存读取, 可能为 null)
- `addListener(VehicleListener)` / `removeListener(VehicleListener)`

### VehicleListener 回调
- `onLineDetectChanged(LineDetectInfo)` — 线检测状态变化
- `onVoltage(int milliVolt)` — 电池电压(mV)
- `onSysStatus(int firstPwrOn)` — 系统状态
- `onBacklightChanged(int dutyPercent)` — 背光百分比
- `onKeyEvent(int rawByte)` — 按键原始字节
- `onBpAbnormal(int code)` — 电源异常
- `onMcuVersion(McuVersion)` — MCU 版本信息
- `onTimeChanged(long epochMillis, boolean isSet)` — MCU 时间变化
- `onReqPwrOff()` — MCU 请求 ARM 关机
- `onBattInfo(int voltageTenthV, int percent)` — 电池信息；当前协议仅携带百分比时 voltageTenthV=-1
- `onCarSpeed(int speedKmh)` — 车速 km/h
- `onMileage(int mileageTenthKm)` — 里程 0.1km
- `onSwitchStatus(int status)` — 开关状态 bitmap
- `onFuelCal(byte[])` — 油耗校准原始数据
- `onEncryptStatus(int status)` — 加密状态
- `onVehicleCfg(byte[])` — 车辆配置原始数据
- `onInsFuelConsume(int consumeTenth)` — 瞬时油耗 0.1 L/100km
- `onMcuHardVersion(String)` — 硬件版本字符串
- `onFactoryCmd(byte[])` — 工厂指令原始数据
- `onFactoryCmdFromCan(byte[])` — CAN 工厂指令原始数据
- `onComTest(byte[])` — 通信测试原始数据
- `onTestInfo(byte[])` — 测试信息原始数据
- `onComTestResponse(byte[])` — 通信测试响应原始数据
- `onStopCharge()` — 停止充电通知
- `onRunNormally()` — 正常运行通知

## McuKeyClient
- `sendKeyEvent(keyValue, keyStatus)` → int
- `postKeyEvent(keyValue, keyStatus)` → int
- `addListener(KeyListener)` / `removeListener(KeyListener)`

### 实现方案
Key 域采用 **方案三：Framework 层 `InputManager.injectInputEvent` 主动注入**。

Service 端通过反射调用 `InputManager.injectInputEvent(InputEvent, int)` 将 MCU 方控事件注入 Android Input 子系统，事件以标准 `KeyEvent` 形式分发，可被系统中任何应用（含 System UI、Launcher、第三方 App）接收。这与 Linux 驱动层接入（方案一）和 VHAL 接入（方案二）有本质区别：

| 维度 | 方案一（Linux 驱动） | 方案二（VHAL） | 方案三（injectInputEvent） |
|---|---|---|---|
| 接入层 | Kernel / dev/input | CarService VHAL | Framework / InputManager |
| 事件类型 | `EV_KEY` | `VEHICLE_PROPERTY_HW_KEY_INPUT` | `KeyEvent` |
| 系统要求 | 需定制 keylayout | 需定制 VHAL + CarService | 需 `INJECT_EVENTS` 权限 |
| 多应用接收 | 是 | 仅订阅 VHAL 的应用 | 是（全局 KeyEvent） |
| 方控耦合 | 需 MCU 模拟 Linux 键码 | 需 MCU 模拟 VHAL 键码 | 保持帕杰罗协议不变 |

> **系统集成要求**：由于 `injectInputEvent` 需要 `android.permission.INJECT_EVENTS`，该权限仅授予 `signature|privileged` 级系统应用。因此 QsmwService 必须以系统签名 APK 预置到 `/system/priv-app/`，并声明 `sharedUserId="android.uid.system"`。

### Key 常量（帕杰罗映射表）
| 常量 | 值 | 说明 |
|---|---|---|
| `KEY_VALUE_NONE` | 0x00 | 无按键/空闲 |
| `KEY_VALUE_POWER` | 0x01 | 电源键 |
| `KEY_VALUE_HOME` | 0x02 | HOME 键 |
| `KEY_VALUE_VOICE` | 0x03 | 语音键 |
| `KEY_VALUE_MUSIC` | 0x04 | 音乐键 |
| `KEY_VALUE_RADIO` | 0x05 | 收音机键 |
| `KEY_VALUE_TEL` | 0x06 | 电话键 |
| `KEY_VALUE_TFT` | 0x07 | TFT 键 |
| `KEY_VALUE_VOL_DEC` | 0x08 | 音量减 |
| `KEY_VALUE_VOL_INC` | 0x09 | 音量加 |
| `KEY_VALUE_MUTE` | 0x0C | 静音键 |
| `KEY_VALUE_MODE` | 0x0D | MODE 键 |
| `KEY_VALUE_PREV` | 0x0E | 上一曲 |
| `KEY_VALUE_NEXT` | 0x0F | 下一曲 |
| `KEY_VALUE_ANSWER` | 0x18 | 接听 |
| `KEY_VALUE_REJECT` | 0x19 | 挂断 |
| `KEY_VALUE_CAMERA` | 0x25 | 摄像头 |
| `KEY_STATUS_RELEASE` | 0x00 | 按键释放/弹起 |
| `KEY_STATUS_SHORT_PRESS` | 0x01 | 短按/按下 |
| `KEY_STATUS_STUCK` | 0x02 | 长按/卡住 |
| `KEY_STATUS_ENCODER_LEFT` | 0x03 | 编码器左旋 |
| `KEY_STATUS_ENCODER_RIGHT` | 0x04 | 编码器右旋 |

> 帕杰罗方控实际使用 9 个按键：`0x03` 语音、`0x04` 音乐、`0x08` 音量-、`0x09` 音量+、`0x0E` 上一曲、`0x0F` 下一曲、`0x18` 接听、`0x19` 挂断、`0x25` 摄像头。  
> `0x0E/0x0F/0x18/0x19/0x25` 为 MC3 范围外扩展值，SDK 校验范围已放宽至 `0x00-0x25`。

### KeyListener 回调
- `onKeyEvent(int keyValue, int keyStatus)` — Key 域按键事件

## McuRadioClient
- `setBand(band)` → int
- `setBand(band, freq)` → int
- `setFrequency(freq)` → int
- `setFreqRange(area, amMin, amMax, fmMin, fmMax, amStep, fmStep)` → int
- `setSeek(mode, pty)` → int
- `seekStop()` → int
- `seekAms()` → int
- `seekForward()` → int
- `seekBackward()` → int
- `seekPty(pty)` → int
- `seekTa()` → int
- `setSwitch(subType, value)` → int
- `setAntPower(on)` → int
- `setRegMute(mute)` → int
- `getLatestState()` → RadioState (纯内存读取, 可能为 null)
- `addListener(RadioListener)` / `removeListener(RadioListener)`

### Radio 常量
| 常量 | 值 | 说明 |
|---|---|---|
| `BAND_FM` | 0 | FM 波段 |
| `BAND_AM` | 1 | AM 波段 |
| `SEEK_STOP` | 0 | 停止搜索 |
| `SEEK_AMS` | 1 | 全频段自动搜存 |
| `SEEK_FORWARD` | 2 | 单步向前 |
| `SEEK_BACKWARD` | 3 | 单步向后 |
| `SEEK_PTY` | 4 | 按节目类型搜索 |
| `SEEK_TA` | 5 | 交通公告搜索 |
| `SWITCH_TA` | 0 | TA 开关 |
| `SWITCH_AF` | 1 | AF 开关 |
| `SWITCH_STEREO` | 2 | 立体声开关 |
| `SWITCH_LOCAL` | 3 | 远程/近程 (0=DX, 1=LOC) |
| `SWITCH_RDS` | 4 | RDS 总开关 |
| `SWITCH_CT` | 5 | CT 时钟同步开关 |

### RadioListener 回调
- `onFreqChanged(RadioState)` — 频率或波段变化
- `onPsPty(int pi, int pty, String ps)` — RDS PS/PTY 信息更新；当前协议不携带 PI，pi 固定为 0
- `onRadioText(String rt)` — RDS RT 文本更新
- `onTaPtyTips(int mode, int value)` — TA/PTY 搜索提示 (mode: 0=TA/1=PTY, value: 0=清除/1=无结果/2=搜索中)
- `onWorkStatus(int status)` — 调谐器工作状态变化

## McuUpgradeClient
- `startUpgrade(String binFilePath)` → int
- `startUpgrade(UpgradeRequest)` → int
- `stopUpgrade()` → int
- `getProgress()` → UpgradeProgress (纯内存读取, 可能为 null)
- `startBootUpgrade(UpgradeRequest)` → int
- `sendBootUpgradeData(byte[] chunk, int index)` → int
- `sendBootUpgradeChecksumMd5(byte[] md5)` → int (16 字节 MD5)
- `sendBootUpgradeChecksum(int checksum)` → int (旧接口保留，返回 ERR_INVALID_ARG)
- `addListener(UpgradeListener)` / `removeListener(UpgradeListener)`

### UpgradeListener 回调
- `onStateChanged(int state)` — 状态机迁移
- `onProgress(UpgradeProgress)` — 进度更新
- `onError(int errorCode, String message)` — 错误或中断
- `onUpgradeAck(int ackCmd)` — MCU 确认帧

## DTOs
- VehicleSnapshot: lineDetect, voltageMv, firstPwrOn, tftDutyPercent, timeEpochMillis, timeIsSet, mcuVersion
- RadioState: band, freq, signal, valid, seekMode, pty, flagStereo, flagTa, flagTp, switchTa, switchAf, afBlink, bandSwitching
- UpgradeProgress: state, totalPackets, sentPackets, currentIndex, packetSize, binSize, stageDesc, phase, currentPacket, bytesDone, bytesTotal, totalRetries, elapsedMs, rateBps, errCode
- UpgradeRequest: binFilePath, projectId, packetSizeOverride
- McuVersion: hwVersion, swVersion, bootVersion
- LineDetectInfo: ig, acc, illumination, reverse, aux, brake, leftTurn, rightTurn

## UpgradeProgress 状态常量
| 常量 | 值 | 说明 |
|---|---|---|
| `STATE_IDLE` | 0 | 空闲 |
| `STATE_REQ` | 1 | 已发送升级请求 |
| `STATE_BEGIN` | 2 | 已开始升级会话 |
| `STATE_WAIT_LEN` | 3 | 等待 MCU 返回长度确认 |
| `STATE_DATA` | 4 | 数据传输中 |
| `STATE_END` | 5 | 已发送结束帧 |
| `STATE_COMPLETE` | 6 | 升级完成 |
| `STATE_ERROR` | 7 | 升级出错 |
| `STATE_ABORTED` | 8 | 升级已中止 |

## McuErrorCode
| 常量 | 值 |
|---|---|
| OK | 0 |
| ERR_LINK_DOWN | -1 |
| ERR_UPGRADE_BUSY | -2 |
| ERR_FILE | -3 |
| ERR_SERVICE | -4 |
| ERR_PERMISSION | -5 |
| ERR_INVALID_ARG | -7 |
| ERR_TIMEOUT | -6 |
| ERR_RECONNECTING | -8 |
| ERR_NOT_IMPLEMENTED | -9 |
| ERR_MCU_DEADLOCK | -10 |

> 注：`ERR_INVALID_ARG` 定义值为 **-7**，`ERR_TIMEOUT` 为 **-6**，这是代码中的实际常量定义顺序，与数值大小无关。

## Protocol Notes
- Public `TIME_INFO` payload is 6 bytes: `YY MM DD hh mm ss`; the `been set` byte exists only in MCU→ARM `TIME_INFO`.
- Public `TRIP_CLEAR` sends one byte: `1/2/3`.
- Public `GPS_INFO` protocol payload is 11 bytes: module status, 4-byte longitude, longitude status, 4-byte latitude, latitude status. The legacy four-argument SDK method maps signed micro-degrees into this payload and ignores speed/heading.
- Public `ARM_SW_VERSION` and `ARM_HW_VERSION` include a one-byte string length prefix.
- Key domain uses `msgType=0x02`; `KEY_A2M_KEY(0x81)` and `KEY_M2A_KEY(0x01)` payloads are `keyValue, keyStatus`.
- Radio `SET_BAND` protocol payload is `band, freqH, freqL`; the one-argument SDK overload sends frequency `0`.
- Boot Update checksum is 16-byte MD5. The old 4-byte int checksum form is not protocol-compliant.
