feat: M6.44+M6.45 收编 — 真机 0.8.45 现场保全(reader章节/key_router按键路由/WiFi连接修复/ncm bench+IME修复)

脏工作区现场收编:devdocs/local/m6_44_45_dirty_tracked.patch(46 个已跟踪文本文件,
+3090/-585, 已对 M6.43 基线干净适配) + m6_44_45_untracked_src.tar.gz 中 patch_v2/ 下
13 个新增源文件, 合成单个提交, 内容与容器工作树(R3ProII/patch_v2)逐字节一致。

- reader 章节: 新增 core/rdchapter.{h,c} — TXT 章节规范解析器, 扫描 512KB 码位对齐段文件
  逐段流式识别标题行(行首缩进≤4/显示宽≤40/行内标点≤1 三重约束防误判), 输出标题+全书字符
  偏移+段号/段内偏移+阿拉伯章号(中文数字归一), 首章前内容归"卷首"; book/reader/rdenc/
  rdlayout/settings 接入章节跳转与进度锚点, 附 chapter_smoke/turn_repro/build_turn_repro.sh
- key_router 按键路由: 新增 meow_launcherd/key_router.{h,c} — launcher-UI 页物理键统一路由
  (P1 仅音量键), 把读取边沿+防抖+长按 held+ramp 推进从 5 个会话收敛为进程内单开;
  各页每圈 key_router_pump() 取防抖后边沿, 动作(调音量/光标导航)仍由调用页决定,
  保留 run_menu 的 meow_mode 分支; App 前台 volume_page 武装时边沿抑制;
  main_menu/now_playing/notif_center/settings_page/subpage 接入, 附 test_key_router_vol/test_np_keys
- WiFi 连接修复: meow_netmgrd connect 有界等待 wpa_state=COMPLETED(普通 10s/隐藏网络 15s,
  env 可调)修设置页连接无响应; list_networks/scan_results 的 \xNN 转义还原始字节, 修中文 SSID
  永不命中致新建同名 open 记录顶影; launcher netmgr_ipc 配读超时; 附 test_wifi_connect(桩
  wpa_cli 驱动 static wifi_connect_ex), test_netmgrd.sh 补用例; wifi_page 同步
- ncm bench: 新增 bench/lyric_sim.c、bench/plpage_sim.c, vol_sync_sim 与 ncm core/app 同步
- SDK IME 修复: shift 大小写、K_ENTER 只提交不关、阻塞 IME 循环喂 on_tick 不被遮挡、
  IME 打开 ~200ms TAP 冷却窗吞 stray-TAP; 附 sdk/test_ime_fix.c
- 同期: meow_audiod 音量异步 ACK 线程(代际防 fd/槽复用竞态)、watchdog 音量收编(首选 audiod
  权威, settings.conf 覆盖路径)、panel 文件搜索(F1-④)与蓝牙只读详情(F1-③)、各类 build 脚本同步

范围红线: third_party/legado-E-main 原开发者未跟踪, 本次不入库(保留 devdocs/local tar 参考);
二进制不入库(后续重编); devdocs/ 不入库。59 文件全部在 patch_v2/ 下, 无 third_party/devdocs。
验证: git apply --check 干净; 59 文件 md5 与容器 patch_v2 逐一比对 59/59 一致(含
key_router.c/rdchapter.c/meow_ui_ime.c/test_wifi_connect.c 抽查); 可执行位保守
(check_lgpl_release.sh 100755, build_turn_repro.sh 100644 与容器一致)。
This commit is contained in:
AskaEth committed 2026-11-04 20:02:28 +08:00
1 parent e87ccdf061
commit 7c7d891570
59 files changed
+5305 -590

No files matched your search

