Screen capture and recording callbacks

  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()); }

Guardsquare

Table of contents