# 版本变更日志

## V2.2.0-E(2026-6-5)

- **手机端 SDK 版本**：`com.rokid.security:phone.sdk:2.2.0-E`
- **眼镜端 SDK 版本**：`com.rokid.security:glass3.open.sdk:2.2.0-E`
- **推荐 OTA 版本**：`1.19.e002-20260603-150201` 及以上

### 眼镜端接口变更

```java
// IDeviceService.aidl
package com.rokid.security.system.server.device;
import com.rokid.security.system.server.device.listener.DeviceEventListener;
import com.rokid.security.system.server.device.listener.BatteryUpdateListener;
import com.rokid.security.system.server.device.listener.IAppVisibilityListener;
import com.rokid.security.system.server.device.listener.IWifiOperationCallback;
import com.rokid.security.glass3.sdk.base.data.device.bean.DeviceStatusInfo;
import com.rokid.security.glass3.sdk.base.data.device.bean.AppPackageInfo;
import com.rokid.security.glass3.sdk.base.data.device.bean.GlassAppConfig;
import com.rokid.security.glass3.sdk.base.data.device.wifi.WifiConnectRequest;
import com.rokid.security.glass3.sdk.base.data.device.wifi.WifiRemoveRequest;
import com.rokid.security.glass3.sdk.base.data.device.wifi.WifiNetworkInfo;

// Declare any non-default types here with import statements

interface IDeviceService {
    /**
     * 连接指定 Wi-Fi。
     *
     * 支持的安全类型：
     * - open: WifiConnectRequest.SECURITY_OPEN
     * - WEP: WifiConnectRequest.SECURITY_WEP，仅用于兼容老旧 AP
     * - WPA/WPA2-PSK: WifiConnectRequest.SECURITY_WPA_PSK
     * - WPA3-Personal/SAE: WifiConnectRequest.SECURITY_WPA3_SAE
     * - WPA/WPA2-Enterprise: WifiConnectRequest.SECURITY_ENTERPRISE
     * - WPA3-Enterprise: WifiConnectRequest.SECURITY_WPA3_ENTERPRISE
     * - WPA2/WPA3 自动模式: WifiConnectRequest.SECURITY_AUTO_PSK，服务端先尝试 SAE，失败后降级 PSK
     *
     * 调用注意：
     * - request.ssid 必填，且不需要调用方额外添加双引号。
     * - PSK/SAE 密码长度需为 8-63 位，或 64 位十六进制 PSK。
     * - Enterprise 场景需要传 request.enterprise；建议配置 CA 证书和 domainSuffixMatch。
     * - callback 可为空；若不为空，会收到阶段回调和最终结果。
     * - 操作由 system-server 异步执行，方法返回不代表连接已经完成。
     *
     * @param request Wi-Fi 连接参数。
     * @param callback 操作进度和最终结果回调。
     * 该操作异步执行，结果通过 callback 返回。
     */
    void connectWifi(in WifiConnectRequest request, IWifiOperationCallback callback);

    /**
     * 移除指定 Wi-Fi 配置。
     *
     * 匹配优先级：
     * - request.networkId >= 0 时，优先按 networkId 精确移除。
     * - 否则按 ssid/bssid 匹配；同名 AP 建议同时传 bssid。
     * - 如果目标网络当前正在连接，disconnectIfCurrent=true 时会先断开再移除配置。
     *
     * @param request Wi-Fi 移除参数，networkId、ssid、bssid 至少传一个。
     * @param callback 操作进度和最终结果回调。
     * 该操作异步执行，结果通过 callback 返回。
     */
    void removeWifi(in WifiRemoveRequest request, IWifiOperationCallback callback);

    /**
     * 获取已连接过的 Wi-Fi 列表。
     *
     * 返回内容来自系统已保存 Wi-Fi 配置，并合并当前 Wi-Fi 连接信息。
     * 列表元素通过 WifiNetworkInfo.status 区分：
     * - STATUS_CURRENT: 当前已连接 Wi-Fi
     * - STATUS_CONNECTING: 当前正在连接 Wi-Fi
     * - STATUS_HISTORY: 历史连接/已保存配置，当前未连接
     *
     * 注意：
     * - 历史记录通常只有 ssid/networkId/securityType/hiddenSsid，bssid/rssi/ip 等实时字段可能为空或默认值。
     * - 如果系统限制 configuredNetworks 可见性，列表可能只包含当前连接信息。
     *
     * @return Wi-Fi 连接记录列表；无记录时返回空列表。
     */
    List<WifiNetworkInfo> getConnectedWifiList();

    /**
     * 获取当前正在连接或已经连接的 Wi-Fi 信息。
     *
     * 当 Wi-Fi 当前已连接时返回 STATUS_CURRENT；
     * 当系统正在连接目标 Wi-Fi 但尚未完成联网时返回 STATUS_CONNECTING；
     * 当当前没有 Wi-Fi 连接/连接中状态时返回 null。
     *
     * @return 当前 Wi-Fi 信息，未连接时返回 null。
     */
    WifiNetworkInfo getCurrentWifiInfo();

}
```


## V2.1.9-E(2026-5-25)

- **手机端 SDK 版本**：`com.rokid.security:phone.sdk:2.1.9-E`
- **眼镜端 SDK 版本**：`com.rokid.security:glass3.open.sdk:2.1.9-E` 
- **推荐 OTA 版本**：`1.17.e002-20260509-150201` 及以上

### 眼镜端接口变更

#### 1. 事件拦截接口

###### 1.1 `GlassSdk.setPrimaryEventListener()`

**位置**: `GlassSdk.kt`

