From 54499f775d1c56fb74421ee8b27be644987ae583 Mon Sep 17 00:00:00 2001 From: xue <2144439972@qq.com> Date: Wed, 14 Jan 2026 17:54:22 +0800 Subject: [PATCH] =?utf8?q?=E5=A2=9E=E5=8A=A0=E5=B1=8F=E4=BF=9D=E6=A0=A1?= =?utf8?q?=E9=AA=8C=EF=BC=88=E5=85=BC=E5=AE=B9=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../leiniao/imsl_ln/ln_api_device_info.c | 151 +++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c index 9dceb04b4..78a84843f 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c @@ -6,11 +6,14 @@ #include #include "wearable_log.h" #include "custom_utils.h" +#include "mbedtls/md5.h" LnCharacterConfig g_ln_character_config = {0}; /* 解析阶段保存一次性屏保URL,避免随后清理全局结构后丢失 */ static char *g_ln_pending_screen_image_url = NULL; +/* 解析阶段保存一次性屏保MD5,用于下载后校验 */ +static char *g_ln_pending_screen_image_md5 = NULL; static void ln_set_pending_screen_image(const char *url) { if (g_ln_pending_screen_image_url) @@ -28,6 +31,77 @@ static void ln_set_pending_screen_image(const char *url) } } } +static void ln_set_pending_screen_image_md5(const char *md5) +{ + if (g_ln_pending_screen_image_md5) + { + free(g_ln_pending_screen_image_md5); + g_ln_pending_screen_image_md5 = NULL; + } + if (md5 && md5[0]) + { + size_t mlen = strlen(md5); + g_ln_pending_screen_image_md5 = (char *)malloc(mlen + 1); + if (g_ln_pending_screen_image_md5) + { + memcpy(g_ln_pending_screen_image_md5, md5, mlen + 1); + } + } +} + +/* 计算文件的MD5值(小写十六进制字符串) */ +static int ln_calc_file_md5_lower_hex(const char *file_path, char out_hex[33]) +{ + if (!file_path || !out_hex) return -1; + FILE *fp = fopen(file_path, "rb"); + if (!fp) return -1; + mbedtls_md5_context ctx; + mbedtls_md5_init(&ctx); + if (mbedtls_md5_starts(&ctx) != 0) { mbedtls_md5_free(&ctx); fclose(fp); return -1; } + unsigned char buf[1024]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { + if (mbedtls_md5_update(&ctx, buf, n) != 0) { mbedtls_md5_free(&ctx); fclose(fp); return -1; } + } + fclose(fp); + unsigned char md5_ctx_buf[16]; + if (mbedtls_md5_finish(&ctx, md5_ctx_buf) != 0) { mbedtls_md5_free(&ctx); return -1; } + mbedtls_md5_free(&ctx); + static const char hex[] = "0123456789abcdef"; + for (int i = 0; i < 16; i++) { + out_hex[i * 2] = hex[(md5_ctx_buf[i] >> 4) & 0x0F]; + out_hex[i * 2 + 1] = hex[md5_ctx_buf[i] & 0x0F]; + } + out_hex[32] = '\0'; + return 0; +} + +/* 校验文件的MD5值 */ +static int ln_verify_screen_image_md5(const char *file_path, const char *expected_md5) +{ + if (file_path == NULL || expected_md5 == NULL || expected_md5[0] == '\0') { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] md5 verify: invalid args"); + return -1; + } + char calc[33] = {0}; + if (ln_calc_file_md5_lower_hex(file_path, calc) != 0) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] md5 verify: calc failed for %s", file_path); + return -1; + } + char expect_lower[33] = {0}; + size_t elen = strlen(expected_md5); + size_t copy_len = elen > 32 ? 32 : elen; + for (size_t i = 0; i < copy_len; i++) { + char ch = expected_md5[i]; + if (ch >= 'A' && ch <= 'Z') ch = (char)(ch - 'A' + 'a'); + expect_lower[i] = ch; + } + expect_lower[32] = '\0'; + int ok = (strcmp(calc, expect_lower) == 0) ? 1 : 0; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] md5 verify result=%s, calc=%s, expect=%s", + ok ? "OK" : "FAIL", calc, expect_lower); + return ok; +} /* 动态字符串工具 */ static inline char *ln_strdup_s(const char *src) { @@ -132,12 +206,53 @@ void ln_download_screen_image(void) if (http_file_download_init(NULL) != 0) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 初始化下载服务失败"); + // 清理资源 + if (g_ln_pending_screen_image_md5) + { + free(g_ln_pending_screen_image_md5); + g_ln_pending_screen_image_md5 = NULL; + } return; } int ret = uapi_ln_download_file_resume_with_retry(url, save_path, 5); if (ret == 0) { + // 下载成功,进行MD5校验(如果有MD5值) + if (g_ln_pending_screen_image_md5 && g_ln_pending_screen_image_md5[0] != '\0') + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载完成,开始MD5校验,期望MD5=%s", + g_ln_pending_screen_image_md5); + + if (ln_verify_screen_image_md5(save_path, g_ln_pending_screen_image_md5) != 1) + { + // MD5校验失败,删除文件并报告失败 + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保MD5校验失败,删除文件"); + uapi_delete_files(LN_DEVICE_SCREEN_IMAGE_FILE); + push_download_fail_notification(DOWNLOAD_FILE_TYPE_SCREEN_IMAGE); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载结束,ret=%d (MD5校验失败)", ret); + uapi_ln_set_file_download_type(LN_FILE_DOWNLOAD_TYPE_NONE); + http_file_download_deinit(); + /* 一次性使用后释放 */ + free(g_ln_pending_screen_image_url); + g_ln_pending_screen_image_url = NULL; + if (g_ln_pending_screen_image_md5) + { + free(g_ln_pending_screen_image_md5); + g_ln_pending_screen_image_md5 = NULL; + } + return; + } + else + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保MD5校验通过"); + } + } + else + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载成功(旧协议,无MD5校验)"); + } + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载成功,通知去切换屏保"); push_download_success_notification(DOWNLOAD_FILE_TYPE_SCREEN_IMAGE); } @@ -155,6 +270,11 @@ void ln_download_screen_image(void) /* 一次性使用后释放 */ free(g_ln_pending_screen_image_url); g_ln_pending_screen_image_url = NULL; + if (g_ln_pending_screen_image_md5) + { + free(g_ln_pending_screen_image_md5); + g_ln_pending_screen_image_md5 = NULL; + } } } @@ -235,6 +355,7 @@ void ln_parse_character_config_startUp(const char *json_buf, int json_len) cJSON *hairStyle = cJSON_GetObjectItem(avatar, "hairStyle"); cJSON *eyesShape = cJSON_GetObjectItem(avatar, "eyesShape"); cJSON *deviceScreen = cJSON_GetObjectItem(avatar, "deviceScreen"); + cJSON *deviceScreenMd5 = cJSON_GetObjectItem(avatar, "deviceScreenMd5"); cJSON *deviceScreenAsEmoji = cJSON_GetObjectItem(avatar, "screenImageAsEmoji"); ln_assign_dup(&g_ln_character_config.avatar_hair_color, @@ -252,6 +373,19 @@ void ln_parse_character_config_startUp(const char *json_buf, int json_len) ln_assign_dup(&g_ln_character_config.avatar_screen_image, (deviceScreen && cJSON_IsString(deviceScreen)) ? deviceScreen->valuestring : ""); + // 解析deviceScreenMd5字段(新协议支持MD5校验,兼容旧协议没有MD5的情况) + if (deviceScreenMd5 && cJSON_IsString(deviceScreenMd5)) + { + ln_set_pending_screen_image_md5(deviceScreenMd5->valuestring); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 解析到deviceScreenMd5: %s", deviceScreenMd5->valuestring); + } + else + { + // 旧协议没有MD5字段,清空MD5值 + ln_set_pending_screen_image_md5(NULL); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 未解析到deviceScreenMd5,使用旧协议(无MD5校验)"); + } + g_ln_character_config.avatar_screen_image_as_emoji = (deviceScreenAsEmoji && cJSON_IsBool(deviceScreenAsEmoji)) ? (cJSON_IsTrue(deviceScreenAsEmoji) ? 1 : 0) : 0; } @@ -413,6 +547,7 @@ void ln_parse_character_config(const char *json_buf, int json_len) cJSON *hairStyle = cJSON_GetObjectItem(avatar, "hairStyle"); cJSON *eyesShape = cJSON_GetObjectItem(avatar, "eyesShape"); cJSON *deviceScreen = cJSON_GetObjectItem(avatar, "deviceScreen"); + cJSON *deviceScreenMd5 = cJSON_GetObjectItem(avatar, "deviceScreenMd5"); cJSON *deviceScreenAsEmoji = cJSON_GetObjectItem(avatar, "screenImageAsEmoji"); ln_assign_dup(&g_ln_character_config.avatar_hair_color, @@ -430,6 +565,19 @@ void ln_parse_character_config(const char *json_buf, int json_len) ln_assign_dup(&g_ln_character_config.avatar_screen_image, (deviceScreen && cJSON_IsString(deviceScreen)) ? deviceScreen->valuestring : ""); + // 解析deviceScreenMd5字段(新协议支持MD5校验,兼容旧协议没有MD5的情况) + if (deviceScreenMd5 && cJSON_IsString(deviceScreenMd5)) + { + ln_set_pending_screen_image_md5(deviceScreenMd5->valuestring); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 解析到deviceScreenMd5: %s", deviceScreenMd5->valuestring); + } + else + { + // 旧协议没有MD5字段,清空MD5值 + ln_set_pending_screen_image_md5(NULL); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 未解析到deviceScreenMd5,使用旧协议(无MD5校验)"); + } + g_ln_character_config.avatar_screen_image_as_emoji = (deviceScreenAsEmoji && cJSON_IsBool(deviceScreenAsEmoji)) ? (cJSON_IsTrue(deviceScreenAsEmoji) ? 1 : 0) : 0; } @@ -464,8 +612,9 @@ void ln_parse_character_config(const char *json_buf, int json_len) if (g_ln_character_config.avatar_screen_image && strlen(g_ln_character_config.avatar_screen_image) > 0) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保图片不为空,下载屏保图片"); - /* 解析阶段先拷贝URL,避免稍后清理结构体后URL丢失 */ + /* 解析阶段先拷贝URL和MD5,避免稍后清理结构体后丢失 */ ln_set_pending_screen_image(g_ln_character_config.avatar_screen_image); + // MD5已在上面解析时设置,这里不需要再次设置 if(uapi_ln_get_file_download_type() == LN_FILE_DOWNLOAD_TYPE_NONE && uapi_ln_is_file_downloading() == 0) { push_download_file_notification(DOWNLOAD_FILE_TYPE_SCREEN_IMAGE); -- 2.17.1