真机 0.8.49 现场 /usr/bin/meow_netmgrd 是 P1(eb07415) 之前的旧件:`bt status` 无
connected= 字段、`bt connected|devices|a2dp` 连子命令都不存在(被当成写级拒绝)、
`bt paired` 落到 `bluetoothctl paired` → "Invalid command in menu main"。上游只有
NETMGR_UNREACH 才回退直连,INVAL/PERM 直接返回 → 三个列表恒空。
本单修掉「部署新件后依然存在」的三个真 bug + 一个解析缺陷:
1. `bluetoothctl paired` → `paired-devices`(bt_status 计数、bt paired 子命令两处);
`devices` 只用于补名,不再当配对权威。
2. A2DP:v4.1.1 没有 `a2dp list`。改为 `list-pcms` 取端点 → 每端点 `info <path>`;
MAC 只从 D-Bus 路径的 `dev_XX_..` 段解(真机路径带 `/a2dpsrc/source` 段,
按「最后一段取 token」会拿到 "source")。info 查不到就跳过,不编造。
3. `run_cmd` 四态(成功有输出/成功无输出/非 0 退出/超时/起不来)+ stderr 有界抓取
(≤256B):旧实现只回字节数、失败也返回 0 → on/off/connect/disconnect/pair 恒报成功。
写操作改为下发后复查真态(hcitool con / bluetoothctl info Paired: / hciconfig
power),超时或未收敛如实报 MEOW_ERR_NODEV。兼容外壳 run_cmd() 契约不变。
4. 扫描缓存:实时 `[NEW] Device` 行以 ANSI ESC 开头(真机 od -c 证据),旧解析器只认
行首 → 实时行全丢。新增 bt_clean_ansi()/bt_scan_line_parse():剥 ESC/CSI/控制符、
去 `[bluetooth]# ` 提示符、只认 [NEW] 与裸 Device、排除 [CHG]/[DEL];bt_scan_on 改用
g_bluetoothctl(原来硬编码,桩对 scan 无效)。
解析器全部抽成纯函数并用真机原始字节做单测(test_bt_ctl.c 扩充:四态、ANSI 样本、
截断/空输出、list-pcms/info、hcitool con)。test_netmgrd.sh 桩改 list-pcms/info +
真机 ANSI 缓存样本;顺带修掉两处宿主可移植性问题(/tmp 不可写→$TMPDIR、桩脚本在
PATH=/bin:/usr/bin 下用了 cat/rm→改 shell 内建,test_wifi_connect 的 g_wpa_cli 悬垂
栈指针)。
按塔要求:本 commit 不含二进制、不改 fw_content.conf(netmgrd 产物由 M51 统一重编)。
build_netmgrd.sh 只做 ROOT/ZIGCC/OUT 环境变量化(旧硬编码 $HOME/Project/R3ProII),
M51 复用;time32 接入仍归 M43。
176 lines
6.5 KiB
C
176 lines
6.5 KiB
C
/* test_wifi_connect.c — WiFi 设置页 bug1/bug2/bug4 宿主桩测试(2026-09-15)。
|
|
*
|
|
* 通过 #include meow_netmgrd.c 直接驱动 static 的 wifi_connect_ex(),绕过 daemon 的
|
|
* SO_PEERCRED 系统身份鉴权(宿主 CLI=CALL_OTHER 写级必拒,无法经 IPC 测连接路径)。
|
|
* 桩 wpa_cli 由本程序写入临时目录;控制文件(status_mode/list/save_fail_once)决定
|
|
* status 序列 / list_networks 原文 / save_config 是否首败,调用日志写入 <dir>/log 供断言。
|
|
*
|
|
* 编译:cc -Wall -Wextra -I../meow_launcherd test_wifi_connect.c -o test_wifi_connect
|
|
*/
|
|
#define _GNU_SOURCE
|
|
#define main meow_netmgrd_main
|
|
#include "meow_netmgrd.c"
|
|
#undef main
|
|
|
|
#include <sys/stat.h>
|
|
|
|
static char g_dir[128];
|
|
static int g_pass = 0, g_fail = 0;
|
|
|
|
static void ck(const char *name, int cond, const char *detail)
|
|
{
|
|
if (cond) { g_pass++; printf("PASS %s\n", name); }
|
|
else { g_fail++; printf("FAIL %s: %s\n", name, detail ? detail : ""); }
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void putf(const char *name, const char *fmt, ...)
|
|
{
|
|
char path[192], body[512];
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vsnprintf(body, sizeof body, fmt, ap);
|
|
va_end(ap);
|
|
snprintf(path, sizeof path, "%s/%s", g_dir, name);
|
|
FILE *f = fopen(path, "w");
|
|
if (f) { fputs(body, f); fclose(f); }
|
|
}
|
|
|
|
static int log_has(const char *needle)
|
|
{
|
|
char path[192], buf[8192];
|
|
snprintf(path, sizeof path, "%s/log", g_dir);
|
|
FILE *f = fopen(path, "r");
|
|
if (!f) return 0;
|
|
size_t n = fread(buf, 1, sizeof buf - 1, f);
|
|
fclose(f);
|
|
buf[n] = 0;
|
|
return strstr(buf, needle) != NULL;
|
|
}
|
|
|
|
static void reset_log(void)
|
|
{
|
|
char path[192];
|
|
snprintf(path, sizeof path, "%s/log", g_dir);
|
|
FILE *f = fopen(path, "w");
|
|
if (f) fclose(f);
|
|
}
|
|
|
|
static void write_stub(void)
|
|
{
|
|
char path[192];
|
|
snprintf(path, sizeof path, "%s/wpa_cli", g_dir);
|
|
FILE *f = fopen(path, "w");
|
|
if (!f) { fprintf(stderr, "stub open fail\n"); exit(2); }
|
|
fputs(
|
|
"#!/bin/sh\n"
|
|
"D=\"${MN_WIFI_DIR:-/tmp}\"\n"
|
|
"catf() { [ -f \"$1\" ] || return 0; while IFS= read -r l || [ -n \"$l\" ]; do echo \"$l\"; done < \"$1\"; }\n"
|
|
"echo \"$@\" >> \"$D/log\"\n"
|
|
"case \"$1\" in\n"
|
|
" status)\n"
|
|
" m=completed\n"
|
|
" if [ -f \"$D/status_mode\" ]; then IFS= read -r m < \"$D/status_mode\"; fi\n"
|
|
" if [ \"$m\" = \"associating\" ]; then\n"
|
|
" echo \"wpa_state=ASSOCIATING\"; echo \"ssid=MyWiFi\"\n"
|
|
" else\n"
|
|
" echo \"wpa_state=COMPLETED\"; echo \"ssid=MyWiFi\"\n"
|
|
" echo \"ip_address=10.78.193.50\"; echo \"signal=-45\"\n"
|
|
" fi\n"
|
|
" ;;\n"
|
|
" list_networks)\n"
|
|
" if [ -f \"$D/list\" ]; then catf \"$D/list\"\n"
|
|
" else printf 'network id / ssid / bssid / flags\\n0\\tMyWiFi\\tany\\t[CURRENT]\\n'\n"
|
|
" fi\n"
|
|
" ;;\n"
|
|
" add_network) echo 1 ;;\n"
|
|
" set_network) echo OK ;;\n"
|
|
" enable_network) echo OK ;;\n"
|
|
" select_network) echo OK ;;\n"
|
|
" disconnect) echo OK ;;\n"
|
|
" save_config)\n"
|
|
" v=\"\"\n"
|
|
" if [ -f \"$D/save_fail_once\" ]; then IFS= read -r v < \"$D/save_fail_once\"; fi\n"
|
|
" if [ \"$v\" = \"1\" ]; then echo \"\" > \"$D/save_fail_once\"; echo FAIL; else echo OK; fi\n"
|
|
" ;;\n"
|
|
" set) echo OK ;;\n"
|
|
" remove_network) echo OK ;;\n"
|
|
" *) echo OK ;;\n"
|
|
"esac\n"
|
|
"exit 0\n",
|
|
f);
|
|
fclose(f);
|
|
chmod(path, 0755);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
/* 宿主临时目录:Termux/Android 上 /tmp 不可写 → 跟随 $TMPDIR */
|
|
const char *tmp = getenv("TMPDIR");
|
|
if (!tmp || !tmp[0]) tmp = "/tmp";
|
|
snprintf(g_dir, sizeof g_dir, "%s/mnwifi.XXXXXX", tmp);
|
|
if (!mkdtemp(g_dir)) { fprintf(stderr, "mkdtemp fail\n"); return 2; }
|
|
write_stub();
|
|
setenv("MN_WIFI_DIR", g_dir, 1);
|
|
|
|
g_mode_meow = 1;
|
|
g_connect_wait_ms = 400;
|
|
g_connecth_wait_ms = 400;
|
|
{
|
|
static char p[192];
|
|
snprintf(p, sizeof p, "%s/wpa_cli", g_dir);
|
|
g_wpa_cli = p;
|
|
}
|
|
|
|
ctl_result_t r;
|
|
|
|
/* ① 成功:命中既有 id=0 + 带新 psk → 更新凭据,等待 COMPLETED,成功报 ip。 */
|
|
putf("status_mode", "completed");
|
|
ctl_reset(&r, MEOW_OK);
|
|
reset_log();
|
|
wifi_connect_ex("MyWiFi,secret", &r, 0);
|
|
ck("connect-success code", r.code == MEOW_OK, r.text);
|
|
ck("connect-success text ip", strstr(r.text, "已连接") && strstr(r.text, "ip=10.78.193.50"), r.text);
|
|
ck("connect-success psk update", log_has("set_network 0 psk"), "");
|
|
ck("connect-success enable all", log_has("enable_network all"), "");
|
|
|
|
/* ② 错误密码/不在范围:status 恒 ASSOCIATING → 超时失败,新建 id 被回收。 */
|
|
putf("status_mode", "associating");
|
|
reset_log();
|
|
ctl_reset(&r, MEOW_OK);
|
|
wifi_connect_ex("OtherWiFi", &r, 0);
|
|
ck("connect-timeout code", r.code == MEOW_ERR_NODEV, r.text);
|
|
ck("connect-timeout text", strstr(r.text, "连接失败") != NULL, r.text);
|
|
ck("connect-timeout remove new", log_has("remove_network 1"), "");
|
|
|
|
/* ③ 中文 SSID \xNN 还原匹配:list_networks 原文转义,输入 UTF-8 名 → 命中 id=0 不新建。 */
|
|
putf("status_mode", "completed");
|
|
putf("list", "network id / ssid / bssid / flags\n0\t\\xe5\\xa4\\xa9\\xe5\\xa4\\xa9\tany\t[CURRENT]\n");
|
|
reset_log();
|
|
ctl_reset(&r, MEOW_OK);
|
|
wifi_connect_ex("天天", &r, 0);
|
|
ck("connect-escape match", r.code == MEOW_OK, r.text);
|
|
ck("connect-escape no add", !log_has("add_network"), "");
|
|
|
|
/* ④ connecth:隐藏网络命中既有 id → 追加 scan_ssid 1。 */
|
|
putf("status_mode", "completed");
|
|
putf("list", "network id / ssid / bssid / flags\n0\tMyWiFi\tany\t[CURRENT]\n");
|
|
reset_log();
|
|
ctl_reset(&r, MEOW_OK);
|
|
wifi_connect_ex("MyWiFi", &r, 1);
|
|
ck("connecth scan_ssid", r.code == MEOW_OK && log_has("set_network 0 scan_ssid 1"), r.text);
|
|
|
|
/* ⑤ save_config 首败 → set update_config 1 重试 → 持久化成功。 */
|
|
putf("status_mode", "completed");
|
|
putf("list", "network id / ssid / bssid / flags\n0\tMyWiFi\tany\t[CURRENT]\n");
|
|
putf("save_fail_once", "1");
|
|
reset_log();
|
|
ctl_reset(&r, MEOW_OK);
|
|
wifi_connect_ex("MyWiFi", &r, 0);
|
|
ck("save-config retry", r.code == MEOW_OK && !strstr(r.text, "记录未持久化"), r.text);
|
|
ck("save-config set update_config", log_has("set update_config 1"), "");
|
|
|
|
printf("==============================\nPASS=%d FAIL=%d\n", g_pass, g_fail);
|
|
return g_fail == 0 ? 0 : 1;
|
|
}
|