#!/system/bin/sh
# ============================================================
#  perfctl - K70E (Dimensity 8300-Ultra) 性能调度核心引擎
#  四挡模式: 省电(powersave) / 均衡(balance) / 狂暴(beast) / 激进(insane)
#  设计原则: 跨机型安全, 所有节点先探测再写, 失败静默跳过
# ============================================================

# ---------- 模块目录推导 ----------
# 依次尝试: BINDIR(KernelSU 注入) > $0 的目录 > $0 的上级目录 > $PWD 拼接 > 已知安装路径
# 注意: 经 setsid/后台 fork 后 $0 可能退化成相对路径, 所以相对路径要拼 $PWD 再判断。
_PWD=$(pwd 2>/dev/null)
[ -n "$_PWD" ] || _PWD=/

resolve_moddir() {
    # 收集候选: BINDIR / $0 所在目录 / $0 所在目录的父目录
    _d1="${0%/*}"        # bin/perfctl -> bin ; /x/bin/perfctl -> /x/bin ; ./pf -> .
    [ "$_d1" = "$0" ] && _d1="."      # $0 无目录部分(如直接 ./perfctl 传 "")
    if [ "$_d1" = "." ]; then
        _d2=".."
    else
        _d2="${_d1%/*}"
        [ "$_d2" = "$_d1" ] && _d2="."   # 无上级斜杠(如单个 "bin")
    fi
    for _rc in "$BINDIR" "$_d1" "$_d2"; do
        [ -z "$_rc" ] && continue
        case "$_rc" in
            /*) [ -f "$_rc/module.prop" ] && { echo "$_rc"; return 0; } ;;
            *)  [ -f "$_PWD/$_rc/module.prop" ] && { echo "$_PWD/$_rc"; return 0; } ;;
        esac
    done
    # 兜底: 已知安装路径(真机)
    for _rc in /data/adb/modules/k70e_perf /data/adb/modules_update/k70e_perf; do
        [ -f "$_rc/module.prop" ] && { echo "$_rc"; return 0; }
    done
    echo /data/adb/modules/k70e_perf
}
MODDIR=$(resolve_moddir)
RUNDIR=$MODDIR/run
STATE=$RUNDIR/mode
PIDF=$RUNDIR/daemon.pid
STOPF=$RUNDIR/daemon.stop
DAEMONF=$RUNDIR/daemon.on
LOGF=$RUNDIR/perfctl.log
INFOC=$RUNDIR/sysinfo
CLUSTC=$RUNDIR/clusters
mkdir -p "$RUNDIR" 2>/dev/null

# ---------- 工具函数 ----------
w()   { [ -e "$1" ] || return 0; chmod 0644 "$1" 2>/dev/null; echo "$2" > "$1" 2>/dev/null; return 0; }
we()  { [ -e "$1" ] || return 0; echo "$2" > "$1" 2>/dev/null; return 0; }
rd()  { [ -e "$1" ] && cat "$1" 2>/dev/null; }
has() { [ -e "$1" ]; }

# 无 fork 读取: 用 shell 内建 read + 重定向(比 $(cat) 快得多)
# 用法: rv VAR /path/to/file
rv() {
    _v=""
    if [ -r "$2" ]; then
        IFS= read -r _v < "$2" 2>/dev/null
    fi
    eval "$1=\$_v"
}

# 纯 shell basename(零 fork)。只处理以 / 分隔的路径, 去掉末尾斜杠。
# 用法: bn /sys/class/devfreq/mtk-dvfsrc-devfreq
bn() {
    _s="$1"
    case "$_s" in
        */) _s="${_s%/}" ;;   # 去尾斜杠
    esac
    echo "${_s##*/}"
}

# 日志轮转计数: 内存里记账, 不再每次 wc -c(那是一次 fork)
LOGN=0
log() {
    echo "[$(date '+%m-%d %H:%M:%S')] $*" >> "$LOGF" 2>/dev/null
    LOGN=$((LOGN + 1))
    # 约每 200 行才做一次体积检查(单条日志 ~40 字节, 200 条 ≈ 8KB)
    if [ "$LOGN" -ge 200 ]; then
        LOGN=0
        _sz=$(wc -c < "$LOGF" 2>/dev/null)
        case "$_sz" in ''|*[!0-9]*) return 0 ;; esac
        if [ "$_sz" -gt 20480 ]; then
            tail -n 120 "$LOGF" > "$LOGF.tmp" 2>/dev/null && mv "$LOGF.tmp" "$LOGF" 2>/dev/null
        fi
    fi
    return 0
}

# ---------- 档位持久化: 双写 + 回读校验 ----------
# 只写模块目录内的 run/mode 不够健壮, 两类真实故障都会让用户看到
# "重新打开应用, 档位退回均衡":
#   1) 模块重装 / 更新会清空模块目录 -> 档位丢失
#   2) WebUI 首次 exec 时若 run/ 建不出来, echo 的报错被 2>/dev/null 吞掉,
#      status 永远读不到档位 -> 每次打开都按默认 2 显示
# 对策: 主存 run/mode; 备存**模块目录之外**的 /data/adb/k70e_perf/mode
# (模块重装也不会丢)。两处都写, 写入后立刻回读校验; 都失败才告警。
PSTATEROOT=/data/adb
PSTATEDIR=$PSTATEROOT/k70e_perf
PSTAT=$PSTATEDIR/mode