+156 -52
View File
@@ -85,6 +85,10 @@ static char g_net_policy[160]= "/data/meow/netmgrd/net_policy.conf"; /* MEOW_NET
#define NET_POLL_SEC_DEF 2 /* 接口轮询/状态节拍(秒;MEOW_NET_POLL_SEC 覆盖,测试用) */
#define RUNCMD_TMO_MS 1500 /* 后端命令(wpa_cli/hciconfig/etc)有界收割限时(ms) */
/* WiFi 设置页 bug1(2026-09-15):connect 有界等待 wpa_state=COMPLETED 的上限(ms)。
* 普通 ~10s;隐藏网络(connecth,主动探测+关联更慢)~15s。env 可调(宿主桩测试缩短)。 */
#define WIFI_CONNECT_WAIT_MS 10000 /* MEOW_WIFI_CONNECT_WAIT_MS */
#define WIFI_CONNECTH_WAIT_MS 15000 /* MEOW_WIFI_CONNECTH_WAIT_MS */
#define IFACE_MAX 16
#define RFKILL_MAX 8
#define DNS_MAX 8
@@ -112,6 +116,8 @@ static char g_net_policy[160]= "/data/meow/netmgrd/net_policy.conf"; /* MEOW_NET
#define DNS_SELF_IP "127.0.0.1"
static int g_poll_sec = NET_POLL_SEC_DEF;
static int g_connect_wait_ms = WIFI_CONNECT_WAIT_MS; /* MEOW_WIFI_CONNECT_WAIT_MS */
static int g_connecth_wait_ms = WIFI_CONNECTH_WAIT_MS; /* MEOW_WIFI_CONNECTH_WAIT_MS */
static int g_dns_port = DNS_PORT_DEF; /* MEOW_DNS_PORT */
static int g_dns_disabled = 0; /* MEOW_DNS_DISABLE=1:禁用 DNS 代理(宿主测试无 root 绑 53) */
@@ -2650,13 +2656,82 @@ static const char *str_capsafe(const char *s, char *tmp, size_t cap)
return tmp;
}
/* wpa_cli 单命令封装:拼 PATH 前缀 + 有界收割(RUNCMD_TMO_MS)。返回 run_cmd 读到的字节数。 */
static int wpa_cmd(char *out, int cap, const char *args)
{
char cmd[512];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s %s 2>/dev/null", g_wpa_cli, args);
return run_cmd(out, cap, RUNCMD_TMO_MS, cmd);
}
/* WiFi 设置页 bug4-②(2026-09-15):list_networks/scan_results 对非 ASCII 字节输出 \xNN 转义
* (中文 SSID)——还原为原始字节串再与输入 SSID 做同表示比较;不还原则中文 SSID 永不命中
* 既有网络 → 新建同名 open 记录顶影原记录。net.c wpa_decode_ssid / netmgr_ipc.c nm_decode_ssid
* 已有两份同功能实现(后续可收拢,本次先在 netmgrd 补齐)。 */
static int wpa_hexval(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static void wpa_unescape_ssid(const char *src, char *dst, size_t dcap)
{
size_t o = 0;
while (*src && o + 1 < dcap) {
if (src[0] == '\\' && src[1] == 'x' && wpa_hexval(src[2]) >= 0 && wpa_hexval(src[3]) >= 0) {
dst[o++] = (char)((wpa_hexval(src[2]) << 4) | wpa_hexval(src[3]));
src += 4;
} else {
dst[o++] = *src++;
}
}
dst[o] = 0;
}
/* WiFi 设置页 bug4-③(2026-09-15):save_config 结果校验。wpa_supplicant 仅在 conf 含
* update_config=1 时允许写盘;固件模板 /etc/wpa_supplicant.conf 无此项 → 首次 FAIL 时先
* `set update_config 1`(运行期开关,wpa_supplicant 标准用法;成功落盘后文件自带
* update_config=1)再重试一次。返回 1=已持久化;0=仍失败(调用方须在报文/日志明示
* 「记录未持久化」)。仅 MeowOS 模式调用(写盘红线不变,NETMGR_DESIGN §14)。 */
static int wifi_save_config(void)
{
char out[160];
if (wpa_cmd(out, sizeof out, "save_config") > 0 && strstr(out, "OK")) return 1;
wpa_cmd(out, sizeof out, "set update_config 1");
if (wpa_cmd(out, sizeof out, "save_config") > 0 && strstr(out, "OK")) {
net_log("info", "write", "save_config 初次 FAIL → set update_config 1 重试成功(此后 conf 自带该位)");
return 1;
}
net_log("warn", "write", "save_config 失败(含 set update_config 1 重试):记录未持久化");
return 0;
}
/* WiFi 设置页 bug1(2026-09-15):连接状态机——有界等待 wpa_state=COMPLETED(每 200ms 查
* status,上限 timeout_ms,防卡死)。返回 1=已关联(out 带回 ssid/ip 供诚实报文);0=超时。 */
static int wifi_wait_completed(int timeout_ms, wifi_t *out)
{
int waited = 0;
for (;;) {
wifi_t w;
wifi_status(&w);
if (w.connected) { if (out) *out = w; return 1; }
if (waited >= timeout_ms) return 0;
struct timespec ts = { 0, 200 * 1000000L };
nanosleep(&ts, NULL);
waited += 200;
}
}
static void wifi_scan(ctl_result_t *r)
{
char cmd[160];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s scan 2>/dev/null", g_wpa_cli);
char buf[128];
run_cmd(buf, sizeof buf, RUNCMD_TMO_MS, cmd);
struct timespec ts = { 0, 300 * 1000000L }; /* 触发后稍候再读结果(有界)。 */
/* P3(WiFi 设置页附带,2026-09-15):触发后 300ms→1500ms 再读结果——真机扫描常 1-2s,
* 300ms 读到的多是上一轮陈旧结果。IPC 客户端 per-cmd 超时相应放宽(netmgr_ipc scan=3.5s)。 */
struct timespec ts = { 1, 500 * 1000000L };
nanosleep(&ts, NULL);
char bufout[MEOW_IPC_MAX - 16];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s scan_results 2>/dev/null", g_wpa_cli);
@@ -2675,7 +2750,13 @@ static void wifi_networks(ctl_result_t *r)
ctl_sets(r, MEOW_OK, buf);
}
static void wifi_connect(const char *arg, ctl_result_t *r)
/* WiFi 连接(bug1/bug2/bug4 修复,2026-09-15)。hidden=1 走隐藏网络流程(connecth):
* 对所用 id(新建/命中皆然)追加 scan_ssid=1,并把 COMPLETED 等待上限放宽(主动探测+关联更慢)。
* 流程:匹配既有(\xNN 还原同表示比较)→ 新建或(命中且带新凭据时)更新 psk →
* enable+select → 有界等待 wpa_state=COMPLETED(不再无条件报成功)→
* 成功:enable_network all(消除 select 的 disabled 副作用)+ save_config(校验+重试);
* 失败:本次新建的网络 remove 回收(防坏记录堆积),报文明示失败原因。 */
static void wifi_connect_ex(const char *arg, ctl_result_t *r, int hidden)
{
char ssid[96] = "", psk[96] = "";
if (arg && arg[0]) {
@@ -2694,11 +2775,10 @@ static void wifi_connect(const char *arg, ctl_result_t *r)
str_capsafe(psk, tmp_psk, sizeof tmp_psk);
if (!tmp_ssid[0]) { ctl_sets(r, MEOW_ERR_INVAL, "connect: ssid 缺失"); return; }
/* 找既有网络:list_networks → 匹配 ssid(剥引号比较);命中则复用其 id。 */
char cmd[200];
/* 找既有网络:list_networks → 匹配 ssid(剥引号 + \xNN 还原后同表示比较);命中则复用其 id。 */
char cmd[256];
char list[1024];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s list_networks 2>/dev/null", g_wpa_cli);
int n = run_cmd(list, sizeof list, RUNCMD_TMO_MS, cmd);
int n = wpa_cmd(list, sizeof list, "list_networks");
int id = -1;
if (n > 0) {
char *ln = strtok(list, "\n");
@@ -2709,61 +2789,81 @@ static void wifi_connect(const char *arg, ctl_result_t *r)
char *ss = tab0 + 1;
char *tab1 = strchr(ss, '\t');
if (tab1) *tab1 = 0;
char clean[128], norm_clean[128];
char clean[128], unesc[160], norm_clean[128];
size_t cl = strlen(ss);
if (cl >= 2 && ss[0] == '"' && ss[cl-1] == '"') {
memcpy(clean, ss + 1, cl - 2); clean[cl - 2] = 0;
} else {
snprintf(clean, sizeof clean, "%s", ss);
}
str_capsafe(clean, norm_clean, sizeof norm_clean);
wpa_unescape_ssid(clean, unesc, sizeof unesc); /* bug4-②:中文 SSID 转义还原 */
str_capsafe(unesc, norm_clean, sizeof norm_clean);
if (strcmp(norm_clean, tmp_ssid) == 0) id = this_id;
}
ln = strtok(NULL, "\n");
}
}
char out[128];
int created = 0;
if (id < 0) {
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s add_network 2>/dev/null", g_wpa_cli);
if (run_cmd(out, sizeof out, RUNCMD_TMO_MS, cmd) <= 0) {
if (wpa_cmd(out, sizeof out, "add_network") <= 0) {
ctl_sets(r, MEOW_ERR_NODEV, "connect: add_network 失败 (wpa_cli 不可用?)");
return;
}
id = atoi(out);
if (id < 0) { ctl_sets(r, MEOW_ERR_INVAL, "connect: add_network 无 id"); return; }
snprintf(cmd, sizeof cmd,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin %s set_network %d ssid '\"%s\"' 2>/dev/null",
g_wpa_cli, id, tmp_ssid);
run_cmd(out, sizeof out, RUNCMD_TMO_MS, cmd);
if (tmp_psk[0]) {
snprintf(cmd, sizeof cmd,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin %s set_network %d psk '\"%s\"' 2>/dev/null",
g_wpa_cli, id, tmp_psk);
} else {
snprintf(cmd, sizeof cmd,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin %s set_network %d key_mgmt NONE 2>/dev/null",
g_wpa_cli, id);
}
run_cmd(out, sizeof out, RUNCMD_TMO_MS, cmd);
created = 1;
snprintf(cmd, sizeof cmd, "set_network %d ssid '\"%s\"'", id, tmp_ssid);
wpa_cmd(out, sizeof out, cmd);
if (tmp_psk[0]) snprintf(cmd, sizeof cmd, "set_network %d psk '\"%s\"'", id, tmp_psk);
else snprintf(cmd, sizeof cmd, "set_network %d key_mgmt NONE", id);
wpa_cmd(out, sizeof out, cmd);
} else if (tmp_psk[0]) {
/* bug4-①/bug1-④:命中既有网络且调用方带了新 psk → 先更新凭据再连
* (旧记录密码错误可经扫描路径重输修复;psk 为空则不动既有凭据)。 */
snprintf(cmd, sizeof cmd, "set_network %d psk '\"%s\"'", id, tmp_psk);
wpa_cmd(out, sizeof out, cmd);
}
if (hidden) {
/* bug2:隐藏 AP 必须 scan_ssid=1,supplicant 才会主动发 Probe Request(命中既有也要补)。 */
snprintf(cmd, sizeof cmd, "set_network %d scan_ssid 1", id);
wpa_cmd(out, sizeof out, cmd);
}
{ /* enable + select 该网络(复用/新建皆用)。 */
char en[256], sel[256];
snprintf(en, sizeof en, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s enable_network %d 2>/dev/null",
g_wpa_cli, id);
run_cmd(out, sizeof out, RUNCMD_TMO_MS, en);
snprintf(sel, sizeof sel, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s select_network %d 2>/dev/null",
g_wpa_cli, id);
run_cmd(out, sizeof out, RUNCMD_TMO_MS, sel);
/* 期3:MeowOS 模式 connect 后持久化 wpa_supplicant.conf(HibyOS 只读由服务端门控,这里仅 MeowOS 生效)。 */
if (g_mode_meow) {
char sc[160];
snprintf(sc, sizeof sc, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s save_config 2>/dev/null", g_wpa_cli);
run_cmd(out, sizeof out, RUNCMD_TMO_MS, sc);
}
snprintf(cmd, sizeof cmd, "enable_network %d", id);
wpa_cmd(out, sizeof out, cmd);
snprintf(cmd, sizeof cmd, "select_network %d", id);
wpa_cmd(out, sizeof out, cmd);
}
char msg[140];
snprintf(msg, sizeof msg, "connect: ssid=%s network_id=%d (已 enable+select%s)", tmp_ssid, id,
g_mode_meow ? "+save_config" : "");
/* bug1:有界等待 wpa_state=COMPLETED——密码错误/AP 不在范围/握手失败不再误报成功。 */
wifi_t w;
int wait_ms = hidden ? g_connecth_wait_ms : g_connect_wait_ms;
if (!wifi_wait_completed(wait_ms, &w)) {
if (created) { /* 本次新建且失败 → 回收防坏记录堆积(bug4 联动) */
snprintf(cmd, sizeof cmd, "remove_network %d", id);
wpa_cmd(out, sizeof out, cmd);
if (g_mode_meow) wifi_save_config();
}
net_log("info", "write", "wifi connect ssid=%s id=%d 超时未关联(%dms)", tmp_ssid, id, wait_ms);
char msg[128];
snprintf(msg, sizeof msg, "连接失败:密码错误或信号不佳(超时未关联)");
ctl_sets(r, MEOW_ERR_NODEV, msg);
return;
}
/* bug4-④:验证成功后 enable_network all 复位其它网络 disabled 态(select_network 副作用),
* 再 save_config 落盘(bug4-③:结果校验,FAIL 时 set update_config 1 重试)。
* 写盘仍受 MeowOS 模式门控(红线不变,NETMGR_DESIGN §14)。 */
int persisted = 0;
if (g_mode_meow) {
wpa_cmd(out, sizeof out, "enable_network all");
persisted = wifi_save_config();
}
net_log("info", "write", "wifi connect ssid=%s id=%d 已连接 ip=%s persisted=%d",
tmp_ssid, id, w.ip[0] ? w.ip : "-", persisted);
char msg[160];
snprintf(msg, sizeof msg, "已连接:%s ip=%s%s",
w.ssid[0] ? w.ssid : tmp_ssid, w.ip[0] ? w.ip : "-",
(g_mode_meow && !persisted) ? "(记录未持久化)" : "");
ctl_sets(r, MEOW_OK, msg);
}
@@ -2840,7 +2940,9 @@ static void wifi_ctl_execute(const char *cmd, const char *arg, ctl_result_t *r)
} else if (!strcmp(cmd, "networks")) {
wifi_networks(r);
} else if (!strcmp(cmd, "connect")) {
wifi_connect(arg, r);
wifi_connect_ex(arg, r, 0);
} else if (!strcmp(cmd, "connecth")) {
wifi_connect_ex(arg, r, 1); /* bug2:隐藏网络(scan_ssid=1 + 更长 COMPLETED 等待) */
} else if (!strcmp(cmd, "disconnect")) {
char cmd2[160];
snprintf(cmd2, sizeof cmd2, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s disconnect 2>/dev/null", g_wpa_cli);
@@ -2857,13 +2959,13 @@ static void wifi_ctl_execute(const char *cmd, const char *arg, ctl_result_t *r)
g_wpa_cli, nid);
char buf[128];
int n = run_cmd(buf, sizeof buf, RUNCMD_TMO_MS, cmd2);
/* 期4:移除后持久化(MeowOS 模式;HibyOS 只读由服务端门控)。 */
if (g_mode_meow) {
snprintf(cmd2, sizeof cmd2, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s save_config 2>/dev/null", g_wpa_cli);
run_cmd(buf, sizeof buf, RUNCMD_TMO_MS, cmd2);
}
char msg[64];
snprintf(msg, sizeof msg, "wifi forget: network_id=%d 已移除%s", nid, g_mode_meow ? "+save_config" : "");
/* 期4:移除后持久化(MeowOS 模式;HibyOS 只读由服务端门控)。
* bug4-③:save_config 结果校验(FAIL 时 set update_config 1 重试),报文不再谎称已落盘。 */
int persisted = 0;
if (g_mode_meow) persisted = wifi_save_config();
char msg[96];
snprintf(msg, sizeof msg, "wifi forget: network_id=%d 已移除%s", nid,
g_mode_meow ? (persisted ? "+save_config" : "(记录未持久化)") : "");
ctl_sets(r, n >= 0 ? MEOW_OK : MEOW_ERR_NODEV, msg);
} else if (!strcmp(cmd, "rfkill")) {
rfkill_ctl(arg, r);
@@ -3704,7 +3806,7 @@ static int cli_policy(int op, const char *entity, const char *arg)
static const char *usage(void)
{
return "status | iface [name] | wifi scan|networks|connect <ssid>[,psk]|disconnect|on|off"
return "status | iface [name] | wifi scan|networks|connect <ssid>[,psk]|connecth <ssid>[,psk]|disconnect|on|off"
" | bt status|on|off|paired|connected|a2dp|devices|scan on|off|connect|disconnect|pair|unpair <mac>"
" | rfkill [<type> <on|off>] | udhcpc renew|release [if]"
" | apps | conns [entity] | top | dns status|stats|flush"
@@ -3757,9 +3859,9 @@ static int cli_run(int n, char **v)
if (!strcmp(cmd, "wifi")) {
if (!arg) { fprintf(stderr, "wifi 子命令缺失(%s)\n", usage()); return 1; }
if (!strcmp(arg, "on") || !strcmp(arg, "off")) return cli_cmd_result(MSG_NET_WIFI_CTL, arg, NULL);
if (!strcmp(arg, "connect")) {
if (!arg2) { fprintf(stderr, "connect 需 <ssid>[,psk]\n"); return 1; }
return cli_cmd_result(MSG_NET_WIFI_CTL, "connect", arg2);
if (!strcmp(arg, "connect") || !strcmp(arg, "connecth")) {
if (!arg2) { fprintf(stderr, "%s 需 <ssid>[,psk]\n", arg); return 1; }
return cli_cmd_result(MSG_NET_WIFI_CTL, arg, arg2);
}
if (!strcmp(arg, "forget")) {
if (!arg2) { fprintf(stderr, "forget 需 <network_id>\n"); return 1; }
@@ -3821,6 +3923,8 @@ int main(int argc, char **argv)
if ((e = getenv("MEOW_WIFI_IF")) && e[0]) g_wifi_if = e;
if ((e = getenv("MEOW_NET_SOCK")) && e[0]) g_sock = e;
if ((e = getenv("MEOW_NET_POLL_SEC")) && atoi(e) > 0) g_poll_sec = atoi(e);
if ((e = getenv("MEOW_WIFI_CONNECT_WAIT_MS")) && atoi(e) >= 200) g_connect_wait_ms = atoi(e);
if ((e = getenv("MEOW_WIFI_CONNECTH_WAIT_MS")) && atoi(e) >= 200) g_connecth_wait_ms = atoi(e);
if ((e = getenv("MEOW_TCP")) && e[0]) snprintf(g_proc_tcp, sizeof g_proc_tcp, "%s", e);
if ((e = getenv("MEOW_UDP")) && e[0]) snprintf(g_proc_udp, sizeof g_proc_udp, "%s", e);
if ((e = getenv("MEOW_AMS_SOCK")) && e[0]) snprintf(g_ams_sock, sizeof g_ams_sock, "%s", e);