fix(launcher): M47 设置页蓝牙/WiFi 开关只认真实态 — 三态查询 + 待确认收敛 + 未知态呈现

用户原话:「开关都存在显示问题 我都不能判断它是不是真的开启还是关闭(包括 WiFi 页也是)」。
根因不是「没读到硬件态」,而是「读到/没读到都被拿去当用户意图的装饰」:
- bt_page.c:103 开关初值来自 settings 布尔;:112-114 下发后立刻 g_sys.bt_on = on(显示意图);
  :129 c->bt_on = nm_bt_powered() 赋值后全文无人引用(M45 也独立发现)。
- wifi_page.c:40 读不到 settings 时默认 1(显示已开)。
- netmgrd 的 bt/wifi 查询本身把「问不到」塌缩成 0(nm_bt_powered/nm_wifi_connected)。

本单只解「状态可信」,不碰扫描/配对/音频(另有 M45/M46 单)。

1) 真实态三态(-1 未知 / 0 关 / 1 开)
- net.c 新增 net_bt_power_probe()(hciconfig hci0 UP/DOWN;无输出=未知)与
  net_wifi_power_probe()(rfkill wlan soft/hard 汇总,回退 wlan0 IFF_UP;读不到=未知)。
- netmgr_ipc.c 新增 nm_bt_power_state()(daemon bt status 的 available/powered 权威,不可达
  回退直连探测)与 nm_wifi_power_state()(netmgrd 的 wifi status 只有 wpa_state、不报射频态,
  故取本地 rfkill——daemon 的 wifi on/off 改的就是同一份内核状态)。
- 两个子页的 top_sw/rows 只读这两个函数;settings 布尔降级为「意图持久化」,不再决定外观。
- 「读不到默认 1」删除:未知就是未知。

2) 点击 → 待确认 → 真实态定稿
- 纯函数 meow_toggle_{display,pending,rollback,error}(bt_page.c,无 I/O/UI):
  只有 probe 确认才算收敛;probe 未知继续等,绝不因 last_real 恰好等于意图就宣布成功。
- 页面:落盘意图 → 下发 → 250ms 轮询(BT 上限 4s、WiFi 上限 5s;hciconfig 0.3~2s、
  wifi_up.sh 起 supplicant/DHCP 更慢,故不用 20ms 级忙轮询)→ 真实态定稿;失败/超时把
  settings 回滚到已确认真实态 + s_dd_modal 可见报错,不静默、不乐观。
- subpage.c:切换前先画「正在开启/关闭…(等待设备确认)」条(待确认态与稳定态可区分);
  切换后回读 top_sw 定稿(取消原「点了就翻面」),live tick 也回读,外部改动驻留期间可见。

3) 第三态呈现(未知 ≠ 已关)
- SDK meow_switch 只有两态:未知时开关停「关」并在左侧画灰色「?」;行内另有
  「蓝牙状态未知 / WiFi 开关状态未知」文字说明。选「?」是因为它不占用开关控件本身、
  且「关+?」不会被读成「已关」。

4) 顺带修 net.c 三条开关失真(塔追加的 M45 盘点,同一病根)
- s_net_toggle_bt() 补 rfkill soft(与 netmgrd 的 bt on/off 同一语义;开:先 unblock 再 up;
  关:先 down 再 block)。此前只发 hciconfig → 「BT 页关过再从头开」会被 rfkill 拦下。
- s_net_sync_from_sys() WiFi 判据从 wlan_connected()(已关联)改为射频态;BT 用三态,
  读不到不写(旧写法把「网卡 up 未关联」写成 0,把未知写成已关)。
- rfkill 同类多节点(本机 2 wlan + 2 bluetooth)按「任一 block 即 block」读、全部写入。

测试(`bash patch_v2/meow_launcherd/build_launcher.sh test`,新增 test 入口)
- test_bt_page.c:P4 三态与未知占位、P5 收敛四例(达成/下发失败/超时/未知)、P6 收敛矩阵
  12 组(真实开/关 × 下发成功/失败/超时 × probe 未知),全 PASS(64 条断言)。
- test_netmgr_ipc.c:新增 nm_bt_power_state 解析(powered=1/0、available=0、缺字段→-1)、
  nm_wifi_power_state 透传、不可达回退=未知、wifi_rows 未知态占位;并修掉两处既有测试缺陷
  (行内 UI 桩缺失导致本文件此前根本链不过;直连连接桩统一返回 "0\n" 导致等满 10s 必失败)。