```kotlin
@JvmStatic
fun setPrimaryEventListener(listener: DeviceEventListener?)
```

**说明**:

- 设置主设备事件监听器
- SDK 内部持有唯一 DeviceEventListener 槽位
- 通过此方法注册的监听器会收到除 Leqi 拦截之外的所有事件

**使用场景**: 需要监听设备事件（如按键、电量等）但不想被乐奇拦截器干扰

###### 1.2 `GlassSdk.registerLeqiInterceptor()`

**位置**: `GlassSdk.kt`

```kotlin
@JvmStatic
fun registerLeqiInterceptor(context: Context, interceptor: LeqiInterceptor)
```

**说明**:

- 注册乐奇语音指令拦截器
- 应用在前台时，拦截器生效；退到后台后自动失效
- 建议在 Activity.onCreate 中调用，onDestroy 中调用 unregisterLeqiInterceptor()

**参数**:

- `context`: Android Context，用于生命周期管理
- `interceptor`: 拦截器实现

**使用示例**:

```kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        GlassSdk.registerLeqiInterceptor(this, object : LeqiInterceptor {
            override fun onLeqiInstruction(extra: String?): Boolean {
                // 返回 true 表示拦截，false 表示不拦截
                return handleCustomInstruction(extra)
            }
        })
    }
    
    override fun onDestroy() {
        GlassSdk.unregisterLeqiInterceptor()
        super.onDestroy()
    }
}
```

###### 1.3 `GlassSdk.unregisterLeqiInterceptor()`

**位置**: `GlassSdk.kt`

```kotlin
@JvmStatic
fun unregisterLeqiInterceptor()
```

**说明**:

- 注销乐奇语音指令拦截器
- 通常在 Activity.onDestroy 中调用

###### 1.4 `LeqiInterceptor` 接口

**位置**: `LeqiInterceptor.kt` (新增文件)

```kotlin
interface LeqiInterceptor {
    /**
     * 乐奇语音指令回调
     * @param extra 额外参数（JSON 格式）
     * @return true 表示拦截该指令，false 表示放行
     */
    fun onLeqiInstruction(extra: String?): Boolean
}
```

**说明**:

- 定义乐奇语音指令拦截器接口
- 应用可实现此接口自定义语音指令处理逻辑

#### 2. CameraShareHelper 配置切换相关

###### 2.1 `CameraShareHelper.restartSurfaceWithConfig()`

**位置**: `CameraShareHelper.kt`

```kotlin
fun restartSurfaceWithConfig(config: CameraShareConfig, callback: SurfaceCallback)
```

**说明**:

- **重要**: 在同一 callbackId 下切换配置，避免新建 Helper 导致 system-server 残留多个 Surface session
- 内部先调用 `releaseSurface()` 再调用 `initSurfaceWithConfig()`
- **必须在 GL 线程调用**

**参数**:

- `config`: 新的相机配置（分辨率、帧率、防抖、zoom）
- `callback`: 回调接口

**使用场景**: 

- 动态切换相机分辨率或帧率
- 避免创建多个 Helper 实例导致内存泄漏

**使用示例**:

```kotlin
// 初始配置
val config1 = CameraShareConfig(
    previewWidth = 1280,
    previewHeight = 720,
    previewTargetFps = 15,
    zoomLevel = 1
)
helper.initSurfaceWithConfig(config1, callback)

// 切换到新配置（复用同一个 helper）
val config2 = CameraShareConfig(
    previewWidth = 1920,
    previewHeight = 1080,
    previewTargetFps = 24,
    zoomLevel = 2
)
helper.restartSurfaceWithConfig(config2, callback)
```

**注意事项**:

- ⚠️ 必须在 GL 线程调用（与 initSurfaceWithConfig 相同线程）
- ⚠️ 不要创建多个 CameraShareHelper 实例来切换配置

###### 2.2 `CameraShareHelper.restartNv21ExportWithConfig()`

**位置**: `CameraShareHelper.kt`

```kotlin
fun restartNv21ExportWithConfig(enableMix: Boolean, config: CameraShareConfig, callback: Nv21Callback)
```

**说明**:

- 在同一 callbackId 下切换 NV21 导出配置，避免多 Helper 残留 session
- 内部先调用 `releaseNv21Export()` 再调用 `initNv21ExportWithConfig()`

**参数**:

- `enableMix`: 是否启用 Mix 模式（camera + screen 叠加）
- `config`: 新的相机配置
- `callback`: NV21 回调接口

**使用场景**: 

- 动态切换 NV21 导出的分辨率或帧率
- 避免创建多个 Helper 实例导致内存泄漏

#### 3. 状态查询 API

###### 3.1 `CameraShareHelper.isSurfaceActive()`

**位置**: `CameraShareHelper.kt`

```kotlin
fun isSurfaceActive(): Boolean
```

**说明**:

- 检查 Surface 共享是否处于活跃状态
- 返回 true 表示 Surface 已初始化且未释放

**使用场景**: 

- 在切换配置前检查当前状态
- 避免重复初始化或释放

###### 3.2 `CameraShareHelper.isNv21Active()`

**位置**: `CameraShareHelper.kt`

```kotlin
fun isNv21Active(): Boolean
```

**说明**:

- 检查 NV21 导出是否处于活跃状态
- 返回 true 表示 NV21 导出已初始化且未释放

**使用场景**: 

- 在切换配置前检查当前状态
- 资源清理时的状态判断

#### 4. 重点说明

###### 1. 内存泄漏修复相关 API

**背景**: 之前版本存在 Surface Session 重复创建导致内存泄漏的问题

