diff --git a/SYSTEM_SETTINGS.md b/SYSTEM_SETTINGS.md index 9faa9ff..7858f74 100644 --- a/SYSTEM_SETTINGS.md +++ b/SYSTEM_SETTINGS.md @@ -62,6 +62,10 @@ | **网络** | `network.wifi_enabled` | bool | true | WiFi 开关(写 wlan0;设置页开关经 `wifi_up.sh`/`ip link set wlan0`,`ensure_wifi()` 尊重此值——关=0 则不再自动连网) | | | `network.bluetooth_enabled` | bool | false | 蓝牙开关(写 `hciconfig hci0 up/down`;默认关=少耗电) | | **系统** | `system.hostname` | string | `meowos` | 主机名(RFC1035 首标签;bootmenu 的 S89meow_svcd 开机读它 `sethostname`,缺省 `meowos`;供自研 mDNS 应答器 `meow_mdns` 公布 `.local`)[源:M2.54] | +| | `system.pin` | string | 空 | **锁屏 4 位 PIN**:`:`(sha256+salt,格式与面板 `panel.pin` 全等,绝无明文)。空=未设(锁屏关闭)。**panel.pin 迁移**:`panel.pin` 已设而本键未设时锁屏接受 `panel.pin`,首次验证成功后并入本键;此后以本键为准。**兜底**:SSH root 删本键即解锁(无其它后门)[源:用户 2026-09-02 锁屏定案] | +| | `system.pin_lock_on_wake` | int 0/1 | **1** | 息屏唤醒后锁屏(默认开);0=关(唤醒直进主屏,电源菜单「锁定」手动锁仍可用)。开机不锁(boot 进主屏不拦) | +| | `system.pin_lock_until` | int(epoch 秒) | 0 | 错 PIN 锁定截止戳。**持久化到 settings**(防 launcher 重启清零;面板进程内存方案重启失效,此处落盘);截止前锁屏页禁输入并倒计 | +| | `system.pin_fails` | int | 0 | 连续错 PIN 计数(0-4;达 5 触发锁定并清零)。持久化防「关机重启重试」 | > **字体尺寸标记(M2.5x,随「加载大字库」一起落地)**:字体/主题选择器**每个字体选项标注库大小标记**,如 `MiSans Regular(common≈4k)` / `gbk≈20k` / `full`,直观标注码位/体积量级,让用户知道所选字库大小(M2.6 含字重:` ()`)。对应 `MeowOS/fonts///font.json` 的 `charset`(`common|gbk|full`)+ `glyph_count`;档位枚举 `MEOW_FONT_TIER_COMMON`(默认/≈4k)/`GBK`(大/≈20k)/`FULL`(全/可选);`gen_font.py --charset common|gbk|full [--weight ]` 按档生成。定案见 MEOWOS_SPEC_REF §5.2「字体档位/大字库」与 §5.7.8 [源:用户 2026-08-30]。 diff --git a/patch_v2/meow_launcherd/app_launcher.c b/patch_v2/meow_launcherd/app_launcher.c index cb2eb9b..e81fb75 100644 --- a/patch_v2/meow_launcherd/app_launcher.c +++ b/patch_v2/meow_launcherd/app_launcher.c @@ -1431,6 +1431,53 @@ static int run_app_notif_overlay(struct app *a, pid_t pid) return 0; } +/* M2.x 锁屏:App 前台会话唤醒后的锁屏叠加(复用电源菜单/通知中心 SIGSTOP+读回+叠加+接管模板)。 + * 语义与 run_app_notif_overlay 同构:媒体保活(本 App 播放中)不 SIGSTOP(断供 PCM),SDK present + * 见 /tmp/meow_notif_active 自抑上屏;否则 SIGSTOP 冻结 App,run_lock_screen 全屏接管;解锁/SIGCONT。 + * App 死亡不在本函数内探测(run_lock_screen 阻塞,waitpid 循环在返回后统一回收,杀死进程 SIGCONT 无害)。 + * 返回 0(锁屏已结束;是否解锁/息屏由 run_lock_screen 返回值决定,本层不关心)。 */ +static int run_app_lock_overlay(struct app *a, pid_t pid) +{ + if (!back_buf_ok || !fb) { logmsg("lock-overlay: no back_buf (direct mode) skip"); return 0; } + int keep_running = meow_media_app_playing(a->id); + if (keep_running) { + logmsg("lock-overlay: open (app=%s pid=%d) PLAYING -> no SIGSTOP (SDK present suppressed)", + a->name, pid); + } else { + logmsg("lock-overlay: open (app=%s pid=%d) SIGSTOP", a->name, pid); + if (kill(pid, SIGSTOP) < 0) { + logmsg("lock-overlay: SIGSTOP %d fail: %s", pid, strerror(errno)); + } else { + for (int i = 0; i < 100; i++) { + if (proc_is_stopped(pid)) break; + usleep(10000); + } + } + } + int nfd = open(NOTIF_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (nfd >= 0) { write(nfd, "1", 1); close(nfd); } + int pfd = open(POWMENU_ACTIVE, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (pfd >= 0) { write(pfd, "1", 1); close(pfd); } + + meow_sys_light(); + meow_theme_reload(); + pm_capture_app_frame(); + + int lr = run_lock_screen(); + + pm_capture_app_frame(); + meow_ui_flush(); + unlink(NOTIF_ACTIVE); + unlink(POWMENU_ACTIVE); + if (!keep_running) { + kill(pid, SIGCONT); + logmsg("lock-overlay: closed (lr=%d) -> SIGCONT app", lr); + } else { + logmsg("lock-overlay: closed (lr=%d) -> app never stopped (media keep-alive), no SIGCONT", lr); + } + return 0; +} + /* launch_app_chain:launch_app / launch_activity 的共享核心。 * chain 非空 → 向子进程注入 MEOW_ACTIVITY/MEOW_CHAIN 并初始化 nav_stack(M3);空/NULL → 裸启动。 */ static int launch_app_chain(struct app *a, const char *chain) @@ -1909,6 +1956,8 @@ static int launch_app_chain(struct app *a, const char *chain) meow_panel_on(); meow_auto_slept = 0; fb_pan_buf0_force(); + /* M2.x 锁屏:App 前台唤醒后若需锁屏 → SIGSTOP 叠加锁屏页;解锁/SIGCONT 回应用 */ + if (lck_lock_required(1)) run_app_lock_overlay(a, pid); } } if (screen_off_timeout > 0 && meow_screen_on && !meow_auto_slept && !fg_keep_awake()) { @@ -1931,6 +1980,8 @@ static int launch_app_chain(struct app *a, const char *chain) meow_panel_on(); meow_auto_slept = 0; fb_pan_buf0_force(); + /* M2.x 锁屏:App 前台唤醒后若需锁屏 → SIGSTOP 叠加锁屏页;解锁/SIGCONT 回应用 */ + if (lck_lock_required(1)) run_app_lock_overlay(a, pid); } } if (vold && !app_prev_vold) { diff --git a/patch_v2/meow_launcherd/build_launcher.sh b/patch_v2/meow_launcherd/build_launcher.sh index 90b2f21..dcf8e5f 100755 --- a/patch_v2/meow_launcherd/build_launcher.sh +++ b/patch_v2/meow_launcherd/build_launcher.sh @@ -28,7 +28,7 @@ SYSROOT="$ROOT/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf" subpage.c wifi_page.c bt_page.c netmgr_ipc.c now_playing.c \ font_theme.c keys.c util.c meow_dep.c \ meow_touch.c meow_settings.c meow_notif.c meow_media.c meow_bgplay.c \ - meow_ams_start.c \ + meow_ams_start.c lock_screen.c lock_screen_ui.c \ "../sdk/meow_ui.a" "$SYSROOT/lib/libz.a" -lpthread -o meow_launcherd echo "== meow_launcherd ==" ls -la meow_launcherd diff --git a/patch_v2/meow_launcherd/lock_screen.c b/patch_v2/meow_launcherd/lock_screen.c new file mode 100644 index 0000000..e4eb6c0 --- /dev/null +++ b/patch_v2/meow_launcherd/lock_screen.c @@ -0,0 +1,353 @@ +/* lock_screen.c — MeowOS 锁屏 4 位 PIN(纯逻辑;宿主可单测 lock_screen.h)。 + * + * 与 meowpanel.c 的访问级 PIN(PANEL_ENHANCE_PLAN)**同源同格式**:`:`。 + * 内联 SHA-256 取自 meow_verify/sha256.c(FIPS 180-4,公有领域参考写法),前缀 lck_ 避免符号冲突; + * 算法与 meowpanel.c 的 mp_sha256 逐字一致 → 面板生成的 panel.pin 无需重设即可被本模块校验。 + * + * 日志用 fprintf(stderr)(前缀 [lock]),不依赖 launcher 的 logmsg —— 宿主测试可直接 #include 本文件。 + * 依赖 meow_settings_* API(同一 settings.conf),持久化走 meow_settings_save()(含 boot.default + * 同步,幂等)。 + */ +#include "lock_screen.h" +#include "meow_settings.h" +#include +#include +#include +#include +#include +#include + +#define LCK_LOG(...) fprintf(stderr, "[lock] " __VA_ARGS__) + +/* ---- 最小 SHA-256(FIPS 180-4,公有领域参考写法;内联拷贝自 meow_verify/sha256.c,前缀 lck_) ---- */ +static const uint32_t lck_sha256_k[64] = { + 0x428a2f98u,0x71374491u,0xb5c0fbcfu,0xe9b5dba5u,0x3956c25bu,0x59f111f1u,0x923f82a4u,0xab1c5ed5u, + 0xd807aa98u,0x12835b01u,0x243185beu,0x550c7dc3u,0x72be5d74u,0x80deb1feu,0x9bdc06a7u,0xc19bf174u, + 0xe49b69c1u,0xefbe4786u,0x0fc19dc6u,0x240ca1ccu,0x2de92c6fu,0x4a7484aau,0x5cb0a9dcu,0x76f988dau, + 0x983e5152u,0xa831c66du,0xb00327c8u,0xbf597fc7u,0xc6e00bf3u,0xd5a79147u,0x06ca6351u,0x14292967u, + 0x27b70a85u,0x2e1b2138u,0x4d2c6dfcu,0x53380d13u,0x650a7354u,0x766a0abbu,0x81c2c92eu,0x92722c85u, + 0xa2bfe8a1u,0xa81a664bu,0xc24b8b70u,0xc76c51a3u,0xd192e819u,0xd6990624u,0xf40e3585u,0x106aa070u, + 0x19a4c116u,0x1e376c08u,0x2748774cu,0x34b0bcb5u,0x391c0cb3u,0x4ed8aa4au,0x5b9cca4fu,0x682e6ff3u, + 0x748f82eeu,0x78a5636fu,0x84c87814u,0x8cc70208u,0x90befffau,0xa4506cebu,0xbef9a3f7u,0xc67178f2u +}; +#define LCK_ROR(x,n) (((x) >> (n)) | ((x) << (32 - (n)))) +#define LCK_CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define LCK_MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define LCK_BSIG0(x) (LCK_ROR(x,2) ^ LCK_ROR(x,13) ^ LCK_ROR(x,22)) +#define LCK_BSIG1(x) (LCK_ROR(x,6) ^ LCK_ROR(x,11) ^ LCK_ROR(x,25)) +#define LCK_SSIG0(x) (LCK_ROR(x,7) ^ LCK_ROR(x,18) ^ ((x) >> 3)) +#define LCK_SSIG1(x) (LCK_ROR(x,17) ^ LCK_ROR(x,19) ^ ((x) >> 10)) + +typedef struct lck_sha256_ctx { + uint32_t state[8]; + uint64_t count; + uint8_t buf[64]; + size_t buflen; +} lck_sha256_ctx; + +static void lck_sha256_init(lck_sha256_ctx *c) +{ + c->state[0] = 0x6a09e667u; c->state[1] = 0xbb67ae85u; + c->state[2] = 0x3c6ef372u; c->state[3] = 0xa54ff53au; + c->state[4] = 0x510e527fu; c->state[5] = 0x9b05688cu; + c->state[6] = 0x1f83d9abu; c->state[7] = 0x5be0cd19u; + c->count = 0; c->buflen = 0; +} + +static void lck_sha256_block(lck_sha256_ctx *c, const uint8_t *p) +{ + uint32_t w[64]; + int i; + for (i = 0; i < 16; i++) + w[i] = ((uint32_t)p[i*4] << 24) | ((uint32_t)p[i*4+1] << 16) | + ((uint32_t)p[i*4+2] << 8) | (uint32_t)p[i*4+3]; + for (i = 16; i < 64; i++) + w[i] = LCK_SSIG1(w[i-2]) + w[i-7] + LCK_SSIG0(w[i-15]) + w[i-16]; + uint32_t a = c->state[0], b = c->state[1], cc = c->state[2], d = c->state[3]; + uint32_t e = c->state[4], f = c->state[5], g = c->state[6], h = c->state[7]; + for (i = 0; i < 64; i++) { + uint32_t t1 = h + LCK_BSIG1(e) + LCK_CH(e,f,g) + lck_sha256_k[i] + w[i]; + uint32_t t2 = LCK_BSIG0(a) + LCK_MAJ(a,b,cc); + h = g; g = f; f = e; e = d + t1; + d = cc; cc = b; b = a; a = t1 + t2; + } + c->state[0] += a; c->state[1] += b; c->state[2] += cc; c->state[3] += d; + c->state[4] += e; c->state[5] += f; c->state[6] += g; c->state[7] += h; +} + +static void lck_sha256_update(lck_sha256_ctx *c, const uint8_t *data, size_t len) +{ + c->count += (uint64_t)len; + if (c->buflen) { + size_t need = 64 - c->buflen; + size_t take = len < need ? len : need; + memcpy(c->buf + c->buflen, data, take); + c->buflen += take; data += take; len -= take; + if (c->buflen == 64) { lck_sha256_block(c, c->buf); c->buflen = 0; } + } + while (len >= 64) { lck_sha256_block(c, data); data += 64; len -= 64; } + if (len) { memcpy(c->buf, data, len); c->buflen = len; } +} + +static void lck_sha256_final(lck_sha256_ctx *c, uint8_t out[32]) +{ + uint64_t bitlen = c->count * 8; + uint8_t *b = c->buf; + size_t bl = c->buflen; + int i; + b[bl++] = 0x80; + if (bl > 56) { + while (bl < 64) b[bl++] = 0; + lck_sha256_block(c, b); + bl = 0; + } + while (bl < 56) b[bl++] = 0; + for (i = 0; i < 8; i++) + b[bl++] = (uint8_t)(bitlen >> (56 - i * 8)); + lck_sha256_block(c, b); + for (i = 0; i < 8; i++) { + out[i*4] = (uint8_t)(c->state[i] >> 24); + out[i*4+1] = (uint8_t)(c->state[i] >> 16); + out[i*4+2] = (uint8_t)(c->state[i] >> 8); + out[i*4+3] = (uint8_t)(c->state[i]); + } +} +#undef LCK_ROR +#undef LCK_CH +#undef LCK_MAJ +#undef LCK_BSIG0 +#undef LCK_BSIG1 +#undef LCK_SSIG0 +#undef LCK_SSIG1 + +/* ---- 哈希基元(格式 `:`,与面板全等)---- */ +static void lck_hex(const unsigned char *raw, size_t n, char *out) +{ + static const char hex[] = "0123456789abcdef"; + for (size_t i = 0; i < n; i++) { + out[i*2] = hex[raw[i] >> 4]; + out[i*2+1] = hex[raw[i] & 0xf]; + } + out[n*2] = 0; +} + +static int lck_read_random(unsigned char *buf, size_t n) +{ + int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) return -1; + size_t got = 0; + while (got < n) { + ssize_t rd = read(fd, buf + got, n - got); + if (rd <= 0) { close(fd); return -1; } + got += (size_t)rd; + } + close(fd); + return 0; +} + +static int lck_make_salt(char *out, int outcap) /* 16B → 32 hex 盐 */ +{ + unsigned char raw[16]; + if (outcap < 33) return -1; + if (lck_read_random(raw, sizeof raw) < 0) return -1; + lck_hex(raw, sizeof raw, out); + return 0; +} + +static int lck_pass_hash(const char *salt, const char *password, char *out, int outcap) +{ + lck_sha256_ctx c; + uint8_t digest[32]; + if (outcap < LCK_HASH_LEN + 1) return -1; + lck_sha256_init(&c); + lck_sha256_update(&c, (const uint8_t *)salt, strlen(salt)); + lck_sha256_update(&c, (const uint8_t *)password, strlen(password)); + lck_sha256_final(&c, digest); + lck_hex(digest, sizeof digest, out); + return 0; +} + +static int lck_make_hash(const char *password, char *out, int outcap) /* salt:hex */ +{ + char salt[33], h[LCK_HASH_LEN + 1]; + if (lck_make_salt(salt, sizeof salt) < 0) return -1; + if (lck_pass_hash(salt, password, h, sizeof h) < 0) return -1; + snprintf(out, (size_t)outcap, "%s:%s", salt, h); + return 0; +} + +/* 校验 `salt:hex` 是否匹配明文(恒定比较,不反解)。stored 为 `salt:hex` 完整串。 */ +static int lck_hash_matches(const char *stored, const char *password) +{ + if (!stored || !stored[0] || !password) return 0; + char tmp[160]; + snprintf(tmp, sizeof tmp, "%s", stored); + char *colon = strchr(tmp, ':'); + if (!colon) return 0; + *colon = 0; + char want[LCK_HASH_LEN + 1]; + if (lck_pass_hash(tmp, password, want, sizeof want) != 0) return 0; + if (strlen(colon + 1) != LCK_HASH_LEN) return 0; + return memcmp(want, colon + 1, LCK_HASH_LEN) == 0; +} + +/* ---- 设置读取辅助(NULL 表示键缺失/空)---- */ +static char *lck_get(const char *key) +{ + char buf[MEOW_SET_MAX_LEN]; + if (meow_settings_get_str(key, buf, sizeof buf) != 0) return NULL; + if (!buf[0]) return NULL; /* 空值=未设 */ + char *r = strdup(buf); + return r; +} +static int lck_get_int(const char *key) +{ + int v = 0; + if (meow_settings_get_int(key, &v) != 0) v = 0; + return v; +} +static int lck_set_int(const char *key, int v) +{ + return meow_settings_set_int(key, v); +} +static int lck_persist(void) +{ + return meow_settings_save(); +} + +/* ---- 公开接口 ---- */ +int lck_pin_valid(const char *pin) +{ + if (!pin) return 0; + size_t l = strlen(pin); + if (l != LCK_PIN_LEN) return 0; + for (size_t i = 0; i < l; i++) + if (pin[i] < '0' || pin[i] > '9') return 0; + return 1; +} + +int lck_pin_configured(void) +{ + char *sp = lck_get(LCK_KEY_PIN); + int s = (sp != NULL); + free(sp); + if (s) return 1; + char *pp = lck_get(LCK_KEY_PANEL_PIN); + int p = (pp != NULL); + free(pp); + return p; +} + +int lck_lock_required(int allow_disable) +{ + if (!lck_pin_configured()) return 0; /* 未设凭据:锁屏无鉴别物,不开 */ + if (allow_disable) { + int wake = 1; + if (meow_settings_get_int(LCK_KEY_WAKE, &wake) != 0) wake = 1; + return wake ? 1 : 0; + } + return 1; /* 手动锁:不看 WAKE 键 */ +} + +int lck_pin_check(const char *pin) +{ + if (!lck_pin_valid(pin)) return 0; + char *sp = lck_get(LCK_KEY_PIN); + if (sp) { + int ok = lck_hash_matches(sp, pin); + free(sp); + return ok; + } + /* system.pin 未设:迁移期回退校验 panel.pin */ + char *pp = lck_get(LCK_KEY_PANEL_PIN); + if (pp) { + int ok = lck_hash_matches(pp, pin); + free(pp); + return ok; + } + return 0; +} + +int lck_pin_set(const char *pin) +{ + if (!lck_pin_valid(pin)) return MEOW_ERR_INVAL; + char hash[160]; /* 32(salt) + ':' + 64(sha256 hex) + NUL = 98 */ + if (lck_make_hash(pin, hash, sizeof hash) != 0) return MEOW_ERR_IO; + if (meow_settings_set_str(LCK_KEY_PIN, hash) != MEOW_OK) return MEOW_ERR_INVAL; + lck_set_int(LCK_KEY_FAILS, 0); + lck_set_int(LCK_KEY_LOCK_UNTIL, 0); + lck_persist(); + LCK_LOG("pin set (%d digits, hash stored salt:sha256)\n", LCK_PIN_LEN); + return MEOW_OK; +} + +int lck_pin_clear(void) +{ + if (meow_settings_set_str(LCK_KEY_PIN, "") != MEOW_OK) return MEOW_ERR_INVAL; + lck_set_int(LCK_KEY_FAILS, 0); + lck_set_int(LCK_KEY_LOCK_UNTIL, 0); + lck_persist(); + LCK_LOG("pin cleared (unlock)\n"); + return MEOW_OK; +} + +int lck_pin_success(void) +{ + char *sp = lck_get(LCK_KEY_PIN); + if (!sp) { + /* 首次成功:把 panel.pin 并入 system.pin(一次性迁移),此后 system.pin 为准 */ + char *pp = lck_get(LCK_KEY_PANEL_PIN); + if (pp) { + meow_settings_set_str(LCK_KEY_PIN, pp); + LCK_LOG("success: migrated panel.pin -> system.pin\n"); + free(pp); + } else { + LCK_LOG("success: no system.pin and no panel.pin?!\n"); + } + } + free(sp); + lck_set_int(LCK_KEY_FAILS, 0); + lck_set_int(LCK_KEY_LOCK_UNTIL, 0); + lck_persist(); + return MEOW_OK; +} + +int lck_pin_fail(void) +{ + time_t now = time(NULL); + int fails = lck_get_int(LCK_KEY_FAILS); + int until = lck_get_int(LCK_KEY_LOCK_UNTIL); + /* 已锁定:不再递增(锁屏页本应禁输入;防御) */ + if (until > 0 && (time_t)until > now) + return (int)((time_t)until - now); + fails++; + if (fails >= LCK_PIN_MAX_FAILS) { + until = (int)now + LCK_PIN_LOCK_SEC; + fails = 0; + LCK_LOG("fail: %d consecutive -> LOCK %ds (until=%d)\n", + LCK_PIN_MAX_FAILS, LCK_PIN_LOCK_SEC, until); + } else { + until = 0; + LCK_LOG("fail: attempt %d/%d\n", fails, LCK_PIN_MAX_FAILS); + } + lck_set_int(LCK_KEY_FAILS, fails); + lck_set_int(LCK_KEY_LOCK_UNTIL, until); + lck_persist(); + return (until > 0 && (time_t)until > now) ? (int)((time_t)until - now) : 0; +} + +time_t lck_lock_remaining(void) +{ + time_t now = time(NULL); + int until = lck_get_int(LCK_KEY_LOCK_UNTIL); + if (until > 0 && (time_t)until > now) + return (time_t)until - now; + if (until > 0) { + /* 截止已过:清锁定戳+计数(进程内解禁),落盘 */ + LCK_LOG("lock expired (%d < now=%ld), clearing\n", until, (long)now); + lck_set_int(LCK_KEY_LOCK_UNTIL, 0); + lck_set_int(LCK_KEY_FAILS, 0); + lck_persist(); + } + return 0; +} diff --git a/patch_v2/meow_launcherd/lock_screen.h b/patch_v2/meow_launcherd/lock_screen.h new file mode 100644 index 0000000..4111f21 --- /dev/null +++ b/patch_v2/meow_launcherd/lock_screen.h @@ -0,0 +1,76 @@ +/* lock_screen.h — MeowOS 锁屏 4 位 PIN(系统级安全,纯逻辑;宿主可单测)。 + * + * 模型(用户定案,2026-09-02): + * - 设置键 `system.pin`:4 位数字 PIN 的 sha256+salt 哈希,存 settings.conf。 + * 格式与面板 panel.pin **全等**:`:`,salt 16B + * /dev/urandom → 32 hex;绝不存明文、绝不在日志/响应中输出 hash。 + * - 面板同源迁移:`panel.pin` 已设而 `system.pin` 未设时,锁屏接受 panel.pin,并在 + * **首次验证成功后并入 system.pin**(lck_pin_success 把 panel.pin 值拷入 system.pin)。 + * - 锁屏时机:息屏唤醒后(默认开,`system.pin_lock_on_wake=1` 可关)+ 电源菜单「锁定」 + * 手动锁;**开机不锁**(boot 进主屏不拦,本逻辑不被 boot 路径调用)。 + * - 错 5 次锁 5 分钟:`system.pin_fails`(连续错计数)与 `system.pin_lock_until`(锁定 + * 截止 epoch 秒)**持久化到 settings.conf**——launcher 重启不清零(面板的进程内存方案 + * 在重启后失效,这里要落盘)。 + * - 兜底:SSH root 删 `system.pin` 键即解锁(无其它后门)。 + * + * 本头只声明纯逻辑接口;渲染/输入循环在 lock_screen_ui.c(run_lock_screen)。逻辑依赖 + * meow_settings_* API(与 meowpanel 的 settings_get_value 语义同源,走同一 settings.conf)。 + * 宿主测试:#include "lock_screen.c" + 真实 meow_settings.c(-DMEOW_SETTINGS_FILE 覆盖)。 + */ +#ifndef MEOW_LOCK_SCREEN_H +#define MEOW_LOCK_SCREEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define LCK_PIN_LEN 4 /* 恰好 4 位数字 */ +#define LCK_PIN_MAX_FAILS 5 /* 连续错 PIN 锁定阈值 */ +#define LCK_PIN_LOCK_SEC 300 /* 锁定 5 分钟 */ +#define LCK_HASH_LEN 64 /* sha256 hex 长度 */ + +/* ---- 设置键(必须已在 meow_settings.c g_defs 注册)---- */ +#define LCK_KEY_PIN "system.pin" +#define LCK_KEY_WAKE "system.pin_lock_on_wake" +#define LCK_KEY_LOCK_UNTIL "system.pin_lock_until" +#define LCK_KEY_FAILS "system.pin_fails" +#define LCK_KEY_PANEL_PIN "panel.pin" /* 面板 PIN(read-only 兼容源) */ + +/* PIN 合法:恰好 4 位数字(0-9)。NULL/长度不符/含非数字 → 0。 */ +int lck_pin_valid(const char *pin); + +/* 是否已配锁屏凭据:system.pin 设 或 panel.pin 设(锁屏可开)。 */ +int lck_pin_configured(void); + +/* 唤醒是否应锁屏:system.pin_lock_on_wake(默认 1) 且 已配凭据。 + * 仅由「息屏唤醒」路径调用——boot 路径不调,天然开机不锁。 + * allow_disable=0 时忽略 WAKE 键(电源菜单「锁定」手动锁用,锁前需已配凭据)。 */ +int lck_lock_required(int allow_disable); + +/* 明文校验:system.pin 优先;未设时回退校验 panel.pin(迁移)。恒定比较,不反解。 */ +int lck_pin_check(const char *pin); + +/* 设置/修改 system.pin(明文→sha256+salt 落盘):校验 4 位数字;清连续错计数+锁定戳;**落盘**。 + * 返回 MEOW_OK / 负错误。设置页/CLI 设锁屏 PIN 走此。 */ +int lck_pin_set(const char *pin); +/* 清除 system.pin(解锁兜底,SSH root 删键即解锁的同义 API):清计数+锁定戳,**落盘**。 */ +int lck_pin_clear(void); + +/* 记录一次成功验证:首次成功时把 panel.pin 并入 system.pin(迁移);清连续错计数 + 清 + * 锁定戳;**落盘**。返回 MEOW_OK。 */ +int lck_pin_success(void); + +/* 记录一次失败:递增连续错计数(持久化);达 LCK_PIN_MAX_FAILS → 设锁定戳(now+300s)并清 + * 计数。已处于锁定(now<截止)时不递增。返回**剩余锁定秒**(0=未锁)。 + * 调用方须先 lck_lock_remaining() 确认未锁才让输入;已锁时本函数不改变截止。 */ +int lck_pin_fail(void); + +/* 剩余锁定秒(0=未锁定)。若截止已过,自动清锁定戳+计数并落盘(进程内已解禁)。 */ +time_t lck_lock_remaining(void); + +#ifdef __cplusplus +} +#endif +#endif /* MEOW_LOCK_SCREEN_H */ diff --git a/patch_v2/meow_launcherd/lock_screen_ui.c b/patch_v2/meow_launcherd/lock_screen_ui.c new file mode 100644 index 0000000..b28ccd8 --- /dev/null +++ b/patch_v2/meow_launcherd/lock_screen_ui.c @@ -0,0 +1,258 @@ +/* lock_screen_ui.c — MeowOS 锁屏页(系统级全屏页面:launcher 渲染,App 不可见不可触)。 + * + * 用户定案(2026-09-02): + * - 息屏唤醒后(默认开,system.pin_lock_on_wake=1 可关)+ 电源菜单「锁定」手动锁;开机不锁。 + * - 数字键盘直接用 SDK 按钮画(不调 IME);输入 4 位自动校验;错 5 次锁 5 分钟(持久化)。 + * - 正确 → 进主屏(return 1);锁屏页期间电源键=息屏(允许再睡,return 0),音量键不动; + * 不响应左缘返回 / 通知中心手势(本函数不实现这些手势,触摸只认键盘 tap)。 + * - 输入不回显数字(● 遮罩)。兜底见 lock_screen.c(SSH root 删 system.pin 即解锁)。 + * + * 渲染/输入循环与电源菜单同构(meow_touch + kd_* + kp_*),阻塞到解锁或电源息屏。 + * 纯逻辑在 lock_screen.c(可宿主单测);本文件只做 UI 与输入胶水。 + */ +#include "meow_launcher_internal.h" + +/* ---- 键盘几何(480×720):3 列×4 行圆角按钮 ---- */ +#define LK_X0 32 /* 首列左缘 */ +#define LK_COLW 130 /* 列宽 */ +#define LK_COLG 8 /* 列间隙 */ +#define LK_Y0 320 /* 首行顶缘 */ +#define LK_ROW_H 82 /* 行高 */ +#define LK_ROWG 10 /* 行间隙 */ +#define LK_ROWS 4 +#define LK_COLS 3 +/* 12 键(行优先):digit=-2 清空 / -1 退格 / >=0 数字 */ +#define LK_ACT_CLR (-2) +#define LK_ACT_BS (-1) +static const int lk_keys[LK_ROWS][LK_COLS] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 }, + { LK_ACT_CLR, 0, LK_ACT_BS }, +}; + +/* 键盘按钮 row*3+col 的命中矩形(x,y,w,h) */ +static void lk_key_rect(int row, int col, int *x, int *y, int *w, int *h) +{ + *x = LK_X0 + col * (LK_COLW + LK_COLG); + *y = LK_Y0 + row * (LK_ROW_H + LK_ROWG); + *w = LK_COLW; + *h = LK_ROW_H; +} + +/* 居中文本(真实字体优先;回退 ASCII 点阵) */ +static void lk_text(int cx, int y, const char *s, int size, uint32_t color) +{ + if (mfont_ready) { + int w = meow_font_width_text(s, size); + meow_font_drawtext((uint16_t *)fb, cx - w / 2, y, s, rgb565(color), size); + } else { + draw_text(cx - (int)strlen(s) * 3, y + 1, s, 1, color); + } +} + +/* 单个 PIN 圆点:filled=1 实心(已输入),0=空心(未输入)。 */ +static void lk_pin_dot(int cx, int cy, int r, int filled, uint32_t ink, uint32_t bg) +{ + if (filled) { + fill_circle(cx, cy, r, ink); + } else { + fill_circle(cx, cy, r, ink); /* 外圈(描边) */ + fill_circle(cx, cy, r - 3, bg); /* 内孔(表面底色) */ + } +} + +static void lk_draw_pin(int entered, const meow_ui_theme_t *th) +{ + int cx = fb_xres / 2; + int y = 210; + int gap = 48, r = 20; + int x0 = cx - (3 * gap) / 2; /* 首圆圆心;四圆等距 gap */ + for (int i = 0; i < LCK_PIN_LEN; i++) + lk_pin_dot(x0 + i * gap, y, r, i < entered, th->primary, th->surface); +} + +static void lk_draw(int entered, const char *status, time_t locked_until) +{ + const meow_ui_theme_t *th = s_ui_theme(); + fill_rect(0, 0, fb_xres, fb_yres, th->surface); /* 全屏表面底色 */ + lk_text(fb_xres / 2, 120, locked_until > 0 ? "已锁定" : "输入 PIN 解锁", 24, th->on_surface); + lk_draw_pin(entered, th); + lk_text(fb_xres / 2, 260, status, 18, status[0] ? th->error : th->on_surface_variant); + for (int r = 0; r < LK_ROWS; r++) { + for (int c = 0; c < LK_COLS; c++) { + int x, y, w, h; lk_key_rect(r, c, &x, &y, &w, &h); + fill_round_rect(x, y, w, h, 12, th->surface_container); + int key = lk_keys[r][c]; + if (key >= 0) { + char d[2] = { (char)('0' + key), 0 }; + if (mfont_ready) { + int fw = meow_font_width_text(d, 30); + meow_font_drawtext((uint16_t *)fb, x + w / 2 - fw / 2, + y + LK_ROW_H / 2 - meow_font_line_height(30) / 2, + d, rgb565(th->on_surface), 30); + } else { + draw_text(x + w / 2 - 7, y + LK_ROW_H / 2 - 14, d, 2, th->on_surface); + } + } else if (key == LK_ACT_CLR) { + const char *s = "C"; const char *t = s; + if (mfont_ready) t = "清空"; + lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t, 22, th->on_surface); + } else { /* LK_ACT_BS */ + const char *t = "DEL"; + if (mfont_ready) t = "退格"; + lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t, 22, th->on_surface); + } + } + } + meow_dirty_full(); + fb_pan_buf0_force(); + meow_ui_flush(); +} + +/* 阻塞式锁屏页。返回 1=已解锁,0=锁屏期间电源键息屏。 */ +int run_lock_screen(void) +{ + int fbfd = open(FB_DEV, O_RDWR); + if (fbfd >= 0) { ioctl(fbfd, FBIOBLANK, 0); close(fbfd); } /* 亮起面板 */ + meow_screen_on = 1; + fb_pan_buf0_force(); + + meow_touch mt; mt_reset(&mt); + int tfd = open(DEV_TOUCH, O_RDONLY | O_NONBLOCK); + struct keypoll kp_power, kp_keys; + kp_open(&kp_power, DEV_POWER); + kp_open(&kp_keys, DEV_KEYS); + + int entered = 0; + char digits[LCK_PIN_LEN + 1] = {0}; + char status[64] = ""; + time_t lock_until = 0; + { + time_t rem = lck_lock_remaining(); + if (rem > 0) { lock_until = time(NULL) + rem; snprintf(status, sizeof status, "已锁定"); } + } + time_t last_countdown = 0; + + lk_draw(entered, status, lock_until); /* 首帧 */ + + int unlocked = 0; + for (;;) { + time_t now = time(NULL); + int redraw = 0; + + /* 锁定到期自动解禁(清计数+戳),仅重画状态行 */ + if (lock_until > 0 && now >= lock_until) { + lck_lock_remaining(); + lock_until = 0; + status[0] = 0; + redraw = 1; + } + if (lock_until > 0 && now != last_countdown) { + last_countdown = now; + time_t rem = lock_until - now; + if (mfont_ready) snprintf(status, sizeof status, "已锁定,请 %d:%02d 后重试", + (int)(rem / 60), (int)(rem % 60)); + else snprintf(status, sizeof status, "LOCKED %d:%02d", (int)(rem / 60), (int)(rem % 60)); + redraw = 1; + } + + /* 电源键=息屏(允许再睡);音量键本页不动 */ + if (kp_power.ok) { + kp_poll(&kp_power, NULL, NULL); + if (kp_is_down(&kp_power, KEY_POWER)) { + logmsg("lock: power key -> screen off (stay locked)"); + meow_panel_off(); + break; /* return 0 */ + } + } + kp_poll(&kp_keys, NULL, NULL); + (void)kp_is_down(&kp_keys, KEY_VOLUMEDOWN); + (void)kp_is_down(&kp_keys, KEY_VOLUMEUP); + + /* 触摸:只认键盘 tap(滑动手势忽略:不左缘返回、不通知中心下拉) */ + if (tfd >= 0) { + struct pollfd pfd = { tfd, POLLIN, 0 }; + if (poll(&pfd, 1, 0) > 0) { + unsigned char ev[16]; + while (read(tfd, ev, sizeof ev) == (ssize_t)sizeof ev) { + uint16_t type = *(uint16_t *)(ev + 8), code = *(uint16_t *)(ev + 10); + int32_t val = *(int32_t *)(ev + 12); + if (type == EV_ABS) { + if (code == ABS_X || code == ABS_MT_POSITION_X) mt_abs(&mt, val, mt.last_y); + else if (code == ABS_Y || code == ABS_MT_POSITION_Y) mt_abs(&mt, mt.last_x, val); + } else if (type == EV_KEY && code == BTN_TOUCH) { + if (val && !mt.down) { + mt_down(&mt); + } else if (!val && mt.down) { + int dx, dy; + if (mt_up(&mt, &dx, &dy)) { + int handled = 0; + for (int r = 0; r < LK_ROWS && !handled; r++) { + for (int c = 0; c < LK_COLS && !handled; c++) { + int x, y, w, h; lk_key_rect(r, c, &x, &y, &w, &h); + if (!mt_in(&mt, x, y, w, h)) continue; + handled = 1; + int key = lk_keys[r][c]; + if (lock_until > 0) break; /* 锁定期间键盘不响应 */ + if (key >= 0) { + if (entered < LCK_PIN_LEN) { + digits[entered++] = (char)('0' + key); + digits[entered] = 0; + } + } else if (key == LK_ACT_CLR) { + entered = 0; digits[0] = 0; + } else if (key == LK_ACT_BS) { + if (entered > 0) { entered--; digits[entered] = 0; } + } + redraw = 1; + if (entered == LCK_PIN_LEN) { + if (lck_pin_check(digits)) { + lck_pin_success(); + logmsg("lock: PIN OK -> unlock"); + unlocked = 1; + goto done; + } else { + int rem = lck_pin_fail(); + entered = 0; digits[0] = 0; + if (rem > 0) { + lock_until = time(NULL) + rem; + last_countdown = 0; + snprintf(status, sizeof status, "已锁定"); + } else { + if (mfont_ready) snprintf(status, sizeof status, "PIN 错误"); + else snprintf(status, sizeof status, "WRONG PIN"); + } + } + } + } + } + } + } + } + } + } + } + + /* 整页重绘(锁屏低频交互,简单整页即可) */ + if (redraw) lk_draw(entered, status, lock_until); + usleep(POLL_MS * 1000); + } +done: + if (tfd >= 0) close(tfd); + kp_close(&kp_power); + kp_close(&kp_keys); + logmsg("lock: exit unlocked=%d screen_on=%d", unlocked, meow_screen_on); + return unlocked; +} + +/* 电源菜单「锁定」/ 唤醒共用的进入点。未配凭据 → no-op(无鉴别物,锁了也解不开)。 + * 返回与 run_lock_screen 同(1 已解锁 / 0 电源息屏 / 0 无凭据跳过)。 */ +int meow_lock_screen_engage(void) +{ + if (!lck_pin_configured()) { + logmsg("lock: no system.pin/panel.pin configured -> skip lock (no-op)"); + return 0; + } + return run_lock_screen(); +} diff --git a/patch_v2/meow_launcherd/main_menu.c b/patch_v2/meow_launcherd/main_menu.c index 0a6527a..4e176f4 100644 --- a/patch_v2/meow_launcherd/main_menu.c +++ b/patch_v2/meow_launcherd/main_menu.c @@ -1141,10 +1141,31 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state, if (meow_screen_on) { /* M2.8 Bug 3-residual:唤醒不是 draw 事件,LCDC 在 FBIOBLANK(4)->(0) * 后指向的内容可能已失效/为空;必须把当前界面(应用列表)整体重绘到 - * buffer0 并强制 re-pan,而非仅翻页/仅 dirty 重绘(M2.7 仍黑屏根因)。 */ + * buffer0 并强制 re-pan,而非仅翻页/仅 dirty 重绘(M2.7 仍黑屏根因)。 + * M2.x 锁屏:唤醒后若需锁屏(system.pin_lock_on_wake=1 且已配 PIN)→ + * 先弹阻塞式锁屏页;解锁才重绘主屏,息屏则留 dirty 下次再锁。 */ meow_auto_slept = 0; /* M2.17:手动/自动息屏后电源单击唤醒均清自动态 */ last_input_ms = now_ms(); /* M2.18 Bug 1:用真实墙钟(防 t 过期回绕) */ - meow_repaint_surface(apps, napps, state, cursor); + if (lck_lock_required(1)) { + int lr = run_lock_screen(); + /* 重播按键防抖基线(阻塞期间按键可能已变化,防误判新边沿) */ + memset(&kd_pwr, 0, sizeof kd_pwr); + memset(&kd_vold, 0, sizeof kd_vold); + memset(&kd_volu, 0, sizeof kd_volu); + memset(&kd_play, 0, sizeof kd_play); + kp_poll(&kp_power, NULL, NULL); + kp_poll(&kp_keys, NULL, NULL); + m_prev_pwr = kp_is_down(&kp_power, KEY_POWER); + m_prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN); + m_prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP); + m_prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE); + if (lr == 1) + meow_repaint_surface(apps, napps, state, cursor); /* 已解锁重绘主屏 */ + else + dirty = 1; /* 锁屏期间电源息屏 → 屏已关,留 dirty 下次亮屏再锁 */ + } else { + meow_repaint_surface(apps, napps, state, cursor); + } } else { dirty = 1; /* 息屏:面板已黑,留 dirty 供下次亮屏重绘 */ last_input_ms = now_ms(); /* M2.18 Bug 1:用真实墙钟 */ @@ -1531,7 +1552,26 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state, meow_panel_on(); meow_auto_slept = 0; last_input_ms = now_ms(); - meow_repaint_surface(apps, napps, state, cursor); /* M2.8 唤醒重绘 */ + /* M2.x 锁屏:自动息屏唤醒后若需锁屏 → 弹阻塞式锁屏页;解锁才重绘,息屏留 dirty */ + if (lck_lock_required(1)) { + int lr = run_lock_screen(); + memset(&kd_pwr, 0, sizeof kd_pwr); + memset(&kd_vold, 0, sizeof kd_vold); + memset(&kd_volu, 0, sizeof kd_volu); + memset(&kd_play, 0, sizeof kd_play); + kp_poll(&kp_power, NULL, NULL); + kp_poll(&kp_keys, NULL, NULL); + m_prev_pwr = kp_is_down(&kp_power, KEY_POWER); + m_prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN); + m_prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP); + m_prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE); + if (lr == 1) + meow_repaint_surface(apps, napps, state, cursor); /* 已解锁重绘主屏 */ + else + dirty = 1; /* 锁屏期间电源息屏 → 屏已关,留 dirty 下次亮屏再锁 */ + } else { + meow_repaint_surface(apps, napps, state, cursor); /* M2.8 唤醒重绘 */ + } } if (screen_off_timeout > 0 && meow_screen_on && !meow_auto_slept && !fg_keep_awake()) { /* M2.18 Bug 1: 用 idle_ms_since 取差,杜绝 t305,居中 240) */ #define PM_GAPY 140 /* 相邻行圆心 y 间距 */ @@ -821,6 +822,10 @@ void settings_sync_cache_listener(const char *key); void settings_volume_adj(int delta); int settings_volume_get(void); int show_power_menu(void); +/* M2.x 锁屏:阻塞式锁屏页(渲染+输入);返回 1=已解锁(回主屏),0=锁屏期间电源键息屏(屏已关)。 + * meow_lock_screen_engage() 供电源菜单「锁定」与唤醒路径用;未配凭据时 no-op。 */ +int run_lock_screen(void); +int meow_lock_screen_engage(void); int stop_player(void); /* 电源管理服务 IPC(SYSTEM_FRAMEWORK_SERVICES §3.2:launcher 电源动作经 meow_svc_power 执行) */ int pm_power_ipc(uint32_t cmd, uint32_t flag, const char *arg, char *res, int reslen); diff --git a/patch_v2/meow_launcherd/meow_launcherd b/patch_v2/meow_launcherd/meow_launcherd index 18628c6..bc8096f 100755 Binary files a/patch_v2/meow_launcherd/meow_launcherd and b/patch_v2/meow_launcherd/meow_launcherd differ diff --git a/patch_v2/meow_launcherd/meow_settings.c b/patch_v2/meow_launcherd/meow_settings.c index 2bf2d01..1046305 100644 --- a/patch_v2/meow_launcherd/meow_settings.c +++ b/patch_v2/meow_launcherd/meow_settings.c @@ -147,6 +147,22 @@ static const meow_setting_def g_defs[] = { /* 系统(M2.54:主机名,bootmenu S89meow_svcd 开机读 system.hostname sethostname; * 缺省 meowos;RFC1035 首标签,供 meow_mdns 公布 .local) */ { "system.hostname", SET_STR, "meowos", 0, 0, NULL, NULL }, + /* 锁屏 4 位 PIN(系统级安全;格式与面板 panel.pin 全等: + * `:`,salt 16B /dev/urandom → 32 hex,绝不存明文。 + * 空 = 未设锁屏 PIN(锁屏功能关闭)。与面板同源:panel.pin 已设而本键未设时,锁屏 + * 接受 panel.pin 并在首次成功后并入本键(lck_pin_success,见 lock_screen.c)。 */ + { "system.pin", SET_STR, "", 0, 0, NULL, NULL }, + /* 息屏唤醒后锁屏(int 0/1,默认 1=开)。0=关(唤醒直进主屏;电源菜单「锁定」手动锁仍可用)。 */ + { "system.pin_lock_on_wake", SET_INT, "1", 0, 1, NULL, NULL }, + /* 错 PIN 锁定截止戳(int,epoch 秒;0=未锁定)。**持久化到 settings**(防 launcher 重启 + * 清零——面板进程内存方案在重启后失效,此处落盘)。截止前锁屏页禁输入并倒计。 */ + { "system.pin_lock_until", SET_INT, "0", 0, 0, NULL, NULL }, + /* 连续错 PIN 计数(int 0-4;达 5 触发锁定并清零)。持久化防「关机重启重试」。 */ + { "system.pin_fails", SET_INT, "0", 0, 0, NULL, NULL }, + /* 面板访问级 PIN(meowpanel 写入,格式与本表 system.pin 全等)。launcher 注册它仅为: + * ①锁屏迁移期读取(system.pin 未设时接受 panel.pin);②本表 save 重写全文件时**保留** + * 该行(write_full_conf 只写已注册键,不注册会把面板已设的 panel.pin 抹掉)。launcher 不写它。 */ + { "panel.pin", SET_STR, "", 0, 0, NULL, NULL }, }; /* ---- 日志(自包含,不依赖 launcher 的 logmsg;前缀 "settings:")---- */ diff --git a/patch_v2/meow_launcherd/pm_icons.h b/patch_v2/meow_launcherd/pm_icons.h index df567eb..ca76dd1 100644 --- a/patch_v2/meow_launcherd/pm_icons.h +++ b/patch_v2/meow_launcherd/pm_icons.h @@ -59,11 +59,71 @@ static const unsigned char PM_ICON_CANCEL[3136]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; -static const pm_icon_t pm_icons[5] = { +static const unsigned char PM_ICON_LOCK[3136]={ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +static const pm_icon_t pm_icons[6] = { { PM_ICON_POWER }, { PM_ICON_RESTART }, { PM_ICON_BACK }, { PM_ICON_UPGRADE }, + { PM_ICON_LOCK }, { PM_ICON_CANCEL }, }; diff --git a/patch_v2/meow_launcherd/power_menu.c b/patch_v2/meow_launcherd/power_menu.c index ff6d05a..8a3c4f5 100644 --- a/patch_v2/meow_launcherd/power_menu.c +++ b/patch_v2/meow_launcherd/power_menu.c @@ -2,11 +2,13 @@ /* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/ #include "meow_launcher_internal.h" -static const int pm_cy[PM_NUM] = { 250, 250, 390, 390, 530 }; /* 圆心 y(行距 140) */ +/* M2.x 锁屏:电源菜单增「锁定」手动锁。网格 2 列×3 行:0=关机 1=重启 2=回HiBy 3=升级 + * 4=锁定 5=取消;取消仍是安全默认项(PM_NUM-1),电源键确认/导航不会误触发关机。 */ +static const int pm_cy[PM_NUM] = { 250, 250, 390, 390, 530, 530 }; /* 圆心 y(行距 140) */ -static const char *pm_labels_cn[PM_NUM] = { "关机", "重启", "回HiBy", "升级", "取消" }; +static const char *pm_labels_cn[PM_NUM] = { "关机", "重启", "回HiBy", "升级", "锁定", "取消" }; -static const char *pm_labels_en[PM_NUM] = { "POWER OFF", "RESTART", "HIBYOS", "UPGRADE", "CANCEL" }; +static const char *pm_labels_en[PM_NUM] = { "POWER OFF", "RESTART", "HIBYOS", "UPGRADE", "LOCK", "CANCEL" }; /* 电源菜单圆钮图标:从 pm_icons.h 的 56×56 alpha 位图 blit 到 fb(以圆钮中心 cx,cy 为锚居中)。 @@ -318,7 +320,7 @@ int show_power_menu(void) logmsg("pm: idle %ds -> close menu + auto screen-off (act=cancel)", screen_off_timeout); meow_panel_off(); meow_auto_slept = 1; - act = 4; /* 取消(关菜单) */ + act = PM_NUM - 1; /* 取消(关菜单) */ break; } @@ -328,12 +330,19 @@ int show_power_menu(void) if (tfd >= 0) close(tfd); /* M2.11 B:电源键在菜单 = 按当前项确认。act 与电源动作逐项核对: - * act==4(取消) -> 只关菜单,绝不关机(return 0,永不到电源动作); + * act==PM_NUM-1(取消) -> 只关菜单,绝不关机(return 0,永不到电源动作); + * act==4(锁定) -> 手动锁(meow_lock_screen_engage,未配 PIN 则 no-op),关菜单; * act==0(关机)/act==1(重启)/act==2(回HibyOS)/act==3(升级模式) -> 才经服务 IPC 执行电源动作。 * 只有 act 为 0/1/2/3 才可能发电源动作;power 键本身绝不触发关机。 */ logmsg("pm: act=%d -> %s", act, - act == 0 ? "关机" : act == 1 ? "重启" : act == 2 ? "回HibyOS" : act == 3 ? "升级模式" : "取消"); - if (act == 4) { logmsg("pm: cancel (close menu only)"); return 0; } + act == 0 ? "关机" : act == 1 ? "重启" : act == 2 ? "回HibyOS" : act == 3 ? "升级模式" + : act == 4 ? "锁定" : "取消"); + if (act == PM_NUM - 1) { logmsg("pm: cancel (close menu only)"); return 0; } + if (act == 4) { + logmsg("pm: LOCK SCREEN"); + meow_lock_screen_engage(); + return 0; /* 关菜单;run_menu 继续(锁屏页已阻塞到解锁或电源息屏) */ + } /* 动作统一经电源管理服务 IPC(meow_svc_power);launcher 不再直连 poweroff/reboot(2)/写 mtd5/ * 写 boot 默认——关机/重启/升级/回HiBy 的动作原语都在服务内实现(§3.2 单一动作链路)。 */ /* 公共服务:执行动作;服务不可达 → 按「取消」返回(launcher 存活,绝不误报已动作卡死)。 diff --git a/patch_v2/meow_launcherd/test_lock_screen.c b/patch_v2/meow_launcherd/test_lock_screen.c new file mode 100644 index 0000000..b5c92e6 --- /dev/null +++ b/patch_v2/meow_launcherd/test_lock_screen.c @@ -0,0 +1,123 @@ +/* test_lock_screen.c — M6.x 宿主测试:锁屏 4 位 PIN(lock_screen.c 纯逻辑 + 真实 meow_settings 后端)。 + * + * 直接 #include "meow_settings.c" + "lock_screen.c" 于同一 TU(同 test_notif_tf.c 手法),以「真实 + * settings.conf」验证:设置/校验/错5锁定持久化(重启不清)/唤醒路径触发/panel.pin 迁移。 + * 与面板面板访问级 PIN 同格式(`salt:sha256(salt||pin)`),sha256 算法与 meowpanel 逐字一致。 + * + * 运行:gcc -D_GNU_SOURCE -DMEOW_SETTINGS_FILE=\"/tmp/meow_lock_test/settings.conf\" \ + * -o test_lock_screen test_lock_screen.c mydir? —— 实际在 launcher 目录: + * gcc -D_GNU_SOURCE -DMEOW_SETTINGS_FILE=\"/tmp/meow_lock_test/settings.conf\" \ + * -I. -o test_lock_screen test_lock_screen.c && ./test_lock_screen + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +/* 覆盖存储路径到临时目录(需父目录存在;test 内 mkdir) */ +#include "meow_settings.c" +#include "lock_screen.c" + +static int g_fail = 0; +#define CHECK(cond, msg) do { \ + if (!(cond)) { fprintf(stderr, "FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); g_fail++; } \ + else fprintf(stderr, "ok: %s\n", msg); \ +} while (0) + +int main(void) +{ + const char *conf = MEOW_SETTINGS_FILE; + mkdir("/tmp/meow_lock_test", 0755); + unlink(conf); + unlink("/data/meow_boot_default"); /* 尽力清掉 save 的 boot.default 同步残留(非 root 会失败,无碍) */ + + /* ---- 初始化(空文件:默认值激活)---- */ + CHECK(meow_settings_init() == MEOW_OK, "init (empty conf)"); + CHECK(lck_pin_configured() == 0, "no pin configured initially"); + CHECK(lck_lock_required(1) == 0, "wake-required=0 when no pin"); + + /* ---- 设置/校验(lck_pin_set 落盘 + 校验正确/错误)---- */ + CHECK(lck_pin_set("1234") == MEOW_OK, "set pin 1234"); + CHECK(lck_pin_configured() == 1, "configured after set"); + CHECK(lck_lock_required(1) == 1, "wake-required=1 (pin set, wake=1 default)"); + CHECK(lck_pin_check("1234") == 1, "check correct pin"); + CHECK(lck_pin_check("0000") == 0, "check wrong pin"); + CHECK(lck_pin_valid("1234") == 1, "valid 4-digit"); + CHECK(lck_pin_valid("12") == 0, "invalid len 2"); + CHECK(lck_pin_valid("12a4") == 0, "invalid non-digit"); + CHECK(lck_pin_valid("") == 0, "invalid empty"); + CHECK(lck_pin_set("12") == MEOW_ERR_INVAL, "reject set non-4-digit"); + + /* ---- 唤醒路径触发 / 开关 ---- */ + CHECK(lck_lock_required(0) == 1, "manual lock(allow_disable=0) always required"); + meow_settings_set_int("system.pin_lock_on_wake", 0); + meow_settings_save(); + CHECK(lck_lock_required(1) == 0, "wake-required=0 after wake=0"); + CHECK(lck_lock_required(0) == 1, "manual still required after wake=0"); + meow_settings_set_int("system.pin_lock_on_wake", 1); + meow_settings_save(); + CHECK(lck_lock_required(1) == 1, "wake-required=1 after wake=1"); + /* 开机不锁是结构性(boot 路径不调 lck_lock_required)——此处断言决策函数正确即隔离该项 */ + + /* ---- 错 5 次锁定(持久化,重启不清)---- */ + CHECK(lck_lock_remaining() == 0, "not locked before fails"); + int r1 = lck_pin_fail(); /* 1 */ + int r2 = lck_pin_fail(); /* 2 */ + int r3 = lck_pin_fail(); /* 3 */ + int r4 = lck_pin_fail(); /* 4 */ + CHECK(r1 == 0 && r2 == 0 && r3 == 0 && r4 == 0, "first 4 fails not locked"); + int r5 = lck_pin_fail(); /* 5 -> lock */ + CHECK(r5 > 280 && r5 <= 300, "5th fail locks ~300s"); + time_t rem1 = lck_lock_remaining(); + CHECK(rem1 > 280 && rem1 <= 300, "lock_remaining reflects lockout"); + + /* 模拟 launcher 重启(重读 settings.conf)→ 锁定戳持久,不因重启清零 */ + meow_settings_shutdown(); + CHECK(meow_settings_init() == MEOW_OK, "re-init after shutdown (simulated reboot)"); + time_t re = lck_lock_remaining(); + CHECK(re > 280 && re <= 300, "lock persists after reboot (persisted)"); + + /* 锁定期间:正确 PIN 仍被识别(但 UI 层禁 input;此处验证逻辑不因锁定改变凭据) */ + CHECK(lck_pin_check("1234") == 1, "correct pin still recognized during lockout"); + + /* 锁定到期:把截止戳拨到过去 → remaining 自动清 + 清零(进程内解禁) */ + meow_settings_set_int("system.pin_lock_until", (int)time(NULL) - 1); + meow_settings_save(); + CHECK(lck_lock_remaining() == 0, "expired lock auto-clears"); + int fails_now; meow_settings_get_int("system.pin_fails", &fails_now); + CHECK(fails_now == 0, "fail counter cleared after expiry"); + + /* ---- panel.pin 迁移:system.pin 未设 + panel.pin 已设 → 接受 panel.pin;首成功并入 system.pin ---- */ + /* 造一个合法 panel.pin(复用本库的哈希:先 set 得 hash 值,清 system.pin 转写 panel.pin) */ + lck_pin_set("5678"); + char pphash[MEOW_SET_MAX_LEN]; + CHECK(meow_settings_get_str("system.pin", pphash, sizeof pphash) == MEOW_OK, "get system.pin hash"); + meow_settings_set_str("system.pin", ""); /* 模拟 system.pin 未设 */ + meow_settings_set_str("panel.pin", pphash); /* 面板已设 panel.pin(同 hash 格式) */ + meow_settings_save(); + CHECK(lck_pin_configured() == 1, "configured via panel.pin only"); + CHECK(lck_lock_required(1) == 1, "wake-required via panel.pin"); + CHECK(lck_pin_check("5678") == 1, "accept panel.pin when system.pin absent"); + CHECK(lck_pin_check("1234") == 0, "reject wrong pin during migration"); + /* 首次成功 → 并入 system.pin */ + lck_pin_success(); + char sp2[MEOW_SET_MAX_LEN]; + CHECK(meow_settings_get_str("system.pin", sp2, sizeof sp2) == MEOW_OK && sp2[0], "system.pin set after migrate"); + CHECK(strcmp(sp2, pphash) == 0, "system.pin == panel.pin after migrate"); + CHECK(lck_pin_check("5678") == 1, "still accept after migrate (via system.pin)"); + + /* ---- 清除(兜底解锁):lck_pin_clear 后不再要求锁 ---- */ + /* lck_pin_clear 只清 system.pin;panel.pin 仍存(迁移源),先清掉再断言全部未配 */ + meow_settings_set_str("panel.pin", ""); + meow_settings_save(); + CHECK(lck_pin_clear() == MEOW_OK, "pin clear"); + CHECK(lck_pin_configured() == 0, "not configured after clear"); + CHECK(lck_lock_required(1) == 0, "wake-required=0 after clear"); + + unlink(conf); + printf("\n%s\n", g_fail ? "SOME TESTS FAILED" : "ALL PASS"); + return g_fail ? 1 : 0; +} diff --git a/patch_v2/meow_launcherd/util.c b/patch_v2/meow_launcherd/util.c index 7170bd9..5e75a9e 100644 --- a/patch_v2/meow_launcherd/util.c +++ b/patch_v2/meow_launcherd/util.c @@ -847,8 +847,10 @@ void meow_sys_light(void) bt_role_refresh(0); /* M2.48:节流探测蓝牙角色(约 5s 一次,不阻塞主循环) */ } +/* M2.x 锁屏:电源菜单扩为 6 钮(2 列×3 行),列坐标 0/1/0/1/0/1;取消(PM_NUM-1)落右下。 */ const int pm_cx[PM_NUM] = { PM_CARD_CX - PM_GAPX / 2, PM_CARD_CX + PM_GAPX / 2, - PM_CARD_CX - PM_GAPX / 2, PM_CARD_CX + PM_GAPX / 2, PM_CARD_CX }; + PM_CARD_CX - PM_GAPX / 2, PM_CARD_CX + PM_GAPX / 2, + PM_CARD_CX - PM_GAPX / 2, PM_CARD_CX + PM_GAPX / 2 }; static struct meow_ipc_msg ipc_ring[MEOW_IPC_RING];