save_mode() {
    case "$1" in
        1|2|3|4) _m="$1" ;;
        *) return 1 ;;
    esac
    _ok=0
    mkdir -p "$RUNDIR" 2>/dev/null
    if echo "$_m" > "$STATE" 2>/dev/null; then
        rv _chk "$STATE"
        [ "$_chk" = "$_m" ] && _ok=1
    fi
    # 备存仅在 /data/adb 真实存在时才尝试, 避免在非 Android 环境乱建目录
    if [ -d "$PSTATEROOT" ]; then
        mkdir -p "$PSTATEDIR" 2>/dev/null
        if echo "$_m" > "$PSTAT" 2>/dev/null; then
            rv _chk "$PSTAT"
            [ "$_chk" = "$_m" ] && _ok=1
        fi
    fi
    if [ "$_ok" = 1 ]; then return 0; fi
    log "警告: 档位持久化失败(两个位置都没写进去), 重新打开会回到均衡"
    return 1
}

# 零 fork 读取: 结果放进 $LOADED_MODE, 不用命令替换(保持 status 的低 fork)
LOADED_MODE=2
load_mode() {
    rv _lm "$STATE"
    case "$_lm" in 1|2|3|4) LOADED_MODE=$_lm; return 0 ;; esac
    rv _lm "$PSTAT"
    case "$_lm" in 1|2|3|4) LOADED_MODE=$_lm; return 0 ;; esac
    LOADED_MODE=2
    return 0
}

# 数值钳制: clamp <val> <min> <max>
clamp() {
    v=$1; a=$2; b=$3
    case "$v" in ''|*[!0-9]*) v=0 ;; esac
    [ "$v" -lt "$a" ] && v=$a
    [ "$v" -gt "$b" ] && v=$b
    echo "$v"
}

# 百分比 -> 绝对频率(Hz), 按硬件上限换算
pct_hw() { echo $(( $2 * $1 / 100 )); }

# 在 avail(空格分隔, 降序) 中取 >= target 的最小频点, 没有则取最大
snap() {
    avail="$1"; target="$2"; best=""
    for f in $avail; do
        case "$f" in ''|*[!0-9]*) continue ;; esac
        if [ "$f" -ge "$target" ]; then
            if [ -z "$best" ] || [ "$f" -lt "$best" ]; then best="$f"; fi
        fi
    done
    if [ -z "$best" ]; then
        for f in $avail; do
            case "$f" in ''|*[!0-9]*) continue ;; esac
            if [ -z "$best" ] || [ "$f" -gt "$best" ]; then best="$f"; fi
        done
    fi
    echo "$best"
}

# 安全写频: 先压 min -> 抬 max -> 抬 min, 避免 min>max 被拒
set_freq() {
    d="$1"; mn="$2"; mx="$3"
    has "$d/scaling_max_freq" || return 0
    [ -n "$mx" ] && we "$d/scaling_max_freq" "$mx"
    [ -n "$mn" ] && we "$d/scaling_min_freq" "$mn"
    return 0
}

# ---------- 簇探测 (自适应, 不写死 policy 编号) ----------
CPU0=""; CPU1=""; CPU2=""
detect_clusters() {
    # 簇布局开机后不变, 命中缓存则零开销
    if [ -f "$CLUSTC" ]; then
        _cl=""
        IFS= read -r _cl < "$CLUSTC" 2>/dev/null
        _n=0
        for _c in $_cl; do
            _n=$((_n + 1))
            case "$_n" in
                1) CPU0="$_c" ;;
                2) CPU1="$_c" ;;
                3) CPU2="$_c" ;;
            esac
        done
        [ -n "$CPU0" ] && return 0
    fi

    CPU0=""; CPU1=""; CPU2=""
    for p in 0 1 2 3 4 5 6 7 8 9; do
        d="/sys/devices/system/cpu/cpufreq/policy$p"
        [ -d "$d" ] || continue
        # 零 fork 读取(替代 $(cat ...))
        rv m "$d/cpuinfo_max_freq"
        [ -z "$m" ] && rv m "$d/scaling_max_freq"
        [ -z "$m" ] && continue
        if   [ -z "$CPU0" ]; then CPU0="$p:$m"
        elif [ -z "$CPU1" ]; then CPU1="$p:$m"
        elif [ -z "$CPU2" ]; then CPU2="$p:$m"
        fi
    done
    # 写缓存
    if [ -n "$CPU0" ]; then
        printf '%s %s %s\n' "$CPU0" "$CPU1" "$CPU2" > "$CLUSTC" 2>/dev/null
    fi
}

# 返回 "policy:max:min:avail" (按 % 取频点)
# $1=cluster编号(1/2/3)  $2=min%  $3=max%
freq_for() {
    case "$1" in
        1) c="$CPU0" ;;
        2) c="$CPU1" ;;
        3) c="$CPU2" ;;
        *) return 0 ;;
    esac
    [ -z "$c" ] && return 0
    p=${c%%:*}; hw=${c##*:}
    d="/sys/devices/system/cpu/cpufreq/policy$p"
    rv avail "$d/scaling_available_frequencies"
    tmn=$(pct_hw "$2" "$hw")
    tmx=$(pct_hw "$3" "$hw")
    [ "$tmn" -gt "$tmx" ] && tmn=$tmx
    if [ -n "$avail" ]; then
        mn=$(snap "$avail" "$tmn")
        mx=$(snap "$avail" "$tmx")
    else
        mn=$tmn; mx=$tmx
    fi
    [ "$mn" -gt "$mx" ] && mn=$mx
    echo "$p:$mn:$mx"
}

# 递归遍历目录, 匹配路径做替换(用于热区名不固定)
find_nodes() {
    # $1 = 查找根目录, $2 = 名称匹配
    for f in $(find "$1" -maxdepth 4 -name "$2" 2>/dev/null); do
        echo "$f"
    done
}

# ---------- 各子系统 ----------
apply_cpu_cluster() {
    # $1=policy $2=min $3=max $4=governor
    d="/sys/devices/system/cpu/cpufreq/policy$1"
    set_freq "$d" "$2" "$3"
    [ -n "$4" ] && has "$d/scaling_governor" && we "$d/scaling_governor" "$4"
    return 0
}