**解决方案**: 

- 新增 `restartSurfaceWithConfig()` 和 `restartNv21ExportWithConfig()`
- 这两个方法会在同一 callbackId 下复用 session，避免创建多个 session

**迁移建议**:

```kotlin
// ❌ 旧方式（会导致内存泄漏）
var helper = CameraShareHelper()
helper.initSurfaceWithConfig(config1, callback)
// ... 需要切换配置时
helper.releaseSurface()
helper = CameraShareHelper()  // 创建新实例 ← 问题所在
helper.initSurfaceWithConfig(config2, callback)

// ✅ 新方式（推荐）
val helper = CameraShareHelper()
helper.initSurfaceWithConfig(config1, callback)
// ... 需要切换配置时
helper.restartSurfaceWithConfig(config2, callback)  // 复用 helper
```

###### 2. 乐奇拦截器使用注意事项

**生命周期管理**:

- 拦截器只在应用前台时生效
- 使用 LifecycleOwner 自动管理，无需手动启停
- 但建议在 onDestroy 中调用 `unregisterLeqiInterceptor()` 确保清理

**拦截逻辑**:

```kotlin
GlassSdk.registerLeqiInterceptor(this, object : LeqiInterceptor {
    override fun onLeqiInstruction(extra: String?): Boolean {
        return try {
            val json = JSONObject(extra ?: "{}")
            val command = json.optString("command")
            
            when (command) {
                "take_photo" -> {
                    // 自定义拍照逻辑
                    takePhoto()
                    true  // 拦截，不让乐奇处理
                }
                else -> false  // 不拦截，交给乐奇处理
            }
        } catch (e: Exception) {
            false  // 解析失败，不拦截
        }
    }
})
```

###### 3. 配置变更回调的使用

虽然本次没有新增回调方法，但现有的回调现在会被正确触发：

**SurfaceCallback**:

```kotlin
object : CameraShareHelper.SurfaceCallback {
    override fun onSurfaceShareConfigChanged(
        width: Int, 
        height: Int, 
        appliedPreviewFps: Int, 
        videoStabilizationEnabled: Boolean
    ) {
        // 当配置变更时会自动回调
        Log.d(TAG, "Config changed: ${width}x${height}@${appliedPreviewFps}fps")
    }
    
    override fun onZoomLevelChanged(zoomLevel: Int) {
        // Zoom 等级变更时回调
        Log.d(TAG, "Zoom changed: $zoomLevel")
    }
}
```

**Nv21Callback**:

```kotlin
object : CameraShareHelper.Nv21Callback {
    override fun onNv21ExportResolutionChanged(
        width: Int, 
        height: Int, 
        appliedPreviewFps: Int
    ) {
        // NV21 导出分辨率变更时回调
    }
    
    override fun onNv21ExportRuntimeParamsChanged(
        appliedPreviewFps: Int, 
        videoStabilizationEnabled: Boolean
    ) {
        // 运行时参数变更时回调
    }
    
    override fun onZoomLevelChanged(zoomLevel: Int) {
        // Zoom 等级变更时回调
    }
}
```

## V2.1.8-E(2026-5-9)

- **手机端 SDK 版本**：`com.rokid.security:phone.sdk:2.1.8-E`
- **眼镜端 SDK 版本**：`com.rokid.security:glass3.open.sdk:2.1.8-E-20260518.101638-5` 
- **推荐 OTA 版本**：`1.17.e002-20260509-150201` 及以上

### 手机端接口变更

#### WiFi P2P H264 解码控制（核心改动）

手机端接收 H264 时，默认仍自动转 NV21，外部调用方可通过 SDK 关闭
在 `IWifiP2PClientOperate` 接口中新增：

```kotlin
// 设置是否自动将 H264 解码为 NV21（默认开启）
fun setAutoDecodeH264ToNv21(enable: Boolean)

// 查询当前是否开启 H264 自动解码为 NV21
fun isAutoDecodeH264ToNv21Enabled(): Boolean
```

**功能说明**:

- **默认行为**: 保持向后兼容，默认开启 H264 自动解码为 NV21
- **性能优化**: 当应用只需要 H264 数据时，可以关闭自动解码，节省 CPU 和内存
- **灵活控制**: 外部调用方可以根据实际需求动态开关

**使用场景**:

```kotlin
// 场景1: 只需要 H264 流进行网络传输，关闭自动解码
wifiP2PClientService?.setAutoDecodeH264ToNv21(false)

// 场景2: 需要 NV21 数据进行 AI 识别，开启自动解码（默认）
wifiP2PClientService?.setAutoDecodeH264ToNv21(true)
```

#### GlassVideoStreamParam 数据类

```kotlin
class GlassVideoStreamParam{
    var fps = 20
    var bitrate = 5184000
    /** 为 true 时本次视频流使用 AR 叠加编码（与眼镜端 USE_AR_MIX 一致），默认关闭 */
    var isARMixEnabled = false
    /** 期望的视频流宽度，-1 表示使用眼镜端默认分辨率 */
    var width = -1
    /** 期望的视频流高度，-1 表示使用眼镜端默认分辨率 */
    var height = -1
}
```

#### IDevice 接口扩展

在 `requestVideoStream` 方法中增加参数：

```kotlin
fun requestVideoStream(
    tag: String,
    videoStreamParam: GlassVideoStreamParam = GlassVideoStreamParam(),
    callback: (isSuccess: Boolean) -> Unit
)
```

**功能说明**:

- 支持手机端请求眼镜端摄像头数据时指定自定义分辨率
- 与 open-sdk 的 CameraShareConfig 配合使用，实现端到端的自定义分辨率控制
- 默认值为 0 时表示使用系统默认配置，保持向后兼容

