Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_vol_ramp_jack.c
T
MeowOS 4bafdb5d69 fix(keys): 插孔消抖 + 音量 ramp 3s 上限 + 亮度写日志 — 耳机插拔 ADC 幽灵音量键修复
真机实证(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/ 有主未动)
2026-09-01 04:28:10 +08:00

190 lines
8.1 KiB
C
Raw 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_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;
}