logic/pin(纯 C):加盐迭代哈希(FNV-1a ×1000 轮 + 每次设置新随机盐,16 位 hex)经
meow_config_* 持久化 pin.enabled/pin.hash/pin.salt;提供 set/verify/change/disable 与
4-8 位纯数字校验;enabled=1 但盐/哈希残缺时 fail-closed;落盘半途失败会还原旧盐,不留
「新盐+旧哈希」的双失效状态。
ui/settings(ACT_SETTINGS):MD3 分组表单(meow_listview 容器圆角卡 + 段头标签)——
阅读方向 RTL/LTR 走 mr_reader_set_direction;启动 PIN 开关与修改(开启/修改均两遍
MEOW_IME_NUM 确认);版本与主题状态行(跟随系统 + 当前明暗,随 app.c 主题 listener
整帧重绘)。控件一律在 on_enter 建(栈顶=ACT_SETTINGS),避免 §6.1 owner 绑到
ACT_LIBRARY 的跨页命中泄漏。
ui/lock(ACT_LOCK):lock_view_should_show() 读 logic/pin;启用时启动先入锁屏,验证前
不绘制任何书库内容;「输入 PIN」按钮弹系统数字键盘(MEOW_IME_NUM),错 PIN 就地提示
重试(无惩罚性锁定),正确则 page_pop 回书库;on_event 消费 SWIPE——左缘返回不能绕过
PIN 或退出 App(meow_view_dispatch_event 先于 app.c 的 on_swipe)。
logic/reader(评审 P1 修复,最小持久化):mr_reader_set_direction 成功切换后 best-effort
写 meow_config("reader.dir","rtl"/"ltr"),写失败只打 WARN 不阻断本次切换;
mr_reader_get_direction 首次调用惰性读一次配置恢复(无配置/非法值 → 默认 RTL),之后
纯内存快路径。宿主测试经 -DMR_READER_TEST_HOOKS 的 mr_reader_test_reset() 模拟重启。
测试/构建:test_pin(85 项,PIN 全分支,含单键半途落盘失败);test_settings_reader_persist
(19 项:默认/落盘/重启恢复/坏值/非法值/只读一次/写失败只告警);可选宿主集成
test_settings_lock_host(真实 SDK view/activity/widget + IME 注入,端到端验证错/对 PIN
解锁与设置页两遍开启;SDK 无源码时 SKIP);run_tests.sh 保持 46/46 并纳入上述测试
(reader.c 的配置/日志依赖由 test/settings_reader_stub.c 存根),build_tuku.sh 加入
logic/pin.c。交叉构建静态 MIPS ELF(zig-mipsel)零警告。
安全口径:PIN 防随手翻看,不是安全边界——TF 卡可拔出直读、配置可改;存储不使用加密库,
锁屏与设置页文案均如实标注该局限。
93 lines
2.4 KiB
C
93 lines
2.4 KiB
C
/* settings_reader_stub.c — 宿主测试用 meow_config_* 与 meow_log 最小实现(内存 KV)。
|
||
*
|
||
* 只为让 logic/reader.c 在不链 SDK 的宿主测试里可编译/可断言:
|
||
* - meow_config_get/set:8 个键的内存表,可注入写失败(模拟无卡/只读);
|
||
* - meow_log:只统计 WARN 次数(验证「写失败只告警、不阻断切换」)。
|
||
* 与 test_pin.c 自带存根同思路,但抽成共用文件供多个 reader 相关测试链接。
|
||
*/
|
||
#include "settings_reader_stub.h"
|
||
#include "include/meow_ui_config.h"
|
||
#include "include/meow_ui_log.h"
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#define ST_MAX 8
|
||
static struct { char key[64]; char val[64]; int used; } g_kv[ST_MAX];
|
||
static int g_fail_writes;
|
||
static int g_warns;
|
||
|
||
static int kv_find(const char *key)
|
||
{
|
||
for (int i = 0; i < ST_MAX; i++)
|
||
if (g_kv[i].used && strcmp(g_kv[i].key, key) == 0) return i;
|
||
return -1;
|
||
}
|
||
|
||
void mr_test_cfg_clear(void)
|
||
{
|
||
memset(g_kv, 0, sizeof g_kv);
|
||
g_fail_writes = 0;
|
||
}
|
||
|
||
void mr_test_cfg_put(const char *key, const char *val)
|
||
{
|
||
if (!key || !key[0]) return;
|
||
int i = kv_find(key);
|
||
if (i < 0) {
|
||
for (i = 0; i < ST_MAX; i++) if (!g_kv[i].used) break;
|
||
if (i >= ST_MAX) return;
|
||
g_kv[i].used = 1;
|
||
snprintf(g_kv[i].key, sizeof g_kv[i].key, "%s", key);
|
||
}
|
||
snprintf(g_kv[i].val, sizeof g_kv[i].val, "%s", val ? val : "");
|
||
}
|
||
|
||
int mr_test_cfg_get(const char *key, char *out, int outsz)
|
||
{
|
||
if (!out || outsz <= 0) return 0;
|
||
out[0] = '\0';
|
||
int i = kv_find(key);
|
||
if (i < 0) return 0;
|
||
snprintf(out, (size_t)outsz, "%s", g_kv[i].val);
|
||
return 1;
|
||
}
|
||
|
||
void mr_test_cfg_fail_writes(int on)
|
||
{
|
||
g_fail_writes = on ? 1 : 0;
|
||
}
|
||
|
||
int mr_test_log_warns(void)
|
||
{
|
||
return g_warns;
|
||
}
|
||
|
||
void mr_test_log_clear(void)
|
||
{
|
||
g_warns = 0;
|
||
}
|
||
|
||
/* ---- 被测代码调用的 SDK 接口 ---- */
|
||
int meow_config_set(const char *key, const char *val)
|
||
{
|
||
if (!key || !key[0]) return MEOW_CFG_ERR_INVAL;
|
||
if (g_fail_writes) return MEOW_CFG_ERR_IO;
|
||
mr_test_cfg_put(key, val);
|
||
return MEOW_CFG_OK;
|
||
}
|
||
|
||
int meow_config_get(const char *key, char *out, int outsz)
|
||
{
|
||
if (!out || outsz <= 0) return MEOW_CFG_ERR_INVAL;
|
||
out[0] = '\0';
|
||
if (!mr_test_cfg_get(key, out, outsz)) return MEOW_CFG_ERR_NOENT;
|
||
return MEOW_CFG_OK;
|
||
}
|
||
|
||
void meow_log(meow_log_level lvl, const char *fmt, ...)
|
||
{
|
||
(void)fmt;
|
||
if (lvl >= MEOW_LOG_WARN) g_warns++;
|
||
}
|