**使用示例**:

```kotlin
// 请求 1280x720@30fps 的视频流
val extra = GlassVideoStreamParam(width = 1280, height = 720, fps = 30)
deviceService?.requestVideoStream("my_tag", extra) { success ->
    if (success) {
        // 开始接收视频流
    }
}
```

### 眼镜端接口变更

#### GlassSdk.kt 增强

- **新增兼容方法**：为离线指令服务添加了三个兼容性方法，确保新旧版本 SDK 之间的向后兼容：
  - `setOfflineCmdLanguage(language: String): Boolean` - 设置离线指令语言
  - `setOfflineCmdWords(language: String, voiceActions: List<VoiceAction>): Boolean` - 按语言覆盖离线指令词
  - `clearOfflineCmdWords(language: String): Boolean` - 清空某语言下的离线指令词

这些方法通过 try-catch 机制处理新旧 system-server 版本的兼容性问题。

#### CameraShareHelper.kt 新功能

- **配置化相机共享**：新增支持配置的相机共享方法
  - `initSurfaceWithConfig(config: CameraShareConfig, callback: SurfaceCallback)` - 可配置的 Surface 共享初始化
  - `initNv21ExportWithConfig(enableMix: Boolean, config: CameraShareConfig, callback: Nv21Callback)` - 可配置的 NV21 导出

- **动态配置变更通知**：新增回调方法支持运行时配置变更
  - `onSurfaceShareConfigChanged(width: Int, height: Int, appliedPreviewFps: Int, videoStabilizationEnabled: Boolean)` - Surface 共享配置变更通知
  - `onNv21ExportResolutionChanged(width: Int, height: Int, appliedPreviewFps: Int)` - NV21 导出分辨率变更通知
  - `onNv21ExportRuntimeParamsChanged(appliedPreviewFps: Int, videoStabilizationEnabled: Boolean)` - NV21 导出运行时参数变更

- **Zoom 功能支持**：新增 Zoom 等级变更回调
  - `onZoomLevelChanged(zoomLevel: Int)` - Zoom 等级变更通知（在 SurfaceCallback 和 Nv21Callback 中均支持）

- **获取支持的预览分辨率**：新增方法获取 Camera API 支持的预览分辨率列表
  - `getSupportedPreviewSizes(): List<Triple<Int, Int, Boolean>>` - 获取支持的预览分辨率(camera sensor orientation 是旋转270°的，因此和initSurfaceWithConfig以及initNv21ExportWithConfig中传入的分辨率宽高是对调的 )

#### IDeviceService.aidl 新增接口

```javascript
interface IDeviceService {
    
      /**
       * 设置拍摄灯光
       * @param timestamp 时间戳
        */
     void setCameraLedEnable(boolean enable);


      /**
       * 设置网络访问方式 0 手机访问； 1 glass 访问
       * @param timestamp 时间戳
        */
     void setNetworkType(int type);



     int getNetworkType();


    /**
     * 配置应用可见性与第三方应用显示顺序
     * 用于批量配置系统内置应用和第三方应用的显示/隐藏状态。
     *
     * @param appConfig 应用配置信息，包含：
     *                  - appList: 需要隐藏的系统内置应用列表
     *                  - thirdApps: 第三方应用列表,将显示在最前面
     * @param callback 设置结果回调，success=true 表示设置成功，false 表示设置失败
     *
     */
     void configureAppVisibility(in  GlassAppConfig appConfig, IAppVisibilityListener callback);

    /**
     * 设置时间同步服务器地址
     * 支持HTTP/HTTPS协议和NTP协议，根据URL自动识别协议类型
     *
     * @param serverUrl 时间服务器地址，支持以下格式：
     *                  - HTTP/HTTPS协议: https://time.example.com 或 http://time.example.com
     *                  - NTP协议: ntp.example.com 或 192.168.1.100
     *                  - null或空字符串: 使用默认的NTP协议和公共NTP服务器
     *
     * @note 如果URL以http://或https://开头，则使用HTTP协议获取时间
     *       否则使用NTP协议（标准UDP协议，端口123）
     */
    void setTimeServer(String serverUrl);

    /**
     * 获取当前配置的时间服务器地址
     *
     * @return 当前配置的时间服务器地址，如果未配置则返回null
     */
    String getTimeServer();
}
```

#### IMediaServer.aidl  新增接口

