雷鸟:添加获取是否绑定接口
authorcheng <chengyf@zhihuishixun.com>
Wed, 17 Sep 2025 05:56:39 +0000 (13:56 +0800)
committercheng <chengyf@zhihuishixun.com>
Wed, 17 Sep 2025 05:56:39 +0000 (13:56 +0800)
code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.cpp
code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c

index 690e0c3..5b6b6a9 100644 (file)
@@ -677,6 +677,25 @@ uint32_t ln_audio_player_init(void)
     return ERRCODE_SUCC;
 }
 
+uint32_t ln_audio_player_pause(void)
+{
+    if (g_ln_audio_player == nullptr) {
+        return ERRCODE_SUCC;
+    }
+    (void)AudioStreamPlayerPause(g_ln_audio_player);
+    AudioStreamPlayerFlush(g_ln_audio_player);
+    return ERRCODE_SUCC;
+}
+
+uint32_t ln_audio_player_resume(void)
+{
+    if (g_ln_audio_player == nullptr) {
+        return ERRCODE_SUCC;
+    }
+    (void)AudioStreamPlayerResume(g_ln_audio_player);
+    return ERRCODE_SUCC;
+}
+
 int32_t ln_audio_player_write(const uint8_t* data, uint32_t size)
 {
     if (g_ln_audio_player == nullptr) {
index aa84d52..2da9121 100644 (file)
@@ -150,6 +150,8 @@ int32_t AudioStreamPlayerDestroy(AudioStreamPlayerHandle handle);
 uint32_t ln_audio_player_init(void);
 int32_t ln_audio_player_write(const uint8_t* data, uint32_t size);
 int32_t ln_audio_player_get_stats(AudioStreamStats* stats);
+uint32_t ln_audio_player_pause(void);
+uint32_t ln_audio_player_resume(void);
 
 #ifdef __cplusplus
 }
index 53aa2c4..a69ff83 100644 (file)
@@ -40,6 +40,9 @@ char g_ln_last_package_url[512] = {0};   // 最近一次查询到的 OTA 包 URL
 char g_ln_last_target_version[64] = {0}; // 最近一次查询到的 OTA 包目标版本
 char g_ln_last_package_md5[65] = {0};    // 最近一次查询到的 OTA 包 MD5
 int g_ln_last_package_size = 0;          // 最近一次查询到的 OTA 包大小(字节)
+static int g_ln_audio_download_cancel = 0; /* 0=正常, 1=取消 */
+int g_ln_is_device_bound = -1;           /* 设备绑定状态 */
+char g_ln_device_user_id[64] = {0};      // 设备绑定用户ID
 
 /* 获取设备id: 设备唯一标识 */
 char *uapi_ln_get_device_id(void)
@@ -133,6 +136,32 @@ const char *uapi_ln_get_story_choose_audio_path(void)
 {
     return "/user/leiniao/story_choose_audio.mp3";
 }
+
+void uapi_ln_audio_download_is_cancel(int status)
+{
+    g_ln_audio_download_cancel = status;
+    if(g_ln_audio_download_cancel)
+    {
+        ln_audio_player_pause();
+    }
+    else
+    {
+        ln_audio_player_resume();
+    }
+}
+
+/* 获取是否绑定用户 */
+uint32_t uapi_ln_is_device_bound(void)
+{
+    return g_ln_is_device_bound;
+}
+
+/* 获取设备绑定用户ID */
+const char* uapi_ln_get_user_id(void)
+{
+    return g_ln_device_user_id;
+}
+
 /* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
 HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
 {
@@ -583,9 +612,13 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
         return ERRCODE_FAIL;
     }
 
-    // 接收响应数据
+    // 接收响应数据(支持取消)
     do
     {
+        if (g_ln_audio_download_cancel) {
+            printf("[SSE] download canceled by user\n");
+            break;
+        }
         HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
         if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
         {
@@ -848,6 +881,11 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ
 
     while (fgets(line, sizeof(line), fp))
     {
+        if (g_ln_audio_download_cancel) {
+            printf("[SSE] download canceled by user\n");
+            break;
+        }
+
         line_count++;
         
         // 去除行尾的换行符
@@ -965,8 +1003,19 @@ static void ln_http_server_send_task(void const *arg)
         }
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_GET_USER_ID)
         {
-            printf("LN_HTTP_SERVICE_CMD_GET_USER_ID:获取设备绑定用户ID\n");
-            ln_device_get_user_id_http(uapi_ln_get_device_id(), uapi_ln_get_device_token());
+            LnDeviceBindStatus st = LN_DEVICE_BIND_STATUS_ERROR;
+            (void)uapi_ln_device_get_user_id(&st, g_ln_device_user_id, sizeof(g_ln_device_user_id));
+            /* 将内部状态映射为上层更直观的 1/0/ERRCODE_FAIL */
+            if (st == LN_DEVICE_BIND_STATUS_BOUND) {
+                g_ln_is_device_bound = 1;
+            } else if (st == LN_DEVICE_BIND_STATUS_UNBOUND) {
+                g_ln_is_device_bound = 0;
+            } else {
+                g_ln_is_device_bound = ERRCODE_FAIL;
+                g_ln_device_user_id[0] = '\0';
+            }
+            printf("LN_HTTP_SERVICE_CMD_GET_USER_ID:获取设备绑定用户ID: %s\n", uapi_ln_get_user_id());
+            printf("LN_HTTP_SERVICE_CMD_GET_USER_ID:获取设备绑定用户ID状态: %d\n", uapi_ln_is_device_bound());
         }
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_OTA_GET_TASK)
         {
@@ -1028,30 +1077,32 @@ static void ln_http_server_send_task(void const *arg)
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PAUSE)
         {
             printf("LN_HTTP_SERVICE_CMD_AUDIO_PAUSE:暂停播放\n");
-            #ifdef BOARD_N9
-            if (ln_story_player_simple_pause())
-            {
-                printf("Playback paused successfully\n");
-            }
-            else
-            {
-                printf("Failed to pause playback\n");
-            }
-            #endif
+            // #ifdef BOARD_N9
+            // if (ln_story_player_simple_pause())
+            // {
+            //     printf("Playback paused successfully\n");
+            // }
+            // else
+            // {
+            //     printf("Failed to pause playback\n");
+            // }
+            // #endif
+            uapi_ln_audio_download_is_cancel(1);
         }
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_RESUME)
         {
             printf("LN_HTTP_SERVICE_CMD_AUDIO_RESUME:恢复播放\n");
-            #ifdef BOARD_N9
-            if (ln_story_player_simple_resume())
-            {
-                printf("Playback resumed successfully\n");
-            }
-            else
-            {
-                printf("Failed to resume playback\n");
-            }
-            #endif
+            // #ifdef BOARD_N9
+            // if (ln_story_player_simple_resume())
+            // {
+            //     printf("Playback resumed successfully\n");
+            // }
+            // else
+            // {
+            //     printf("Failed to resume playback\n");
+            // }
+            // #endif
+            uapi_ln_audio_download_is_cancel(0);
         }
         if (mail.data != NULL)
         {
index 6c2dfe0..965f7b8 100644 (file)
@@ -124,6 +124,15 @@ typedef struct {
     char md5[65];              /* MD5(来自任务),仅状态为COMPLETED时有效 */
 } OtaDownloadMsg;
 
+/* 设备绑定状态 */
+typedef enum
+{
+    LN_DEVICE_BIND_STATUS_UNBOUND = 0,      /* 未绑定,data 为 null */
+    LN_DEVICE_BIND_STATUS_BOUND = 1,        /* 已绑定,返回有效用户ID */
+    LN_DEVICE_BIND_STATUS_AUTH_FAILURE = 2, /* 认证失败(Token 无效或过期)*/
+    LN_DEVICE_BIND_STATUS_ERROR = 3         /* 其他错误(网络/解析等)*/
+} LnDeviceBindStatus;
+
 /* 初始化 LN HTTP 服务 */
 errcode_t uapi_ln_http_server_init(void); 
 /* 通用发送接口:向 LN HTTP 发送队列投递消息 */
@@ -151,6 +160,12 @@ const char *uapi_ln_ota_get_saved_path(void);
 const char *uapi_ln_get_speechchat_audio_path(void);
 /* 获取选择故事录音音频路径 */
 const char *uapi_ln_get_story_choose_audio_path(void);
+/* 音频下载/播放控制 */
+void uapi_ln_audio_download_is_cancel(int status);
+/* 获取是否绑定用户 */
+uint32_t uapi_ln_is_device_bound(void);
+/* 获取设备绑定用户ID */
+const char* uapi_ln_get_user_id(void);
 
 /* ============================== 共享全局状态(跨文件) ============================== */
 extern char *g_device_token;
@@ -172,6 +187,7 @@ uint32_t ln_token_load_json(char **token_out, uint64_t *expire_timestamp_out);
 uint32_t ln_token_is_expired(int *expired_out);
 /* 主动刷新设备Token(调用HTTP接口),并落盘保存 */
 uint32_t uapi_ln_refresh_device_token(void);
+uint32_t uapi_ln_device_get_user_id(LnDeviceBindStatus *out_status, char *out_user_id, uint32_t out_user_id_len);
 
 /* ============================== 各接口函数声明 ============================== */
 uint32_t ln_get_device_token_http(const char *device_id,
index 4d8e233..fdbe99d 100644 (file)
@@ -1,5 +1,7 @@
 #include "ln_api.h"
 #include <stdio.h>
+#include <string.h>
+#include <cJSON.h>
 
 // 获取设备角色信息
 uint32_t ln_device_get_character_http(const char *device_id,
@@ -140,3 +142,118 @@ end:
     HttpClientClse(&client);
     return ERRCODE_SUCC;
 }
+
+/*
+ * 简单实用的同步接口:获取设备绑定用户ID
+ *   - LN_DEVICE_BIND_STATUS_BOUND: 已绑定,out_user_id 返回用户ID
+ *   - LN_DEVICE_BIND_STATUS_UNBOUND: 未绑定
+ *   - LN_DEVICE_BIND_STATUS_AUTH_FAILURE: 认证失败
+ *   - LN_DEVICE_BIND_STATUS_ERROR: 其他错误
+ */
+uint32_t uapi_ln_device_get_user_id(LnDeviceBindStatus *out_status, char *out_user_id, uint32_t out_user_id_len)
+{
+    if (out_status == NULL || out_user_id == NULL || out_user_id_len == 0)
+    {
+        return ERRCODE_FAIL;
+    }
+    *out_status = LN_DEVICE_BIND_STATUS_ERROR;
+    out_user_id[0] = '\0';
+
+    const char *device_id = uapi_ln_get_device_id();
+    const char *token = uapi_ln_get_device_token();
+    if (!device_id || !token)
+    {
+        *out_status = LN_DEVICE_BIND_STATUS_AUTH_FAILURE;
+        return ERRCODE_FAIL;
+    }
+
+/* 发起请求 */
+#ifdef SUPPORT_CAT1_SERVICE
+    cat1_api_add_cat1_vote();
+    cat1_api_wakeup_cat1(1);
+    cat1_api_keep_wakeup(1);
+    cat1_api_disable_cat1_cmd(1);
+    LOS_Msleep(200);
+#endif
+
+    HttpClient client = (HttpClient){0};
+    HttpClientData clientData = (HttpClientData){0};
+    client.isHttp = false;
+    client.remotePort = 443;
+
+    char header_buf[512];
+    int hdr = snprintf(header_buf, sizeof(header_buf),
+                       "deviceId: %s\r\n"
+                       "deviceToken: %s\r\n"
+                       "Content-Type: application/json\r\n",
+                       device_id, token);
+    if (hdr <= 0 || hdr >= (int)sizeof(header_buf))
+    {
+        return ERRCODE_FAIL;
+    }
+    HttpClientSetCustomHeader(&client, header_buf);
+
+    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE);
+    if (prep != HTTP_SUCCESS)
+    {
+        return ERRCODE_FAIL;
+    }
+
+    HTTPC_RESULT res = HttpClientGetRequest(&client, LN_DEVICE_GET_USER_ID_URL, &clientData);
+    if (res != HTTP_SUCCESS)
+    {
+        HttpClientUnprepare(&clientData);
+        HttpClientClse(&client);
+        return ERRCODE_FAIL;
+    }
+
+    int code = HttpClientGetResponseCode(&client);
+    if (code == 200)
+    {
+        /* 成功响应:解析JSON,判断 data 是否为 null 或字符串 */
+        if (clientData.responseBuf && clientData.responseBufLen > 0)
+        {
+            int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
+            char *buf = (char *)malloc((size_t)real_len + 1);
+            if (buf)
+            {
+                memcpy(buf, clientData.responseBuf, (size_t)real_len);
+                buf[real_len] = '\0';
+                cJSON *root = cJSON_Parse(buf);
+                if (root)
+                {
+                    cJSON *data = cJSON_GetObjectItem(root, "data");
+                    if (data == NULL || cJSON_IsNull(data))
+                    {
+                        *out_status = LN_DEVICE_BIND_STATUS_UNBOUND;
+                    }
+                    else if (cJSON_IsString(data) && data->valuestring && data->valuestring[0])
+                    {
+                        *out_status = LN_DEVICE_BIND_STATUS_BOUND;
+                        strncpy(out_user_id, data->valuestring, out_user_id_len - 1);
+                        out_user_id[out_user_id_len - 1] = '\0';
+                    }
+                    else
+                    {
+                        *out_status = LN_DEVICE_BIND_STATUS_ERROR;
+                    }
+                    cJSON_Delete(root);
+                }
+                free(buf);
+            }
+        }
+    }
+    else if (code == 401)
+    {
+        /* 认证失败 */
+        *out_status = LN_DEVICE_BIND_STATUS_AUTH_FAILURE;
+    }
+    else
+    {
+        *out_status = LN_DEVICE_BIND_STATUS_ERROR;
+    }
+
+    HttpClientUnprepare(&clientData);
+    HttpClientClse(&client);
+    return ERRCODE_SUCC;
+}
index 1ba1080..1c6fa8d 100644 (file)
@@ -55,7 +55,7 @@ static void ln_mkdir_p(const char *path)
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_mkdir_p: path is NULL");
         return;
     }
-    (void)mkdir(LN_TOKEN_DIR, 0755);
+    (void)mkdir(path, 0755);
 }
 
 /* 保存设备Token到本地 */
@@ -66,6 +66,7 @@ static uint32_t ln_token_save_json(const char *token, uint64_t expire_timestamp)
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: token is NULL");
         return ERRCODE_FAIL;
     }
+    ln_mkdir_p(LN_DATA_PATH);
     ln_mkdir_p(LN_TOKEN_DIR);
 
     /* 生成create_time字符串 */
@@ -86,7 +87,7 @@ static uint32_t ln_token_save_json(const char *token, uint64_t expire_timestamp)
     if (text == NULL)
         return ERRCODE_FAIL;
 
-    FILE *fp = fopen(LN_TOKEN_FILE, "wb");
+    FILE *fp = fopen(LN_TOKEN_FILE, "wb+");
     if (!fp)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: fopen failed");