apply_gpu() {
    # $1=governor $2=min% $3=max% $4=min_floor(Hz 绝对值,可空)
    for g in /sys/class/devfreq/*mali* /sys/class/devfreq/*gpu* /sys/class/devfreq/*gpufreq*; do
        [ -d "$g" ] || continue
        rv avail "$g/available_frequencies"
        rv hw "$g/max_freq"
        # hw 缺失时: 纯 shell 从 available 里取最大值(不 fork sort/tail)
        if [ -z "$hw" ] && [ -n "$avail" ]; then
            for _f in $avail; do
                case "$_f" in ''|*[!0-9]*) continue ;; esac
                if [ -z "$hw" ] || [ "$_f" -gt "$hw" ]; then hw=$_f; fi
            done
        fi
        [ -z "$hw" ] && continue
        if [ -n "$avail" ]; then
            mx=$(snap "$avail" "$(pct_hw "$3" "$hw")")
            mn=$(snap "$avail" "$(pct_hw "$2" "$hw")")
        else
            mx=$(pct_hw "$3" "$hw"); mn=$(pct_hw "$2" "$hw")
        fi
        if [ -n "$4" ]; then
            [ "$mn" -lt "$4" ] && mn=$4
        fi
        [ "$mn" -gt "$mx" ] && mn=$mx
        [ -n "$1" ] && has "$g/governor" && we "$g/governor" "$1"
        [ -n "$mx" ] && has "$g/max_freq" && we "$g/max_freq" "$mx"
        [ -n "$mn" ] && has "$g/min_freq" && we "$g/min_freq" "$mn"
    done
    return 0
}

apply_ged() {
    # MTK GED 游戏引擎: $1=gpu_boost $2=gx_game $3=force_cpu $4=kpi $5=smart
    P=/sys/module/ged/parameters
    [ -d "$P" ] || return 0
    [ -n "$1" ] && has "$P/gpu_boost_enable"   && we "$P/gpu_boost_enable" "$1"
    [ -n "$2" ] && has "$P/gx_game_mode"       && we "$P/gx_game_mode" "$2"
    [ -n "$3" ] && has "$P/gx_force_cpu_boost" && we "$P/gx_force_cpu_boost" "$3"
    [ -n "$4" ] && has "$P/is_GED_KPI_enabled" && we "$P/is_GED_KPI_enabled" "$4"
    [ -n "$5" ] && has "$P/ged_smart_boost"    && we "$P/ged_smart_boost" "$5"
    return 0
}

apply_eas() {
    # EAS 能效感知调度: $1=0/1  $2=task_util_est 0/1
    has /proc/sys/kernel/sched_energy_aware && we /proc/sys/kernel/sched_energy_aware "$1"
    has /proc/sys/kernel/sched_pelt_multiplier && we /proc/sys/kernel/sched_pelt_multiplier "$2"
    return 0
}

apply_uclamp() {
    # 任务放置偏好: $1=top-app min $2=top-app max $3=foreground max $4=rt min
    if has /dev/cpuctl/top-app/cpu.uclamp.min; then
        we /dev/cpuctl/top-app/cpu.uclamp.min "$1"
        we /dev/cpuctl/top-app/cpu.uclamp.max "$2"
    fi
    has /dev/cpuctl/foreground/cpu.uclamp.max && we /dev/cpuctl/foreground/cpu.uclamp.max "$3"
    has /dev/cpuctl/rt/cpu.uclamp.min && we /dev/cpuctl/rt/cpu.uclamp.min "$4"
    has /dev/cpuctl/background/cpu.uclamp.max && we /dev/cpuctl/background/cpu.uclamp.max "40"
    return 0
}

apply_stune() {
    # $1=schedtune.boost(top-app) $2=prefer_idle $3=background boost
    for s in /dev/stune/top-app /dev/cpuctl/top-app /dev/cpuset/top-app; do
        [ -d "$s" ] || continue
        has "$s/schedtune.boost" && we "$s/schedtune.boost" "$1"
        has "$s/schedtune.prefer_idle" && we "$s/schedtune.prefer_idle" "$2"
    done
    for b in /dev/stune/background /dev/cpuctl/background; do
        [ -d "$b" ] || continue
        has "$b/schedtune.boost" && we "$b/schedtune.boost" "$3"
    done
    return 0
}

apply_vm() {
    # $1=swappiness $2=dirty_ratio $3=dirty_bg $4=vfs_cache $5=page_cluster $6=min_free_kb $7=extra_gc
    we /proc/sys/vm/swappiness "$1"
    we /proc/sys/vm/dirty_ratio "$2"
    we /proc/sys/vm/dirty_background_ratio "$3"
    we /proc/sys/vm/vfs_cache_pressure "$4"
    we /proc/sys/vm/page-cluster "$5"
    we /proc/sys/vm/stat_interval "5"
    has /proc/sys/kernel/sched_child_runs_first && we /proc/sys/kernel/sched_child_runs_first "$7"
    return 0
}

apply_io() {
    # $1=scheduler $2=read_ahead_kb $3=nr_requests $4=rq_affinity
    for q in /sys/block/sd*/queue /sys/block/mmcblk*/queue /sys/block/dm-*/queue; do
        [ -d "$q" ] || continue
        if [ -n "$1" ] && has "$q/scheduler"; then
            av=$(rd "$q/scheduler")
            case "$av" in
                *"[$1]"*) : ;;
                *"$1"*) we "$q/scheduler" "$1" ;;
                *) : ;;
            esac
        fi
        [ -n "$2" ] && has "$q/read_ahead_kb"   && we "$q/read_ahead_kb" "$2"
        [ -n "$3" ] && has "$q/nr_requests"     && we "$q/nr_requests" "$3"
        [ -n "$4" ] && has "$q/rq_affinity"     && we "$q/rq_affinity" "$4"
        [ -e "$q/iostats" ] && we "$q/iostats" "0"
    done
    return 0
}