```java
interface IMediaServer {
  /**
     * 开始跨进程Surface共享（仅渲染camera画面到Surface，不含NV21数据）
     * @param surface 客户端创建的Surface，用于接收相机画面
     * @param callback 生命周期回调
     */
    void startCameraShare(in Surface surface, ICameraSurfaceCallback callback);

    /**
     * 停止跨进程Surface共享
     * @param callback 之前注册的回调
     */
    void stopCameraShare(ICameraSurfaceCallback callback);

    /**
     * 开始NV21数据导出（通过SharedMemory双缓冲共享帧数据）
     * @param callback 回调接口，通过onNv21FrameAvailable接收帧数据
     * @param enableMix 是否启用叠加模式（camera + screen叠加），
     *                  false: 导出纯camera的NV21数据
     *                  true:  导出camera叠加屏幕内容后的NV21数据
     */
    void startCameraNv21Export(ICameraShareCallback callback, boolean enableMix);

    /**
     * 停止NV21数据导出
     * @param callback 之前注册的回调
     */
    void stopCameraNv21Export(ICameraShareCallback callback);

    /**
     * 关闭相机
     * @param callback 回调接口，true关闭相机成功，false相机未关闭
     */
    void closeCamera(ICameraCloseCallback callback);

    /**
     * 开始NV21导出（可指定预览分辨率、目标帧率、防抖；与旧接口二进制兼容由客户端选择调用）。
     * 多会话共享同一物理相机时，后发起的配置会抢占管线；已有会话通过
     * {@link com.rokid.security.system.server.media.callback.ICameraShareCallback#onNv21ExportResolutionChanged}
     * 或 {@link com.rokid.security.system.server.media.callback.ICameraShareCallback#onNv21ExportRuntimeParamsChanged} 同步。
     */
    void startCameraNv21ExportWithConfig(ICameraShareCallback callback, boolean enableMix, in CameraShareConfig config);

    /**
     * 开始跨进程Surface共享（可配置版本）
     * @param surface 客户端创建的Surface，用于接收相机画面
     * @param callback 生命周期回调
     * @param enableMix 是否启用AR Mix模式（camera + screen叠加）
     * @param config 配置项，与 CameraShareConfig 一致
     */
    void startCameraShareWithConfig(in Surface surface, ICameraSurfaceCallback callback, boolean enableMix, in CameraShareConfig config);

    /**
     * 获取 Camera API 支持的预览分辨率列表
     * @return 支持的分辨率列表（Camera 原始分辨率，横屏方向）
     */
    List<CameraSize> getSupportedPreviewSizes();
}
```

#### ICameraSurfaceCallback.aidl 新增接口

```java
/**
 * 跨进程Surface共享的轻量回调（仅生命周期，不含NV21数据）
 */
interface ICameraSurfaceCallback {

    /**
     * 相机已打开
     * @param width 相机宽度
     * @param height 相机高度
     */
    void onCameraOpened(int width, int height);

    /**
     * 相机已关闭
     */
    void onCameraClosed();

    /**
     * 发生错误
     * @param code 错误码
     * @param msg 错误信息
     */
    void onError(int code, String msg);

    /**
     * 回调唯一标识
     */
    String getCallbackId();

    /**
     * Surface共享配置已变更（分辨率、帧率、防抖等）
     * 当通过 startCameraShareWithConfig 更新配置时触发，避免重启相机
     * @param width 新的宽度
     * @param height 新的高度
     * @param appliedPreviewFps 协商后的帧率
     * @param videoStabilizationEnabled 防抖是否启用
     */
    void onSurfaceShareConfigChanged(int width, int height, int appliedPreviewFps, boolean videoStabilizationEnabled);

    /**
     * Zoom 等级已变更
     * @param zoomLevel 当前的 zoom 等级（整数）
     */
    void onZoomLevelChanged(int zoomLevel);
}

```

#### ICameraShareCallback.aidl 新增接口

```java
interface ICameraShareCallback {

    /**
     * 相机已打开，提供分辨率和NV21双缓冲SharedMemory
     * @param width 相机宽度
     * @param height 相机高度
     * @param nv21Buffer0 NV21双缓冲区0
     * @param nv21Buffer1 NV21双缓冲区1
     */
    void onCameraOpened(int width, int height, in SharedMemory nv21Buffer0, in SharedMemory nv21Buffer1);

    /**
     * 新的NV21帧已写入指定的缓冲区
     * @param bufferIndex 缓冲区索引 (0 或 1)
     * @param width 帧宽度
     * @param height 帧高度
     * @param timestamp 帧时间戳(毫秒)
     */
    void onNv21FrameAvailable(int bufferIndex, int width, int height, long timestamp);

    /**
     * 相机已关闭
     */
    void onCameraClosed();

    /**
     * 发生错误
     * @param code 错误码
     * @param msg 错误信息
     */
    void onError(int code, String msg);

    /**
     * 回调唯一标识
     */
    String getCallbackId();
    /**
     * 新的NV21帧已写入指定的缓冲区（异步版本，性能更优）
     * @param bufferIndex 缓冲区索引 (0 或 1)
     * @param width 帧宽度
     * @param height 帧高度
     * @param timestamp 帧时间戳(毫秒)
     */
    oneway void onNv21FrameAvailableAsync(int bufferIndex, int width, int height, long timestamp);
    /**
     * NV21 输出尺寸或 SharedMemory 缓冲区已切换（多应用抢占管线、或会话主动变更配置）。
     * 客户端应关闭旧映射，使用新的 SharedMemory 重新 map，其后的 {@link #onNv21FrameAvailable} 以本回调的宽高为准。
     *
     * @param width NV21 逻辑宽度
     * @param height NV21 逻辑高度
     * @param nv21Buffer0 新双缓冲 0
     * @param nv21Buffer1 新双缓冲 1
     * @param appliedPreviewFps 经设备协商后的预览 AE 目标帧率区间上界（代表性值）
     */
    void onNv21ExportResolutionChanged(int width, int height, in SharedMemory nv21Buffer0, in SharedMemory nv21Buffer1, int appliedPreviewFps);

    /**
     * 输出尺寸未变，但预览帧率或防抖开关因管线重配置发生变化。
     *
     * @param appliedPreviewFps 协商后的预览帧率代表性值（AE 区间上界）
     * @param videoStabilizationEnabled 当前是否请求开启视频防抖
     */
    void onNv21ExportRuntimeParamsChanged(int appliedPreviewFps, boolean videoStabilizationEnabled);

    /**
     * Zoom 等级已变更
     * @param zoomLevel 当前的 zoom 等级（整数）
     */
    void onZoomLevelChanged(int zoomLevel);
}
```

## V2.1.7-E(2026-4-7)

