fix(android): tamper-lock can't linger armed while protection is off (stuck "locked" UI)
Repro: after a reinstall / external VPN-revoke, `filter_enabled` flipped to false but `tamper_armed` stayed true. Result: buildDeviceState reported tamperLock:true purely from `tamper_armed` → UI mapped that to appDeletionLock:true → lockedIn:true → showed the green "protected & locked" card with no toggles → no way to reactivate. (The a11y service didn't block — handleProtectedSettingsBlock checks isProtectionEnabled — but it kept logging every settings-navigation, wasting CPU.) "Armed but disabled" is an invalid state. - RebreakAccessibilityService: top guard is now `if (!isTamperLockArmed() || !isProtectionEnabled()) return` — fully passive (no logging) whenever protection is off, regardless of a stale tamper flag. - RebreakProtectionModule.buildDeviceState: tamperLock = tamper_armed && filter_enabled. - RebreakProtectionModule.isVpnEffectivelyOn (revoke branch) and RebreakVpnService.onRevoke now clear `tamper_armed` together with `filter_enabled` — the two can't desync. Self-heals: opening the blocker page after the update re-fetches state → tamperLock:false → toggles back. Also: the tamper-block toast is now Lyra-voiced instead of a shield emoji (a real avatar image isn't possible — Android 11+ ignores Toast.setView() for app toasts; lyra-persona can refine the wording). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fc7a243c9b
commit
3c2aee7bda
@ -307,7 +307,10 @@ class RebreakProtectionModule : Module() {
|
||||
return mapOf(
|
||||
"vpn" to isVpnEffectivelyOn(ctx),
|
||||
"accessibility" to isAccessibilityServiceEnabled(ctx),
|
||||
"tamperLock" to isTamperLockArmed(ctx),
|
||||
// Ein armed-aber-Schutz-aus Tamper-Lock ist effektiv KEIN Lock — sonst
|
||||
// zeigt die UI „verriegelt" ohne dass der User je rauskommt (Desync-Fall:
|
||||
// `tamper_armed` noch true, aber `filter_enabled` schon false).
|
||||
"tamperLock" to (isTamperLockArmed(ctx) && isEnabledFlag(ctx)),
|
||||
"blocklistCount" to count,
|
||||
"blocklistLastSyncAt" to lastSyncAt,
|
||||
)
|
||||
@ -368,8 +371,14 @@ class RebreakProtectionModule : Module() {
|
||||
val flag = isEnabledFlag(ctx)
|
||||
Log.d(TAG, "isVpnEffectivelyOn: live=$live, prepareIntent=${intent != null}, flag=$flag")
|
||||
if (intent != null) {
|
||||
// Permission entzogen → definitely off
|
||||
if (flag) prefs(ctx).edit().putBoolean(KEY_ENABLED, false).apply()
|
||||
// Permission entzogen → definitely off. Auch den Tamper-Lock mit-disarmen,
|
||||
// sonst bleibt der State desynct (tamper armed, Schutz aus) → „verriegelt"-UI.
|
||||
if (flag) {
|
||||
prefs(ctx).edit()
|
||||
.putBoolean(KEY_ENABLED, false)
|
||||
.putBoolean(KEY_TAMPER_ARMED, false)
|
||||
.apply()
|
||||
}
|
||||
return false
|
||||
}
|
||||
return live || flag
|
||||
|
||||
@ -52,13 +52,14 @@ class RebreakAccessibilityService : AccessibilityService() {
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
|
||||
if (event == null) return
|
||||
|
||||
// Globaler Kill-Switch: Dieser Service tut NUR etwas, wenn der User den
|
||||
// App-Lock explizit armed hat. Ist er nicht armed (Default, inkl. frischem
|
||||
// Onboarding und nach einem abgelaufenen Cooldown der `disarmTamperLock`
|
||||
// aufgerufen hat) → vollständig passiv. Der a11y-Service kann sich nicht
|
||||
// selbst deaktivieren, also ist das hier die einzige Stelle wo wir ihn
|
||||
// stilllegen.
|
||||
if (!isTamperLockArmed()) return
|
||||
// Globaler Kill-Switch: Dieser Service tut NUR etwas, wenn der Schutz
|
||||
// aktiv ist (`filter_enabled`) UND der User „App-Lock" explizit armed hat
|
||||
// (`tamper_armed`). Beides muss stimmen — sonst vollständig passiv (auch
|
||||
// kein Logging). Deckt ab: frisches Onboarding, abgelaufener Cooldown
|
||||
// (disarmTamperLock), und den Desync-Fall „tamper noch armed aber Schutz
|
||||
// aus" (z.B. VPN extern revoked). Der a11y-Service kann sich nicht selbst
|
||||
// deaktivieren, also ist das hier die einzige Stelle wo wir ihn stilllegen.
|
||||
if (!isTamperLockArmed() || !isProtectionEnabled()) return
|
||||
|
||||
val pkg = event.packageName?.toString() ?: return
|
||||
if (pkg !in WATCHED_SETTINGS_PACKAGES) return
|
||||
@ -131,10 +132,14 @@ class RebreakAccessibilityService : AccessibilityService() {
|
||||
performGlobalAction(GLOBAL_ACTION_BACK)
|
||||
}, 200)
|
||||
|
||||
// Toast in Lyra's Stimme statt eines kühlen Shield-Icons (Android 11+
|
||||
// ignoriert setView/Custom-Layouts für App-Toasts → kein echtes Avatar-
|
||||
// Bild möglich; deshalb signiert der Text mit „Lyra:"). lyra-persona darf
|
||||
// den Wortlaut gern noch feinschleifen.
|
||||
mainHandler.post {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
"🛡 Diese Einstellung ist während des Schutzes gesperrt",
|
||||
"Lyra: Das ist während deines Schutzes gesperrt 💛 Wenn du wirklich raus willst, geht das in der App.",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
}
|
||||
|
||||
@ -162,9 +162,21 @@ class RebreakVpnService : VpnService() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
/** Persistierte Flag (gleicher Storage wie der Plugin) clearen — nur
|
||||
* bei explizitem User-Revoke (System-Settings-Toggle aus). */
|
||||
private fun clearEnabledFlag() = setEnabledFlag(false)
|
||||
/** Persistierte Flags (gleicher Storage wie der Plugin) clearen — nur bei
|
||||
* explizitem User-Revoke (System-Settings-Toggle aus). Mit `filter_enabled`
|
||||
* geht auch `tamper_armed` weg: ein Tamper-Lock ohne aktiven Schutz ist
|
||||
* Unsinn und würde den State desynchen → „verriegelt"-UI ohne Ausweg. */
|
||||
private fun clearEnabledFlag() {
|
||||
try {
|
||||
applicationContext.getSharedPreferences("rebreak_filter_prefs", Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean("filter_enabled", false)
|
||||
.putBoolean("tamper_armed", false)
|
||||
.apply()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "clearEnabledFlag failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/** Setzt die Plugin-Prefs-Flag synchron mit dem tatsächlichen Service-State. */
|
||||
private fun setEnabledFlag(value: Boolean) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user