Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
_2026-04-20_

## English

Lower minimum Android version to 9 (API 28). Experimental — tested builds work on Android 10+, Android 9 support is unverified and needs community reports.

## Русский

Понижена минимальная версия Android до 9 (API 28). Экспериментально — сборки протестированы на Android 10+, работа на Android 9 не проверена и требует отчётов от пользователей.
2 changes: 1 addition & 1 deletion lsposed/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ android {

defaultConfig {
applicationId = "dev.okhsunrog.vpnhide"
minSdk = 29
minSdk = 28
targetSdk = 35
versionCode = 700
versionName = buildVersion
Expand Down
14 changes: 10 additions & 4 deletions lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/DashboardData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1094,10 +1094,16 @@ private fun runJavaProtectionCheck(cm: ConnectivityManager): JavaResult {
if (!notVpn) failed++
VpnHideLog.d(TAG, "java: hasCapability(NOT_VPN)=$notVpn")

val info = caps.transportInfo
val isVpnTi = info?.javaClass?.name?.contains("VpnTransportInfo") == true
if (isVpnTi) failed++
VpnHideLog.d(TAG, "java: transportInfo=${info?.javaClass?.name} isVpn=$isVpnTi")
// NetworkCapabilities.getTransportInfo() was introduced in API 29;
// on API 28 (Android 9) the leak path does not exist, so skip the check.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val info = caps.transportInfo
val isVpnTi = info?.javaClass?.name?.contains("VpnTransportInfo") == true
if (isVpnTi) failed++
VpnHideLog.d(TAG, "java: transportInfo=${info?.javaClass?.name} isVpn=$isVpnTi")
} else {
VpnHideLog.d(TAG, "java: transportInfo check skipped (API < 29)")
}

val vpnNets =
cm.allNetworks.count {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,11 @@ private fun checkTransportInfo(
cm: ConnectivityManager,
name: String,
): CheckResult {
// NetworkCapabilities.getTransportInfo() is API 29+; the leak path does
// not exist on Android 9, so the check trivially passes.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return CheckResult(name, true, "PASS: transportInfo unavailable (API < 29)")
}
val net = cm.activeNetwork ?: return CheckResult(name, true, "PASS: no active network")
val caps = cm.getNetworkCapabilities(net) ?: return CheckResult(name, true, "PASS: no capabilities")
val info = caps.transportInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,12 @@ class HookEntry : IXposedHookLoadPackage {
private fun watchTargetUidsFile() {
val dir = "/data/system"
val filename = "vpnhide_uids.txt"

// FileObserver(File, Int) is API 29+; use the String-path form for API 28 compatibility.
@Suppress("DEPRECATION")
val observer =
object : android.os.FileObserver(
File(dir),
dir,
CREATE or CLOSE_WRITE or MOVED_TO or MODIFY,
) {
override fun onEvent(
Expand Down
6 changes: 5 additions & 1 deletion lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/HookLog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import android.os.FileObserver
import de.robv.android.xposed.XposedBridge
import java.io.File

// FileObserver(File, Int) requires API 29; we stay compatible with API 28
// (Android 9) by using the String-path constructor throughout.

/**
* [XposedBridge.log] wrapper gated by a filesystem flag set from the app.
* Used by LSPosed hooks running inside `system_server`, where we don't
Expand All @@ -26,9 +29,10 @@ internal object HookLog {
fun install() {
reload()
if (watcher != null) return
@Suppress("DEPRECATION")
val observer =
object : FileObserver(
File("/data/system"),
"/data/system",
CREATE or CLOSE_WRITE or MOVED_TO or MODIFY,
) {
override fun onEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,10 @@ internal object PackageVisibilityHooks {
}

private fun watchConfigFiles() {
// FileObserver(File, Int) is API 29+; use the String-path form for API 28 compatibility.
@Suppress("DEPRECATION")
val observer =
object : FileObserver(File("/data/system"), CREATE or CLOSE_WRITE or MOVED_TO or MODIFY) {
object : FileObserver("/data/system", CREATE or CLOSE_WRITE or MOVED_TO or MODIFY) {
override fun onEvent(
event: Int,
path: String?,
Expand Down