- **手机端 SDK 版本**：`com.rokid.security:phone.sdk:2.1.7-E`
- **眼镜端 SDK 版本**：`com.rokid.security:glass3.open.sdk:2.1.7-E`
- **推荐 OTA 版本**：`1.11.e003-20260331-150201` 及以上

### 手机端接口变更

**移除了 RTC 模块：**删除了 `EngineParam.kt` 和 `MobileEngineServerImpl.kt`（分别在 `sdk-base` 和 `sdk-server` 目录下）中所有与 RTC 相关的引擎参数和初始化配置。也就是说，SDK 不再包含实时音视频通话相关功能。

#### 手机端接口新增AR录制

在 `IWifiP2PClientOperate` 接口中新增了 `getIARMixControl()` 方法，返回 `IARMixControl` 对象。集成方可通过 `PSecuritySDK.getWifiP2PClientService()` 获取 P2P 能力对象，再调用 `getIARMixControl()` 获取 AR 录制控制接口，从而在手机端对 AR 录制进行设置（如开启/关闭、参数调整等）。

> 使用示例 1

```kotlin
private val mIARMixControl by lazy {
    PSecuritySDK.getWifiP2PClientService()?.getIARMixControl()
}

// 设置 AR 混录开关
binding.switchArMix.setOnCheckedChangeListener { _, isChecked ->
    if (GlobalData.btConnectState.value) {
        mIARMixControl?.setARMixEnabled(isChecked) { success ->
            if (!success) {
                L.i(TAG, "setARMixEnabled: Fail")
                getARMixState()
            }
        }
    } else {
        Toast.makeText(this, "请先完成设备蓝牙连接", Toast.LENGTH_LONG).show()
    }
}

// 获取 AR 混录状态
private fun getARMixState() {
    if (GlobalData.btConnectState.value) {
        val activityRef = WeakReference(this)
        mIARMixControl?.getARMixState(
            action = { isOpen ->
                activityRef.get()?.runOnUiThread {
                    L.i(TAG, "getARMixState: $isOpen")
                }
            },
            onFail = { errMsg, code ->
                activityRef.get()?.runOnUiThread {
                    L.i(TAG, "getARMixState: onFail --- errMsg=$errMsg")
                }
            }
        )
    }
}
```

> 使用示例 2

```kotlin
class GlassVideoStreamParam {
    var fps = 20                     // 帧率
    var bitrate = 5184000            // 视频比特率
    /**
     * 是否启用 AR 叠加模式（相机画面 + 屏幕画面）
     * 为 true 时，通过 P2P 获取的视频流为相机叠加屏幕内容后的实时画面
     * 默认值为 false
     */
    var isARMixEnabled = false
}

// 请求视频流
val param = GlassVideoStreamParam().apply {
    this.fps = fps
    this.bitrate = bitrate
    this.isARMixEnabled = true
}
PSecuritySDK.getAbsDeviceInfoService()?.requestVideoStream(VIDEO_TAG, videoStreamParam = param) {}
```

### 眼镜端接口变更

#### IMediaServer新增如下四个接口

```kotlin
public interface IMediaServer extends IInterface {
	/**
     * 开始跨进程Surface共享（仅渲染camera画面到Surface，不含NV21数据）
     * @param surface 客户端创建的Surface，用于接收相机画面
     * @param callback 生命周期回调
     */
    void startCameraShare(in Surface surface, ICameraSurfaceCallback callback);

    /**
     * 停止跨进程Surface共享
     * @param callback 之前注册的回调
     */
    void stopCameraShare(ICameraSurfaceCallback callback);

    /**
     * 开始NV21数据导出（通过SharedMemory双缓冲共享帧数据）
     * @param callback 回调接口，通过onNv21FrameAvailable接收帧数据
     * @param enableMix 是否启用叠加模式（camera + screen叠加），
     *                  false: 导出纯camera的NV21数据
     *                  true:  导出camera叠加屏幕内容后的NV21数据
     */
    void startCameraNv21Export(ICameraShareCallback callback, boolean enableMix);

    /**
     * 停止NV21数据导出
     * @param callback 之前注册的回调
     */
    void stopCameraNv21Export(ICameraShareCallback callback);
}
```

>  使用方式可参考 Demo 示例中的 `SendMessageActivity` 类。

#### 增加 `IMediaServer` 接口参数

在 `startRecord` 方法中新增 `isArRMixEnabled` 参数（通过在 `RecordConfig` 中增加该字段实现），用于控制是否开启 AR 混录功能。

```kotlin
@Keep
@Parcelize
data class RecordConfig(
    val path: String,
    val min: Int,
    val enableAudio: Boolean,
    val width: Int = 1280,
    val height: Int = 720,
    val fps: Int = 20,
    /**
     * 本次录制是否启用 AR 叠加（相机画面 + 屏幕画面）。
     * 默认值为 false，旧客户端不传该字段时保持 false。
     * 实际生效时，眼镜端会与手机端的「AR 录屏」持久开关合并处理。
     */
    val isArRMixEnabled: Boolean = false,
) : Parcelable {
    override fun toString(): String {
        return "RecordConfig(path='$path', min=$min, enableAudio=$enableAudio, width=$width, height=$height, fps=$fps, isArRMixEnabled=$isArRMixEnabled)"
    }
}
```

>  使用示例可参考 Demo 示例中的 `SdkMediaActivity` 类。

## V2.1.5-E(2026-2-5)

