Accessibility services on Android are currently one of the weakest points of the operating system from the security standpoint. In case your app has high security and privacy requirements, you should consider implementing a mechanism to hide the sensitive data in case an untrusted screen reader is present.
The best and most future proof way to implement protection against untrusted screen readers is to use App Shielding / RASP technology. However, there is also an easier (yet, not so robust) workaround. This post explains the gist on how Android apps can prevent leakage of the sensitive data from the app via the accessibility services.
1. Test Your App
To assess the situation of your app, you can test it with our Screen Logger testing app. The app logs everything potential malware can see in your app by using the accessibility services. In case you do not see any sensitive data, you can have a coffee…
2. Prepare an Accessibility Monitoring Service
First, you need to identify that there are non-whitelisted screen readers — apps with the accessibility features — present and active. To do so, you should prepare an isolated class, as a singleton (or using any other more suitable method for keeping a single instance), that perform the following steps:
1. Scan the active accessibility readers using:
AccessibilityManager::getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK).More info is in Android Documentation.
2. Check each item against the app whitelist you prepare. You can build your own whitelist, or drop us a line for ours…
3. In case there is an active screen-reader that is not present on a whitelist, store the flag indicating this situation in an internal variable and fire a notification that can be intercepted by all classes — implementing the Observable interface is a good candidate for this.
3. Implement a PrivateContentView
After you detect any untrusted screen reader, you need to make sure that the sensitive data does not leak from the app via accessibility services. You can assure this by implementing a special PrivateContentView class as a View subclass that sets the IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS flag. This way, you can hide the contents of all views that are inside of it whenever needed. You should then use PrivateContentView to wrap any sensitive data you need to protect.

This view should do the following:
1. Have IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS by default, to make sure the data does not leak after the app starts.
2. Check the stored value of the accessibility monitoring service, respond to the notification fired from the accessibility monitoring service by checking the stored value.
3. Setting an IMPORTANT_FOR_ACCESSIBILITY_AUTO flag in case there is no untrusted screen reader present, to enable proper app functionality to people with accessibility requirements.
4. How About Widgets?
(Update)
In case your Android app uses widgets, the situation with protecting sensitive data is a bit more complex. Since all content is displayed in the RemoteView, some attributes, such as accessibility flags, cannot be updated dynamically via code (unless you consider hacky reflection).
However, you can still set an android:importantForAccessibility attribute in the widget XML layout file. Based on whether an untrusted screenreader is active or not, you can then update your RemoteView with the correct layout during any update call.
val views = RemoteViews(
context.packageName, R.layout.test_app_widget
)
appWidgetManager.updateAppWidget(appWidgetId, views)Impact on the users
Because of the way accessibility services work on Android, it is not possible to block screen readers selectively. Users with accessibility requirements (blind, limited hand movement, …) who installed an app that is not on the whitelist will need to disable the accessibility service for that particular app in settings. In case more users have an issue with the same app, evaluate it and consider adding it on the whitelist.