How Does Android 15 Protect Against Screen Spying?
New system, new security features
The source code for Android 15 was released on September 3rd, as announced on the Android Developers Blog, and Google started rolling out Android 15 for Pixel devices on October 15th.
Improvements in privacy and security features have been announced and commented on in many articles [security.googleblog.com, android-developers.googleblog.com, androidauthority.com, kaspersky.com]. Two of the new features address protection against screen spying. Now, as a developer, you can add a callback so the application knows when the screen is being recorded. Moreover, the system will automatically protect windows containing passwords from appearing in a screen recording or casting.
In this article, we discuss the advantages that Android 15 brings with the new anti-screen-spying features as well as its limitations. We will explain and demonstrate how security problems may occur despite the Android 15 security advances and how our standing recommendations [secure_flag, secure-in-app-keyboard] are still the best protection against unwanted screen spying. You will find guidance and the necessary code snippets in our Security Research Center so you can adapt your protections accordingly.
Do we still need FLAG_SECURE
on Android 15?
Benefits of the new security features
1. Visibility into when screen recording starts and stops
The recording state can be used to identify whether the screen is being recorded or not. There will be two possible states: recording or not recording.
It is possible to register a callback that will be called whenever the recording state changes. In order to do so, we first need to declare permission in the Android manifest:
<uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
Now we are ready to implement our callback, screenRecordingCallback
, and register it by calling the addScreenRecordingCallback
method:
Executor executor = Executors.newSingleThreadExecutor();
Consumer screenRecordingCallback = state -> {
if (state == WindowManager.SCREEN_RECORDING_STATE_VISIBLE) {
Toast.makeText(this, "[!] Screen recording started!",
Toast.LENGTH_SHORT).show());
} else if (state == WindowManager.SCREEN_RECORDING_STATE_NOT_VISIBLE) {
Toast.makeText(this, "[!] Screen recording stopped!",
Toast.LENGTH_SHORT).show());
}
};
windowManager.addScreenRecordingCallback(executor, screenRecordingCallback);
The result can be observed in the animation below:
This is a great improvement, as the app can now know when it is being recorded and react accordingly. This augments the screenshot detection feature that was already available on Android 14 [developer.android.com, medium.com]. Moreover, a screen recording callback will also detect any app using the MediaProjection
API to take screenshots. Screenshots via buttons cannot be detected via a screen recording callback but can be detected via a screen capture callback on Android 14.
2. Passwords are sheltered from prying eyes
In Android 15, when the system detects that a screen recording session is ongoing, it will automatically protect windows containing a visible password from appearing in the recording. In order to see this password protection approach in action, we used a setup with only one device (an emulator), which is shown below on the left. To the right, the screen of the device being captured through Scrcpy is shown. The system does not detect Scrcpy as a screen recorder. For the screen recorder, we used AZ Screen Recorder. As you can see below, even though Scrcpy is consistently enabled, protections were only triggered when we enabled AZ Screen Recorder, which was detected. At this point, you can see that the screen on the right becomes black if a password field exists.
Residual security risks to consider
1. How does your organization define screen recording?
We have just seen that Scrcpy is not recognized as a screen recorder. That’s also the case for the Android /system/bin/screenrecord
binary. It seems that, for Android 15, screen recorders are only identified as those that rely on the MediaProjection API.
2. Are you sure you can catch everything?
We mentioned above that the screenRecordingCallback
executes when there is a change in the recording state. Now, what happens if the screen was already being recorded when the application starts? You got it: the callback will not execute. Because it has not witnessed any change in the recording state since it happened when the app was not running yet, the callback does not identify the screen recording. This means that the app’s screen could be recorded without the app being aware of the recording! A positive aspect is that if the app is “minimized” and then resumed, the callback will execute. This partially mitigates the risk.
The good thing is that, as the system is aware of the ongoing screen sharing, password windows are still protected.
Let’s see it! Observe, in the animation below, that there is no message or alert that the screen is being recorded. However, the login screen is not shared, and we see the message saying that it was hidden as this view contains a password.
3. When things become serious
When looking at the developer options, we can find one to “disable screen share protections,” which is described as “turn off system protections for sensitive app content for upcoming screen share sessions.” Note the word “upcoming,” as this is relevant for the attacks we’ll explain later.
Basically, if this option is enabled, our passwords won’t be safe anymore. We can think of a user being tricked, through social engineering, into enabling this option. But there is something else, this option can also be enabled with shell privilege (if the user is convinced to enable their device’s developer mode) or with root privilege. It can be achieved, for instance, by executing any of the commands below:
# content insert --uri content://settings/global --bind name:s:disable_screen_share_protections_for_apps_and_notifications --bind value:i:1
Or
# settings put global disable_screen_share_protections_for_apps_and_notifications 1
Let’s see how an attack could occur. In order to evade the screen recording callback, the attacker will start recording before the application starts.
- The attacker disables the protection.
- The attacker starts recording the screen.
- The application starts.
- The screen recording callback is not executed (as the recording state has not changed since the app started).
- The user goes to the login screen, as the protection is disabled, the screen is shared.
Some mitigations can be implemented. For instance, a content observer could be used to watch the protection status. The developer could also have a thread periodically checking the state of the protection. If the protection is found to be disabled, these mechanisms will react accordingly, e.g., showing a warning to the user.
Let’s see it:
Sadly, both mechanisms can be bypassed by following the sequence below:
1. Kill the app.
2. Disable the protection.
3. Start a screen recording session.
4. Re-enable the protection.
Step (1) allows an attacker to exploit the fact that the screen recording callback won’t execute if the screen was already being recorded when the app started. Step (2) makes the screen recording session started in Step (3) go unnoticed, Step (4) prevents the settings checks from triggering. Thus, when the app starts next, it will not notice the attack, and passwords will be leaked through the screen, as the protection will be disabled.
The attack is shown in the animation below. Note that, when we can see the login screen in the Scrcpy projection (to the right), it means that the password window is not being protected.
One point of good news: if the app goes to the background and then returns to the foreground, the screen recording callback will execute and detect the attack. This is shown in the animation below:
The right approach
The new features to protect apps' privacy from screen spying are effective and useful in most situations. You can now adapt the behavior of your application to react to the event of having another app watching your app's screen and passwords, and, most of the time, they will be protected. However, we have seen that sensitive data can still be leaked through the screen, especially on devices with ADB enabled or when malware has root privilege. This is why a multilayered security approach is needed. The new features offered by Android should be combined with FLAG_SECURE
to protect sensitive views. So, coming back to our initial question, "Do we still need FLAG_SECURE
?" The answer is yes, given the examples shared throughout this blog. Moreover, most users won’t have an Android 15 device in the coming years, making FLAG_SECURE
important for legacy devices.
Ready to get started? Check out our latest updates on the Security Research Center to learn about:
Screen Recording Callback
Screen Sharing Security Settings Monitoring