- **手机端 SDK 版本**：`com.rokid.security:phone.sdk:2.1.5-E`
- **眼镜端 SDK 版本**：`com.rokid.security:glass3.open.sdk:2.1.5-E`
- **推荐 OTA 版本**：`1.11.e003-20260131-150201` 及以上

### 手机端接口变更

#### IBTRingClientListener 接口新增方法：

```kotlin
interface IBTRingClientListener {

    /***
     * 蓝牙连接状态回调
     * @param extra 回调的参数
     */
    fun onConnect(extra: RingExtra)

}
```

### 眼镜端接口变更

#### IDeviceService 接口新增方法：

```kotlin
void setSystemTime(long var1) throws RemoteException;

// 使用示例，在sdk初始化成功之后，设置眼镜端系统时间
GlassSdk.getGlassDeviceService()?.setSystemTime(1770014682490)
```

## V2.1.04-E(2026-1-16)

- **手机端 SDK 版本**：`com.rokid.security:phone.sdk:2.1.04-E`
- **眼镜端 SDK 版本**：`com.rokid.security:glass3.open.sdk:2.1.04-E`
- **推荐 OTA 版本**：`1.11.e002-20260116-150201` 及以上

### 新增功能

* 国标/RTC支持横屏1080p显示
* 新增扫一扫链接 WiFi应用，[WiFi 二维码生成工具](https://glassar.rokid.com/userguide/rokid-glass/wifi)
* 眼镜端新增蓝牙、app连接、低电量提示音
* 手机端新增默认开启应用配置，选择后，眼镜开机默认进入该应用
* 修复移除单个离线语音指令

### 手机端接口变更

#### IDevice 接口新增方法：

```kotlin
package com.rokid.security.phone.sdk.api.device

import com.rokid.security.sdk.base.common.GlassVideoStreamParam
import com.rokid.security.sdk.base.common.out.GlassDeviceInfo


interface IDevice {

	/**
     * 请求拉取眼镜端视频流
     * @param tag 请求视频流标识, 用于取消请求视频流
     * @param videoStreamParam  修改视频帧率
     * @param callback true请求视频流成功,false请求视频流失败
     */
    fun requestVideoStream(tag: String,videoStreamParam: GlassVideoStreamParam = GlassVideoStreamParam(),callback: (isSuccess: Boolean) -> Unit)

    /**
     * 停止拉取眼镜端视频流
     * @param tag 停止视频流标识, 用于取消停止视频流
     * @param callback true停止视频流成功,false停止视频流失败
     */
    fun stopVideoStream(tag: String,callback: (isSuccess: Boolean) -> Unit)
    /**
     * 请求拉取眼镜端音频流
     * @param tag 音频流标识, 用于取消请求音频流
     * @param callback true请求音频流成功,false请求音频流失败
     */
    fun requestAudioStream(tag: String,callback: (isSuccess: Boolean) -> Unit)
    /**
     * 停止拉取眼镜端音频流
     * @param tag 音频流标识, 用于取消停止音频流
     * @param callback true停止音频流成功,false停止音频流失败
     */
    fun stopAudioStream(tag: String,callback: (isSuccess: Boolean) -> Unit)
}
```

#### IFileOperate 接口新增方法：

```kotlin
package com.rokid.security.phone.sdk.api.msg.api

import com.rokid.security.phone.sdk.api.msg.listener.FileReceiveListener
import com.rokid.security.phone.sdk.api.msg.listener.FileReceiveV2Listener
import com.rokid.security.phone.sdk.api.msg.listener.IMessageListener
import com.rokid.security.phone.sdk.base.data.callback.IResult
import java.io.File

/**
 * Created by wjm on 2025/5/9
 */
interface IFileOperate {
   
    /**
     *  添加本端文件接收的监听器
     *  @param listener
     */
    @Deprecated(
        message = "V2.1.4版本废弃,addFileReceiveV2Listener方法",
        replaceWith = ReplaceWith("addFileReceiveV2Listener(listener)"),
        level = DeprecationLevel.WARNING
    )
    fun addFileReceiveListener(listener: FileReceiveListener)

    /**
     * 移除本端文件接收的监听器
     *  @param listener
     */
    @Deprecated(
        message = "V2.1.4版本废弃,removeFileReceiveV2Listener方法",
        replaceWith = ReplaceWith("removeFileReceiveV2Listener(listener)"),
        level = DeprecationLevel.WARNING
    )
    fun removeFileReceiveListener(listener: FileReceiveListener)

    /**
     *  添加本端文件接收的监听器
     *  @param listener
     */
    fun addFileReceiveV2Listener(listener: FileReceiveV2Listener)

    /**
     * 移除本端文件接收的监听器
     *  @param listener
     */
    fun removeFileReceiveV2Listener(listener: FileReceiveV2Listener)

}
```

#### IBluetoothRing 接口修改方法：

```kotlin
package com.rokid.security.phone.sdk.api.bluetooth.ring.api

import android.bluetooth.BluetoothDevice
import com.rokid.security.phone.sdk.api.bluetooth.classic.listener.IClassicBTClientListener

/**
 * Author: zhangshengwei
 * Date: 2025/6/24
 */
interface IBluetoothRing {

    /**
     * 开启经典蓝牙扫描
     * @param timeoutMillis 扫描超时时间设置
     * */
    fun startScan(
        deviceNameFilter:List<String> = listOf("D01"),
        timeoutMillis: Long = 20000L,
    ){}
}
```

### 眼镜端接口变更

#### ICommonInfoListener 接口新增方法：

```kotlin
/**
 * Created by wjm on 2025/9/3
 */
interface ICommonInfoListener {

    /**
     * 眼镜端的配置信息
     */
    fun onConfig(config: String)
}
```

####  PreviewResolution 录制时视频文件对应的分辨率

```kotlin
/**
 * 录制时视频文件对应的分辨率
 */
object PreviewResolution{
    val ResolutionInfo_720P  = ResolutionInfo("720P", 1280, 720) //预览
    val ResolutionInfo_1080P  = ResolutionInfo("1080P", 1080,1920) //预览

    // 新增横屏摄像头预览
    val ResolutionInfo_1080P_Land  = ResolutionInfo("1080P_Land", 1920, 1080) //预览
}
```

## v2.1.2 (2025-12-15)

- **手机端 SDK 版本**：`com.rokid.security:phone.sdk:2.1.2-E`
- **眼镜端 SDK 版本**：`com.rokid.security:glass3.open.sdk:2.1.2-E`
- **推荐 OTA 版本**：`1.10.e001-20251215-150202` 及以上

### 新增功能

- 新增离线 TTS 语音小模型支持
- 新增按键监听功能
- 系统人脸识别算法升级为 NPU 加速，识别速度更快
- 默认开发者模式

### 手机端接口变更

#### IWifiP2PClientOperate 接口新增方法：

```kotlin
interface IWifiP2PClientOperate {
    /**
     * 获取 P2P 群组信息
     * @param action 回调函数，返回 WifiP2pGroup 对象
     */
    fun getGroupInfo(action: (group: WifiP2pGroup?) -> Unit)

    /**
     * 获取 P2P 连接策略管理实例
     * @return IP2PConnectControl 接口实例
     */
    fun getIP2PConnectControl(): IP2PConnectControl
}
```

#### **IAIChat 接口新增方法**：

```kotlin
interface IAIChat {
    /**
     * 移除 AI 聊天监听器
     */
    fun removeAiChatListener()

    /**
     * 获取运行时聊天记录
     * @return 可变的聊天记录列表
     */
    fun getRunTimeChatList(): MutableList<ChatBean>
}
```

### 眼镜端接口变更

#### GlassSdk 新增服务获取方法

```kotlin
object GlassSdk {
    /**
     * 获取图像采集服务（人脸/车牌等）
     * @return ICollectService 实例
     */
    @JvmStatic
    fun getGlassCollectService(): ICollectService? {
        return getService(ServerType.COLLECT, ICollectService::class.java)
    }
    
    /**
     * 获取离线 TTS 管理服务
     * @return IOfflineTtsService 实例
     */
    @JvmStatic
    fun getGlassOfflineTtsService(): IOfflineTtsService? {
        return getService(ServerType.SERVICE_OFFLINE_TTS, IOfflineTtsService::class.java)
    }
}
```

#### **新增 ICollectService 接口**：

```kotlin
/**
 * 图像采集服务接口
 * 提供人脸、车牌等图像的采集、传输和查询功能
 */
interface ICollectService {
    
    /**
     * 启动图像采集
     * @param mode 采集模式
     */
    fun startCollect(mode: Int)
    
    /**
     * 停止图像采集
     */
    fun stopCollect()
    
    /**
     * 设置图像采集监听器
     * @param listener 采集监听器实例
     */
    fun setCollectListener(listener: IGlassCollectListener)
    
    /**
     * 移除图像采集监听器
     * @param listener 要移除的监听器实例
     */
    fun removeCollectListener(listener: IGlassCollectListener)
    
    /**
     * 发送人脸图片到指定目标
     * @param identifier 目标标识符（如设备ID、用户ID等）
     * @param param 采集参数，包含采集配置信息
     * @param callback 发送结果回调
     */
    fun sendFacePicture(identifier: String, param: CollectParam, callback: ICollectCallback)
    
    /**
     * 发送车牌识别图片到指定目标
     * @param identifier 目标标识符
     * @param platNo 车牌号码
     * @param callback 发送结果回调
     */
    fun sendLPRPicture(identifier: String, platNo: String, callback: ICollectCallback)
    
    /**
     * 切换采集模式
     * @param mode 新的采集模式
     */
    fun changeMode(mode: Int)
    
    /**
     * 获取人脸小图
     * @param frameId 帧ID
     * @param trackId 跟踪ID
     * @return 人脸小图Bitmap，获取失败时返回null
     */
    fun getFaceSmallBitmap(frameId: Long, trackId: Long): Bitmap?
    
    /**
     * 获取车牌小图
     * @param plateNo 车牌号码
     * @return 车牌小图Bitmap，获取失败时返回null
     */
    fun getLprSmallBitmap(plateNo: String): Bitmap?
}
```

#### **新增 IOfflineTtsService 接口**：

```kotlin
interface IOfflineTtsService {
    /**
     * 播放指定文本的语音
     * @param ttsMsg 需要播报的文本内容
     */
    fun playTtsMsg(ttsMsg: String)
}
```

#### **IMediaServer 接口新增监听器管理方法**：

```kotlin
interface IMediaServer {
    /**
     * 注册媒体服务状态监听器
     * @param listener 监听器实例
     */
    fun setMediaStateLister(listener: IMediaStateLister)

    /**
     * 注销媒体服务状态监听器
     * @param listener 监听器实例
     */
    fun removeMediaStateLister(listener: IMediaStateLister)
}
```

#### **IOfflineRecServer 接口新增人脸识别结果上报方法**：

```kotlin
interface IOfflineRecServer {
    /**
     * 上报人脸识别结果
     * @param info 人脸识别提交信息
     * @param face 人脸图像 Bitmap
     */
    fun submitRecognizedFaceInfo(info: RecognizeFaceSubmitInfo, face: Bitmap)
}
```
