mirror of
https://github.com/okhsunrog/vpnhide.git
synced 2026-10-19 15:44:36 +00:00
refactor: generalize optional feature settings
This commit is contained in:
parent
8400a27bfb
commit
e712e3b3ec
15 changed files with 47 additions and 49 deletions
|
|
@ -106,20 +106,20 @@ pub fn read_canonical() -> Result<String> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Read one reboot-gated kernel feature without waiting for PackageManager.
|
||||
/// Boot loaders query features before loading their backend, so disabled
|
||||
/// features never register their probes at all.
|
||||
pub(crate) fn kernel_boot_feature_enabled(feature: &str) -> Result<bool> {
|
||||
/// Read one optional feature without waiting for PackageManager. Kernel boot
|
||||
/// loaders query their relevant features before loading the backend, so a
|
||||
/// disabled load-time feature never registers its probes at all.
|
||||
pub(crate) fn optional_feature_enabled(feature: &str) -> Result<bool> {
|
||||
let config = parse_canonical(&read_canonical()?)?;
|
||||
Ok(config.settings.kernel_boot_features.contains(feature))
|
||||
Ok(config.settings.optional_features.contains(feature))
|
||||
}
|
||||
|
||||
/// Optional boot hardening is never a prerequisite for the baseline backend.
|
||||
/// A corrupt desired-state file therefore falls back to the disabled default;
|
||||
/// late-start config delivery still reports the parse error and refuses a
|
||||
/// partial snapshot.
|
||||
pub(crate) fn kernel_boot_feature_enabled_or_default(feature: &str) -> bool {
|
||||
match kernel_boot_feature_enabled(feature) {
|
||||
pub(crate) fn optional_feature_enabled_or_default(feature: &str) -> bool {
|
||||
match optional_feature_enabled(feature) {
|
||||
Ok(enabled) => enabled,
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
|
|
@ -251,7 +251,7 @@ pub(crate) fn load_kpm_boot() -> Result<KpmBootReport> {
|
|||
));
|
||||
}
|
||||
let filesystem_hiding =
|
||||
kernel_boot_feature_enabled_or_default(KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS);
|
||||
optional_feature_enabled_or_default(OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS);
|
||||
let client = match KpmClient::detect_outcome()? {
|
||||
KpmClientDetection::Ready(client) => client,
|
||||
KpmClientDetection::AwaitingAuthentication(_) => {
|
||||
|
|
@ -310,8 +310,8 @@ fn activate_kpm_with_pm_wait(wait: PmReadyWait, conflict_is_error: bool) -> Resu
|
|||
let canonical = read_canonical()?;
|
||||
let filesystem_hiding = parse_canonical(&canonical)?
|
||||
.settings
|
||||
.kernel_boot_features
|
||||
.contains(KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS);
|
||||
.optional_features
|
||||
.contains(OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS);
|
||||
let wire = project_native_with_pm_wait(&canonical, NativeHookFamily::Kpm, wait)?;
|
||||
// Re-check after the (possibly long) PackageManager wait: the .ko may have
|
||||
// been loaded meanwhile, in which case we must not configure the KPM.
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
|||
use crate::{
|
||||
APATCH_DIR, KPM_CTL_LOCK, KpmBootOutcome, KpmBootReport, PORTS_CHAIN4, PORTS_CHAIN6,
|
||||
PORTS_STATUS_DIR, Result, activate_kmod_boot, activate_kpm_boot, activate_ports_recorded,
|
||||
activate_zygisk_boot, kernel_boot_feature_enabled, kernel_boot_feature_enabled_or_default,
|
||||
kmod_backend_present, load_kpm_boot, write_atomic,
|
||||
activate_zygisk_boot, kmod_backend_present, load_kpm_boot, optional_feature_enabled,
|
||||
optional_feature_enabled_or_default, write_atomic,
|
||||
};
|
||||
|
||||
const KMOD_STATUS_DIR: &str = "/data/adb/vpnhide_kmod";
|
||||
|
|
@ -198,7 +198,7 @@ pub fn boot_load_kmod() -> Result<()> {
|
|||
let module_prop = read_module_properties(&module_dir.join("module.prop"));
|
||||
let (kprobes, kretprobes) = read_kprobe_config();
|
||||
let (filesystem_hiding, filesystem_config_exit, filesystem_config_error) =
|
||||
match kernel_boot_feature_enabled(FILESYSTEM_BOOT_FEATURE) {
|
||||
match optional_feature_enabled(FILESYSTEM_BOOT_FEATURE) {
|
||||
Ok(enabled) => (enabled, i32::from(!enabled), String::new()),
|
||||
Err(err) => (false, 2, err.to_string()),
|
||||
};
|
||||
|
|
@ -332,9 +332,7 @@ pub fn boot_load_kpm() -> Result<()> {
|
|||
}
|
||||
|
||||
if Path::new(APATCH_DIR).is_dir() {
|
||||
let filesystem_hiding = Some(kernel_boot_feature_enabled_or_default(
|
||||
FILESYSTEM_BOOT_FEATURE,
|
||||
));
|
||||
let filesystem_hiding = Some(optional_feature_enabled_or_default(FILESYSTEM_BOOT_FEATURE));
|
||||
log_android(
|
||||
"vpnhide",
|
||||
"kpm: APatch/FolkPatch runtime — deferring load to service activator",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
is_app_uid, pm_list_packages, pm_list_users, wait_for_pm_ready,
|
||||
};
|
||||
|
||||
pub const KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS: &str = Hook::FilesystemIfacePaths.name();
|
||||
pub const OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS: &str = Hook::FilesystemIfacePaths.name();
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -35,7 +35,7 @@ pub struct Settings {
|
|||
#[serde(default)]
|
||||
pub remember_superkey: bool,
|
||||
#[serde(default)]
|
||||
pub kernel_boot_features: BTreeSet<String>,
|
||||
pub optional_features: BTreeSet<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ fn kmod_projection_includes_the_optional_filesystem_hook() {
|
|||
let cfg = parse_canonical(
|
||||
r#"{
|
||||
"version": 1,
|
||||
"settings": { "kernelBootFeatures": ["filesystem_iface_paths"] },
|
||||
"settings": { "optionalFeatures": ["filesystem_iface_paths"] },
|
||||
"apps": { "com.example.full": { "native": true } }
|
||||
}"#,
|
||||
)
|
||||
|
|
@ -181,8 +181,8 @@ fn kmod_projection_includes_the_optional_filesystem_hook() {
|
|||
|
||||
assert!(
|
||||
cfg.settings
|
||||
.kernel_boot_features
|
||||
.contains(KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS)
|
||||
.optional_features
|
||||
.contains(OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS)
|
||||
);
|
||||
assert_eq!(
|
||||
project_native_with_resolver_for_family(&cfg, &resolver, NativeHookFamily::Kmod),
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ This document defines four things:
|
|||
It does **not** define the canonical JSON schema, package-to-UID projection,
|
||||
native-backend selection, boot order, APatch credential persistence, or the
|
||||
arguments used to load `.ko`/KPM artifacts. In particular,
|
||||
`settings.kernelBootFeatures` and the `filesystem_hiding=1` loader argument are
|
||||
`settings.optionalFeatures` and the `filesystem_hiding=1` loader argument are
|
||||
not control-v2 records. See [storage.md](storage.md) for desired state and
|
||||
activation, and [state.md](state.md) for paths, lifetimes, and boot sequencing.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ The single managed desired-state file.
|
|||
|
||||
- Format: JSON object, `version: 1`, `debug: Boolean`, `apps: { package ->
|
||||
roles }`, `settings.rememberSuperkey: Boolean`,
|
||||
`settings.kernelBootFeatures: [feature name]`,
|
||||
`settings.optionalFeatures: [feature name]`,
|
||||
`settings.autoHideVpnServices: Boolean`, `settings.autoHideVpnName: Boolean`,
|
||||
`settings.autoHiddenPackages: [package]`.
|
||||
- Roles per package: `java`, `native` (`Boolean` or hook-name array),
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ Shape (illustrative):
|
|||
},
|
||||
"settings": {
|
||||
"rememberSuperkey": false,
|
||||
"kernelBootFeatures": []
|
||||
"optionalFeatures": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -140,12 +140,12 @@ selection; the native wire carries the resolved `hookmask`.
|
|||
The `rememberSuperkey` boolean lives here (it is a preference, not a secret). The
|
||||
superkey itself does **not** (§6).
|
||||
|
||||
### 2.2 Optional kernel boot features
|
||||
### 2.2 Optional features
|
||||
|
||||
`settings.kernelBootFeatures` is a canonical **desired-state field**, not a
|
||||
control-v2 record. It names expensive or experimental kernel capabilities that
|
||||
must be selected before a backend installs its global hooks. Unknown names are
|
||||
preserved so newer versions can extend the set without a schema migration.
|
||||
`settings.optionalFeatures` is a canonical **desired-state field**, not a
|
||||
control-v2 record. It names expensive or experimental capabilities whose
|
||||
backend-specific activation is defined here. Unknown names are preserved so
|
||||
newer versions can extend the set without a schema migration.
|
||||
|
||||
The current projection is:
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ or KPatch-Next `kpatch kpm ctl0`.
|
|||
### Optional filesystem hook loader contract
|
||||
|
||||
Optional boot features are outside control v2. When canonical
|
||||
`settings.kernelBootFeatures` contains `filesystem_iface_paths`, the production
|
||||
`settings.optionalFeatures` contains `filesystem_iface_paths`, the production
|
||||
activator passes the exact KPM load argument `filesystem_hiding=1`. When it is
|
||||
disabled, the activator passes a null/absent argument; the explicit
|
||||
`filesystem_hiding=0` spelling is also accepted as disabled but is not emitted by
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ static int nr_targets;
|
|||
static uint32_t default_hookmask;
|
||||
static uint32_t active_hook_mask;
|
||||
/* Locked at module load: these globally hot hooks cannot be added or removed by
|
||||
* a later ctl0 config. The canonical kernelBootFeatures setting is projected
|
||||
* a later ctl0 config. The canonical optionalFeatures setting is projected
|
||||
* into KPM load args by the activator. */
|
||||
static bool filesystem_hiding_requested;
|
||||
|
||||
|
|
|
|||
|
|
@ -174,10 +174,10 @@ internal fun AppPickerScreen(
|
|||
nativeBackendId = targets.displayNativeBackendId,
|
||||
nativeHookFamily = nativeHookFamily,
|
||||
filesystemHidingEnabled =
|
||||
KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS in
|
||||
OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS in
|
||||
targets.canonicalConfig
|
||||
?.settings
|
||||
?.kernelBootFeatures
|
||||
?.optionalFeatures
|
||||
.orEmpty(),
|
||||
portsInstalled = targets.portsModuleInstalled,
|
||||
onToggle = { layer ->
|
||||
|
|
|
|||
|
|
@ -1576,10 +1576,10 @@ internal suspend fun loadDashboardState(
|
|||
|
||||
filesystemHidingDashboardMessage(
|
||||
desiredEnabled =
|
||||
KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS in
|
||||
OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS in
|
||||
targetsSnapshot.canonicalConfig
|
||||
?.settings
|
||||
?.kernelBootFeatures
|
||||
?.optionalFeatures
|
||||
.orEmpty(),
|
||||
sections = shellSnapshot,
|
||||
res = res,
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ internal fun FilesystemHidingSettingsSection() {
|
|||
targets
|
||||
?.canonicalConfig
|
||||
?.settings
|
||||
?.kernelBootFeatures
|
||||
?.optionalFeatures
|
||||
.orEmpty()
|
||||
val enabled = KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS in enabledFeatures
|
||||
val enabled = OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS in enabledFeatures
|
||||
val runtimeState =
|
||||
remember(enabled, rootSnapshot) {
|
||||
resolveFilesystemHidingState(
|
||||
|
|
@ -167,11 +167,11 @@ private fun writeFilesystemHidingSetting(enabled: Boolean): Int {
|
|||
base.copy(
|
||||
settings =
|
||||
base.settings.copy(
|
||||
kernelBootFeatures =
|
||||
optionalFeatures =
|
||||
if (enabled) {
|
||||
base.settings.kernelBootFeatures + KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS
|
||||
base.settings.optionalFeatures + OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS
|
||||
} else {
|
||||
base.settings.kernelBootFeatures - KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS
|
||||
base.settings.optionalFeatures - OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS
|
||||
},
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import org.json.JSONObject
|
|||
|
||||
internal const val CANONICAL_CONFIG_FILE = "/data/system/vpnhide_config.json"
|
||||
internal const val SUPERKEY_FILE = "/data/adb/vpnhide/superkey"
|
||||
internal val KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS = HookIds.Hook.FILESYSTEM_IFACE_PATHS.hookName
|
||||
internal val OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS = HookIds.Hook.FILESYSTEM_IFACE_PATHS.hookName
|
||||
|
||||
internal data class CanonicalConfig(
|
||||
val version: Int = 1,
|
||||
|
|
@ -18,7 +18,7 @@ internal data class CanonicalConfig(
|
|||
|
||||
internal data class CanonicalSettings(
|
||||
val rememberSuperkey: Boolean = false,
|
||||
val kernelBootFeatures: Set<String> = emptySet(),
|
||||
val optionalFeatures: Set<String> = emptySet(),
|
||||
val autoHideVpnServices: Boolean = true,
|
||||
val autoHideVpnName: Boolean = false,
|
||||
val autoHideExcludedPackages: Set<String> = emptySet(),
|
||||
|
|
@ -146,7 +146,7 @@ internal fun parseCanonicalConfig(raw: String): CanonicalConfig? {
|
|||
settings =
|
||||
CanonicalSettings(
|
||||
rememberSuperkey = settingsJson?.optBoolean("rememberSuperkey", defaultSettings.rememberSuperkey) == true,
|
||||
kernelBootFeatures = parseStringSet(settingsJson?.optJSONArray("kernelBootFeatures")),
|
||||
optionalFeatures = parseStringSet(settingsJson?.optJSONArray("optionalFeatures")),
|
||||
autoHideVpnServices =
|
||||
settingsJson?.optBoolean("autoHideVpnServices", defaultSettings.autoHideVpnServices)
|
||||
?: defaultSettings.autoHideVpnServices,
|
||||
|
|
@ -404,8 +404,8 @@ internal fun canonicalConfigJson(config: CanonicalConfig): String =
|
|||
append(" \"rememberSuperkey\": ")
|
||||
append(config.settings.rememberSuperkey)
|
||||
append(",\n")
|
||||
append(" \"kernelBootFeatures\": [")
|
||||
config.settings.kernelBootFeatures.toSortedSet().forEachIndexed { index, feature ->
|
||||
append(" \"optionalFeatures\": [")
|
||||
config.settings.optionalFeatures.toSortedSet().forEachIndexed { index, feature ->
|
||||
if (index != 0) append(", ")
|
||||
appendJsonString(feature)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ class FilesystemHidingDataTest {
|
|||
@Test
|
||||
fun `filesystem hiding remains opt in by default`() {
|
||||
assertFalse(
|
||||
KERNEL_BOOT_FEATURE_FILESYSTEM_IFACE_PATHS in
|
||||
CanonicalSettings().kernelBootFeatures,
|
||||
OPTIONAL_FEATURE_FILESYSTEM_IFACE_PATHS in
|
||||
CanonicalSettings().optionalFeatures,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class StorageConfigTest {
|
|||
},
|
||||
"settings": {
|
||||
"rememberSuperkey": true,
|
||||
"kernelBootFeatures": ["filesystem_iface_paths", "future_feature"],
|
||||
"optionalFeatures": ["filesystem_iface_paths", "future_feature"],
|
||||
"autoHideVpnServices": false,
|
||||
"autoHideVpnName": true,
|
||||
"autoHideExcludedPackages": ["com.vpn.false_positive"],
|
||||
|
|
@ -79,7 +79,7 @@ class StorageConfigTest {
|
|||
assertEquals(
|
||||
CanonicalSettings(
|
||||
rememberSuperkey = true,
|
||||
kernelBootFeatures = setOf("filesystem_iface_paths", "future_feature"),
|
||||
optionalFeatures = setOf("filesystem_iface_paths", "future_feature"),
|
||||
autoHideVpnServices = false,
|
||||
autoHideVpnName = true,
|
||||
autoHideExcludedPackages = setOf("com.vpn.false_positive"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue