Reduce per-process hook overhead for resource-constrained devices

Every scoped app's cold start pays whatever cost the hook installation
itself takes, so it's the one place worth trimming here — there's no
standing service or background work anywhere else in the module.

Previously any enabled feature caused both Settings.Secure and
Settings.Global to get hooked with the full combined key set, even though
each key only ever lives on one of them (accessibility/mock-location keys
are Secure-only, adb/dev-options keys are Global-only). Splitting the key
maps by actual canonical class means an app that only needs e.g. ADB+dev-
options hidden skips hooking Settings.Secure entirely, roughly halving the
reflective hook-install calls for that case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Claude Sonnet 5
2026-08-06 21:46:16 +06:00
parent 242d03cea0
commit fbdf5dedf6
@@ -14,17 +14,34 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
private const val SELF_PACKAGE = "space.bdeshi.veil" private const val SELF_PACKAGE = "space.bdeshi.veil"
private val NULL_STRING_KEYS_BY_FEATURE = mapOf( // Keyed by the Settings class each key actually lives on, so we only ever install hooks on
// the one class that could plausibly be asked about a given feature — never both.
private val SECURE_NULL_STRING_KEYS_BY_FEATURE = mapOf(
Features.ACCESSIBILITY to setOf("enabled_accessibility_services"), Features.ACCESSIBILITY to setOf("enabled_accessibility_services"),
) )
private val ZERO_VALUE_KEYS_BY_FEATURE = mapOf( private val SECURE_ZERO_VALUE_KEYS_BY_FEATURE = mapOf(
Features.ACCESSIBILITY to setOf("accessibility_enabled", "touch_exploration_enabled"), Features.ACCESSIBILITY to setOf("accessibility_enabled", "touch_exploration_enabled"),
Features.ADB to setOf("adb_enabled", "adb_wifi_enabled"),
Features.DEV_OPTIONS to setOf("development_settings_enabled"),
Features.MOCK_LOCATION to setOf("mock_location", "allow_mock_location"), Features.MOCK_LOCATION to setOf("mock_location", "allow_mock_location"),
) )
private val GLOBAL_ZERO_VALUE_KEYS_BY_FEATURE = mapOf(
Features.ADB to setOf("adb_enabled", "adb_wifi_enabled"),
Features.DEV_OPTIONS to setOf("development_settings_enabled"),
)
private fun keysForEnabledFeatures(
enabled: Set<String>,
byFeature: Map<String, Set<String>>,
): Set<String> {
if (enabled.isEmpty() || byFeature.isEmpty()) return emptySet()
val result = mutableSetOf<String>()
for (feature in enabled) {
byFeature[feature]?.let { result.addAll(it) }
}
return result
}
class VeilHook : IXposedHookLoadPackage { class VeilHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: LoadPackageParam) { override fun handleLoadPackage(lpparam: LoadPackageParam) {
@@ -38,11 +55,15 @@ class VeilHook : IXposedHookLoadPackage {
hookAccessibilityManager(lpparam) hookAccessibilityManager(lpparam)
} }
val nullStringKeys = enabled.flatMap { NULL_STRING_KEYS_BY_FEATURE[it].orEmpty() }.toSet() val secureNullKeys = keysForEnabledFeatures(enabled, SECURE_NULL_STRING_KEYS_BY_FEATURE)
val zeroValueKeys = enabled.flatMap { ZERO_VALUE_KEYS_BY_FEATURE[it].orEmpty() }.toSet() val secureZeroKeys = keysForEnabledFeatures(enabled, SECURE_ZERO_VALUE_KEYS_BY_FEATURE)
if (nullStringKeys.isNotEmpty() || zeroValueKeys.isNotEmpty()) { if (secureNullKeys.isNotEmpty() || secureZeroKeys.isNotEmpty()) {
hookSettingsClass("android.provider.Settings\$Secure", lpparam, nullStringKeys, zeroValueKeys) hookSettingsClass("android.provider.Settings\$Secure", lpparam, secureNullKeys, secureZeroKeys)
hookSettingsClass("android.provider.Settings\$Global", lpparam, nullStringKeys, zeroValueKeys) }
val globalZeroKeys = keysForEnabledFeatures(enabled, GLOBAL_ZERO_VALUE_KEYS_BY_FEATURE)
if (globalZeroKeys.isNotEmpty()) {
hookSettingsClass("android.provider.Settings\$Global", lpparam, emptySet(), globalZeroKeys)
} }
if (Features.ROOT_APPS in enabled) { if (Features.ROOT_APPS in enabled) {