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:
1 parent
ebd64d07e1
commit
b20e3c43e9
10 files changed
+708
-69
No files matched your search
@@ -8,16 +8,25 @@
|
||||
* P2 bt_act 命中 → 正确下发 netmgrd 命令(已连接→disconnect、已配对未连→connect、输出目标
|
||||
* 切换、编解码器循环);
|
||||
* P3 附近设备仅列未配对(已配对者归「已配对设备」)。
|
||||
* P4 M47 开关三态:真实态未知(-1) → top_sw 回 -1 + 行内「蓝牙状态未知」(不当已关);
|
||||
* P5 M47 点击收敛:下发成功且真实态达成 → 定稿、不弹窗;下发失败/超时/未知 → 回滚 + 弹窗;
|
||||
* P6 M47 收敛矩阵(纯函数 meow_toggle_*,真实态×下发结果×探测 全组合)。
|
||||
*
|
||||
* 编译(在 patch_v2/meow_launcherd 下):
|
||||
* gcc -I../sdk -I. -Wall -Wextra -fsanitize=undefined test_bt_page.c -o test_bt_page
|
||||
* 运行:./test_bt_page
|
||||
* 运行:./test_bt_page(或 `bash build_launcher.sh test`)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
/* M47:把收敛等待改成宿主可接受的尺度(生产 4000ms/250ms,见 bt_page.c)。 */
|
||||
#define BT_TOGGLE_SETTLE_MS 80
|
||||
#define BT_TOGGLE_POLL_MS 10
|
||||
|
||||
#include "meow_launcher_internal.h"
|
||||
#include "bt_page.c" /* bt_rows/bt_act/bt_top_sw/bt_detail 平移到本 TU */
|
||||
@@ -29,9 +38,12 @@ static bt_ep_t g_eps[8]; static int g_eps_n;
|
||||
static char g_last_cmd[128]; /* 记录 act 下发命令,如 "disconnect:<mac>" */
|
||||
static int g_output_bt; /* audio.output_bt 桩值 */
|
||||
static char g_codec[16] = "auto"; /* bt.codec 桩值 */
|
||||
static int g_bt_state = 1; /* M47:真实态三态桩(-1 未知 / 0 / 1) */
|
||||
static int g_onoff_rc = MEOW_OK; /* M47:nm_bt_onoff 下发结果桩 */
|
||||
static int g_bt_en_setting = 1; /* M47:network.bluetooth_enabled 桩值(意图/回滚观察点) */
|
||||
|
||||
int nm_bt_powered(void) { return 1; }
|
||||
int nm_bt_onoff(int on) { (void)on; return 0; }
|
||||
int nm_bt_power_state(void) { return g_bt_state; }
|
||||
int nm_bt_onoff(int on) { (void)on; return g_onoff_rc; }
|
||||
int nm_bt_connected(meow_net_t *out, int max) {
|
||||
if (max > g_conn_n) max = g_conn_n;
|
||||
memcpy(out, g_conn, (size_t)max * sizeof *out); return max;
|
||||
@@ -65,14 +77,14 @@ void net_bt_mode_str(char *buf, int len) { snprintf(buf, (size_t)len, "主模式
|
||||
int meow_settings_get_bool(const char *k, int *v)
|
||||
{
|
||||
if (v) *v = 0;
|
||||
if (!strcmp(k, "network.bluetooth_enabled")) { if (v) *v = 1; return 0; }
|
||||
if (!strcmp(k, "network.bluetooth_enabled")) { if (v) *v = g_bt_en_setting; return 0; }
|
||||
if (!strcmp(k, "audio.output_bt")) { if (v) *v = g_output_bt; return 0; }
|
||||
return -1;
|
||||
}
|
||||
int meow_settings_set_bool(const char *k, int v)
|
||||
{
|
||||
if (!strcmp(k, "audio.output_bt")) { g_output_bt = v; return 0; }
|
||||
if (!strcmp(k, "network.bluetooth_enabled")) return 0;
|
||||
if (!strcmp(k, "network.bluetooth_enabled")) { g_bt_en_setting = v; return 0; }
|
||||
return -1;
|
||||
}
|
||||
int meow_settings_get_str(const char *k, char *b, int n)
|
||||
@@ -91,9 +103,26 @@ void paint_sb_right(void) {}
|
||||
void logmsg(const char *fmt, ...) { (void)fmt; }
|
||||
void bt_role_refresh(int force) { (void)force; }
|
||||
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; }
|
||||
static int g_modal_n = 0; /* M47:可见错误提示(弹窗)计数 */
|
||||
static char g_modal_line[128] = "";
|
||||
int s_dd_modal(const char *title, const char *item[S_NET_MAX], int n, int cur)
|
||||
{
|
||||
(void)title; (void)cur;
|
||||
g_modal_n++;
|
||||
g_modal_line[0] = 0;
|
||||
if (item && n > 0 && item[0]) snprintf(g_modal_line, sizeof g_modal_line, "%s", item[0]);
|
||||
return -1;
|
||||
}
|
||||
int meow_subpage_run(sub_page_t *p) { (void)p; return 0; }
|
||||
|
||||
/* M47:真实时钟(收敛等待用),毫秒单调。 */
|
||||
unsigned long long now_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (unsigned long long)ts.tv_sec * 1000ULL + (unsigned long long)(ts.tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, ...) do { if (cond) { printf(" PASS: " __VA_ARGS__); printf("\n"); } \
|
||||
else { printf(" FAIL: " __VA_ARGS__); printf("\n"); g_fail++; } } while (0)
|
||||
@@ -111,6 +140,13 @@ static int find_act(const sub_row_t *r, int n, int from, const char *pref)
|
||||
if (r[i].kind == SUB_ACT && r[i].label && !strncmp(r[i].label, pref, strlen(pref))) return i;
|
||||
return -1;
|
||||
}
|
||||
/* 按 label 精确找任意行(M47 未知态占位行用) */
|
||||
static int row_label(const sub_row_t *r, int n, const char *lbl)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
if (r[i].label && !strcmp(r[i].label, lbl)) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void fill_state(void)
|
||||
{
|
||||
@@ -230,6 +266,92 @@ int main(void)
|
||||
bt_act(&c, ci, rows[ci].act);
|
||||
CHECK(strcmp(g_codec, "SBC") == 0, "编解码器 auto → SBC (实际=%s)", g_codec);
|
||||
|
||||
printf("== P4 M47 开关三态(未知 ≠ 已关)==\n");
|
||||
int sw = 0;
|
||||
g_bt_state = -1; /* 真实态未知 */
|
||||
memset(&c, 0, sizeof c); c.full = 1; fill_state();
|
||||
CHECK(bt_top_sw(&c, &sw) == 1 && sw == -1, "真实态未知 → top_sw *on=-1 (实际=%d)", sw);
|
||||
n = bt_rows(&c, rows, SUB_ROWS_MAX);
|
||||
CHECK(row_label(rows, n, "蓝牙状态未知") >= 0, "未知态 → 行内含「蓝牙状态未知」");
|
||||
g_bt_state = 0; /* 真实态为关 */
|
||||
memset(&c, 0, sizeof c); c.full = 1;
|
||||
sw = -2;
|
||||
bt_top_sw(&c, &sw);
|
||||
CHECK(sw == 0, "真实态关 → top_sw *on=0 (实际=%d)", sw);
|
||||
n = bt_rows(&c, rows, SUB_ROWS_MAX);
|
||||
CHECK(row_label(rows, n, "蓝牙状态未知") < 0, "真实态已知 → 无未知占位行");
|
||||
|
||||
printf("== P5 M47 点击收敛:定稿 / 回滚 + 可见错误 ==\n");
|
||||
/* 5a 下发成功且真实态已达成 → 立即定稿,无弹窗,settings/状态栏=真实态 */
|
||||
g_bt_state = 1; g_onoff_rc = MEOW_OK; g_modal_n = 0; g_bt_en_setting = 0;
|
||||
memset(&c, 0, sizeof c);
|
||||
unsigned long long t0 = now_ms();
|
||||
bt_top_sw_set(&c, 1);
|
||||
unsigned long long dt = now_ms() - t0;
|
||||
CHECK(g_modal_n == 0, "已达成 → 无错误弹窗 (弹窗数=%d)", g_modal_n);
|
||||
CHECK(g_bt_en_setting == 1, "已达成 → settings 定稿为 1");
|
||||
CHECK(g_sys.bt_on == 1, "已达成 → 状态栏 g_sys.bt_on=1");
|
||||
CHECK(dt < BT_TOGGLE_SETTLE_MS, "已达成 → 不必等满超时 (%llums)", dt);
|
||||
|
||||
/* 5b 下发失败、真实态仍为开(要求关)→ 回滚到开 + 可见弹窗 */
|
||||
g_bt_state = 1; g_onoff_rc = MEOW_ERR_NODEV; g_modal_n = 0; g_bt_en_setting = 1;
|
||||
memset(&c, 0, sizeof c);
|
||||
bt_top_sw_set(&c, 0);
|
||||
CHECK(g_modal_n == 1, "下发失败 → 有可见错误弹窗");
|
||||
CHECK(g_bt_en_setting == 1, "下发失败 → settings 回滚为真实态 1 (实际=%d)", g_bt_en_setting);
|
||||
CHECK(g_sys.bt_on == 1, "下发失败 → 状态栏仍为真实态 1");
|
||||
|
||||
/* 5c 下发成功但真实态不收敛(要求开、真实一直关)→ 等满上限后回滚 + 弹窗 */
|
||||
g_bt_state = 0; g_onoff_rc = MEOW_OK; g_modal_n = 0; g_bt_en_setting = 1;
|
||||
memset(&c, 0, sizeof c);
|
||||
t0 = now_ms();
|
||||
bt_top_sw_set(&c, 1);
|
||||
dt = now_ms() - t0;
|
||||
CHECK(g_modal_n == 1, "超时未收敛 → 有可见错误弹窗(%s)", g_modal_line);
|
||||
CHECK(g_bt_en_setting == 0, "超时未收敛 → settings 回滚为真实态 0");
|
||||
CHECK(g_sys.bt_on == 0, "超时未收敛 → 状态栏=真实态 0");
|
||||
CHECK(dt >= BT_TOGGLE_SETTLE_MS, "确实等满了上限 (%llums)", dt);
|
||||
|
||||
/* 5d 真实态未知 → 等满上限后仍是未知 + 弹窗(绝不把未知显示成成功) */
|
||||
g_bt_state = -1; g_onoff_rc = MEOW_OK; g_modal_n = 0; g_bt_en_setting = 1;
|
||||
memset(&c, 0, sizeof c);
|
||||
bt_top_sw_set(&c, 1);
|
||||
CHECK(g_modal_n == 1 && strstr(g_modal_line, "无法确认") != NULL,
|
||||
"未知 → 弹窗明说无法确认 (%s)", g_modal_line);
|
||||
CHECK(c.bt_on == -1, "未知 → 定稿态仍为 -1 (实际=%d)", c.bt_on);
|
||||
CHECK(g_bt_en_setting == 0, "未知 → settings 不留意图值 1(回滚为 0)");
|
||||
|
||||
printf("== P6 M47 收敛矩阵(真实态 × 下发结果 × 探测 × 超时)==\n");
|
||||
{
|
||||
static const struct {
|
||||
int last, want, ok, probe, to; /* 输入 */
|
||||
int disp, pend, rb, err; /* 期望输出 */
|
||||
const char *what;
|
||||
} m[] = {
|
||||
{ 1, 1, 1, 1, 0, 1, 0, 0, 0, "真开·下发成功·探测开" },
|
||||
{ 1, 0, 1, 0, 0, 0, 0, 0, 0, "真开·下发成功·探测关" },
|
||||
{ 1, 0, 1, 1, 1, 1, 0, 1, 1, "真开·下发成功·未收敛(超时)" },
|
||||
{ 1, 0, 0, 1, 0, 1, 0, 1, 1, "真开·下发失败·探测仍开" },
|
||||
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, "真开·下发失败但探测已关(不误报)" },
|
||||
{ 0, 1, 1, 1, 0, 1, 0, 0, 0, "真关·下发成功·探测开" },
|
||||
{ 0, 1, 1, 0, 1, 0, 0, 1, 1, "真关·下发成功·未收敛(超时)" },
|
||||
{ 0, 1, 0, 0, 0, 0, 0, 1, 1, "真关·下发失败·仍关" },
|
||||
{ 1, 0, 1, -1, 0, 1, 1, 1, 0, "真开·下发成功·探测未知(继续等)" },
|
||||
{ 1, 0, 1, -1, 1, 1, 0, 1, 1, "真开·下发成功·探测未知(超时)" },
|
||||
{-1, 1, 1, -1, 1, -1, 0, 1, 1, "未知·下发成功·探测仍未知(超时)" },
|
||||
{-1, 1, 1, -1, 0, -1, 1, 1, 0, "未知·下发成功·探测仍未知(继续等)" },
|
||||
};
|
||||
for (size_t i = 0; i < sizeof m / sizeof m[0]; i++) {
|
||||
int d = meow_toggle_display(m[i].want, m[i].last, m[i].ok, m[i].probe, m[i].to);
|
||||
int p = meow_toggle_pending(m[i].want, m[i].last, m[i].ok, m[i].probe, m[i].to);
|
||||
int r = meow_toggle_rollback(m[i].want, m[i].last, m[i].ok, m[i].probe, m[i].to);
|
||||
int e = meow_toggle_error(m[i].want, m[i].last, m[i].ok, m[i].probe, m[i].to);
|
||||
CHECK(d == m[i].disp && p == m[i].pend && r == m[i].rb && e == m[i].err,
|
||||
"%s → disp=%d pend=%d rb=%d err=%d (期望 %d/%d/%d/%d)",
|
||||
m[i].what, d, p, r, e, m[i].disp, m[i].pend, m[i].rb, m[i].err);
|
||||
}
|
||||
}
|
||||
|
||||
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
|
||||
return g_fail;
|
||||
}
|
||||
Reference in new issue
Block a user