apply_net() {
    # TCP BBR + fq 拥塞控制 (蜂窝/游戏延迟优化)
    for cc in /proc/sys/net/ipv4/tcp_congestion_control; do
        [ -e "$cc" ] || continue
        av=$(rd /proc/sys/net/ipv4/tcp_available_congestion_control)
        case "$av" in
            *bbr*) we "$cc" "bbr" ;;
            *) we "$cc" "cubic" ;;
        esac
    done
    has /proc/sys/net/core/default_qdisc && we /proc/sys/net/core/default_qdisc "fq"
    has /proc/sys/net/ipv4/tcp_fastopen && we /proc/sys/net/ipv4/tcp_fastopen "1"
    return 0
}

apply_sched() {
    # 内核调度器通用: $1=sched_min_granularity $2=wakeup_granularity
    has /proc/sys/kernel/sched_min_granularity_ns && we /proc/sys/kernel/sched_min_granularity_ns "$1"
    has /proc/sys/kernel/sched_wakeup_granularity_ns && we /proc/sys/kernel/sched_wakeup_granularity_ns "$2"
    return 0
}

# 关停不必要的后台省电策略(仅狂暴/激进)
boost_hint() {
    # 关闭 scx / 提升 uclamp 上限
    has /sys/kernel/rcu_expedited && we /sys/kernel/rcu_expedited "1"
    return 0
}

# ---------- 模式定义 ----------
# 参数顺序: cpu1_min cpu1_max cpu2_min cpu2_max cpu3_min cpu3_max
apply_mode() {
    case "$1" in
    1)
        # ========== 省电 (天天挂机 / 待机久) ==========
        detect_clusters
        c1=$(freq_for 1 20 45); c2=$(freq_for 2 20 42); c3=$(freq_for 3 25 50)
        s1=${c1#*:}; s1=${s1%%:*}; e1=${c1##*:}
        s2=${c2#*:}; s2=${s2%%:*}; e2=${c2##*:}
        s3=${c3#*:}; s3=${s3%%:*}; e3=${c3##*:}
        apply_cpu_cluster "${c1%%:*}" "$s1" "$e1" "schedutil"
        apply_cpu_cluster "${c2%%:*}" "$s2" "$e2" "schedutil"
        apply_cpu_cluster "${c3%%:*}" "$s3" "$e3" "schedutil"
        apply_gpu "simple_ondemand" 10 38 ""
        apply_ged 0 0 0 0 0
        apply_eas 1 1
        apply_uclamp 0 100 90 0
        apply_stune -10 1 -20
        apply_vm 160 15 5 150 3 0 0
        apply_io "kyber" 256 64 0
        apply_net
        apply_sched 3000000 4000000
        ;;
    2)
        # ========== 均衡 (推荐日常) ==========
        detect_clusters
        c1=$(freq_for 1 35 75); c2=$(freq_for 2 35 72); c3=$(freq_for 3 40 85)
        s1=${c1#*:}; s1=${s1%%:*}; e1=${c1##*:}
        s2=${c2#*:}; s2=${s2%%:*}; e2=${c2##*:}
        s3=${c3#*:}; s3=${s3%%:*}; e3=${c3##*:}
        apply_cpu_cluster "${c1%%:*}" "$s1" "$e1" "schedutil"
        apply_cpu_cluster "${c2%%:*}" "$s2" "$e2" "schedutil"
        apply_cpu_cluster "${c3%%:*}" "$s3" "$e3" "schedutil"
        apply_gpu "simple_ondemand" 20 70 ""
        apply_ged 1 0 0 0 1
        apply_eas 1 1
        apply_uclamp 10 100 100 0
        apply_stune 0 1 0
        apply_vm 100 20 8 120 3 0 0
        apply_io "kyber" 512 128 1
        apply_net
        apply_sched 2000000 3000000
        ;;
    3)
        # ========== 狂暴 (游戏满帧) ==========
        detect_clusters
        c1=$(freq_for 1 70 100); c2=$(freq_for 2 65 100); c3=$(freq_for 3 80 100)
        s1=${c1#*:}; s1=${s1%%:*}; e1=${c1##*:}
        s2=${c2#*:}; s2=${s2%%:*}; e2=${c2##*:}
        s3=${c3#*:}; s3=${s3%%:*}; e3=${c3##*:}
        apply_cpu_cluster "${c1%%:*}" "$s1" "$e1" "schedutil"
        apply_cpu_cluster "${c2%%:*}" "$s2" "$e2" "schedutil"
        apply_cpu_cluster "${c3%%:*}" "$s3" "$e3" "performance"
        apply_gpu "performance" 45 100 ""
        apply_ged 1 1 1 1 1
        apply_eas 0 1
        apply_uclamp 25 100 100 0
        apply_stune 10 0 0
        apply_vm 80 30 12 100 4 0 1
        apply_io "none" 2048 512 1
        apply_net
        apply_sched 1000000 1500000
        boost_hint
        ;;
    4)
        # ========== 激进 (极限压榨, 发热高) ==========
        detect_clusters
        c1=$(freq_for 1 95 100); c2=$(freq_for 2 90 100); c3=$(freq_for 3 100 100)
        s1=${c1#*:}; s1=${s1%%:*}; e1=${c1##*:}
        s2=${c2#*:}; s2=${s2%%:*}; e2=${c2##*:}
        s3=${c3#*:}; s3=${s3%%:*}; e3=${c3##*:}
        apply_cpu_cluster "${c1%%:*}" "$s1" "$e1" "performance"
        apply_cpu_cluster "${c2%%:*}" "$s2" "$e2" "performance"
        apply_cpu_cluster "${c3%%:*}" "$s3" "$e3" "performance"
        apply_gpu "performance" 80 100 ""
        apply_ged 1 1 1 1 1
        apply_eas 0 2
        apply_uclamp 40 100 100 20
        apply_stune 20 0 0
        apply_vm 60 40 15 80 4 0 1
        apply_io "none" 4096 1024 1
        apply_net
        apply_sched 500000 1000000
        boost_hint
        ;;
    esac
    return 0
}

mode_name() {
    case "$1" in
        1) echo "省电" ;;
        2) echo "均衡" ;;
        3) echo "狂暴" ;;
        4) echo "激进" ;;
        *) echo "未知" ;;
    esac
}

mode_en() {
    case "$1" in
        1) echo "powersave" ;;
        2) echo "balance" ;;
        3) echo "beast" ;;
        4) echo "insane" ;;
        *) echo "unknown" ;;
    esac
}

