Files
MeowOS 3b6a73ac10 feat(launcher, NETMGR_DESIGN §12): 设置页 WiFi/蓝牙子页迁 netmgr IPC + WiFi 新网络录入 UI
- wifi_page/bt_page 查询/操作改走 netmgrd IPC(netmgr_ipc.c 客户端:
  socket /usr/data/meow_net.sock,HELLO 鉴权 → WIFI_CTL/BT_CTL → RESULT)。
- WiFi 新网络录入:扫描结果点选 →(加密网络)SDK IME 输密码 → connect →
  结果反馈 → 刷新已存网络;开放网络直连;当前连接详情含断开。
- 双轨过渡:netmgrd 不可达(NETMGR_UNREACH)回退 net.c 直连 wpa_cli/hciconfig
  (保功能),页面显示「网络管理服务未运行(直连模式)」降级占位(不崩)。
- BT 仅迁 on/off/status/paired(netmgrd BT_CTL 无连接/配对子命令,其余直连)。
- net.c net_wifi_scan 补 sec 字段(wifi_page.c 补 meow_net_t.sec)。
- 测试:+test_netmgr_ipc.c(假 socket 帧:扫描渲染/连接成功失败两态/降级占位)。
2026-09-01 22:09:31 +08:00

261 lines
13 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test_netmgr_ipc.c — M6.0 宿主桩测试:设置页 netmgr IPC 客户端 / WiFi 新网络录入逻辑。
*
* 假 socket 帧:本测试起一个 AF_UNIX 服务器线程,按 Meow IPC 帧(16B 头 + 负载)应答
* MSG_HELLO→ACK、MSG_NET_WIFI_CTL/BT_CTL→*_RESULT(netmgrd 同构),让 launcher 侧
* netmgr_ipc.c(netmgr_wifi / nm_wifi_* 系列 / netmgr_ok)与 wifi_page.c(wifi_rows)跑真实数据。
*
* 覆盖(打桩帧):
* ① 扫描列表渲染:netmgrd 返回 scan_results → nm_wifi_scan / wifi_rows 解析出 SSID/mac/sec,
* 加密网络 sec=WPA-PSK、开放网络 sec=Open;
* ② 连接成功/失败两态:connect 子命令 MEOW_OK / MEOW_ERR_NODEV → nm_wifi_connect 返回值+错误文本;
* ③ 降级占位:MEOW_NET_SOCK 指向不存在路径 → netmgr_ok()==0、netmgr_wifi=NETMGR_UNREACH、
* nm_wifi_scan 回退直连(桩返回 0 不崩),且 wifi_rows 渲染「网络管理服务未运行(直连模式)」占位。
*
* 编译(链 netmgr_ipc.o;net.c 回退函数/UI 用桩替代,同 test_media.c 惯例):
* gcc -I../sdk -I. -Wall -Wextra -c netmgr_ipc.c -o netmgr_ipc.test.o
* gcc -I../sdk -I. -Wall -Wextra test_netmgr_ipc.c netmgr_ipc.test.o -lpthread -o test_netmgr_ipc
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <assert.h>
#include "meow_launcher_internal.h" /* meow_net_t / S_NET_MAX / nm_* / sub_page_t etc. */
#include "wifi_page.c" /* 把静态 wifi_ctx_t/wifi_rows 平移进本 TU(沿用既有模式) */
/* ---------- net.c 直连回退路径桩(netmgrd 不可达时走这里;宿主无 wpa_cli/bluetoothctl) ---------- */
int wlan_connected(void) { return 0; }
int net_wifi_saved(meow_net_t *out, int max) { (void)out; (void)max; return 0; }
int net_wifi_scan(meow_net_t *out, int max) { (void)out; (void)max; return 0; }
void net_wifi_ssid(char *buf, int len) { (void)len; buf[0] = 0; }
int net_wifi_info(char (*info)[48], int max) { (void)info; (void)max; return 0; }
int hci_powered(void) { return 0; }
int net_bt_paired(meow_net_t *out, int max) { (void)out; (void)max; return 0; }
void s_net_toggle_wifi(int on) { (void)on; }
void s_net_toggle_bt(int on) { (void)on; }
void net_wifi_scan_trigger(void) {}
char *popen_read_tmo(int tmo_ms, const char *cmd)
{ (void)tmo_ms; (void)cmd; return strdup("0\n"); } /* 模拟 wpa_cli add_network 返回 id=0 */
/* ---------- launcher/UI/IME 桩(wifi_page.c 链接所需;测试不触碰这些路径) ---------- */
static unsigned long long g_test_ms = 0;
int meow_settings_get_bool(const char *k, int *v) { (void)k; if (v) *v = 1; return 1; }
int meow_settings_set_bool(const char *k, int v) { (void)k; (void)v; return 0; }
int meow_settings_save(void) { return 0; }
void paint_sb_right(void) {}
void logmsg(const char *fmt, ...) { (void)fmt; }
unsigned long long now_ms(void) { return ++g_test_ms; }
void bt_role_refresh(int force) { (void)force; }
meow_sys_state g_sys; /* extern meow_sys_state g_sys */
int s_dd_modal(const char *title, const char *item[S_NET_MAX], int n, int cur)
{ (void)title; (void)item; (void)n; (void)cur; return -1; }
int meow_subpage_run(sub_page_t *p) { (void)p; return 0; }
int meow_ui_text_input(meow_ime_ctx *ctx) { (void)ctx; return 0; } /* 取消:不进 IME 路径 */
/* ---------- 假 netmgrd 服务器 ---------- */
typedef struct {
int lfd;
int fail_connect; /* 1=connect 返回 MEOW_ERR_NODEV */
const char *scan_text;
const char *networks_text;
const char *status_text;
const char *bt_status_text;
const char *bt_paired_text;
} fake_net_t;
static int rw_all(int fd, uint8_t *b, size_t n)
{ size_t o = 0; while (o < n) { ssize_t r = read(fd, b + o, n - o); if (r <= 0) return -1; o += (size_t)r; } return 0; }
static int wr_all(int fd, const uint8_t *b, size_t n)
{ size_t o = 0; while (o < n) { ssize_t r = write(fd, b + o, n - o); if (r <= 0) return -1; o += (size_t)r; } return 0; }
static void fk_send(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC, rlen = len;
uint16_t ver = MEOW_IPC_VERSION, t = type;
memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &t, 2);
memcpy(hdr + 8, &req, 4); memcpy(hdr + 12, &rlen, 4);
if (wr_all(fd, hdr, sizeof hdr) < 0) return;
if (len && pl) wr_all(fd, pl, len);
}
static void fk_result(int fd, uint16_t type, uint32_t req, uint32_t code, const char *txt)
{
m_net_cmd_result_t r;
memset(&r, 0, sizeof r);
r.code = code;
size_t pl = txt ? strlen(txt) : 0;
if (pl > sizeof r.text - 1) pl = sizeof r.text - 1;
r.text_len = (uint16_t)pl;
memcpy(r.text, txt, pl);
r.text[pl] = 0;
fk_send(fd, type, req, &r, (uint32_t)(8 + pl + 1));
}
static void fk_handle(int c, fake_net_t *f)
{
for (;;) {
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (rw_all(c, hdr, sizeof hdr) < 0) return;
uint32_t magic, rlen, req; uint16_t ver, type;
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(&type, hdr + 6, 2);
memcpy(&req, hdr + 8, 4); memcpy(&rlen, hdr + 12, 4);
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return;
uint8_t pl[MEOW_IPC_MAX];
if (rlen) { if (rlen > sizeof pl) rlen = sizeof pl; if (rw_all(c, pl, rlen) < 0) return; }
if (type == MSG_HELLO) {
m_ack_t a; memset(&a, 0, sizeof a); a.code = MEOW_OK;
fk_send(c, MSG_ACK, req, &a, sizeof a);
continue;
}
m_net_cmd_t q; memset(&q, 0, sizeof q);
if (rlen > sizeof q) rlen = sizeof q;
memcpy(&q, pl, rlen);
if (type == MSG_NET_WIFI_CTL) {
if (!strcmp(q.cmd, "scan")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, f->scan_text);
else if (!strcmp(q.cmd, "networks")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, f->networks_text);
else if (!strcmp(q.cmd, "status")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, f->status_text);
else if (!strcmp(q.cmd, "connect")) {
char buf[200];
if (f->fail_connect) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_ERR_NODEV,
"connect: add_network 失败 (wpa_cli 不可用?)");
else { snprintf(buf, sizeof buf, "connect: ssid=%s network_id=5 (已 enable+select)", q.arg);
fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, buf); }
}
else if (!strcmp(q.cmd, "disconnect")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, "wifi disconnect: 已下发");
else if (!strcmp(q.cmd, "on") || !strcmp(q.cmd, "off")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, "wifi on/off: rfkill wlan done");
else fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_ERR_INVAL, "wifi: 未知子命令");
continue;
}
if (type == MSG_NET_BT_CTL) {
if (!strcmp(q.cmd, "status")) fk_result(c, MSG_NET_BT_RESULT, req, MEOW_OK, f->bt_status_text);
else if (!strcmp(q.cmd, "paired")) fk_result(c, MSG_NET_BT_RESULT, req, MEOW_OK, f->bt_paired_text);
else if (!strcmp(q.cmd, "on") || !strcmp(q.cmd, "off")) fk_result(c, MSG_NET_BT_RESULT, req, MEOW_OK, "bt on/off: hci0 done");
else fk_result(c, MSG_NET_BT_RESULT, req, MEOW_ERR_INVAL, "bt: 未知子命令");
continue;
}
return; /* 未知帧:断开 */
}
}
static void *fk_server(void *arg)
{
fake_net_t *f = arg;
for (;;) {
int c = accept(f->lfd, NULL, NULL);
if (c < 0) return NULL;
fk_handle(c, f);
close(c);
}
}
static int start_server(fake_net_t *f, const char *path)
{
f->lfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (f->lfd < 0) return -1;
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
strncpy(a.sun_path, path, sizeof a.sun_path - 1);
unlink(path);
if (bind(f->lfd, (struct sockaddr *)&a, sizeof a) < 0) return -2;
if (listen(f->lfd, 5) < 0) return -3;
pthread_t t;
return pthread_create(&t, NULL, fk_server, f) == 0 ? 0 : -4;
}
static int row_has_label(const sub_row_t *rows, int n, const char *needle)
{ for (int i = 0; i < n; i++) if (rows[i].label && strstr(rows[i].label, needle)) return 1; return 0; }
int main(void)
{
char sock[110];
snprintf(sock, sizeof sock, "/tmp/meow_net_test_%d.sock", (int)getpid());
fake_net_t f;
memset(&f, 0, sizeof f);
f.scan_text =
"bssid / frequency / signal level / flags / ssid\n"
"00:11:22:33:44:01 2412 -45 [WPA-PSK-CCMP][ESS] Home2G\n"
"00:11:22:33:44:02 2417 -60 [WPA2-PSK][ESS] Office\n"
"00:11:22:33:44:03 2442 -70 [ESS] FreeWiFi\n";
f.networks_text =
"network id / ssid / bssid / flags\n"
"0\tHome2G\t00:11:22:33:44:01\t[WPA-PSK-CCMP]\n";
f.status_text = "wifi state=COMPLETED connected=1 ssid=Home2G ip=192.168.1.5 rssi=-45";
f.bt_status_text = "bt available=1 powered=1 address=00:1A:7D:DA:71:13 paired=2";
f.bt_paired_text = "Device 00:1A:7D:DA:71:13 Headphones\nDevice 00:1A:7D:DA:71:14 Speaker\n";
if (start_server(&f, sock) != 0) { fprintf(stderr, "fake server start fail\n"); return 1; }
setenv("MEOW_NET_SOCK", sock, 1);
/* ---- ① 扫描列表渲染:nm_wifi_scan 解析(IPsec/开放安全类型) ---- */
meow_net_t scan[S_NET_MAX];
int sn = nm_wifi_scan(scan, S_NET_MAX);
assert(sn >= 2 && "scan: 至少解析 2 条(BSSID 去重后)");
assert(!strcmp(scan[0].name, "Home2G") && "scan[0].name=Home2G");
assert(!strcmp(scan[0].sec, "WPA-PSK-CCMP") && "scan[0].sec 加密");
assert(!strcmp(scan[1].sec, "WPA2-PSK") && "scan[1].sec 加密");
/* FreeWiFi 无 WPA → Open */
int open_idx = -1;
for (int i = 0; i < sn; i++) if (!strcmp(scan[i].name, "FreeWiFi")) open_idx = i;
assert(open_idx >= 0 && "scan 含 FreeWiFi");
assert(!strcmp(scan[open_idx].sec, "Open") && "开放网络 sec=Open");
printf("[A] nm_wifi_scan: %d 条, Home2G/WPA-PSK-CCMP, FreeWiFi/Open PASS\n", sn);
/* ---- wifi_rows:附近网络分组渲染(含 SSID/sec 摘要行) ---- */
{
wifi_ctx_t c; memset(&c, 0, sizeof c);
sub_row_t rows[SUB_ROWS_MAX];
int rn = wifi_rows(&c, rows, SUB_ROWS_MAX);
assert(row_has_label(rows, rn, "附近网络") && "rows 有「附近网络」分组");
assert(row_has_label(rows, rn, "Home2G") && "rows 渲染扫描 SSID Home2G");
assert(row_has_label(rows, rn, "FreeWiFi") && "rows 渲染开放网络 FreeWiFi");
assert(row_has_label(rows, rn, "已保存网络") && "rows 有「已保存网络」分组");
assert(!row_has_label(rows, rn, "网络管理服务未运行") && "netmgrd 在 → 无降级占位");
printf("[B] wifi_rows 渲染: 附近/已保存/扫描SSID 均现, 无降级占位 PASS\n");
}
/* ---- ② 连接成功/失败两态 ---- */
char err[192]; err[0] = 0;
f.fail_connect = 0;
int rc = nm_wifi_connect("Home2G", "pass1234", err, sizeof err);
assert(rc == MEOW_OK && "connect 成功返回 MEOW_OK");
assert(strstr(err, "network_id=5") && "connect 成功带回 network_id");
printf("[C] connect 成功: rc=%d err=%s PASS\n", rc, err);
f.fail_connect = 1;
err[0] = 0;
rc = nm_wifi_connect("Home2G", "bad", err, sizeof err);
assert(rc == MEOW_ERR_NODEV && "connect 失败返回 MEOW_ERR_NODEV");
assert(strstr(err, "add_network") && "connect 失败带回原因");
printf("[D] connect 失败: rc=%d err=%s PASS\n", rc, err);
/* ---- ③ 降级占位:MEOW_NET_SOCK 指向不存在路径 ----
* 先发一次 netmgr_wifi(内部强制重连,失败 → 置可达缓存=0),再断言 netmgr_ok()。 */
setenv("MEOW_NET_SOCK", "/tmp/meow_net_nonexistent_zz.sock", 1);
assert(netmgr_wifi("status", NULL, err, sizeof err) == NETMGR_UNREACH && "netmgr_wifi=NETMGR_UNREACH");
assert(netmgr_ok() == 0 && "netmgr_ok()==0(服务不可达)");
err[0] = 0;
int dr = nm_wifi_connect("Home2G", "x", err, sizeof err);
assert(dr == MEOW_OK && "不可达→回退直连(桩返回成功,保功能)");
int nr = nm_wifi_scan(scan, S_NET_MAX);
assert(nr == 0 && "不可达→回退直连扫描(桩返回 0,不崩)");
{
wifi_ctx_t c; memset(&c, 0, sizeof c);
sub_row_t rows[SUB_ROWS_MAX];
int rn = wifi_rows(&c, rows, SUB_ROWS_MAX);
assert(row_has_label(rows, rn, "网络管理服务未运行(直连模式)") && "降级占位渲染");
printf("[E] 降级: netmgr_ok=0, UNREACH, 回退不崩, 占位渲染 PASS\n");
}
printf("ALL PASS\n");
unlink(sock);
return 0;
}