Accessibility service
Haven uses an AccessibilityService. That is a powerful API, so this page is precise about what it is used for, what it reads, what it ignores, and what happens to what it reads.
Why an accessibility service is necessary#
Three of Haven's four protection layers cannot be built any other way on Android:
| Layer | What it needs | Why nothing else works |
|---|---|---|
| Gambling-app block | To know which app came to the foreground | There is no other API that reports the foreground app in real time without usage-stats access, which is broader and retrospective |
| Financial protection | The same signal, for user-chosen money apps | As above |
| Blocked-site redirect | The address bar of a known browser | A DNS block produces a cold browser error page; there is no way to replace it from outside the browser |
| Page-content watch | The visible text and structure of a browser page | The alternative — inspecting network traffic — would require breaking TLS, which Haven will not do |
That last row is the important one. The accessibility route reads what is already on the user's own screen, on the user's own device. The alternative would be intercepting encrypted traffic. Haven considers the accessibility route strictly less invasive, and it is an architectural rule of the project that TLS is never touched.
What the service is configured to receive#
<accessibility-service
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault|flagReportViewIds"
android:canRetrieveWindowContent="true"
android:description="@string/financial_accessibility_description"
android:notificationTimeout="50" />- Window-state changes tell Haven which app is in the foreground.
- Window-content changes, together with content retrieval, let Haven read the address bar and the visible page text — in known browser packages only, checked in code.
- No gesture dispatch, no global actions to manipulate other apps, no key-event filtering, no touch exploration.
What is read, and what is ignored#
| Read | Scope |
|---|---|
| The foreground package name | Every app |
| That package's display label | Once per unseen package, from the device's own package manager |
| The address bar text | Known browsers only |
| The visible page text and structure | Known browsers only, and only when the domain rules neither block nor allow the host |
| Ignored entirely |
|---|
| The window content of every non-browser app |
| Banking, payment and crypto apps — only the fact that one is in the foreground is used |
| Messaging apps, email, notes, photos, health apps, everything else |
| Passwords and form fields — Haven never harvests input fields |
The address bar is a special case: it is kept in the tree so page structure is measured correctly, but its text is stripped before scoring. It contains user input or someone else's link, and treating it as page content would let a link get any domain permanently blocked. See Detection layers.
What happens to what is read#
This is the part that matters most:
- Page text is inspected in memory and discarded. It is never written to storage and never transmitted anywhere.
- Address-bar text is never stored. It is compared against the on-device rules and dropped.
- Nothing read through this service is sent off the device. Ever.
- The only thing that outlives a scan is a matched domain name, which is written to the local block log and, for a confirmed catch, to the user's own filters — where they can see it and remove it.
Every decision is made by pure, on-device code. No screen content is uploaded for classification.
The user-facing description#
Android shows this text in the accessibility settings screen, in the app's chosen language:
Gives you a calm pause when you open a bank, payment or crypto app that you picked. In your browser, it replaces the error page of a blocked site with a calm moment instead. It only reads which app is open and — only in known browsers — the web address and the text you can see on the page, so it can spot blocked sites it does not know yet. Page text is never saved and never sent anywhere. Only a matched website name goes into your own filters and log, on this phone.
Consent, revocation and honesty#
- The user grants this explicitly, in Android's own settings. Haven cannot grant it itself and does not try.
- It can be revoked at any time from the same screen. Haven does not attempt to detect, discourage or prevent revocation.
- Haven checks whether the service is actually running and says so in the interface. A protection app that looks on while it is silently inert is worse than one that is honestly off — so the App-check screen will name the missing switch rather than promise that a flagged app "stays closed".
- Android silently revokes the permission on every app update. Haven therefore re-offers it after an update rather than treating an old "not now" as permanent.
Performance discipline#
Because the event callback is hot, it is written to stay cheap: the settings it needs are observed reactively and cached in memory rather than read from storage, the cheap checks (is this a browser, is protection on) run first, per-package verdicts are memoised against the rule-set version, and a page scan walks the accessibility tree exactly once under a hard node budget.
Compliance summary#
| Google Play accessibility requirement | Haven |
|---|---|
| The service must be used for accessibility-adjacent functionality that benefits the user | It blocks gambling content and adds a reflection pause for a user who asked for both |
| Prominent disclosure of what it does | Shown before the permission is requested, and in the system description above |
| No collection of personal or sensitive data through the service | Page text is discarded; nothing read is transmitted |
| No use for advertising, analytics or profiling | No analytics or advertising SDK exists in the app |
| The user must be able to disable it | From Android settings at any time, without interference |