mode_id() {
    case "$1" in
        1|powersave|省电) echo 1 ;;
        2|balance|均衡) echo 2 ;;
        3|beast|狂暴) echo 3 ;;
        4|insane|激进) echo 4 ;;
        *) echo "" ;;
    esac
}

# ---------- 回读校验 ----------
verify() {
    detect_clusters
    for c in "$CPU0" "$CPU1" "$CPU2"; do
        [ -z "$c" ] && continue
        p=${c%%:*}
        d="/sys/devices/system/cpu/cpufreq/policy$p"
        mx=$(rd "$d/scaling_max_freq")
        mn=$(rd "$d/scaling_min_freq")
        echo "cpu$p:$mn-$mx"
    done
    return 0
}

# ---------- 状态输出 ----------
# ---------- 静态信息缓存 ----------
# 机型/SOC/内核这些开机后不会变, 缓存到文件避免每次 status 都 fork getprop/grep
INFOC=$RUNDIR/sysinfo
gen_sysinfo() {
    _model=$(getprop ro.product.model 2>/dev/null)
    _dev=$(getprop ro.product.device 2>/dev/null)
    _kern=$(uname -r 2>/dev/null)
    _soc=""
    # 只用一次 grep, 且优先用最可能命中的文件
    for _bp in /system/build.prop /vendor/build.prop /odm/etc/build.prop; do
        [ -r "$_bp" ] || continue
        _soc=$(grep -m1 'ro\.soc\.model' "$_bp" 2>/dev/null | cut -d= -f2)
        [ -n "$_soc" ] && break
    done
    [ -z "$_soc" ] && _soc=$(getprop ro.soc.model 2>/dev/null)
    [ -z "$_soc" ] && _soc="未知"
    # 一次写入
    printf '%s\n' "$_model" "$_dev" "$_soc" "$_kern" > "$INFOC" 2>/dev/null
    printf '%s\n' "$_model" "$_dev" "$_soc" "$_kern"
}
# 读取最近 N 行日志(纯 shell, 零 fork)。
# 环形缓冲: 单遍扫描内存里只保留最后 N 行, 输出顺序天然正确。
# 用法: tail_log N   → 直接打到 stdout
tail_log() {
    _n="$1"
    case "$_n" in ''|*[!0-9]*) _n=8 ;; esac
    [ "$_n" -lt 1 ] && _n=1
    [ -r "$LOGF" ] || return 0
    _i=0
    _k=0
    # 用 _k 记录已写入的槽数, _i 为写指针(循环回绕)
    while IFS= read -r _ln; do
        eval "_L$_i=\$_ln"
        _i=$((_i + 1))
        [ "$_i" -ge "$_n" ] && _i=0
        [ "$_k" -lt "$_n" ] && _k=$((_k + 1))
    done < "$LOGF"
    # 从最旧到最新输出: 若已写满, 起点就是写指针(它指向最旧的槽)
    if [ "$_k" -lt "$_n" ]; then
        _i=0
    fi
    _c=0
    while [ "$_c" -lt "$_k" ]; do
        eval "_ln=\${_L$_i}"
        echo "$_ln"
        _i=$((_i + 1))
        [ "$_i" -ge "$_n" ] && _i=0
        _c=$((_c + 1))
    done
    return 0
}

