- with-rebreak-protection-android plugin now copies the source
accessibility_service_config.xml via withDangerousMod instead of generating
it from a string. Eliminates the silent regression where prebuild wrote
flagReportViewIds + missing packageNames, leaving Samsung's content scan
unable to read OEM dialogs.
- ProtectionOnboardingSheet refresh() now calls activateFamilyControls()
once a11y is detected as enabled, so armTamperLock() actually runs.
Previously the sheet auto-completed on getDeviceState() alone, leaving
tamper_armed=false and the service permanently passive.
- RebreakProtectionModule.isAccessibilityServiceEnabled() now trusts the
AccessibilityManager list as authoritative when AM is available (even when
empty). Settings.Secure fallback only kicks in if AM is null/exception.
Fixes the banner falsely showing "Schutz aktiv" when the system has
unbound the service but ENABLED_ACCESSIBILITY_SERVICES still holds the id.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
After an APK reinstall (or an OS low-memory kill that START_STICKY didn't recover
promptly), the VpnService dies but `filter_enabled` stays true. isVpnEffectivelyOn
then reports vpn:true (from the flag) → tamperLock:true → lockedIn:true → the green
"protection active" card with no toggles, while in reality nothing is filtering.
New native reconcileVpn(): if `filter_enabled` && !RebreakVpnService.isRunning &&
VpnService.prepare()==null → startVpnService(). Wired into _layout.tsx enforceProtection()
(runs on launch / foreground / 15s poll), called before reading combined state. No-op
on iOS/web. If the VPN consent was revoked, isVpnEffectivelyOn already clears the flag,
so that case self-resolves too.
Net behavior: while `filter_enabled` is true (user hasn't exited via the cooldown),
the app keeps the VPN alive. Exiting still goes through the cooldown → forceDisable →
filter_enabled=false → reconcile leaves it off. DiGA-compliant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
The AccessibilityService used to also do a browser-address-bar filter (read the
URL bar of Chrome/Firefox/etc., hash-match against blocklist.bin, GLOBAL_ACTION_BACK
on a hit) as a "layer 2" alongside the VpnService DNS filter. That's redundant
(the VPN catches everything network-level, in browsers AND apps), fragile (per-browser
view-IDs), and produced ghost-blocks (VPN off, a11y still blocking sites). The DNS
filter is the protection; the a11y service's only real value-add is tamper-resistance.
So the a11y service now does ONLY the tamper-lock, and only when the user has armed
"App-Lock": block opening protection-critical settings (disable the ReBreak VPN,
uninstall the app, disable the a11y service itself). Top-level guard is now simply
`if (!isTamperLockArmed()) return` — when App-Lock isn't armed the service is fully
passive. Getting out is still via the regular deactivation cooldown (which disarms
the tamper-lock and stops the VPN).
- RebreakAccessibilityService.kt: removed browser-URL extraction, BROWSER_PACKAGES,
URL_BAR_IDS, hashList loading, throttle bookkeeping, the block-toast. Kept the
settings-watchdog (it already covered VPN settings via VpnSettings/vpndialogs +
the vpn-page keyword cluster) and adjusted its keyword lists to the new a11y
service summary (old summary kept as a legacy fallback for stale installs).
- accessibility_service_config.xml: dropped browser packages + flagRequestEnhancedWebAccessibility.
- strings.xml (de+en): a11y permission copy reframed — it safeguards the VPN/uninstall,
it doesn't filter your browser; ends with "you can always exit via the cooldown".
- lib/protection.ts: comment-only (activateFamilyControls logic unchanged).
- locales de/en: App-Lock card copy ("Familienzugriff aktiv" → "Verriegelt — ...",
"...ReBreak oder den Filter im Impuls abschaltest"), genericised the iOS Screen-Time
error string.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
apps/rebreak-native/.gitignore had bare `ios/` + `android/` patterns meant for the
Expo-prebuild output dirs — but with no leading slash they also matched
modules/rebreak-protection/{android,ios}, so the entire custom expo native module
(RebreakProtectionModule.kt, RebreakAccessibilityService.kt, RebreakVpnService.kt,
the DNS filter, the iOS NEFilter extension, podspec, ...) was never tracked. A
fresh clone / CI / `git clean` would lose it.
Anchor the prebuild patterns (`/ios/`, `/android/`), keep ignoring the module's
build artifacts (build/, .cxx/, .gradle/, Pods/), and commit the source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>