release.py patched kmod/zygisk/ports module.prop but not the KPM one
(kmod/kpm/module/module.prop), which was added after the script was
written — so the KPM module shipped with a stale versionCode=701. Add it
to the patched files and set it to v1.0.0/10000 for this release.
customize.sh warned about the single-active conflict whenever the vpnhide_kmod
module directory merely existed, and told the user to *uninstall* it. But the
boot loader (post-fs-data.sh / service.sh) only refuses to load the KPM when
vpnhide_kmod is present AND not disabled — a disabled .ko lets the KPM load.
Make the install check mirror that runtime guard (present AND no `disable`
flag), and reword the message to say disabling is sufficient. Verified on a
Pixel 8 Pro: with vpnhide_kmod disabled (not uninstalled), the KPM loads and
configures, and the .ko is not in lsmod.
Verified on a Pixel 4a (APatch, kernel 4.14) and a Pixel 8 Pro (KSU-Next +
KPatch-Next-Module, GKI 6.1):
- KPM runtime probe: drop the dead /data/adb/ksu/bin/kpatch guess (KSU-Next
ships kpatch via the KPatch-Next-Module, not ksu/bin) and the unused PATH
check. The real signals are /data/adb/ap (APatch — loads KPMs via supercall,
no CLI on disk) and /data/adb/modules/KPatch-Next/bin/kpatch (the module, same
on Magisk/KSU/KSU-Next). Mirror the same trim in the activator's find_kpatch
and the KPM post-fs-data, documenting that APatch goes through the supercall.
- root_manager: APatch and KSU both ship a /data/adb/magisk compat layer (the
APatch 4a has /data/adb/magisk + magisk.db), so the old order reported APatch
as 'magisk'. Check magisk first and let the exclusive ksu/ap dirs override it.
- reword the KPM needs-runtime string: drop the misplaced 'APatch already
includes it' note (an APatch user never sees this string — the runtime flag is
already true via /data/adb/ap).
When the KPM stands down for a co-installed .ko backend (§1.5), the boot
flow previously let the activator's silent skip be recorded by service.sh
as 'activator 1 configured' — load_status falsely claimed the KPM was
loaded. The .ko+KPM conflict banner also keyed on both backends being
active, a state that can never be observed (two live kernel hookers freeze
the device), so users were never warned about the redundant co-install.
- activator: activate_kpm_boot returns KpmBootOutcome; a kmod conflict is
DeferredConflict (not silent Ok), mapped to exit code 3 in the kpm bin.
- kpm service.sh: conflict dir-check before the APatch superkey branch
(so a co-installed .ko isn't masked as awaiting_superkey), and exit 3
is recorded as a truthful 'conflict' status instead of 'configured'.
- app: surface the deferred-conflict (load_status runtime=conflict for the
current boot) as a warning; new dashboard_issue_native_conflict_deferred
string (EN+RU) telling the user to remove one kernel backend.
- post-fs-data.sh: replace the comment claiming a non-existent kernel-half
guard with the actual layered, fail-safe userspace design.
The KPM boot script was out of step with the rest of the migration:
- it sourced debug from its own /data/adb/vpnhide_kpm/debug_logging, which the
app never writes — so KPM debug was always off. Read the one canonical flag
(/data/system/vpnhide_debug_logging) instead, folded into the ctl0 config's
`debug` line like the .ko/zygisk scripts.
- it wrote the lsposed channel (vpnhide_uids.txt) as plain UIDs, which the new
system_server reader can't parse. Emit the `vpnhide 1 config` wire via the
same build_config helper, so a KPM-only install still feeds the Java layer.
Refs #21.
Package the cross-version .kpm as one flashable module (kmod/kpm/module +
kmod/kpm/build.py -> vpnhide-kpm.zip), analogous to the .ko and Zygisk
modules, per protocol §7.4:
- post-fs-data.sh loads the KPM via the KernelPatch runtime early in boot.
Keyless KPatch-Next (Magisk/KSU) loads automatically; APatch (superkey)
records awaiting_superkey for the app. Single-active guard: refuses to load
when the .ko backend is installed (§1.5).
- service.sh resolves package->UID once PackageManager is up, pushes a
'vpnhide 1 config' snapshot via 'kpatch kpm ctl0', and feeds the lsposed
UID file (the always-active Java layer).
- customize.sh seeds the persistent /data/adb/vpnhide_kpm config dir.
CI gains a 'kpm' publish job (build + upload vpnhide-kpm.zip) wired into the
release job's needs; the new module scripts join the shellcheck list. The
kpm-qemu jobs remain the runtime test gate.
The boot-script CLI invocations follow §7.3 and are fail-safe (they record
load_status and exit rather than risk a bootloop); the exact on-device
kpatch CLI paths get confirmed in device testing.