cmd_status() {
    detect_clusters
    load_mode
    cur=$LOADED_MODE

    # 守护存活: 标记文件 + /proc 直接判断(零 fork, 不用 kill)
    d_on=0
    if [ -f "$DAEMONF" ]; then
        rv dp "$PIDF"
        [ -n "$dp" ] && [ -d "/proc/$dp" ] && d_on=1
    fi

    # 三个簇的 governor(零 fork 读取)
    gov1=""; gov2=""; gov3=""
    [ -n "$CPU0" ] && rv gov1 "/sys/devices/system/cpu/cpufreq/policy${CPU0%%:*}/scaling_governor"
    [ -n "$CPU1" ] && rv gov2 "/sys/devices/system/cpu/cpufreq/policy${CPU1%%:*}/scaling_governor"
    [ -n "$CPU2" ] && rv gov3 "/sys/devices/system/cpu/cpufreq/policy${CPU2%%:*}/scaling_governor"

    # GPU: 第一个匹配的 devfreq。用 case 通配替代 ls/echo+basename(省两次 fork)
    gpu_n=""; gpu_f=""; gpu_mx=""; gpu_gv=""
    for g in /sys/class/devfreq/*mali* /sys/class/devfreq/*gpu*; do
        [ -d "$g" ] || continue
        rv gpu_f "$g/cur_freq"
        rv gpu_mx "$g/max_freq"
        rv gpu_gv "$g/governor"
        gpu_n="${g##*/}"
        break
    done

    # 静态信息: 缓存文件直接按行读(零 fork)
    if [ ! -f "$INFOC" ]; then gen_sysinfo > "$INFOC" 2>/dev/null; fi
    MODEL=""; DEVICE=""; SOC=""; KERNEL=""
    if [ -r "$INFOC" ]; then
        { IFS= read -r MODEL; IFS= read -r DEVICE; IFS= read -r SOC; IFS= read -r KERNEL; } < "$INFOC" 2>/dev/null
    fi

    rv eas /proc/sys/kernel/sched_energy_aware
    rv btmp /sys/class/power_supply/battery/temp
    rv blev /sys/class/power_supply/battery/capacity
    rv tcpcc /proc/sys/net/ipv4/tcp_congestion_control

    # 模式名: 用 case 直接取, 不 fork 函数
    case "$cur" in
        1) mn="省电"; me="powersave" ;;
        2) mn="均衡"; me="balance" ;;
        3) mn="狂暴"; me="beast" ;;
        4) mn="激进"; me="insane" ;;
        *) mn="未知"; me="unknown" ;;
    esac

    # 电池温度归一: Android 标准单位是 0.1℃(320 = 32.0℃), 原样保留。
    # 少数内核直接给整数摄氏度(< 100 视为 ℃), 才需要 ×10 归一。
    case "$btmp" in ''|*[!0-9]*) btmp="" ;; esac
    if [ -n "$btmp" ] && [ "$btmp" -gt 0 ] && [ "$btmp" -lt 100 ]; then
        btmp=$((btmp * 10))
    fi

    # 单次 heredoc 输出, 零 echo fork
    cat <<EOF
MODE=$cur
MODE_NAME=$mn
MODE_EN=$me
MODEL=$MODEL
DEVICE=$DEVICE
SOC=$SOC
KERNEL=$KERNEL
CLUSTER1=${CPU0:-none}
CLUSTER2=${CPU1:-none}
CLUSTER3=${CPU2:-none}
GOV1=$gov1
GOV2=$gov2
GOV3=$gov3
GPU_NAME=$gpu_n
GPU_CUR=$gpu_f
GPU_MAX=$gpu_mx
GPU_GOV=$gpu_gv
DAEMON=$d_on
EAS=$eas
TCP_CC=$tcpcc
BATT_TEMP=$btmp
BATT_LEVEL=$blev
EOF

    # 日志随 status 一并返回(省掉 WebUI 单独跑一次 tail)
    if [ -r "$LOGF" ]; then
        tail_log 8 | while IFS= read -r _ln; do
            echo "LOG=$_ln"
        done
    fi
}

cmd_current() {
    detect_clusters
    load_mode
    cur=$LOADED_MODE
    echo "当前模式: $(mode_name "$cur") ($(mode_en "$cur"))"
    for c in "$CPU0" "$CPU1" "$CPU2"; do
        [ -z "$c" ] && continue
        p=${c%%:*}
        d="/sys/devices/system/cpu/cpufreq/policy$p"
        echo "  policy$p  min=$(rd "$d/scaling_min_freq")  max=$(rd "$d/scaling_max_freq")  gov=$(rd "$d/scaling_governor")"
    done
    for g in /sys/class/devfreq/*mali* /sys/class/devfreq/*gpu*; do
        [ -d "$g" ] || continue
        echo "  GPU ${g##*/}  min=$(rd "$g/min_freq")  max=$(rd "$g/max_freq")  cur=$(rd "$g/cur_freq")  gov=$(rd "$g/governor")"
        break
    done
}

cmd_apply() {
    m=$(mode_id "$1")
    if [ -z "$m" ]; then echo "错误: 未知模式 '$1' (可用: 省电|均衡|狂暴|激进|1-4)"; exit 1; fi
    mkdir -p "$RUNDIR" 2>/dev/null
    # 先落盘档位再应用: 即使后面 apply_mode 中途失败,
    # 重启/重开界面也能读回用户的选择, 不会莫名退回均衡
    save_mode "$m"
    apply_mode "$m"
    # 记录本档期望值, 供守护做轻量比对(避免守护盲目全量重写)
    snapshot_target "$m"
    log "apply mode=$m ($(mode_name "$m"))"
    # 立即回读校验, 不做假成功
    echo "已应用: $(mode_name "$m")"
    cmd_current
}

# ---------- 抗回写守护 ----------
# 资源占用设计(这是界面/系统"卡顿"的关键抑制点):
#   每轮只做"轻量巡检"——零 fork 读 3 个簇的 min/max/governor, 目标是
#   记录在 $TGTF 里的期望值, 只在**确实被系统改写**时才回写。
#   系统不改写 = 一个字节都不写, 也不 fork。
#   每 FULL_EVERY 轮(每 60s)才做一次全量 apply_mode, 兜住那些需要整体
#   重刷的参数(IO/VM/GED 等)。这样守护从"每 15s 轰一次"降到"基本静默"。
TGTF=$RUNDIR/target

