Hides selected packages from selected caller UIDs at the PackageManagerService
Binder stub. Filters getInstalled{Packages,Applications}, queryIntent*,
resolve{Intent,Service}, get{Package,Application}Info, getPackageUid,
getPackagesForUid, getInstaller{PackageName,SourceInfo}. Hooks
IPackageManagerBase with PackageManagerService fallback.
Config via /data/system/vpnhide_hidden_pkgs.txt and
/data/system/vpnhide_observer_uids.txt with inotify live-reload. Callers
with UID < 10000 are exempt to avoid breaking installd / LauncherApps.
logcat via su runs as root and can't see app's own log entries on some
devices. Use Runtime.exec("logcat") directly instead, which reads the
app's own log buffer without needing READ_LOGS permission.
- scripts/update-version.sh — updates source files (module.prop, Cargo.toml, etc.)
- scripts/update-json.sh — generates Magisk/KSU update-json files
update-json must be committed AFTER the release is published to avoid
a race where Magisk sees a new version but the zip doesn't exist yet.
dev_ifconf() changed its signature between 5.10 and 5.15:
5.10: dev_ifconf(struct net *, struct ifconf *ifc, int size)
x1 = kernel pointer (caller did copy_from_user)
5.15+: dev_ifconf(struct net *, struct ifconf __user *uifc)
x1 = userspace pointer
The kretprobe handler assumed 5.15+ (copy_from_user on x1), which
silently failed on 5.10 because copy_from_user on a kernel pointer
returns EFAULT. This left SIOCGIFCONF unfiltered — tun0 visible.
Use LINUX_VERSION_CODE to select the right access method at compile
time. Each kmod build already targets a specific GKI generation, so
this is safe.
Reported by users on Android 12 (5.10) and Android 14 (non-GKI 5.10).
On Android 10 and devices with permissive SELinux, netlink RTM_GETLINK
is not blocked by sepolicy. The existing recvmsg hook covers most
callers (bionic getifaddrs, Java NetworkInterface), but code using
recv() goes through recvfrom via a bare branch — a different syscall
path. Hooking recvfrom directly breaks recv (shadowhook overwrites the
branch target), so we hook recv instead (12 bytes, safe for island mode).
Also switch diagnostic checks from recv to recvmsg so they go through
the hooked path, and add a separate recv-based check for full coverage.
ensureSelfInTargets() and loadDashboardState() ran as independent
coroutines, so the dashboard could read selfNeedsRestart=false before
the init completed. On slower devices (Pixel 4a) this caused "Installed,
inactive" instead of the restart prompt.
- Make selfNeedsRestart nullable; show spinner until resolved
- Sync zygisk targets to module dir so next app fork picks them up
- Show "restart app to activate" instead of misleading "inactive"
- Add scripts/clean-device.sh for testing fresh installs via adb
KernelSU-Next and Magisk fetch the changelog URL and display it as text.
Pointing to the GitHub releases HTML page showed raw HTML tags.
Now points to a raw markdown file that renders correctly.
- Detect LSPosed in all known module paths (zygisk_vector, zygisk_lsposed, lsposed)
- Skip LSPosed config warnings when hooks are already active at runtime
- Check all modules for empty targets, not just LSPosed
- Bump version to v0.5.1
The writeToParcel hooks were mutating the real NetworkCapabilities,
NetworkInfo, and LinkProperties objects in beforeHookedMethod and
restoring them in afterHookedMethod. Between before and after, other
ConnectivityService threads could read the mutated object, causing
IllegalStateException in checkNrisConsistency ("This NRI is already
registered") and crashing system_server.
Fix: create a copy of each object, modify the copy, write the copy
to the Parcel, and skip the original writeToParcel via setResult(null).
The original object is never mutated. ThreadLocal re-entrancy guard
prevents infinite recursion when the copy's writeToParcel triggers
the same hook.
Only get_module_dir() and set_option(DlCloseModuleLibrary) are used,
both available since v2. This fixes module loading on Magisk v27 which
does not support API v5.
- Move search to TopAppBar with expandable SearchBar (back arrow + clear)
- Replace hint card with help icon (?) in TopAppBar opening a dialog
- Add filter icon with dropdown menu (show system apps, Russian apps only)
- Add fastscroller with letter indicator on drag (fastscroller-material3)
- Sort app list alphabetically (was selected-first)
- Remove nested Scaffold to fix double top padding
- Add RussianAppFilter: detects Russian apps by package prefix (ru.*) and
known company prefixes (Sberbank, Tinkoff, Yandex, VK, Ozon, etc.)
- Add JUnit tests for RussianAppFilter
- update-version.sh generates per-KMI update JSON files for kmod and one
for zygisk, pointing to GitHub Release artifacts
- CI injects updateJson URL into module.prop before packaging zips
- module.prop in repo stays clean (no updateJson), CI appends it per-variant
- Update version mismatch issue texts to direct users to KernelSU/Magisk
Modules for updating
- Fix versionName/versionCode back to 0.4.2 (was accidentally 0.4.3 from
test bump)
- Replace top bar filter icon with inline "Show system apps" checkbox
- Add hint card explaining L/K/Z layer toggles and Zygisk caveat
- Move showSystem state inside AppPickerScreen
- Fix RELEASE_OR_CODENAME lint error (requires API 30, min is 29)
- Mark technical check strings as translatable="false"
- Add Android lint step to CI
- Apply ktlint formatting
HookEntry uses BuildConfig.VERSION_NAME instead of unreliable reflection
to write version to the status file. Dashboard shows "Running module
version: X.Y.Z" in the LSPosed card, and warns in Issues when the
running module version differs from the installed app version (reboot
needed to apply update).
Bump to 0.4.3.
Dashboard as new landing screen with module status cards (kmod, zygisk,
LSPosed), aggregated protection checks (native + Java API), and issue
alerts. Uses sealed types for type-safe state modeling (invalid states
are unrepresentable).
Three separate target lists: kmod, zygisk, and lsposed each have
independent targets.txt. Users can configure per-app which layers
protect it (L/K/Z chips in app picker). Service.sh scripts decoupled —
each resolves only its own component, with migration from unified lists.
HookEntry writes /data/system/vpnhide_hook_active with version and
boot_id so the app can detect if LSPosed hooks are active this boot.
VPN Hide app auto-adds itself to all target lists for self-diagnostics.
If just added, shows "restart needed" instead of stale check results.
App hides itself from the app picker and target counts.
Diagnostics tab no longer runs checks without VPN — shows banner only.
Removed "Run All" button (results are cached per process lifetime).
Splash screen follows system dark/light theme via Material3 DayNight.
The VPN Hide app is now the sole UI for target management. WebUI was
KernelSU-Next-only and redundant since the app works on both KSU and
Magisk. Remove webroot/, action.sh, and all references across docs,
install scripts, module descriptions, and code comments.
- Add branding/ directory with logo.png and icon-512.png
- Replace default app icon with chameleon-in-hoodie adaptive icon
(all densities: mdpi through xxxhdpi + monochrome)
- Update both READMEs with centered logo and title (floppa-vpn style)