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 帧:扫描渲染/连接成功失败两态/降级占位)。
This commit is contained in:
1 parent
cd4e2c247f
commit
3b6a73ac10
8 files changed
+922
-54
No files matched your search
@@ -53,12 +53,12 @@ static int bt_top_sw(void *ctx, int *on)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶栏开关切换:写设置 + hciconfig up/down + 触发扫描 + 同步状态栏 */
|
/* 顶栏开关切换:写设置 + on/off(netmgr IPC,回退直连 hciconfig)+ 触发扫描 + 同步状态栏 */
|
||||||
static void bt_top_sw_set(void *ctx, int on)
|
static void bt_top_sw_set(void *ctx, int on)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
meow_settings_set_bool("network.bluetooth_enabled", on);
|
meow_settings_set_bool("network.bluetooth_enabled", on);
|
||||||
s_net_toggle_bt(on);
|
nm_bt_onoff(on);
|
||||||
g_sys.bt_on = on;
|
g_sys.bt_on = on;
|
||||||
bt_role_refresh(1);
|
bt_role_refresh(1);
|
||||||
paint_sb_right();
|
paint_sb_right();
|
||||||
@@ -73,10 +73,12 @@ static int bt_rows(void *ctx, sub_row_t *rbuf, int max)
|
|||||||
{
|
{
|
||||||
bt_ctx_t *c = ctx;
|
bt_ctx_t *c = ctx;
|
||||||
int full = c->full; c->full = 0; /* 消费一次全量标记 */
|
int full = c->full; c->full = 0; /* 消费一次全量标记 */
|
||||||
c->bt_on = hci_powered();
|
c->bt_on = nm_bt_powered(); /* IPC status;回退直连 hci_powered() */
|
||||||
if (full || c->conn_n < 0) { /* 首次/动作后:全量重扫 conn/paired */
|
if (full || c->conn_n < 0) { /* 首次/动作后:全量重扫 conn/paired */
|
||||||
|
/* conn 列表 netmgrd 无子命令(BT_CTL=status/on/off/paired),保持直连 bluetoothctl;
|
||||||
|
* paired 走 IPC(权威),回退直连。 */
|
||||||
c->conn_n = net_bt_connected(c->conn, S_NET_MAX);
|
c->conn_n = net_bt_connected(c->conn, S_NET_MAX);
|
||||||
c->paired_n = net_bt_paired(c->paired, S_NET_MAX);
|
c->paired_n = nm_bt_paired(c->paired, S_NET_MAX);
|
||||||
if (c->conn_n < 0) c->conn_n = 0;
|
if (c->conn_n < 0) c->conn_n = 0;
|
||||||
if (c->paired_n < 0) c->paired_n = 0;
|
if (c->paired_n < 0) c->paired_n = 0;
|
||||||
}
|
}
|
||||||
@@ -86,6 +88,11 @@ static int bt_rows(void *ctx, sub_row_t *rbuf, int max)
|
|||||||
net_bt_mode_str(c->mode, sizeof c->mode);
|
net_bt_mode_str(c->mode, sizeof c->mode);
|
||||||
|
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
/* 降级占位:netmgrd 不可达 → 提示(直连仍在跑,保功能) */
|
||||||
|
if (!netmgr_ok() && n < max) {
|
||||||
|
rbuf[n].kind = SUB_HDR; rbuf[n].label = "网络管理服务未运行(直连模式)";
|
||||||
|
rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++;
|
||||||
|
}
|
||||||
/* 已连接设备 */
|
/* 已连接设备 */
|
||||||
if (n < max) { rbuf[n].kind = SUB_HDR; rbuf[n].label = "已连接设备"; rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++; }
|
if (n < max) { rbuf[n].kind = SUB_HDR; rbuf[n].label = "已连接设备"; rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++; }
|
||||||
if (c->conn_n == 0) {
|
if (c->conn_n == 0) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ SYSROOT="$ROOT/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf"
|
|||||||
"$CC" -I"../sdk" -Os -static \
|
"$CC" -I"../sdk" -Os -static \
|
||||||
main_menu.c statusbar.c fb_compositor.c settings_page.c power_menu.c \
|
main_menu.c statusbar.c fb_compositor.c settings_page.c power_menu.c \
|
||||||
notif_center.c app_launcher.c audio.c backlight.c net.c about.c \
|
notif_center.c app_launcher.c audio.c backlight.c net.c about.c \
|
||||||
subpage.c wifi_page.c bt_page.c now_playing.c \
|
subpage.c wifi_page.c bt_page.c netmgr_ipc.c now_playing.c \
|
||||||
font_theme.c keys.c util.c meow_dep.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_touch.c meow_settings.c meow_notif.c meow_media.c meow_bgplay.c \
|
||||||
meow_ams_start.c \
|
meow_ams_start.c \
|
||||||
|
|||||||
@@ -444,8 +444,10 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
/* 已保存网络 / 扫描结果 / 蓝牙设备名称列表(用于下拉展示与连接动作)。
|
/* 已保存网络 / 扫描结果 / 蓝牙设备名称列表(用于下拉展示与连接动作)。
|
||||||
* M2.76:追加 mac[24](AP BSSID / 蓝牙设备 MAC),供连接/配对/详情展示;无则空串。 */
|
* M2.76:追加 mac[24](AP BSSID / 蓝牙设备 MAC),供连接/配对/详情展示;无则空串。
|
||||||
typedef struct { int id; char name[48]; char mac[24]; } meow_net_t;
|
* M6.0:追加 sec[16](WiFi 扫描结果安全类型:Open/WPA-PSK/...;无则空=未知),
|
||||||
|
* 供「WiFi 新网络录入」判断是否需要弹 IME 输密码。 */
|
||||||
|
typedef struct { int id; char name[48]; char mac[24]; char sec[16]; } meow_net_t;
|
||||||
|
|
||||||
|
|
||||||
/* ================================================================== */
|
/* ================================================================== */
|
||||||
@@ -781,6 +783,33 @@ void s_net_refresh(void);
|
|||||||
void s_net_sync_from_sys(void);
|
void s_net_sync_from_sys(void);
|
||||||
void s_net_toggle_bt(int on);
|
void s_net_toggle_bt(int on);
|
||||||
void s_net_toggle_wifi(int on);
|
void s_net_toggle_wifi(int on);
|
||||||
|
|
||||||
|
/* ================= meow_netmgrd IPC 客户端(netmgr_ipc.c)=================
|
||||||
|
* M6.0 设置页迁移(NETMGR_DESIGN §12):wifi_page/bt_page 的查询/操作统一走 netmgrd
|
||||||
|
* IPC(socket /usr/data/meow_net.sock,env MEOW_NET_SOCK 覆盖)。netmgrd 是 WiFi/BT
|
||||||
|
* 操作权威;其不可达(NETMGR_UNREACH)时回退 net.c 直连 wpa_cli/hciconfig(双轨保功能)。
|
||||||
|
* 迁移注释:此封装是「设置页客户端→权威 IPC」的唯一入口,netmgrd 不可达才走直连回退。 */
|
||||||
|
#define NETMGR_UNREACH (-1000) /* 传输/握手不可达(区别于 netmgrd MEOW_ERR_* 业务码) */
|
||||||
|
int netmgr_ok(void); /* 1=netmgrd 可达(结果约 1s 缓存,节流) */
|
||||||
|
int netmgr_wifi(const char *cmd, const char *arg, char *text, int tlen); /* 返回 code / NETMGR_UNREACH */
|
||||||
|
int netmgr_bt(const char *cmd, const char *arg, char *text, int tlen);
|
||||||
|
|
||||||
|
/* WiFi(IPC 优先;网卡链路态 wlan_connected()/hci_powered() 仍读内核,属只读观测) */
|
||||||
|
int nm_wifi_connected(void);
|
||||||
|
void nm_wifi_ssid(char *buf, int len);
|
||||||
|
int nm_wifi_info(char (*info)[48], int max);
|
||||||
|
int nm_wifi_saved(meow_net_t *out, int max);
|
||||||
|
int nm_wifi_scan(meow_net_t *out, int max);
|
||||||
|
void nm_wifi_scan_trigger(void);
|
||||||
|
int nm_wifi_connect(const char *ssid, const char *psk, char *err, int elen); /* MEOW_OK=成功 */
|
||||||
|
int nm_wifi_disconnect(char *err, int elen);
|
||||||
|
int nm_wifi_toggle(int on);
|
||||||
|
|
||||||
|
/* BT(IPC 优先:on/off/status/paired;连接/断开/配对/附近扫描 netmgrd 无子命令,仍直连) */
|
||||||
|
int nm_bt_powered(void);
|
||||||
|
int nm_bt_onoff(int on);
|
||||||
|
int nm_bt_status(int *powered, int *paired_n, char *addr, int alen);
|
||||||
|
int nm_bt_paired(meow_net_t *out, int max);
|
||||||
const meow_ui_theme_t *s_ui_theme(void);
|
const meow_ui_theme_t *s_ui_theme(void);
|
||||||
void sb_blit_icon(int icon_idx, int x, uint32_t fg, int ink_h);
|
void sb_blit_icon(int icon_idx, int x, uint32_t fg, int ink_h);
|
||||||
int scan_apps(struct app apps[MAX_APPS]);
|
int scan_apps(struct app apps[MAX_APPS]);
|
||||||
|
|||||||
Binary file not shown.
@@ -312,6 +312,22 @@ int net_wifi_scan(meow_net_t *out, int max)
|
|||||||
while (*se && *se != '\n' && *se != '\r') se++;
|
while (*se && *se != '\n' && *se != '\r') se++;
|
||||||
while (se > ssid && (se[-1] == ' ' || se[-1] == '\t')) se--;
|
while (se > ssid && (se[-1] == ' ' || se[-1] == '\t')) se--;
|
||||||
int has = (se > ssid && *ssid);
|
int has = (se > ssid && *ssid);
|
||||||
|
/* M6.0:flags 首个方括号内容 = 安全类型([WPA-PSK-CCMP] 等);纯 [ESS]/[IBSS] = 开放。
|
||||||
|
* 供「WiFi 新网络录入」判断是否需要弹 IME 输密码。 */
|
||||||
|
char sec[16] = "Open";
|
||||||
|
if (br) {
|
||||||
|
char *be = strchr(br + 1, ']');
|
||||||
|
if (be) {
|
||||||
|
int sl = (int)(be - (br + 1));
|
||||||
|
if (sl > 15) sl = 15;
|
||||||
|
if (sl > 0) {
|
||||||
|
memcpy(sec, br + 1, (size_t)sl); sec[sl] = 0;
|
||||||
|
if (!strstr(sec, "WPA") && !strstr(sec, "RSN") && !strstr(sec, "WEP") &&
|
||||||
|
!strstr(sec, "SAE") && !strstr(sec, "EAP"))
|
||||||
|
snprintf(sec, sizeof sec, "Open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/* 去重:同一 BSSID 只留一条,优先具名 SSID */
|
/* 去重:同一 BSSID 只留一条,优先具名 SSID */
|
||||||
int idx = -1;
|
int idx = -1;
|
||||||
for (int j = 0; j < n; j++) if (!strcmp(seen_bssid[j], bssid)) { idx = j; break; }
|
for (int j = 0; j < n; j++) if (!strcmp(seen_bssid[j], bssid)) { idx = j; break; }
|
||||||
@@ -323,6 +339,7 @@ int net_wifi_scan(meow_net_t *out, int max)
|
|||||||
named[idx] = has;
|
named[idx] = has;
|
||||||
out[idx].id = -1;
|
out[idx].id = -1;
|
||||||
snprintf(out[idx].mac, sizeof out[idx].mac, "%s", bssid); /* 保留 BSSID 供详情展示 */
|
snprintf(out[idx].mac, sizeof out[idx].mac, "%s", bssid); /* 保留 BSSID 供详情展示 */
|
||||||
|
snprintf(out[idx].sec, sizeof out[idx].sec, "%s", sec);
|
||||||
if (has) {
|
if (has) {
|
||||||
/* M2.41:扫描结果 SSID 也可能是 wpa_cli 的 \xNN 转义(中文),还原成真 UTF-8 */
|
/* M2.41:扫描结果 SSID 也可能是 wpa_cli 的 \xNN 转义(中文),还原成真 UTF-8 */
|
||||||
char raw[96];
|
char raw[96];
|
||||||
|
|||||||
@@ -0,0 +1,505 @@
|
|||||||
|
/* netmgr_ipc.c — MeowOS launcher → meow_netmgrd IPC 客户端(M6.0 设置页迁移,NETMGR_DESIGN §12)。
|
||||||
|
*
|
||||||
|
* 【迁移注释】wifi_page/bt_page 的 WiFi/BT 查询与操作统一走 netmgrd IPC(socket
|
||||||
|
* /usr/data/meow_net.sock,env MEOW_NET_SOCK 覆盖)。netmgrd 是 WiFi/BT 操作权威
|
||||||
|
* (IPC 0xE4 WIFI_CTL / 0xE6 BT_CTL);本文件是「设置页客户端 → 权威 IPC」的唯一入口。
|
||||||
|
* 【双轨过渡】netmgrd 不可达(NETMGR_UNREACH)时,本文件回退到 net.c 既有的直连
|
||||||
|
* wpa_cli/hciconfig/busybox 读取(保功能);状态栏物理键语义、sdk/amsd/netmgrd 本体不动。
|
||||||
|
*
|
||||||
|
* 鉴权:跟 pm_power_ipc 同款 —— 先 MSG_HELLO(app_id=os.meow.launcher,netmgrd 按
|
||||||
|
* SO_PEERCRED 认出 /usr/bin/meow_launcherd = 系统身份,写级放行),再发 CTL,收 RESULT。
|
||||||
|
* 只读观测(wlan_connected()/hci_powered() 读 /sys 链路态)仍在 net.c;此处只接权威操作。
|
||||||
|
*/
|
||||||
|
#include "meow_launcher_internal.h"
|
||||||
|
|
||||||
|
#define NETMGR_SOCK_DEFAULT "/usr/data/meow_net.sock"
|
||||||
|
/* NETMGR_UNREACH 定义在 meow_launcher_internal.h(测试/调用方共用) */
|
||||||
|
|
||||||
|
/* 可达性缓存:netmgrd 不可达时每次 connect 都秒回 ECONNREFUSED,无成本;缓存约 1s 防热循环。 */
|
||||||
|
static int g_nm_ok = -1; /* -1 未知 / 0 down / 1 up */
|
||||||
|
static time_t g_nm_ok_ts = 0;
|
||||||
|
|
||||||
|
static void nm_cache(int ok) { g_nm_ok = ok; g_nm_ok_ts = time(NULL); }
|
||||||
|
|
||||||
|
/* ---- socket 连接 ---- */
|
||||||
|
static int nm_sock(void)
|
||||||
|
{
|
||||||
|
const char *sock = getenv("MEOW_NET_SOCK");
|
||||||
|
if (!sock || !sock[0]) sock = NETMGR_SOCK_DEFAULT;
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
struct sockaddr_un a;
|
||||||
|
memset(&a, 0, sizeof a);
|
||||||
|
a.sun_family = AF_UNIX;
|
||||||
|
strncpy(a.sun_path, sock, sizeof a.sun_path - 1);
|
||||||
|
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 帧收发(复刻 power_menu.c pw_xchg:读超时 1.5s,绝不让子页主循环卡死)---- */
|
||||||
|
static int nm_w_all(int fd, const uint8_t *b, size_t n)
|
||||||
|
{
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < n) { ssize_t w = write(fd, b + off, n - off); if (w <= 0) return -1; off += (size_t)w; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int nm_r_all(int fd, uint8_t *b, size_t n)
|
||||||
|
{
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < n) { ssize_t r = read(fd, b + off, n - off); if (r <= 0) return -1; off += (size_t)r; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int nm_xchg(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len,
|
||||||
|
uint8_t *rbuf, uint32_t rcap, uint16_t *rtype)
|
||||||
|
{
|
||||||
|
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 (nm_w_all(fd, hdr, sizeof hdr) < 0) return -1;
|
||||||
|
if (len && pl && nm_w_all(fd, pl, len) < 0) return -1;
|
||||||
|
struct pollfd pf = { fd, POLLIN, 0 };
|
||||||
|
int pr = poll(&pf, 1, 1500);
|
||||||
|
if (pr <= 0) return -1;
|
||||||
|
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||||||
|
if (nm_r_all(fd, rhdr, sizeof rhdr) < 0) return -1;
|
||||||
|
uint32_t rmagic, rlen2; uint16_t rver, rt;
|
||||||
|
memcpy(&rmagic, rhdr, 4); memcpy(&rver, rhdr + 4, 2); memcpy(&rt, rhdr + 6, 2);
|
||||||
|
memcpy(&rlen2, rhdr + 12, 4);
|
||||||
|
if (rmagic != MEOW_IPC_MAGIC || rver != MEOW_IPC_VERSION) return -1;
|
||||||
|
if (rlen2 > rcap) rlen2 = rcap;
|
||||||
|
if (rlen2 && nm_r_all(fd, rbuf, rlen2) < 0) return -1;
|
||||||
|
if (rtype) *rtype = rt;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nm_hello(int fd)
|
||||||
|
{
|
||||||
|
m_hello_t h;
|
||||||
|
memset(&h, 0, sizeof h);
|
||||||
|
snprintf(h.app_id, sizeof h.app_id, "%s", "os.meow.launcher");
|
||||||
|
h.version = 1;
|
||||||
|
uint8_t rbuf[256]; uint16_t rtype = 0;
|
||||||
|
if (nm_xchg(fd, MSG_HELLO, 0x100, &h, sizeof h, rbuf, sizeof rbuf, &rtype) < 0 || rtype != MSG_ACK)
|
||||||
|
return -1;
|
||||||
|
m_ack_t ak; memcpy(&ak, rbuf, sizeof ak);
|
||||||
|
return (ak.code == MEOW_OK) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 发一条 WIFI_CTL/BT_CTL 子命令,收 RESULT。返回 netmgrd 业务 code;NETMGR_UNREACH=不可达。 */
|
||||||
|
static int nm_cmd(uint16_t ctl_type, uint16_t res_type, const char *cmd, const char *arg,
|
||||||
|
char *text, int tlen)
|
||||||
|
{
|
||||||
|
m_net_cmd_t q;
|
||||||
|
memset(&q, 0, sizeof q);
|
||||||
|
snprintf(q.cmd, sizeof q.cmd, "%s", cmd ? cmd : "");
|
||||||
|
if (arg) snprintf(q.arg, sizeof q.arg, "%s", arg);
|
||||||
|
int fd = nm_sock();
|
||||||
|
if (fd < 0) { nm_cache(0); return NETMGR_UNREACH; }
|
||||||
|
if (nm_hello(fd) < 0) { close(fd); nm_cache(0); return NETMGR_UNREACH; }
|
||||||
|
uint8_t rbuf[MEOW_IPC_MAX]; uint16_t rtype = 0;
|
||||||
|
memset(rbuf, 0, sizeof rbuf);
|
||||||
|
if (nm_xchg(fd, ctl_type, 0x101, &q, sizeof q, rbuf, sizeof rbuf, &rtype) < 0 || rtype != res_type) {
|
||||||
|
close(fd); nm_cache(0); return NETMGR_UNREACH;
|
||||||
|
}
|
||||||
|
m_net_cmd_result_t res; memcpy(&res, rbuf, sizeof res);
|
||||||
|
close(fd);
|
||||||
|
nm_cache(1);
|
||||||
|
uint32_t tl = res.text_len;
|
||||||
|
if (tl >= (uint32_t)tlen) tl = (uint32_t)(tlen - 1);
|
||||||
|
if (text && tlen > 0) { memcpy(text, res.text, tl); text[tl] = 0; }
|
||||||
|
return (int)res.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int netmgr_wifi(const char *cmd, const char *arg, char *text, int tlen)
|
||||||
|
{ return nm_cmd(MSG_NET_WIFI_CTL, MSG_NET_WIFI_RESULT, cmd, arg, text, tlen); }
|
||||||
|
|
||||||
|
int netmgr_bt(const char *cmd, const char *arg, char *text, int tlen)
|
||||||
|
{ return nm_cmd(MSG_NET_BT_CTL, MSG_NET_BT_RESULT, cmd, arg, text, tlen); }
|
||||||
|
|
||||||
|
int netmgr_ok(void)
|
||||||
|
{
|
||||||
|
if (g_nm_ok >= 0 && time(NULL) - g_nm_ok_ts < 1) return g_nm_ok;
|
||||||
|
int fd = nm_sock();
|
||||||
|
if (fd < 0) { nm_cache(0); return 0; }
|
||||||
|
close(fd); nm_cache(1); return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ================= WiFi(IPC 优先,net.c 直连回退)================= */
|
||||||
|
|
||||||
|
/* —— net.c 的 wpa_decode_ssid 是 static,这里复制最小实现(本模块自含,避免改 net.c 可见性)—— */
|
||||||
|
static int nm_hex(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 nm_decode_ssid(const char *src, char *dst, int dlen)
|
||||||
|
{
|
||||||
|
int o = 0;
|
||||||
|
while (*src && o < dlen - 1) {
|
||||||
|
if (src[0] == '\\' && src[1] == 'x' && nm_hex(src[2]) >= 0 && nm_hex(src[3]) >= 0) {
|
||||||
|
dst[o++] = (char)((nm_hex(src[2]) << 4) | nm_hex(src[3]));
|
||||||
|
src += 4;
|
||||||
|
} else {
|
||||||
|
dst[o++] = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst[o] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* —— netmgrd `wifi status` 单行摘要解析:`wifi state=X connected=N ssid=S ip=I rssi=R`。
|
||||||
|
* 已知字段以「空格+key=」分隔;ssid 可能含空格,需读到下一个已知字段为止。 —— */
|
||||||
|
static int nm_is_status_key(const char *p)
|
||||||
|
{
|
||||||
|
if (!strncmp(p, " ip=", 4) || !strncmp(p, " rssi=", 6) ||
|
||||||
|
!strncmp(p, " connected=", 11) || !strncmp(p, " state=", 7)) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static const char *nm_val(const char *text, const char *key, char *dst, int dlen, int space_ok)
|
||||||
|
{
|
||||||
|
const char *p = strstr(text, key);
|
||||||
|
if (!p) { dst[0] = 0; return p; }
|
||||||
|
p += strlen(key);
|
||||||
|
int o = 0;
|
||||||
|
while (*p && o < dlen - 1) {
|
||||||
|
if (*p == ' ') {
|
||||||
|
if (!space_ok) break;
|
||||||
|
if (nm_is_status_key(p)) break; /* ssid 含空格:停在下一个已知字段 */
|
||||||
|
}
|
||||||
|
dst[o++] = *p++;
|
||||||
|
}
|
||||||
|
dst[o] = 0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
static int nm_parse_connected(const char *text)
|
||||||
|
{
|
||||||
|
char v[8];
|
||||||
|
nm_val(text, "connected=", v, sizeof v, 0);
|
||||||
|
return atoi(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_wifi_connected(void)
|
||||||
|
{
|
||||||
|
char text[256];
|
||||||
|
int rc = netmgr_wifi("status", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) return wlan_connected();
|
||||||
|
if (rc != MEOW_OK) return 0;
|
||||||
|
return nm_parse_connected(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nm_wifi_ssid(char *buf, int len)
|
||||||
|
{
|
||||||
|
buf[0] = 0;
|
||||||
|
char text[256];
|
||||||
|
int rc = netmgr_wifi("status", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) { net_wifi_ssid(buf, len); return; }
|
||||||
|
if (rc != MEOW_OK) return;
|
||||||
|
if (!nm_parse_connected(text)) return;
|
||||||
|
char raw[128];
|
||||||
|
nm_val(text, "ssid=", raw, sizeof raw, 1);
|
||||||
|
nm_decode_ssid(raw, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_wifi_info(char (*info)[48], int max)
|
||||||
|
{
|
||||||
|
char text[256];
|
||||||
|
int rc = netmgr_wifi("status", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) return net_wifi_info(info, max); /* 回退:完整详情(直连 wpa_cli+ip route) */
|
||||||
|
if (rc != MEOW_OK || !nm_parse_connected(text)) return 0;
|
||||||
|
char ssid[40] = "", ip[32] = "", rssi[16] = "";
|
||||||
|
char raw[128];
|
||||||
|
nm_val(text, "ssid=", raw, sizeof raw, 1);
|
||||||
|
nm_decode_ssid(raw, ssid, sizeof ssid);
|
||||||
|
nm_val(text, "ip=", ip, sizeof ip, 0);
|
||||||
|
nm_val(text, "rssi=", rssi, sizeof rssi, 0);
|
||||||
|
int n = 0;
|
||||||
|
if (ssid[0]) snprintf(info[n++], 48, "SSID: %s", ssid);
|
||||||
|
if (ip[0]) snprintf(info[n++], 48, "本机IP: %s", ip);
|
||||||
|
if (rssi[0]) snprintf(info[n++], 48, "信号: %s dBm", rssi);
|
||||||
|
if (n == 0) snprintf(info[n++], 48, "已连接(无详情)");
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* —— 解析 netmgrd `wifi networks`(wpa_cli list_networks 原样文本)→ meow_net_t[];
|
||||||
|
* 直接复用 net.c 既有表头跳行逻辑。返回项数。 —— */
|
||||||
|
static int nm_parse_saved(const char *txt, meow_net_t *out, int max)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
if (!txt || max <= 0) return 0;
|
||||||
|
if (max > S_NET_MAX) max = S_NET_MAX;
|
||||||
|
const char *line = strchr(txt, '\n');
|
||||||
|
if (line) line++;
|
||||||
|
while (line && *line && n < max) {
|
||||||
|
const char *eol = strchr(line, '\n');
|
||||||
|
const char *p = line; while (*p == ' ' || *p == '\t') p++;
|
||||||
|
if (*p && *p != '#') {
|
||||||
|
int id = atoi(p);
|
||||||
|
const char *sp = p; while (*sp && *sp != ' ' && *sp != '\t') sp++;
|
||||||
|
while (*sp == ' ' || *sp == '\t') sp++;
|
||||||
|
const char *se = sp; while (*se && *se != ' ' && *se != '\t' && *se != '\n' && *se != '\r') se++;
|
||||||
|
const char *bp = se; while (*bp == ' ' || *bp == '\t') bp++;
|
||||||
|
const char *be = bp; while (*be && *be != ' ' && *be != '\t' && *be != '\n' && *be != '\r') be++;
|
||||||
|
if (sp != se) {
|
||||||
|
char raw[160];
|
||||||
|
int l = (int)(se - sp);
|
||||||
|
if (l > (int)sizeof raw - 1) l = (int)sizeof raw - 1;
|
||||||
|
memcpy(raw, sp, (size_t)l); raw[l] = 0;
|
||||||
|
out[n].id = id;
|
||||||
|
nm_decode_ssid(raw, out[n].name, sizeof out[n].name);
|
||||||
|
if (be > bp && (be - bp) < (int)sizeof out[n].mac) {
|
||||||
|
memcpy(out[n].mac, bp, (size_t)(be - bp)); out[n].mac[be - bp] = 0;
|
||||||
|
}
|
||||||
|
out[n].sec[0] = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line = eol ? eol + 1 : NULL;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* —— 解析 netmgrd `wifi scan`(wpa_cli scan_results 原样文本)→ meow_net_t[](按 BSSID 去重、
|
||||||
|
* 优先具名;补充 sec 安全类型供「新网络录入」判断是否弹 IME)。返回项数。 —— */
|
||||||
|
static int nm_parse_scan(const char *txt, meow_net_t *out, int max)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
if (!txt || max <= 0) return 0;
|
||||||
|
if (max > S_NET_MAX) max = S_NET_MAX;
|
||||||
|
char seen_bssid[S_NET_MAX][24];
|
||||||
|
int named[S_NET_MAX] = {0};
|
||||||
|
const char *line = strchr(txt, '\n');
|
||||||
|
if (line) line++;
|
||||||
|
while (line && *line && n < max) {
|
||||||
|
const char *eol = strchr(line, '\n');
|
||||||
|
const char *p = line; while (*p == ' ' || *p == '\t') p++;
|
||||||
|
if (*p) {
|
||||||
|
char bssid[24] = "";
|
||||||
|
{ const char *b = p; while (*p && *p != ' ' && *p != '\t') p++;
|
||||||
|
int l = (int)(p - b); if (l > 22) l = 22; memcpy(bssid, b, (size_t)l); bssid[l] = 0; }
|
||||||
|
const char *ssid = "";
|
||||||
|
char sec[16] = "Open";
|
||||||
|
const char *br = strchr(p, '[');
|
||||||
|
if (br) {
|
||||||
|
/* 安全类型 = 首个方括号内容([WPA-PSK-CCMP] 等);纯 [ESS]/[IBSS] = 开放 */
|
||||||
|
const char *be = strchr(br + 1, ']');
|
||||||
|
if (be) {
|
||||||
|
int sl = (int)(be - (br + 1));
|
||||||
|
if (sl > 15) sl = 15;
|
||||||
|
if (sl > 0) {
|
||||||
|
memcpy(sec, br + 1, (size_t)sl); sec[sl] = 0;
|
||||||
|
if (!strstr(sec, "WPA") && !strstr(sec, "RSN") &&
|
||||||
|
!strstr(sec, "WEP") && !strstr(sec, "SAE") && !strstr(sec, "EAP"))
|
||||||
|
snprintf(sec, sizeof sec, "Open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *q = br;
|
||||||
|
while (*q) {
|
||||||
|
while (*q == ' ' || *q == '\t') q++;
|
||||||
|
if (*q == '[') { while (*q && *q != ' ' && *q != '\t') q++; }
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
ssid = q; while (*ssid == ' ' || *ssid == '\t') ssid++;
|
||||||
|
}
|
||||||
|
const char *se = ssid;
|
||||||
|
while (*se && *se != '\n' && *se != '\r') se++;
|
||||||
|
while (se > ssid && (se[-1] == ' ' || se[-1] == '\t')) se--;
|
||||||
|
int has = (se > ssid && *ssid);
|
||||||
|
int idx = -1;
|
||||||
|
for (int j = 0; j < n; j++) if (!strcmp(seen_bssid[j], bssid)) { idx = j; break; }
|
||||||
|
if (idx < 0) {
|
||||||
|
if (n >= max) { line = eol ? eol + 1 : NULL; continue; }
|
||||||
|
idx = n++;
|
||||||
|
strncpy(seen_bssid[idx], bssid, sizeof seen_bssid[idx] - 1);
|
||||||
|
seen_bssid[idx][sizeof seen_bssid[idx] - 1] = 0;
|
||||||
|
named[idx] = has;
|
||||||
|
out[idx].id = -1;
|
||||||
|
snprintf(out[idx].mac, sizeof out[idx].mac, "%s", bssid);
|
||||||
|
snprintf(out[idx].sec, sizeof out[idx].sec, "%s", sec);
|
||||||
|
if (has) {
|
||||||
|
char raw[96];
|
||||||
|
int l = (int)(se - ssid); if (l > (int)sizeof raw - 1) l = (int)sizeof raw - 1;
|
||||||
|
memcpy(raw, ssid, (size_t)l); raw[l] = 0;
|
||||||
|
nm_decode_ssid(raw, out[idx].name, sizeof out[idx].name);
|
||||||
|
} else {
|
||||||
|
snprintf(out[idx].name, sizeof out[idx].name, "(隐藏网络)");
|
||||||
|
}
|
||||||
|
} else if (has && !named[idx]) {
|
||||||
|
char raw[96];
|
||||||
|
int l = (int)(se - ssid); if (l > (int)sizeof raw - 1) l = (int)sizeof raw - 1;
|
||||||
|
memcpy(raw, ssid, (size_t)l); raw[l] = 0;
|
||||||
|
nm_decode_ssid(raw, out[idx].name, sizeof out[idx].name);
|
||||||
|
named[idx] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line = eol ? eol + 1 : NULL;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_wifi_saved(meow_net_t *out, int max)
|
||||||
|
{
|
||||||
|
char text[MEOW_IPC_MAX - 32];
|
||||||
|
int rc = netmgr_wifi("networks", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) return net_wifi_saved(out, max); /* 直连回退 */
|
||||||
|
if (rc != MEOW_OK) return 0;
|
||||||
|
return nm_parse_saved(text, out, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_wifi_scan(meow_net_t *out, int max)
|
||||||
|
{
|
||||||
|
char text[MEOW_IPC_MAX - 32];
|
||||||
|
int rc = netmgr_wifi("scan", NULL, text, sizeof text); /* netmgrd 内含触发+有界等待+读结果 */
|
||||||
|
if (rc == NETMGR_UNREACH) return net_wifi_scan(out, max);
|
||||||
|
if (rc != MEOW_OK) return 0;
|
||||||
|
return nm_parse_scan(text, out, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nm_wifi_scan_trigger(void)
|
||||||
|
{
|
||||||
|
/* IPC:scan 子命令在 netmgrd 内已一次性「触发+等待+读结果」,此处无需单独触发;
|
||||||
|
* 直连回退:异步 wpa_cli scan(结果稍后 net_wifi_scan 读)。 */
|
||||||
|
if (!netmgr_ok()) net_wifi_scan_trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* —— 直连回退:wpa_cli add/set/enable/select 新网络(复刻 netmgrd wifi_connect 逻辑)—— */
|
||||||
|
static int nm_direct_connect(const char *ssid, const char *psk, char *err, int elen)
|
||||||
|
{
|
||||||
|
const char *wpa = "PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli -i wlan0";
|
||||||
|
char cmd[256];
|
||||||
|
snprintf(cmd, sizeof cmd, "%s add_network 2>/dev/null", wpa);
|
||||||
|
char *o = popen_read_tmo(1200, cmd);
|
||||||
|
int id = o ? atoi(o) : -1;
|
||||||
|
if (o) free(o);
|
||||||
|
if (id < 0) { if (err) snprintf(err, elen, "add_network 无 id"); return MEOW_ERR_INVAL; }
|
||||||
|
{
|
||||||
|
char *r;
|
||||||
|
snprintf(cmd, sizeof cmd, "%s set_network %d ssid '\"%s\"' 2>/dev/null", wpa, id, ssid);
|
||||||
|
if ((r = popen_read_tmo(1200, cmd))) free(r);
|
||||||
|
if (psk && psk[0]) {
|
||||||
|
snprintf(cmd, sizeof cmd, "%s set_network %d psk '\"%s\"' 2>/dev/null", wpa, id, psk);
|
||||||
|
if ((r = popen_read_tmo(1200, cmd))) free(r);
|
||||||
|
} else {
|
||||||
|
snprintf(cmd, sizeof cmd, "%s set_network %d key_mgmt NONE 2>/dev/null", wpa, id);
|
||||||
|
if ((r = popen_read_tmo(1200, cmd))) free(r);
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd, "%s enable_network %d 2>/dev/null", wpa, id);
|
||||||
|
if ((r = popen_read_tmo(1200, cmd))) free(r);
|
||||||
|
snprintf(cmd, sizeof cmd, "%s select_network %d 2>/dev/null", wpa, id);
|
||||||
|
if ((r = popen_read_tmo(1200, cmd))) free(r);
|
||||||
|
}
|
||||||
|
if (err) snprintf(err, elen, "connect: ssid=%s network_id=%d", ssid, id);
|
||||||
|
logmsg("netmgr_ipc: [direct-fallback] wifi connect ssid=%s id=%d", ssid, id);
|
||||||
|
return MEOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_wifi_connect(const char *ssid, const char *psk, char *err, int elen)
|
||||||
|
{
|
||||||
|
if (!ssid || !ssid[0]) { if (err) snprintf(err, elen, "ssid 缺失"); return MEOW_ERR_INVAL; }
|
||||||
|
char arg[132];
|
||||||
|
snprintf(arg, sizeof arg, "%s,%s", ssid, psk ? psk : ""); /* connect 带空 psk → netmgrd 复用既有网络 */
|
||||||
|
char text[192];
|
||||||
|
int rc = netmgr_wifi("connect", arg, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) return nm_direct_connect(ssid, psk, err, elen);
|
||||||
|
if (err && elen > 0) snprintf(err, elen, "%s", text);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_wifi_disconnect(char *err, int elen)
|
||||||
|
{
|
||||||
|
char text[128];
|
||||||
|
int rc = netmgr_wifi("disconnect", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) {
|
||||||
|
char cmd[160];
|
||||||
|
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli -i wlan0 disconnect 2>/dev/null");
|
||||||
|
popen_read_tmo(1200, cmd);
|
||||||
|
if (err) snprintf(err, elen, "已下发");
|
||||||
|
return MEOW_OK;
|
||||||
|
}
|
||||||
|
if (err && elen > 0) snprintf(err, elen, "%s", text);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_wifi_toggle(int on)
|
||||||
|
{
|
||||||
|
char text[128];
|
||||||
|
int rc = netmgr_wifi(on ? "on" : "off", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) { s_net_toggle_wifi(on); return MEOW_OK; } /* 直连回退 */
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ================= BT(IPC 优先:on/off/status/paired;其余子命令 netmgrd 无,直连)================= */
|
||||||
|
|
||||||
|
int nm_bt_powered(void)
|
||||||
|
{
|
||||||
|
char text[128];
|
||||||
|
int rc = netmgr_bt("status", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) return hci_powered();
|
||||||
|
if (rc != MEOW_OK) return 0;
|
||||||
|
char v[8];
|
||||||
|
nm_val(text, "powered=", v, sizeof v, 0);
|
||||||
|
return atoi(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_bt_onoff(int on)
|
||||||
|
{
|
||||||
|
char text[128];
|
||||||
|
int rc = netmgr_bt(on ? "on" : "off", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) { s_net_toggle_bt(on); return MEOW_OK; }
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_bt_status(int *powered, int *paired_n, char *addr, int alen)
|
||||||
|
{
|
||||||
|
char text[192];
|
||||||
|
int rc = netmgr_bt("status", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) { /* 直连回退:读内核/tool */
|
||||||
|
if (powered) *powered = hci_powered();
|
||||||
|
if (paired_n) *paired_n = -1;
|
||||||
|
if (addr && alen > 0) addr[0] = 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if (rc == MEOW_OK) {
|
||||||
|
char v[8];
|
||||||
|
if (powered) { nm_val(text, "powered=", v, sizeof v, 0); *powered = atoi(v); }
|
||||||
|
if (paired_n) { nm_val(text, "paired=", v, sizeof v, 0); *paired_n = atoi(v); }
|
||||||
|
if (addr && alen > 0) nm_val(text, "address=", addr, alen, 0);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_bt_paired(meow_net_t *out, int max)
|
||||||
|
{
|
||||||
|
char text[MEOW_IPC_MAX - 32];
|
||||||
|
int rc = netmgr_bt("paired", NULL, text, sizeof text);
|
||||||
|
if (rc == NETMGR_UNREACH) return net_bt_paired(out, max);
|
||||||
|
if (rc != MEOW_OK) return 0;
|
||||||
|
int n = 0;
|
||||||
|
if (max > S_NET_MAX) max = S_NET_MAX;
|
||||||
|
if (text[0]) {
|
||||||
|
char *line = text;
|
||||||
|
while (line && *line && n < max) {
|
||||||
|
char *eol = strchr(line, '\n');
|
||||||
|
if (eol) *eol = 0;
|
||||||
|
char *p = strstr(line, "Device ");
|
||||||
|
if (p) {
|
||||||
|
p += 7;
|
||||||
|
char *mac = p; while (*mac && *mac != ' ' && *mac != '\t') mac++;
|
||||||
|
char *nm2 = mac; while (*nm2 == ' ' || *nm2 == '\t') nm2++;
|
||||||
|
int ml = (int)(mac - p); if (ml > 47) ml = 47;
|
||||||
|
if (ml > 0 && ml < (int)sizeof out[n].mac) {
|
||||||
|
memcpy(out[n].mac, p, (size_t)ml); out[n].mac[ml] = 0;
|
||||||
|
}
|
||||||
|
snprintf(out[n].name, sizeof out[n].name, "%s", *nm2 ? nm2 : p);
|
||||||
|
out[n].id = -1; out[n].sec[0] = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
line = eol ? eol + 1 : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
/* wifi_page.c — MeowOS 系统设置「WiFi 独立子页面」(安卓风,M2.76)。
|
/* wifi_page.c — MeowOS 系统设置「WiFi 独立子页面」(安卓风,M2.76;M6.0 迁移 netmgr IPC)。
|
||||||
*
|
*
|
||||||
* 页面(sub_page_t 脚手架 subpage.c 驱动):
|
* 页面(sub_page_t 脚手架 subpage.c 驱动):
|
||||||
* - 顶栏:返回 + 标题「WiFi」 + 右上开关(network.wifi_enabled)
|
* - 顶栏:返回 + 标题「WiFi」 + 右上开关(network.wifi_enabled)
|
||||||
* - 当前连接:SSID 行,点开 → 详情(SSID/AP MAC/本机 MAC/AP IP/本机 IP/频道)
|
* - 当前连接:SSID 行,点开 → 详情(SSID/本机IP/信号)+ [断开连接]
|
||||||
* - 已保存网络:每项点 → wpa_cli select_network 连接;当前连接的项标「已连接」
|
* - 已保存网络:每项点 → netmgr wifi connect(ssid,复用既有网络);当前连接的项标「已连接」
|
||||||
* - 附近网络:扫描结果(按 BSSID 去重——修复"AP 的 MAC 被当成单独设备列出";
|
* - 附近网络:扫描结果(按 BSSID 去重;中文 SSID 经 wpa_decode_ssid 还原 \xNN → UTF-8)
|
||||||
* 中文 SSID 经 wpa_decode_ssid 还原 \xNN 原始字节 → UTF-8 显示)点开 → 详情(SSID/AP MAC)
|
* 点开 →【WiFi 新网络录入】加密网络弹 SDK IME 输密码(PW 模式)→ netmgr connect → 结果反馈;
|
||||||
|
* 开放网络直连。
|
||||||
*
|
*
|
||||||
* 均复用 net.c 既有读取(net_wifi_saved / net_wifi_scan / net_wifi_info / net_wifi_ssid /
|
* 【M6.0 迁移】全部查询/操作走 netmgr IPC(netmgr_ipc.c 的 nm_* 封装),netmgrd 是 WiFi 权威;
|
||||||
* wlan_connected),写入只走 meow_settings_*;本机 MAC 等详情读 wpa_cli status。
|
* netmgrd 不可达(NETMGR_UNREACH)→ 自动回退 net.c 直连 wpa_cli(双轨保功能),页面显示降级占位
|
||||||
|
* 「网络管理服务未运行(直连模式)」。
|
||||||
*/
|
*/
|
||||||
#include "meow_launcher_internal.h"
|
#include "meow_launcher_internal.h"
|
||||||
|
|
||||||
@@ -16,16 +18,16 @@
|
|||||||
/* action code 判位(act 回调据 act 值定位网络) */
|
/* action code 判位(act 回调据 act 值定位网络) */
|
||||||
#define WF_ACT_CURR (-1) /* 当前连接(SUB_ROW,detail) */
|
#define WF_ACT_CURR (-1) /* 当前连接(SUB_ROW,detail) */
|
||||||
#define WF_ACT_SAVED 0 /* 已保存网络 idx(SUB_ACT,connect) */
|
#define WF_ACT_SAVED 0 /* 已保存网络 idx(SUB_ACT,connect) */
|
||||||
#define WF_ACT_SCAN_BASE 1000 /* 附近网络 1000+idx(SUB_ROW,detail) */
|
#define WF_ACT_SCAN_BASE 1000 /* 附近网络 1000+idx(SUB_ROW,连接) */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
meow_net_t saved[S_NET_MAX];
|
meow_net_t saved[S_NET_MAX];
|
||||||
meow_net_t scan[S_NET_MAX];
|
meow_net_t scan[S_NET_MAX];
|
||||||
int saved_n, scan_n;
|
int saved_n, scan_n;
|
||||||
char ssid[64];
|
char ssid[64];
|
||||||
int connected; /* wlan_connected() */
|
int connected; /* nm_wifi_connected()(IPC>直连回退 wlan_connected) */
|
||||||
int wifi_on; /* network.wifi_enabled */
|
int wifi_on; /* network.wifi_enabled */
|
||||||
long last_scan_ms; /* 扫描触发节流(net_wifi_scan_trigger 间隔) */
|
long last_scan_ms; /* 扫描触发节流 */
|
||||||
} wifi_ctx_t;
|
} wifi_ctx_t;
|
||||||
|
|
||||||
|
|
||||||
@@ -40,20 +42,20 @@ static int wifi_top_sw(void *ctx, int *on)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 顶栏开关切换:写设置 + 开/关网卡 + 触发重扫 + 同步状态栏 */
|
/* 顶栏开关切换:写设置 + 开/关网卡(netmgr on/off,回退直连)+ 触发重扫 + 同步状态栏 */
|
||||||
static void wifi_top_sw_set(void *ctx, int on)
|
static void wifi_top_sw_set(void *ctx, int on)
|
||||||
{
|
{
|
||||||
wifi_ctx_t *c = ctx;
|
wifi_ctx_t *c = ctx;
|
||||||
meow_settings_set_bool("network.wifi_enabled", on);
|
meow_settings_set_bool("network.wifi_enabled", on);
|
||||||
s_net_toggle_wifi(on);
|
nm_wifi_toggle(on);
|
||||||
g_sys.wifi_on = on;
|
g_sys.wifi_on = on;
|
||||||
paint_sb_right(); /* 状态栏 WiFi 图标随开关即时刷新 */
|
paint_sb_right(); /* 状态栏 WiFi 图标随开关即时刷新 */
|
||||||
meow_settings_save();
|
meow_settings_save();
|
||||||
if (on) { net_wifi_scan_trigger(); c->last_scan_ms = now_ms(); }
|
if (on) { nm_wifi_scan_trigger(); c->last_scan_ms = now_ms(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 触发一次 WiFi 扫描(节流:1.5s 内不重复 spawn wpa_cli scan) */
|
/* 触发一次 WiFi 扫描(节流:1.5s 内不重复。IPC 路径 scan 子命令由 nm_wifi_scan 内含触发+读取) */
|
||||||
static void wifi_scan(void *ctx)
|
static void wifi_scan(void *ctx)
|
||||||
{
|
{
|
||||||
wifi_ctx_t *c = ctx;
|
wifi_ctx_t *c = ctx;
|
||||||
@@ -61,28 +63,32 @@ static void wifi_scan(void *ctx)
|
|||||||
long t = (long)now_ms();
|
long t = (long)now_ms();
|
||||||
if (c->last_scan_ms && t - c->last_scan_ms < 1500) return;
|
if (c->last_scan_ms && t - c->last_scan_ms < 1500) return;
|
||||||
c->last_scan_ms = t;
|
c->last_scan_ms = t;
|
||||||
net_wifi_scan_trigger();
|
nm_wifi_scan_trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 填行:刷新数据后构建分组列表 */
|
/* 填行:刷新数据后构建分组列表(netmgr IPC 优先;不可达回退直连 + 降级占位) */
|
||||||
static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
|
static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
|
||||||
{
|
{
|
||||||
wifi_ctx_t *c = ctx;
|
wifi_ctx_t *c = ctx;
|
||||||
/* 刷新数据(popen 读列表;节流触发扫描) */
|
c->connected = nm_wifi_connected();
|
||||||
c->connected = wlan_connected();
|
|
||||||
if (c->connected) {
|
if (c->connected) {
|
||||||
net_wifi_ssid(c->ssid, sizeof c->ssid);
|
nm_wifi_ssid(c->ssid, sizeof c->ssid);
|
||||||
wifi_scan(c);
|
wifi_scan(c);
|
||||||
} else {
|
} else {
|
||||||
c->ssid[0] = 0;
|
c->ssid[0] = 0;
|
||||||
}
|
}
|
||||||
c->saved_n = net_wifi_saved(c->saved, S_NET_MAX);
|
c->saved_n = nm_wifi_saved(c->saved, S_NET_MAX);
|
||||||
c->scan_n = net_wifi_scan(c->scan, S_NET_MAX);
|
c->scan_n = nm_wifi_scan(c->scan, S_NET_MAX);
|
||||||
if (c->saved_n < 0) c->saved_n = 0;
|
if (c->saved_n < 0) c->saved_n = 0;
|
||||||
if (c->scan_n < 0) c->scan_n = 0;
|
if (c->scan_n < 0) c->scan_n = 0;
|
||||||
|
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
/* 降级占位:netmgrd 不可达 → 提示(直连仍在跑,保功能) */
|
||||||
|
if (!netmgr_ok() && n < max) {
|
||||||
|
rbuf[n].kind = SUB_HDR; rbuf[n].label = "网络管理服务未运行(直连模式)";
|
||||||
|
rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++;
|
||||||
|
}
|
||||||
/* 当前连接 */
|
/* 当前连接 */
|
||||||
if (n < max) { rbuf[n].kind = SUB_HDR; rbuf[n].label = "当前连接"; rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++; }
|
if (n < max) { rbuf[n].kind = SUB_HDR; rbuf[n].label = "当前连接"; rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++; }
|
||||||
if (n < max) {
|
if (n < max) {
|
||||||
@@ -99,7 +105,6 @@ static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
|
|||||||
} else for (int i = 0; i < c->saved_n && n < max; i++) {
|
} else for (int i = 0; i < c->saved_n && n < max; i++) {
|
||||||
rbuf[n].kind = SUB_ACT;
|
rbuf[n].kind = SUB_ACT;
|
||||||
rbuf[n].label = c->saved[i].name[0] ? c->saved[i].name : "<未命名>";
|
rbuf[n].label = c->saved[i].name[0] ? c->saved[i].name : "<未命名>";
|
||||||
/* 当前连接的 SSID 标记「已连接」;否则显示 BSSID(若有) */
|
|
||||||
rbuf[n].sub = (c->connected && c->ssid[0] && !strcmp(c->saved[i].name, c->ssid)) ? "已连接"
|
rbuf[n].sub = (c->connected && c->ssid[0] && !strcmp(c->saved[i].name, c->ssid)) ? "已连接"
|
||||||
: (c->saved[i].mac[0] ? c->saved[i].mac : "");
|
: (c->saved[i].mac[0] ? c->saved[i].mac : "");
|
||||||
rbuf[n].on = 0; rbuf[n].act = WF_ACT_SAVED + i;
|
rbuf[n].on = 0; rbuf[n].act = WF_ACT_SAVED + i;
|
||||||
@@ -112,7 +117,8 @@ static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
|
|||||||
} else for (int i = 0; i < c->scan_n && n < max; i++) {
|
} else for (int i = 0; i < c->scan_n && n < max; i++) {
|
||||||
rbuf[n].kind = SUB_ROW;
|
rbuf[n].kind = SUB_ROW;
|
||||||
rbuf[n].label = c->scan[i].name[0] ? c->scan[i].name : "(隐藏网络)";
|
rbuf[n].label = c->scan[i].name[0] ? c->scan[i].name : "(隐藏网络)";
|
||||||
rbuf[n].sub = c->scan[i].mac[0] ? c->scan[i].mac : "";
|
rbuf[n].sub = c->scan[i].sec[0] && strcmp(c->scan[i].sec, "Open") ? c->scan[i].sec
|
||||||
|
: (c->scan[i].mac[0] ? c->scan[i].mac : "");
|
||||||
rbuf[n].on = 0; rbuf[n].act = WF_ACT_SCAN_BASE + i;
|
rbuf[n].on = 0; rbuf[n].act = WF_ACT_SCAN_BASE + i;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
@@ -120,62 +126,106 @@ static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 附近网络详情(只读):SSID / AP MAC / 提示 */
|
/* SDK IME 输密码(PW 模式=ASCII 软键盘输入,现有输入法复用;回车提交/ESC或点背景取消)。
|
||||||
static int wifi_nearby_detail_cb(wifi_ctx_t *c, int scan_idx)
|
* 返回 1=已输入;0=取消。 */
|
||||||
|
static int wifi_ask_psk(char *psk, int maxlen)
|
||||||
|
{
|
||||||
|
meow_ime_ctx ic;
|
||||||
|
memset(&ic, 0, sizeof ic);
|
||||||
|
ic.mode = MEOW_IME_ASCII;
|
||||||
|
ic.maxlen = maxlen;
|
||||||
|
ic.buf[0] = 0;
|
||||||
|
int res = meow_ui_text_input(&ic); /* 阻塞:提交后 ctx.buf 回填最终文本 */
|
||||||
|
if (res && ic.buf[0]) { snprintf(psk, maxlen, "%s", ic.buf); return 1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 附近网络点选 →【WiFi 新网络录入】连接流程。返回 1=重绘。 */
|
||||||
|
static int wifi_connect_scan(wifi_ctx_t *c, int scan_idx)
|
||||||
{
|
{
|
||||||
if (scan_idx < 0 || scan_idx >= c->scan_n) return 0;
|
if (scan_idx < 0 || scan_idx >= c->scan_n) return 0;
|
||||||
char line[S_NET_MAX][48];
|
meow_net_t *s = &c->scan[scan_idx];
|
||||||
const char *txt[S_NET_MAX];
|
if (!s->name[0] || !strcmp(s->name, "(隐藏网络)")) {
|
||||||
int n = 0;
|
const char *txt[1]; txt[0] = "隐藏网络无法直接连接";
|
||||||
snprintf(line[n], sizeof line[0], "SSID: %s", c->scan[scan_idx].name[0] ? c->scan[scan_idx].name : "(隐藏网络)"); txt[n] = line[n]; n++;
|
s_dd_modal("附近网络", txt, 1, -1);
|
||||||
if (c->scan[scan_idx].mac[0]) { snprintf(line[n], sizeof line[n], "AP MAC: %s", c->scan[scan_idx].mac); txt[n] = line[n]; n++; }
|
return 1;
|
||||||
snprintf(line[n], sizeof line[n], "密码/连接由 HibyOS 处理"); txt[n] = line[n]; n++;
|
}
|
||||||
s_dd_modal(c->scan[scan_idx].name[0] ? c->scan[scan_idx].name : "附近网络", txt, n, -1);
|
int open = (s->sec[0] == 0 || !strcmp(s->sec, "Open"));
|
||||||
|
char psk[96] = "";
|
||||||
|
if (!open) {
|
||||||
|
if (!wifi_ask_psk(psk, sizeof psk)) return 1; /* 用户取消输密码 */
|
||||||
|
}
|
||||||
|
char err[160]; err[0] = 0;
|
||||||
|
int rc = nm_wifi_connect(s->name, open ? NULL : psk, err, sizeof err);
|
||||||
|
char line[192];
|
||||||
|
const char *txt[1]; txt[0] = line;
|
||||||
|
if (rc == MEOW_OK) {
|
||||||
|
snprintf(line, sizeof line, "连接成功:%s%s", s->name,
|
||||||
|
err[0] ? "" : "(已启用)");
|
||||||
|
} else {
|
||||||
|
snprintf(line, sizeof line, "连接失败:%s", err[0] ? err : "netmgr 返回错误");
|
||||||
|
}
|
||||||
|
logmsg("wifi_page: scan connect ssid=%s open=%d rc=%d err=%s", s->name, open, rc, err);
|
||||||
|
/* 连接结果反馈(成功后进已保存网络列表由 rows() 重扫刷新) */
|
||||||
|
s_dd_modal(s->name, txt, 1, -1);
|
||||||
|
nm_wifi_scan_trigger(); /* 连上后重扫,已保存/当前连接尽量贴近最新 */
|
||||||
|
c->last_scan_ms = now_ms();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 普通行(SUB_ROW)点击:当前连接详情 / 附近网络详情 */
|
/* 普通行(SUB_ROW)点击:当前连接详情(含断开)/ 附近网络连接 */
|
||||||
static int wifi_detail(void *ctx, int idx, int act)
|
static int wifi_detail(void *ctx, int idx, int act)
|
||||||
{
|
{
|
||||||
(void)idx;
|
(void)idx;
|
||||||
wifi_ctx_t *c = ctx;
|
wifi_ctx_t *c = ctx;
|
||||||
if (act == WF_ACT_CURR) {
|
if (act == WF_ACT_CURR) {
|
||||||
/* 当前连接详情(wpa_cli status + 网关):SSID/AP MAC/本机 MAC/AP IP/本机 IP/频道 */
|
|
||||||
char info[S_NET_MAX][48];
|
char info[S_NET_MAX][48];
|
||||||
const char *txt[S_NET_MAX];
|
const char *txt[S_NET_MAX];
|
||||||
int n = net_wifi_info(info, S_NET_MAX);
|
int n = nm_wifi_info(info, S_NET_MAX);
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
snprintf(info[0], sizeof info[0], "%s", c->connected ? "已连接(无详情)" : "未连接");
|
snprintf(info[0], sizeof info[0], "%s", c->connected ? "已连接(无详情)" : "未连接");
|
||||||
txt[0] = info[0]; n = 1;
|
txt[0] = info[0]; n = 1;
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < n; i++) txt[i] = info[i];
|
for (int i = 0; i < n; i++) txt[i] = info[i];
|
||||||
}
|
}
|
||||||
s_dd_modal(c->ssid[0] ? c->ssid : "当前连接", txt, n, -1);
|
if (c->connected && n < S_NET_MAX) { snprintf(info[n], sizeof info[n], "断开连接"); txt[n] = info[n]; n++; }
|
||||||
|
int pick = s_dd_modal(c->ssid[0] ? c->ssid : "当前连接", txt, n, -1);
|
||||||
|
if (pick >= 0 && c->connected && pick == n - 1) {
|
||||||
|
char err[96]; err[0] = 0;
|
||||||
|
int rc = nm_wifi_disconnect(err, sizeof err);
|
||||||
|
const char *rtxt[1]; char rline[128];
|
||||||
|
rtxt[0] = rline;
|
||||||
|
snprintf(rline, sizeof rline, "%s", rc == MEOW_OK ? "已断开" : (err[0] ? err : "断开失败"));
|
||||||
|
s_dd_modal("断开", rtxt, 1, -1);
|
||||||
|
c->last_scan_ms = now_ms();
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (act >= WF_ACT_SCAN_BASE) return wifi_nearby_detail_cb(c, act - WF_ACT_SCAN_BASE);
|
if (act >= WF_ACT_SCAN_BASE) return wifi_connect_scan(c, act - WF_ACT_SCAN_BASE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 动作行(SUB_ACT)点击:已保存网络 → 连接 */
|
/* 动作行(SUB_ACT)点击:已保存网络 → netmgr connect(ssid;复用既有网络) */
|
||||||
static int wifi_act(void *ctx, int idx, int act)
|
static int wifi_act(void *ctx, int idx, int act)
|
||||||
{
|
{
|
||||||
(void)idx;
|
(void)idx;
|
||||||
wifi_ctx_t *c = ctx;
|
wifi_ctx_t *c = ctx;
|
||||||
if (act >= WF_ACT_SCAN_BASE) return 0; /* 附近是 detail,不走这里 */
|
if (act >= WF_ACT_SCAN_BASE) return 0; /* 附近是 SUB_ROW(detail),不走这里 */
|
||||||
int sidx = act - WF_ACT_SAVED;
|
int sidx = act - WF_ACT_SAVED;
|
||||||
if (sidx < 0 || sidx >= c->saved_n) return 0;
|
if (sidx < 0 || sidx >= c->saved_n) return 0;
|
||||||
if (c->saved[sidx].id < 0) return 0;
|
if (c->saved[sidx].id < 0) return 0;
|
||||||
char cmd[192];
|
char err[160]; err[0] = 0;
|
||||||
snprintf(cmd, sizeof cmd,
|
int rc = nm_wifi_connect(c->saved[sidx].name, NULL, err, sizeof err);
|
||||||
"PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli -i wlan0 select_network %d 2>/dev/null",
|
char line[192]; const char *txt[1]; txt[0] = line;
|
||||||
c->saved[sidx].id);
|
if (rc == MEOW_OK) snprintf(line, sizeof line, "已连接:%s", c->saved[sidx].name);
|
||||||
system(cmd);
|
else snprintf(line, sizeof line, "连接失败:%s", err[0] ? err : "netmgr 返回错误");
|
||||||
net_wifi_scan_trigger();
|
logmsg("wifi_page: saved connect ssid=%s rc=%d err=%s", c->saved[sidx].name, rc, err);
|
||||||
|
s_dd_modal(c->saved[sidx].name, txt, 1, -1);
|
||||||
|
nm_wifi_scan_trigger();
|
||||||
c->last_scan_ms = now_ms();
|
c->last_scan_ms = now_ms();
|
||||||
logmsg("wifi_page: select_network %d", c->saved[sidx].id);
|
|
||||||
return 1; /* 重绘 + 重填行 */
|
return 1; /* 重绘 + 重填行 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +246,7 @@ int run_wifi_page(void)
|
|||||||
p.live = 0; /* WiFi 列表在动作/开关时重扫即可,避免驻留 popen 轮询 */
|
p.live = 0; /* WiFi 列表在动作/开关时重扫即可,避免驻留 popen 轮询 */
|
||||||
p.ctx = &c;
|
p.ctx = &c;
|
||||||
/* 打开页面立即触发一次扫描,附近列表更贴近最新 */
|
/* 打开页面立即触发一次扫描,附近列表更贴近最新 */
|
||||||
net_wifi_scan_trigger();
|
nm_wifi_scan_trigger();
|
||||||
c.last_scan_ms = now_ms();
|
c.last_scan_ms = now_ms();
|
||||||
bt_role_refresh(0); /* 顺带刷新网络缓存(不影响本页) */
|
bt_role_refresh(0); /* 顺带刷新网络缓存(不影响本页) */
|
||||||
return meow_subpage_run(&p);
|
return meow_subpage_run(&p);
|
||||||
|
|||||||
Reference in new issue
Block a user