# 把当前档位的期望频率快照写入 $TGTF(供守护轻量比对)
# 行格式: policy:min:max:governor:strict
#   strict=1 表示该档位要求"钉住 min 下限"(狂暴/激进)
snapshot_target() {
    m="$1"
    detect_clusters
    # 狂暴/激进把 min 当硬下限, 其余档位只做上限保护
    case "$m" in
        3|4) _strict=1 ;;
        *)   _strict=0 ;;
    esac
    {
        for c in "$CPU0" "$CPU1" "$CPU2"; do
            [ -z "$c" ] && continue
            _p=${c%%:*}
            case "$m" in
                1) _mn=20; _mx=45; _gv=schedutil ;;
                2) _mn=35; _mx=75; _gv=schedutil ;;
                3) _mn=70; _mx=100; _gv=schedutil ;;
                4) _mn=95; _mx=100; _gv=performance ;;
            esac
            # 与 apply_mode 中簇2/簇3 的实际档位对齐
            case "$m" in
                1) [ "$_p" -ge 4 ] && { _mn=20; _mx=42; } ;;
                2) [ "$_p" -ge 4 ] && { _mn=35; _mx=72; } ;;
                3) [ "$_p" -ge 4 ] && { _mn=65; _mx=100; } ;;
                4) [ "$_p" -ge 4 ] && { _mn=90; _mx=100; } ;;
            esac
            case "$m" in
                3|4) [ "$_p" -ge 7 ] && _gv=performance ;;
            esac
            _d="/sys/devices/system/cpu/cpufreq/policy$_p"
            rv _av "$_d/scaling_available_frequencies"
            _hw=""
            for _f in ${_av:-}; do
                case "$_f" in ''|*[!0-9]*) continue ;; esac
                if [ -z "$_hw" ] || [ "$_f" -gt "$_hw" ]; then _hw=$_f; fi
            done
            [ -z "$_hw" ] && continue
            if [ -n "$_av" ]; then
                _n=$(snap "$_av" "$(pct_hw "$_mn" "$_hw")")
                _x=$(snap "$_av" "$(pct_hw "$_mx" "$_hw")")
            else
                _n=$(pct_hw "$_mn" "$_hw"); _x=$(pct_hw "$_mx" "$_hw")
            fi
            [ -n "$_n" ] && [ -n "$_x" ] && [ "$_n" -gt "$_x" ] && _n=$_x
            echo "$_p:$_n:$_x:$_gv:$_strict"
        done
    } > "$TGTF" 2>/dev/null
}

# 轻量巡检: 仅比对, 不匹配才写。
# 返回 0 表示"有参数被系统改写并已修回", 1 表示"完全正常, 未做任何写入"。
#
# 语义设计(重要): 目标行格式 policy:min:max:gov:strict
#   strict=1(狂暴/激进) —— 把 min 视为"必须保住的下限", 被压低也要抬回。
#                          这两档存在的意义就是钉住高频下限。
#   strict=0(省电/均衡) —— min 只做"上限保护"(被抬高才拉回);
#                          系统为省电主动压低 min 属正常行为, 不干预,
#                          否则等于和系统省电策略对着干, 徒增功耗与写入。
patrol_once() {
    [ -r "$TGTF" ] || return 1
    _touched=0
    while IFS= read -r _line; do
        [ -z "$_line" ] && continue
        _p=${_line%%:*}; _rest=${_line#*:}
        _n=${_rest%%:*}; _rest=${_rest#*:}
        _x=${_rest%%:*}; _rest=${_rest#*:}
        _gv=${_rest%%:*}; _st=${_rest##*:}
        _d="/sys/devices/system/cpu/cpufreq/policy$_p"
        [ -d "$_d" ] || continue
        rv _cm "$_d/scaling_min_freq"
        case "$_cm" in ''|*[!0-9]*) _cm=0 ;; esac
        rv _cx "$_d/scaling_max_freq"
        case "$_cx" in ''|*[!0-9]*) _cx=0 ;; esac
        rv _cg "$_d/scaling_governor"

        if [ -n "$_n" ] && [ "$_cm" != "$_n" ]; then
            if [ "$_st" = "1" ]; then
                # 性能档: min 必须钉住(高了压低、低了抬高都要纠正)
                we "$_d/scaling_min_freq" "$_n"; _touched=1
            elif [ "$_cm" -gt "$_n" ]; then
                # 省电/均衡: 只纠正"被抬高"的情况
                we "$_d/scaling_min_freq" "$_n"; _touched=1
            fi
        fi
        # max 被压低(系统限频) → 抬回目标。必须 min<目标 才抬,
        # 否则会出现 min>max 被内核拒绝。
        if [ -n "$_x" ] && [ "$_cx" -lt "$_x" ] && [ "$_cm" -lt "$_x" ]; then
            we "$_d/scaling_max_freq" "$_x"; _touched=1
        fi
        # governor 被重置(仅狂暴/激进档有此期望值) → 写回
        if [ -n "$_gv" ] && [ "$_cg" != "$_gv" ]; then
            we "$_d/scaling_governor" "$_gv"; _touched=1
        fi
    done < "$TGTF"
    [ "$_touched" = 1 ] && return 0
    return 1
}

daemon_run() {
    iv="$1"
    case "$iv" in ''|*[!0-9]*) iv=15 ;; esac
    [ "$iv" -lt 5 ] && iv=5
    # 每轮 sleep iv 秒; 累计到 FULL_EVERY 秒才做一次全量重写。
    # 这样 iv=15 时, 轻量巡检每 15s, 全量重写每 60s(而非每 15s 一次)。
    FULL_EVERY=60
    _acc=0
    # 立即落盘 pid, 让 start 分支能尽快确认驻留
    echo $$ > "$PIDF" 2>/dev/null
    load_mode
    _m0=$LOADED_MODE
    snapshot_target "$_m0"
    log "daemon start interval=${iv}s full=${FULL_EVERY}s pid=$$"

    while [ ! -f "$STOPF" ]; do
        # 进程被外部 kill 前若 pid 文件被清掉, 补写一次(防止重复启动)
        rv cp "$PIDF"
        if [ "$cp" != "$$" ]; then
            echo $$ > "$PIDF" 2>/dev/null
        fi
        load_mode
        m=$LOADED_MODE

        # 档位变了: 立刻重算期望值并全量应用(用户刚切档, 要马上生效)
        if [ "$m" != "$_m0" ]; then
            _m0="$m"
            _acc=0
            apply_mode "$m"
            snapshot_target "$m"
            log "daemon mode-change -> $m"
            sleep "$iv"
            continue
        fi

        _acc=$((_acc + iv))
        if [ "$_acc" -ge "$FULL_EVERY" ]; then
            # 低频全量重写: 兜住 IO/VM/GED 等需要整体刷新的参数
            _acc=0
            apply_mode "$m"
        else
            # 高频轻量巡检: 只在被改写时才写, 系统不动就静默
            patrol_once
        fi
        sleep "$iv"
    done
    rm -f "$PIDF" 2>/dev/null
    log "daemon stop"
    exit 0
}

cmd_daemon() {
    case "$1" in
        start)
            iv="${2:-15}"
            if [ -f "$PIDF" ]; then
                old=$(rd "$PIDF")
                if [ -n "$old" ] && kill -0 "$old" 2>/dev/null; then
                    echo "守护已在运行 (pid=$old)"; return 0
                fi
            fi
            rm -f "$STOPF" 2>/dev/null
            echo "on" > "$DAEMONF" 2>/dev/null

            # 关键: KernelSU WebUI / 管理器执行完会回收整个进程树,
            # 普通 `cmd &` 起的后台进程会被一起杀掉, 必须脱离会话。
            # 传绝对路径, 避免子进程里 $0 退化成相对路径导致 MODDIR 推导失败。
            SELF="$MODDIR/bin/perfctl"
            [ -f "$SELF" ] || SELF="$0"
            if command -v setsid >/dev/null 2>&1; then
                setsid sh "$SELF" daemon run "$iv" >/dev/null 2>&1 &
            else
                ( ( sh "$SELF" daemon run "$iv" >/dev/null 2>&1 & ) & )
            fi

            # 等守护把 pid 文件落盘(最多 5 秒), 用真实状态回报, 绝不谎报成功
            k=0
            while [ "$k" -lt 25 ]; do
                if [ -f "$PIDF" ]; then
                    np=$(rd "$PIDF")
                    if [ -n "$np" ] && kill -0 "$np" 2>/dev/null; then
                        echo "守护已启动 (pid=$np, 间隔 ${iv}s)"
                        return 0
                    fi
                fi
                k=$((k + 1))
                sleep 0.2 2>/dev/null || sleep 1
            done
            echo "守护未能驻留, 可能被系统回收。"
            echo "请在终端执行: sh $SELF daemon start $iv"
            return 1
            ;;
        stop)
            echo "stop" > "$STOPF" 2>/dev/null
            rm -f "$DAEMONF" 2>/dev/null
            if [ -f "$PIDF" ]; then
                old=$(rd "$PIDF")
                if [ -n "$old" ]; then
                    kill "$old" 2>/dev/null
                    sleep 0.2 2>/dev/null
                    kill -9 "$old" 2>/dev/null
                fi
                rm -f "$PIDF" 2>/dev/null
            fi
            echo "守护已停止"
            ;;
        run)
            # 内部入口, 供 setsid/nohup 调用
            shift
            daemon_run "${1:-15}"
            ;;
        *)
            if [ -f "$PIDF" ] && kill -0 "$(rd "$PIDF")" 2>/dev/null; then
                echo "状态: 运行中 (pid=$(rd "$PIDF"))"
            else
                echo "状态: 已停止"
            fi
            ;;
    esac
}

