|
Technique summary |
Technique |
Screen capture and recording callbacks |
Against |
Screen recording attacks |
Limitations |
Screen recording callback requires Android ≥15 and the DETECT_SCREEN_RECORDING permission |
Screen capture callback requires Android ≥14 and the DETECT_SCREEN_CAPTURE permission |
The screen recording callback will not trigger if the recording is started before the callback is registered, unless the application is paused and resumed |
Side effects |
None |
Recommendations |
Screen recording callback can be used for applications running on Android 15 and newer. Not recommended as a first line of defense, because it can be bypassed by starting recording before the application starts. |
Screen capture callback
Starting Android 14 you can register a callback that would trigger whenever a screen is captured.
// The callback function for screen capture
private void onScreenCaptureCallback() {
Toast.makeText(this, "Screen being captured!!", Toast.LENGTH_LONG).show();
}
// Register the callback when your activity starts
@Override
protected void onStart() {
super.onStart();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
registerScreenCaptureCallback(getMainExecutor(),
this::onScreenCaptureCallback);
}
}
// Unregister the callback when your activity stops
@Override
protected void onStop() {
super.onStop();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
try {
unregisterScreenCaptureCallback(this::onScreenCaptureCallback);
} catch(IllegalStateException e) {
System.err.println(e.getCause());
e.printStackTrace();
}
}
}
// The callback function for screen capture
private fun onScreenCaptureCallback() {
Toast.makeText(this, "Screen being captured!!", Toast.LENGTH_LONG).show()
}
// Register the callback when your activity starts
override fun onStart() {
super.onStart()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
registerScreenCaptureCallback(mainExecutor) { onScreenCaptureCallback() }
}
}
// Unregister the callback when your activity stops
override fun onStop() {
super.onStop()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
try {
unregisterScreenCaptureCallback { onScreenCaptureCallback() }
} catch (e: IllegalStateException) {
System.err.println(e.cause)
e.printStackTrace()
}
}
}
Screen recording callback
Android 15 provides a means to know when there is a change on the screen recording state. It is possible to register a callback to be called whenever there is a change. The callback receives the state, which can be compared with the values WindowManager.SCREEN_RECORDING_STATE_VISIBLE
and WindowManager.SCREEN_RECORDING_STATE_NOT_VISIBLE
. The callback can run on the main thread or in a background thread.
For example:
public void enableScreenDetection(Activity activity) {
Context context = activity.getApplicationContext();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
Log.d(TAG, "Enabling screen detection (only for Android 15)");
Executor executor = Executors.newSingleThreadExecutor(); // background thread
// Executor executor = context.getMainExecutor(); // main thread
windowManager.addScreenRecordingCallback(executor, state -> {
if (state == WindowManager.SCREEN_RECORDING_STATE_VISIBLE) {
showMessageOnToast(activity, "[!] Screen recording started!");
Log.d(TAG, "Recording");
} else if (state == WindowManager.SCREEN_RECORDING_STATE_NOT_VISIBLE) {
showMessageOnToast(activity, "[!] Screen recording stopped");
Log.d(TAG, "Not recording");
}
});
} else {
showMessageOnToast(activity, "Screen detection is only available from Android 15");
}
}
private void showMessageOnToast(Activity activity, String message) {
activity.runOnUiThread(() ->
Toast.makeText(activity,
message, Toast.LENGTH_SHORT).show());
}