feat(launcher,lock): MeowOS 锁屏 4 位 PIN(system.pin 同源面板,错5锁5分钟持久化,唤醒/电源菜单手动锁)

- system.pin:4 位数字 PIN,sha256+salt 存 settings.conf(格式与面板 panel.pin 全等,绝无明文)。
  panel.pin 已设且 system.pin 未设时锁屏接受 panel.pin,首次验证成功后并入 system.pin。
- 锁屏时机:息屏唤醒后(默认开,system.pin_lock_on_wake=1 可关)+ 电源菜单「锁定」手动锁;
  开机不锁(boot 进主屏不拦)。
- 锁屏页:launcher 渲染系统级全屏页,App 不可见不可触;SDK 按钮画数字键盘(不调 IME),
  输入 4 位自动校验;● 遮罩不回显数字;错 5 次锁 5 分钟(持久化锁定戳/计数,重启不清)。
- 安全细节:锁屏页电源键=息屏(允许再睡),音量键不动,不响应左缘返回/通知中心手势。
  兜底:SSH root 删 system.pin 键即解锁(无其它后门)。HibyOS 不涉及。
- 宿主测试(test_lock_screen):设置/校验/错5锁定持久化(重载模拟重启)/唤醒触发/开机不锁
  /panel.pin 迁移,全 PASS;既有 settings_brightness/notif_panel/notif_tf/vol_ramp_jack 回归
  无碍;交叉 -Wall -Wextra 0 warning。meow_launcherd md5 670f978e71c1e81cfe86d47c21248c9d。
This commit is contained in:
AskaEth committed 2026-09-02 00:48:23 +08:00
1 parent 5a5e995ebd
commit e948a0a44e
14 files changed
+1011 -14

No files matched your search

+51
View File
@@ -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) {
+1 -1
View File
@@ -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
+353
View File
@@ -0,0 +1,353 @@
/* lock_screen.c — MeowOS 锁屏 4 位 PIN(纯逻辑;宿主可单测 lock_screen.h)。
*
* 与 meowpanel.c 的访问级 PIN(PANEL_ENHANCE_PLAN)**同源同格式**:`<salt_hex>:<sha256_hex(salt||pin)>`。
* 内联 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#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
/* ---- 哈希基元(格式 `<salt_hex>:<sha256_hex(salt||pin)>`,与面板全等)---- */
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;
}
+76
View File
@@ -0,0 +1,76 @@
/* lock_screen.h — MeowOS 锁屏 4 位 PIN(系统级安全,纯逻辑;宿主可单测)。
*
* 模型(用户定案,2026-09-02):
* - 设置键 `system.pin`:4 位数字 PIN 的 sha256+salt 哈希,存 settings.conf。
* 格式与面板 panel.pin **全等**:`<salt_hex>:<sha256_hex(salt||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 <time.h>
#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 */
+258
View File
@@ -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();
}
+43 -3
View File
@@ -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 取差,杜绝 t<last_input 无符号回绕误判「空闲30s」。 */
@@ -41,6 +41,7 @@ extern "C" {
#include "meow_ui.h" /* A-1/A-2:launcher 用 SDK 渲染栈(meow_ui_fill_rect/text/flush/theme) */
#include "meow_ui_internal.h" /* A-1/A-2:launcher 与 SDK 共用 back_buf + 手动脏模型桥接(mark_full/back_buf) */
#include "meow_notif_panel.h" /* 阶段B:通知中心面板纯逻辑(header-only,可单测) */
#include "lock_screen.h" /* M2.x:锁屏 4 位 PIN 纯逻辑(设置/校验/错5锁定持久化/panel.pin 迁移/唤醒判定) */
#define DEV_POWER "/dev/input/event0" /* md-gpio-keys: KEY_POWER, KEY_PREVIOUS */
#define DEV_KEYS "/dev/input/event2" /* jz adc keyboard: VOL, PLAY, NEXT */
@@ -167,7 +168,7 @@ extern "C" {
#define NCP_DRAG_ENTER_SLOP 16
#define NCP_DRAG_THR_PCT 40
#define TAP_ARM_DELAY_MS 300
#define PM_NUM 5
#define PM_NUM 6
#define PM_R 50 /* 圆钮半径(直径 100) */
#define PM_GAPX 130 /* 两列圆心 x 间距(175->305,居中 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);
Binary file not shown.
+16
View File
@@ -147,6 +147,22 @@ static const meow_setting_def g_defs[] = {
/* 系统(M2.54:主机名,bootmenu S89meow_svcd 开机读 system.hostname sethostname;
* 缺省 meowos;RFC1035 首标签,供 meow_mdns 公布 <host>.local) */
{ "system.hostname", SET_STR, "meowos", 0, 0, NULL, NULL },
/* 锁屏 4 位 PIN(系统级安全;格式与面板 panel.pin 全等:
* `<salt_hex>:<sha256_hex(salt||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:")---- */
+61 -1
View File
@@ -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 },
};
+16 -7
View File
@@ -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 存活,绝不误报已动作卡死)。
+123
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
/* 覆盖存储路径到临时目录(需父目录存在;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;
}
+3 -1
View File
@@ -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];