真机实证(meow_20260901_034946.log): 4.4 插入瞬间电气瞬态在 jz_adc_aux(event2)
产生 VOL_UP 幽灵按住, vol_ramp 24%→上限50 (225391 ramp 起→227213 爬满,
紧随 audiod jack changed). 修(限 meow_launcherd/+meow_watchdog/):
① launcher audio.c + watchdog 各自读 /sys/class/switch/{headset,balance}/state
(节流缓存 1s/500ms, 热路径不每圈读文件), 变化后 800ms 内忽略音量键边沿
(vol_ramp_begin 不起/在爬 ramp 中止/wd_vol_adj 单步忽略)
② ramp 总时长上限 VOL_RAMP_MAX_MS=3000 兜底防一次幽灵打满(正常长按 24→50 ~1.4s 不伤)
③ write_brightness(val,src) 全 10 调用点带源+逐写 logmsg, 亮度跳上限下次可定位
(grep 全库: 写 backlight 仅 launcher/watchdog/svc_power/bootmenu, 无确定 255 路径)
宿主: 新 test_vol_ramp_jack 15/15 PASS(单击/长按回归+3s cap+消抖忽略/中止/恢复);
test_wd_fw 17/17 + test_settings_brightness + test_notif_tf PASS; MIPS -Wall -Wextra 0 warning
(顺带修 watchdog 既有 sys/poll.h→poll.h 重定向 warning).
watchdog 已热更设备 /usr/data(baa997ea); launcher 7be1d4eb 待固件. fw_content.conf 已更新.
遗留: SDK meow_ui_input.c 音量键路径需同款消抖(sdk/ 有主未动)
190 lines
8.1 KiB
C
190 lines
8.1 KiB
C
/* test_vol_ramp_jack.c — 宿主单测:音量 ramp 的插孔消抖 + 时长上限(2026-09-01)。
|
||
*
|
||
* 背景(真机日志实证):4.4 耳机插入瞬间,插孔电气瞬态在音量键 ADC(jz_adc_aux,
|
||
* event2)上产生 VOL_UP 幽灵按住,launcher vol_ramp 从 24% 一路爬到上限 50
|
||
* (225391「volume UP <- ramp」→ 227213「ramp end -> 50」,紧随 audiod「jack changed」)。
|
||
*
|
||
* 编译运行(在 patch_v2/meow_launcherd 下):
|
||
* gcc -Wall -Wextra -Os -I../sdk \
|
||
* -DJACK_HS_STATE='"/tmp/tvrj_hs"' -DJACK_BAL_STATE='"/tmp/tvrj_bal"' \
|
||
* test_vol_ramp_jack.c -o test_vol_ramp_jack && ./test_vol_ramp_jack
|
||
* 全 PASS -> exit 0。
|
||
*
|
||
* 手法:#include "audio.c" 同 TU 平移(同 test_notif_tf.c 惯例);now_ms 用假时钟
|
||
* (手动推进,3s 上限与 1s 节流都可瞬时测);meow_settings_* 打内存桩。 */
|
||
|
||
/* ---- 桩(先定义,audio.c 经内部头拿到声明) ---- */
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <stdarg.h>
|
||
|
||
static unsigned long long g_fake_now = 100000; /* 假时钟(ms),手动推进 */
|
||
|
||
unsigned long long now_ms(void); /* 前置声明,防与头文件顺序问题 */
|
||
unsigned long long now_ms(void) { return g_fake_now; }
|
||
static void advance_ms(unsigned long long d) { g_fake_now += d; }
|
||
|
||
void logmsg(const char *fmt, ...);
|
||
void logmsg(const char *fmt, ...)
|
||
{
|
||
va_list ap; va_start(ap, fmt);
|
||
fprintf(stderr, " [log] "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
|
||
va_end(ap);
|
||
}
|
||
|
||
/* meow_settings 内存桩:audio.c 只用 get_int("audio.volume*")/set_int/save */
|
||
static int g_store_volume = 24;
|
||
static int g_store_max = 50;
|
||
static int g_store_step = 1;
|
||
static int g_save_count = 0;
|
||
|
||
extern int settings_volume; /* audio.c 的缓存全局(同 TU #include 后链接可见) */
|
||
extern int settings_volume_max;
|
||
extern int settings_volume_step;
|
||
|
||
int meow_settings_get_int(const char *key, int *out)
|
||
{
|
||
if (!strcmp(key, "audio.volume")) { *out = g_store_volume; return 0; }
|
||
if (!strcmp(key, "audio.volume_max")) { *out = g_store_max; return 0; }
|
||
if (!strcmp(key, "audio.volume_step")) { *out = g_store_step; return 0; }
|
||
return -1;
|
||
}
|
||
int meow_settings_get_enum(const char *key, int *out) { (void)key; (void)out; return -1; }
|
||
int meow_settings_get_bool(const char *key, int *out) { (void)key; (void)out; return -1; }
|
||
int meow_settings_set_int(const char *key, int v)
|
||
{
|
||
/* 真代码经 settings_sync_cache_listener 把 backend 值同步到 audio.c 的 settings_volume
|
||
* 缓存全局;桩里直接同步,保持同一语义。 */
|
||
if (!strcmp(key, "audio.volume")) { g_store_volume = v; settings_volume = v; return 0; }
|
||
return -1;
|
||
}
|
||
int meow_settings_save(void) { g_save_count++; return 0; }
|
||
|
||
/* util.c 符号桩(audio.c 引用,宿主无 util.c) */
|
||
int read_file_int(const char *path);
|
||
int read_file_int(const char *path)
|
||
{
|
||
FILE *f = fopen(path, "r");
|
||
if (!f) return -1;
|
||
int v = -1;
|
||
if (fscanf(f, "%d", &v) != 1) v = -1;
|
||
fclose(f);
|
||
return v;
|
||
}
|
||
|
||
/* 其它模块全局桩(settings_sync_cache_* 引用;本测试不走该路径,仅满足链接) */
|
||
int screen_off_timeout = 0;
|
||
int screen_off_mode = 0;
|
||
int power_longpress_menu_ms = 0;
|
||
int power_forceoff_ms = 0;
|
||
int refresh_rate_cfg = 60;
|
||
int refresh_always_max = 0;
|
||
|
||
#include "audio.c"
|
||
|
||
static int g_pass = 0, g_fail = 0;
|
||
static void check(const char *name, int got, int want)
|
||
{
|
||
if (got == want) { g_pass++; printf("PASS %s\n", name); }
|
||
else { g_fail++; printf("FAIL %s: got %d want %d\n", name, got, want); }
|
||
}
|
||
|
||
static void write_file(const char *path, int v)
|
||
{
|
||
FILE *f = fopen(path, "w");
|
||
if (!f) { perror(path); exit(2); }
|
||
fprintf(f, "%d\n", v);
|
||
fclose(f);
|
||
}
|
||
|
||
/* 驱动 ramp 直到停止或步数超限;held=key_held 恒值;每步推进 40ms
|
||
* (模拟真机每步 ~52ms 的 amixer system() 开销:0→100 全程 ~4s,必然越过 3s 上限,
|
||
* 而常见 24→50 窗口 ~1s 不受影响)。 */
|
||
static int drive_ramp(int held, int max_steps)
|
||
{
|
||
int steps = 0;
|
||
while (vol_ramp_dir != 0 && steps < max_steps) {
|
||
advance_ms(40);
|
||
vol_ramp_tick(held);
|
||
steps++;
|
||
}
|
||
return steps;
|
||
}
|
||
|
||
/* 场景起始音量:backend 桩与 audio.c 缓存全局一起置(真代码由 settings_load 同步)。 */
|
||
static void set_vol(int v) { g_store_volume = v; settings_volume = v; }
|
||
|
||
/* 音量上限/跳步同理:vol_clamp 读的是 audio.c 的 settings_volume_max/settings_volume_step。 */
|
||
static void set_max(int v) { g_store_max = v; settings_volume_max = v; }
|
||
|
||
int main(void)
|
||
{
|
||
/* T0 基线回归:无插孔节点(文件不存在)→ 单击起 ramp,到 target 即停(只跳 step 格)。 */
|
||
unlink(JACK_HS_STATE); unlink(JACK_BAL_STATE);
|
||
set_vol(24); set_max(50); g_store_step = 1; g_save_count = 0;
|
||
settings_volume_adj(+1);
|
||
check("T0 ramp started", vol_ramp_dir != 0, 1);
|
||
drive_ramp(0, 100);
|
||
check("T0 click stops at target 25", g_store_volume, 25);
|
||
check("T0 ramp stopped", vol_ramp_dir, 0);
|
||
|
||
/* T1 长按续爬到上限 50(无插孔干扰,正常长按体验不受消抖影响)。 */
|
||
set_vol(24); g_save_count = 0;
|
||
settings_volume_adj(+1);
|
||
drive_ramp(1, 5000); /* 24→50=26 步 ×40ms≈1s,远未到 3s 上限 */
|
||
check("T1 long-press reaches max 50", g_store_volume, 50);
|
||
check("T1 stopped at max", vol_ramp_dir, 0);
|
||
|
||
/* T2 时长上限:max 提到 100(0→100 需 ~100 步 >3s 窗)→ 3s 强制停,不打满。
|
||
* 假时钟 40ms/步 → 3s=75 步 <100 步(4s);drive 300 步必触发 cap。 */
|
||
set_vol(0); set_max(100); g_save_count = 0;
|
||
settings_volume_adj(+1);
|
||
drive_ramp(1, 300);
|
||
check("T2 ramp force-stopped by cap", vol_ramp_dir, 0);
|
||
check("T2 volume below max (not pinned)", g_store_volume < 100, 1);
|
||
check("T2 volume climbed (>0)", g_store_volume > 0, 1);
|
||
printf(" (T2 stopped at volume=%d after cap)\n", g_store_volume);
|
||
|
||
/* T3 插孔消抖:prime 插孔状态(hs=0,bal=0),随后模拟插入 4.4(bal=1)。
|
||
* 变化后 800ms 内:vol_ramp_begin 被忽略;在爬的 ramp 被 tick 中止。 */
|
||
set_max(50); set_vol(24);
|
||
write_file(JACK_HS_STATE, 0); write_file(JACK_BAL_STATE, 0);
|
||
advance_ms(JACK_POLL_MS + 10);
|
||
settings_volume_adj(+1); /* 这次调用顺带 prime 插孔缓存(无变化→不消抖) */
|
||
drive_ramp(0, 100);
|
||
check("T3 prime: normal click still works", g_store_volume, 25);
|
||
|
||
write_file(JACK_BAL_STATE, 1); /* 模拟 4.4 插入 */
|
||
advance_ms(JACK_POLL_MS + 10); /* 过节流窗,下次检测即发现变化 */
|
||
set_vol(24); g_save_count = 0;
|
||
settings_volume_adj(+1); /* 此调用检测变化 → 边沿被忽略 */
|
||
check("T3 press ignored in debounce window", vol_ramp_dir, 0);
|
||
check("T3 volume untouched", g_store_volume, 24);
|
||
|
||
/* T3b 在爬的 ramp 被插孔变化中止(幽灵按住场景:ramp 已起,随后检测到插孔变化)。 */
|
||
/* 先让消抖窗过期:推进 >800ms,再正常起 ramp(长按)。 */
|
||
advance_ms(JACK_DEBOUNCE_MS + 100);
|
||
set_vol(24);
|
||
settings_volume_adj(+1);
|
||
check("T3b ramp restarted after window", vol_ramp_dir, 1);
|
||
vol_ramp_tick(1); advance_ms(20); vol_ramp_tick(1); /* 爬了两步 */
|
||
int before = g_store_volume;
|
||
write_file(JACK_HS_STATE, 1); /* 又插 3.5(变化) */
|
||
advance_ms(JACK_POLL_MS + 10); /* 过节流窗 */
|
||
vol_ramp_tick(1); /* 检测到变化 → 中止 */
|
||
check("T3b in-flight ramp aborted by jack change", vol_ramp_dir, 0);
|
||
check("T3b volume froze at abort point", g_store_volume, before);
|
||
printf(" (T3b aborted at volume=%d)\n", g_store_volume);
|
||
|
||
/* T3c 消抖窗过后恢复正常(误伤可忽略验证)。 */
|
||
advance_ms(JACK_DEBOUNCE_MS + 100);
|
||
settings_volume_adj(+1);
|
||
check("T3c press works again after window", vol_ramp_dir, 1);
|
||
drive_ramp(0, 100);
|
||
|
||
unlink(JACK_HS_STATE); unlink(JACK_BAL_STATE);
|
||
printf("\n%d pass, %d fail\n", g_pass, g_fail);
|
||
return g_fail ? 1 : 0;
|
||
}
|