- 交叉编译 0 warning(-Wall -Wextra,逐文件单独编 + 整链);launcherd 899388 B
  md5 1c88661a9410ac1a4139304362281cdd,同 commit 同步 scripts/build/fw_content.conf 钉值。

真机(10.58.215.50,仅只读自检,未替换 /usr/bin/meow_launcherd、未刷机)
- 二进制放 /usr/data/mnt/sd_0/MeowOS/tmp/ 跑 --sysdump:exit 0,输出 wifi_on=1 / bt_seen=1
  bt_on=0,与设备现状一致(rfkill 全 0 + wlan0 up + hci0 DOWN + netmgrd powered=0)。
- 卡上 du -s /usr/data/mnt/sd_0/Music 前后恒 199164544;临时文件已删;settings 未改。
- launcherd 在 squashfs 里 → 本改动需刷机生效。

已知不在本单:输出目标只写 settings(audiod 仅启动读一次)→ M45 finding / M46;
netmgrd 侧四处失真(未知当关、up 与 unblock 顺序、rfkill 只写首节点、wifi status 无射频字段)
已报 finding;设置主页/通知中心的开关显示仍是 settings(另立单)。
This commit is contained in:
AskaEth committed 2026-11-11 17:16:28 +08:00
1 parent ebd64d07e1
commit b20e3c43e9
10 files changed
+708 -69

No files matched your search

