Untrusted Touch Events in Android
In Android 12, we are making changes to enhance app and platform security to provide our users with a safer experience. After this article, check out our other blog posts that cover security and privacy.
Touch input is the primary means of interaction with an app in Android. Android 12 includes additional measures to ensure that touch events are properly passed to the intended apps to ensure an intuitive and safe UX. Specifically, Android 12 prevents touch events from being delivered to apps if these touches pass through a window from a different app. This behavior change applies to all apps running on Android 12, regardless of `targetSdkVersion`. This helps ensure that users can see what they are interacting with. Read on to learn about alternatives, to see if your app will be affected and how you can test for it.
Use special-purpose APIs if possible
Before checking if your use-case is affected, it’s good to evaluate if your app can utilize one of the following APIs in Android. These easy to use APIs prevent you from having to worry about these restrictions altogether since they are partly controlled by the system, and as such can be trusted. Consider the following:
- Bubbles: Bubbles float on top of other app content, follow the user wherever they go, and can be expanded to reveal app functionality and information.
- Picture-in-Picture (PIP): PIP allows the app to display content in a small window, pinned to a corner of the screen, while the user is navigating between apps or browsing content on the main screen. The user can drag the PIP window around and can click on it to expand or close.
- Notifications: Notifications are the standard way to provide the user with reminders, messages from other people, or other timely information from your app while minimally disrupting use of the device. Users can tap the notification to open your app, or take an action directly from the notification.
- Snackbars and Toasts: If you need to display a message for a brief period of time inside your app, take a look at Snackbars. If you need to display that message while your app is in the background, see if Toasts fit your use-case.
If your use-case fits one of these APIs, it’s strongly recommended to use them. Not only are they easier to use, they are safer, and the user is already familiar with most of them.
Does this affect me?
If your app can’t utilize one of the APIs above and lets touch events pass through its windows, it’s possible that, on Android 12, they will not go through to whatever is underneath as intended.
Examples include, but aren’t limited to, the following:
- Windows of type
TYPE_APPLICATION_OVERLAY
that useFLAG_NOT_TOUCHABLE
. - Activity windows that use
FLAG_NOT_TOUCHABLE
.
If you’re using FLAG_NOT_TOUCHABLE
you’re likely affected, unless your use-case falls into one of the exemptions below:
- Interactions within your app. The overlay only appears above your app.
- Trusted windows. These windows include (but aren’t limited to) Accessibility windows, Input method editor (IME) windows and Assistant windows.
- Invisible windows. The window’s root view is GONE or INVISIBLE.
- Completely transparent windows. The alpha property is 0.0 for the window.
- Sufficiently translucent TYPE_APPLICATION_OVERLAY windows. The window is of type
TYPE_APPLICATION_OVERLAY
and its alpha property is equal to or belowInputManager.getMaximumObscuringOpacityForTouch()
. This value is currently 0.8 as of Developer Preview 3, but might change before final release. In case of multiple overlapping windows of such type their combined opacity is used.
If your use-case doesn’t fall into the list above, touches will be blocked. If this works for your use case, consider removing FLAG_NOT_TOUCHABLE
to remove the intention of letting touches pass-through. If you need to let touches pass-through, you’ll have to adjust your code to fit one of the exemptions above. The next section covers, examples of common patterns that will have to be changed:
Windows with transparent background and no UI
Apps that display some UI in a window with a transparent background can hide their UI at the view level at certain times while adding FLAG_NOT_TOUCHABLE
so the user can interact with what’s behind.
If, like in the diagram above, the app merely hid the UI, either by removing child views or changing their visibility, and added FLAG_NOT_TOUCHABLE
so the user could interact with what’s behind, this won’t work on Android 12 anymore (note the difference from exemptions mentioned before — here we are changing internal views, not the window), since touches to other apps behind will be blocked. Fixing that is easy in this case; you can either:
- Remove the window using
WindowManager.removeView()
, passing the root view. - Make the window invisible by calling
View.setVisibility()
withView.GONE
orView.INVISIBLE
on the root view. - Set the opacity of the window to 0.0 via
LayoutParams.alpha
.
Whenever you need to display that UI again, you just need to reverse the action above.
Unnecessarily large windows
Apps might want to display some small UI while still allowing the user to interact with what is behind the window. Previously, an app could achieve that by simply using a fullscreen window and marking it with FLAG_NOT_TOUCHABLE
, such as in figure 1:
Note that touches that go through the actual UI element would in this case pass-through to the windows behind in previous OS versions. The first suggestion in this case is to take a look at the Toast API to see if that fits your purpose. If not, the solution here is also straightforward and illustrated on the image on the right: you just need to reduce the window boundaries to the actual UI and use FLAG_NOT_TOUCH_MODAL
, at which point you probably want to remove FLAG_NOT_TOUCHABLE
too.
Now touches outside your UI will go directly to the window behind and won’t be blocked.
Translucent windows
If you are using a TYPE_APPLICATION_OVERLAY
window and absolutely need to let touches pass-through it while displaying content, you’ll have to reduce the opacity such that the user is able to reasonably see what they are touching behind the window.
You’ll have to reduce your opacity at the window level, merely changing the opacity of views doesn’t work. You can use LayoutParams.alpha
to reduce the opacity to a value below or equal to InputManager.getMaximumObscuringOpacityForTouch()
like shown on the right. This value is currently 0.8 but might change before the Android 12 final release.
Now, provided you don’t have multiple windows from your app that overlap each other, touches will go through to the window behind. For more details about overlapping windows, take a look at the documentation of FLAG_NOT_TOUCHABLE
.
Accessibility services
As a connected accessibility service, it’s possible to create windows of type TYPE_ACCESSIBILITY_OVERLAY
, which are trusted and thus are exempt from the restrictions described earlier. In order to create such windows, just use the context of your AccessibilityService
to obtain a WindowManager
via getSystemService()
to create said windows.
How to test if your app is affected
If a touch action is blocked by the system, you will see the following message in Logcat: Untrusted touch due to occlusion by PACKAGE_NAME.
Next steps
Check out the documentation if you want to learn more about untrusted touch events and their exceptions.
Happy coding!