cmd_reset() {
    detect_clusters
    for c in "$CPU0" "$CPU1" "$CPU2"; do
        [ -z "$c" ] && continue
        p=${c%%:*}; hw=${c##*:}
        d="/sys/devices/system/cpu/cpufreq/policy$p"
        we "$d/scaling_max_freq" "$hw"
        # 最低频 = available 列表里最后一个(降序), 纯 shell 取, 不 fork awk
        rv av "$d/scaling_available_frequencies"
        if [ -n "$av" ]; then
            for _f in $av; do _lo=$_f; done
            [ -n "$_lo" ] && we "$d/scaling_min_freq" "$_lo"
        fi
        has "$d/scaling_governor" && we "$d/scaling_governor" "schedutil"
    done
    for g in /sys/class/devfreq/*mali* /sys/class/devfreq/*gpu*; do
        [ -d "$g" ] || continue
        has "$g/governor" && we "$g/governor" "simple_ondemand"
        break
    done
    apply_io "" 512 "" ""
    # 恢复默认后必须同步档位文件, 否则 status 仍报旧档位, 界面骗人
    save_mode 2
    echo "已恢复默认参数"
}

cmd_verify() {
    echo "=== 回读校验 ==="
    cmd_current
}

usage() {
    echo "perfctl - K70E 性能调度 $(cat "$MODDIR/run/ver" 2>/dev/null || echo V1.1内测版) by 白Yunmo"
    echo "用法:"
    echo "  perfctl apply <省电|均衡|狂暴|激进|1-4>   切换调度"
    echo "  perfctl current                          查看当前参数"
    echo "  perfctl status                           机器可读状态"
    echo "  perfctl daemon start [间隔秒]            启动抗覆盖守护"
    echo "  perfctl daemon stop                      停止守护"
    echo "  perfctl reset                            恢复默认"
    echo "  perfctl verify                           回读校验"
}

case "$1" in
    apply)   shift; cmd_apply "$@" ;;
    current) cmd_current ;;
    status)  cmd_status ;;
    daemon)  shift; cmd_daemon "$@" ;;
    reset)   cmd_reset ;;
    verify)  cmd_verify ;;
    *)       usage ;;
esac