+76 -12
View File
@@ -11,6 +11,11 @@
* 【M6.0 迁移】全部查询/操作走 netmgr IPC(netmgr_ipc.c 的 nm_* 封装),netmgrd 是 WiFi 权威;
* netmgrd 不可达(NETMGR_UNREACH)→ 自动回退 net.c 直连 wpa_cli(双轨保功能),页面显示降级占位
* 「网络管理服务未运行(直连模式)」。
*
* 【M47 开关可信化】顶栏开关显示态只来自真实态三态查询 nm_wifi_power_state()(rfkill wlan
* soft/hard,回退 wlan0 IFF_UP;探测失败=未知),**读不到不再默认「已开」**;settings 的
* network.wifi_enabled 只承载用户意图。点击先出示「正在开启/关闭…(等待确认)」,下发 → 有界
* 轮询真实态 → 真实态定稿;失败/超时回滚并弹窗(不许乐观显示成功)。
*/
#include "meow_launcher_internal.h"
@@ -27,32 +32,84 @@ typedef struct {
int saved_n, scan_n;
char ssid[64];
int connected; /* nm_wifi_connected()(IPC>直连回退 wlan_connected) */
int wifi_on; /* network.wifi_enabled */
int sw_state; /* 真实射频态三态:-1 未知 / 0 关 / 1 开(M47) */
unsigned long long sw_ts; /* 上面这次查询的时刻(缓存去重) */
long last_scan_ms; /* 扫描触发节流 */
} wifi_ctx_t;
/* M47:开关收敛参数(宿主测试可预先定义改小)。WiFi 走 wifi_up.sh(起 wpa_supplicant +
* 等关联/DHCP),比 BT 慢,所以上限 5s;250ms 探一次(≤20 次),不 20ms 级忙轮询。 */
#ifndef WF_TOGGLE_SETTLE_MS
#define WF_TOGGLE_SETTLE_MS 5000
#endif
#ifndef WF_TOGGLE_POLL_MS
#define WF_TOGGLE_POLL_MS 250
#endif
#define WF_STATE_TTL_MS 800
/* 顶栏开关当前值 */
/* 真实态查询(带缓存)。定义见 netmgr_ipc.c:netmgrd 不报射频态,故取 rfkill/wlan0。 */
extern int nm_wifi_power_state(void);
/* 收敛判定纯函数,实现见 bt_page.c(两个页面共用一套;矩阵测试在 test_bt_page.c) */
extern int meow_toggle_display(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
extern int meow_toggle_pending(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
extern int meow_toggle_rollback(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
extern int meow_toggle_error(int intent, int last_real, int dispatch_ok, int probe, int timed_out);
static int wifi_state_now(wifi_ctx_t *c, int force)
{
unsigned long long t = now_ms();
if (!force && c->sw_ts && t - c->sw_ts < WF_STATE_TTL_MS) return c->sw_state;
c->sw_state = nm_wifi_power_state();
c->sw_ts = t;
return c->sw_state;
}
/* 顶栏开关当前值:只报真实态(-1=未知 → subpage 画「?」)。绝不再「读不到就默认 1」。 */
static int wifi_top_sw(void *ctx, int *on)
{
(void)ctx;
int v = 1;
if (meow_settings_get_bool("network.wifi_enabled", &v) != 0) v = 1;
*on = v;
wifi_ctx_t *c = ctx;
*on = wifi_state_now(c, 0);
return 1;
}
/* 顶栏开关切换:写设置 + 开/关网卡(netmgr on/off,回退直连)+ 触发重扫 + 同步状态栏 */
/* 顶栏开关切换:落盘意图 → 开/关网卡(netmgr on/off,回退直连)→ 有界等待真实态收敛 →
* 真实态定稿/回滚 + 可见错误提示。 */
static void wifi_top_sw_set(void *ctx, int on)
{
wifi_ctx_t *c = ctx;
meow_settings_set_bool("network.wifi_enabled", on);
nm_wifi_toggle(on);
g_sys.wifi_on = on;
paint_sb_right(); /* 状态栏 WiFi 图标随开关即时刷新 */
int want = on ? 1 : 0;
int last_real = wifi_state_now(c, 0);
meow_settings_set_bool("network.wifi_enabled", want); /* 意图持久化 */
meow_settings_save();
if (on) { nm_wifi_scan_trigger(); c->last_scan_ms = now_ms(); }
int dispatch_ok = (nm_wifi_toggle(want) == MEOW_OK);
unsigned long long deadline = now_ms() + WF_TOGGLE_SETTLE_MS;
int probe = wifi_state_now(c, 1), timed_out = 0;
while (meow_toggle_pending(want, last_real, dispatch_ok, probe, timed_out)) {
if (now_ms() >= deadline) { timed_out = 1; break; }
usleep(WF_TOGGLE_POLL_MS * 1000);
probe = wifi_state_now(c, 1);
}
int disp = meow_toggle_display(want, last_real, dispatch_ok, probe, timed_out);
int rb = meow_toggle_rollback(want, last_real, dispatch_ok, probe, timed_out);
int err = meow_toggle_error(want, last_real, dispatch_ok, probe, timed_out);
c->sw_state = disp; c->sw_ts = now_ms();
meow_settings_set_bool("network.wifi_enabled", disp == 1); /* 只留已确认的真实态 */
meow_settings_save();
if (disp >= 0) g_sys.wifi_on = disp;
paint_sb_right(); /* 状态栏 WiFi 图标随「已确认」状态刷新 */
if (disp == 1) { nm_wifi_scan_trigger(); c->last_scan_ms = now_ms(); }
logmsg("wifi_page: toggle want=%d ok=%d probe=%d to=%d disp=%d rollback=%d err=%d",
want, dispatch_ok, probe, timed_out, disp, rb, err);
if (err) {
char line[96];
const char *txt[1]; txt[0] = line;
if (disp < 0) snprintf(line, sizeof line, "无法确认 WiFi 状态(射频/服务无响应)");
else snprintf(line, sizeof line, "切换未生效:当前仍为%s(%dms 内未确认)",
disp ? "开启" : "关闭", WF_TOGGLE_SETTLE_MS);
s_dd_modal("WiFi", txt, 1, -1);
}
}
@@ -72,6 +129,7 @@ static void wifi_scan(void *ctx)
static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
{
wifi_ctx_t *c = ctx;
c->sw_state = wifi_state_now(c, 0); /* 真实射频态(三态);未知时下面显式说明 */
c->connected = nm_wifi_connected();
if (c->connected) {
nm_wifi_ssid(c->ssid, sizeof c->ssid);
@@ -90,6 +148,12 @@ static int wifi_rows(void *ctx, sub_row_t *rbuf, int max)
rbuf[n].kind = SUB_HDR; rbuf[n].label = "网络管理服务未运行(直连模式)";
rbuf[n].sub = NULL; rbuf[n].on = 0; rbuf[n].act = 0; n++;
}
/* 第三态显式呈现(M47):射频态未知 ≠ 已关;顶栏开关同时画「?」,这里补文字说明。 */
if (c->sw_state < 0 && n < max) {
rbuf[n].kind = SUB_ROW; rbuf[n].label = "WiFi 开关状态未知";
rbuf[n].sub = "无法读取射频状态(rfkill/网卡无响应)";
rbuf[n].on = 0; rbuf[n].act = -2; 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) {