A password reveal, recovery seed, medical result, or payment authorization screen is one of those places where a screenshot can outlive the moment by years. Android gives apps a strong platform signal for these windows, but it is not a force field. Use it as one layer in a threat model that also covers logs, notifications, clipboard, accessibility, overlays, backups, and the very ordinary camera pointed at the device.

Choose blocking, detection, or redaction

  • Block the entire window → WindowManager.LayoutParams.FLAG_SECURE (API 1+).

  • Detect certain user screenshots without blocking → Android 14 Activity.ScreenCaptureCallback plus DETECT_SCREEN_CAPTURE.

  • Mark selected views sensitive during eligible screen sharing → Android 15 View.setContentSensitivity.

  • Protect a separately composed SurfaceViewSurfaceView.setSecure(true) before attachment.

  • Prevent playback audio capture → a separate audio-capture policy such as android:allowAudioPlaybackCapture="false"; FLAG_SECURE is visual.

Kotlin: secure the Activity window

SensitiveActivity.ktkotlin
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
 
class SensitiveActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
        setContentView(R.layout.activity_sensitive)
    }
}

Why the flag comes before content

  • FLAG_SECURE belongs to the Activity’s Window, not an individual XML view.

  • Adding it before setContentView reduces the chance of a frame appearing before protection is active.

  • The flag stays set for that Window until it is cleared or the Window is destroyed.

  • Screenshots, MediaProjection-based capture, casting, and non-secure display behavior are enforced by the platform compositor.

  • No runtime permission or user prompt is required for FLAG_SECURE.

Java: the same window-level policy

SensitiveActivity.javajava
import android.os.Bundle;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
 
public final class SensitiveActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
        setContentView(R.layout.activity_sensitive);
    }
}

The Java and Kotlin behavior is identical

  • getWindow() retrieves the Activity window managed by Android.

  • addFlags combines the secure bit with existing window flags instead of replacing them.

  • FLAG_SECURE has constant value 0x00002000, but always use the named constant.

  • This protects dialogs only when their Window is secure; inspect independently created windows and third-party SDK UI.

  • A WebView inside the secured Activity is covered because the containing Window is secure.

Enable protection only for sensitive destinations

Blanket protection can make bug reports, accessibility support, device assistance, presentations, and legitimate record-keeping painful. Prefer dedicated Activities for genuinely sensitive workflows. In a single-Activity app, changing a window flag for one Fragment or navigation destination affects everything currently rendered in that same window.

SecureWindow.ktkotlin
import android.app.Activity
import android.view.WindowManager
 
fun Activity.setScreenCaptureBlocked(blocked: Boolean) {
    if (blocked) {
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
    } else {
        window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
    }
}

Toggling introduces lifecycle responsibility

  • Clearing the flag makes the whole Activity window capturable immediately.

  • Navigation transitions, dialogs, bottom sheets, picture-in-picture, and configuration changes can expose timing gaps.

  • Derive protection from current navigation state rather than scattered button handlers.

  • Use a reference-counted or single-owner policy if nested sensitive components can overlap.

  • For the highest-risk screen, a dedicated always-secure Activity is easier to reason about and test.

Jetpack Compose still uses the host Window

SecureScreen.ktkotlin
import android.app.Activity
import android.view.WindowManager
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalContext
 
@Composable
fun SecureScreen(content: @Composable () -> Unit) {
    val activity = LocalContext.current as Activity
 
    DisposableEffect(activity) {
        activity.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
        onDispose {
            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
        }
    }
 
    content()
}

Compose does not create a security boundary per composable

  • DisposableEffect applies a side effect when the composable enters composition and reverses it on disposal.

  • The cast assumes the Composition’s Context is an Activity; reusable code should safely unwrap ContextWrapper or receive the Activity/Window explicitly.

  • The flag protects the entire host window, not just content.

  • Two secure composables can race if either clears a flag still required by the other; centralize ownership.

  • A navigation-level effect can still expose transition frames, so test the real graph and consider setting protection before navigation.

Android 14 screenshot detection does not block capture

AndroidManifest.xmlxml
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />

This is an install-time notification permission

  • DETECT_SCREEN_CAPTURE allows the app to receive supported screenshot signals.

  • It is not a dangerous runtime permission dialog.

  • Declaring it does not prevent or redact a screenshot.

  • Android shows a system notice when a supported screenshot is detected.

  • Tell users in context why the Activity uses detection so the notice is not surprising.

ConversationActivity.kt (Android 14+)kotlin
private val captureCallback = Activity.ScreenCaptureCallback {
    // Record only the minimum event metadata your policy allows.
    showScreenshotNotice()
}
 
override fun onStart() {
    super.onStart()
    if (Build.VERSION.SDK_INT >= 34) {
        registerScreenCaptureCallback(mainExecutor, captureCallback)
    }
}
 
override fun onStop() {
    if (Build.VERSION.SDK_INT >= 34) {
        unregisterScreenCaptureCallback(captureCallback)
    }
    super.onStop()
}

Registration follows Activity visibility

  • The callback is per Activity and should be registered while it is started.

  • mainExecutor dispatches the callback on the Activity’s main executor.

  • The API provides a signal, not the captured image or a list of visible data.

  • On Android 14 it detects the supported hardware-button screenshot flow, not ADB or instrumentation captures.

  • Treat analytics/alerts as security-sensitive telemetry: minimize, disclose, secure, rate-limit, and avoid claiming certainty.

Use detection and blocking for different goals

  • FLAG_SECURE is the control when content must be excluded from ordinary platform capture.

  • Screenshot detection is appropriate when capture is permitted but the app needs a privacy notice, audit event, or conversation warning.

  • A blocked screenshot may not generate a useful “successful capture” callback; never make detection the enforcement mechanism.

  • Detection cannot prove what was visible, who initiated it, or whether another camera recorded the display.

  • Design product policy before combining both; surprising users with telemetry on an already blocked screen provides little value.

Android 15: mark a view sensitive during screen share

SensitiveField.kt (API 35+)kotlin
if (Build.VERSION.SDK_INT >= 35) {
    accountNumberView.setContentSensitivity(
        View.CONTENT_SENSITIVITY_SENSITIVE
    )
}

This is narrower than permanent FLAG_SECURE

  • CONTENT_SENSITIVITY_SENSITIVE marks a View as displaying sensitive content.

  • During an active eligible MediaProjection session, Android marks the hosting window secure and hides it from remote viewers.

  • Android can also infer sensitivity in AUTO mode from hints such as password and credit-card autofill hints.

  • Android 15’s default on-device system recorder is exempt from these screen-share protections because recordings stay on device.

  • Use FLAG_SECURE when local screenshots/recording must be blocked; use sensitivity when selective screen-share privacy matches the UX.

Secure a SurfaceView before it attaches

SecureVideoView.ktkotlin
val secureSurface = findViewById<SurfaceView>(R.id.secure_surface)
secureSurface.setSecure(true)

Surface security has an ordering requirement

  • SurfaceView.setSecure(true) is available from API 17.

  • Android requires it before the SurfaceView’s containing window is attached to WindowManager.

  • It prevents that surface from appearing in screenshots or on non-secure displays.

  • Protected video buffers/DRM have additional media-pipeline requirements; this method alone is not a DRM design.

  • If the display is non-secure, secure surface content may appear blank rather than falling back to insecure pixels.

Protect the recent-apps preview

  • FLAG_SECURE is commonly used to keep sensitive window content out of task snapshots; verify the result on supported OS/OEM combinations.

  • Apply protection before the sensitive frame becomes visible, not only in onPause.

  • A custom non-sensitive cover screen can provide better context when the app backgrounds, but avoid lifecycle flicker and bypasses.

  • excludeFromRecents removes a task from the Recents list and is a separate navigation/product behavior, not a substitute for secure rendering.

  • Test multi-window, split screen, freeform windows, picture-in-picture, dialogs, and task restoration.

FLAG_SECURE is not complete data-loss prevention

  • A second physical camera can photograph the display.

  • A rooted, compromised, custom, or non-compliant device may bypass expected platform behavior.

  • Sensitive values can leak through logs, crash reports, analytics, notifications, clipboard, autofill, backups, IPC, files, or network traffic.

  • Accessibility and overlay abuse require their own controls and careful compatibility decisions.

  • An app may render the same data into another unprotected Activity, dialog, notification, widget, or external display.

  • Users with legitimate needs may lose a vital support or accessibility workflow.

Do not fight users with fragile tricks

  • Do not continuously detect screenshot files through storage observers; scoped storage and OEM behavior make that unreliable and invasive.

  • Do not request broad storage/media permissions merely to watch screenshots.

  • Do not hide sensitive content only in onPause; capture and lifecycle timing are not an atomic boundary.

  • Do not assume overlay prevention, screen capture prevention, and accessibility privacy are the same control.

  • Do not promise “screenshots are impossible.” State the supported platform behavior and residual risk.

  • Do not punish a user based on a screenshot callback alone; signals can be incomplete and context is unknown.

Complementary protections

  • Minimize how long secrets remain visible; mask and reveal them deliberately.

  • Mark clipboard content sensitive where copying is genuinely needed, and avoid copying OTPs/secrets by default.

  • Use public/redacted notification versions and exclude sensitive text from lock-screen notifications.

  • Protect sensitive touch targets from obscured-touch/tapjacking where appropriate.

  • Mark accessibility data sensitive using supported APIs without breaking legitimate accessibility tools.

  • Use Android Keystore, authenticated encryption, TLS, secure server authorization, and short-lived tokens for the underlying data.

  • Apply root/device-integrity/fraud signals proportionately; do not confuse them with capture enforcement.

Manual device verification

test device connected with USB debuggingadb
adb shell am start -n com.example/.SensitiveActivity
adb exec-out screencap -p > secure-screen.png
file secure-screen.png
Starting: Intent { cmp=com.example/.SensitiveActivity }
secure-screen.png: PNG image data, ...

Inspect the pixels and repeat with real capture paths

  • The command launches the exact sensitive Activity and asks Android for a screenshot.

  • A valid PNG file does not mean protected content leaked; visually inspect that the secure region is blank/redacted.

  • Android 14 screenshot detection deliberately does not report ADB/instrumentation captures.

  • Also test hardware-button screenshots, Quick Settings recording, casting, conferencing/remote support, Recents, external displays, and OEM tools.

  • Run a control test on an intentionally non-secure Activity to prove the test harness can capture normal content.

Automated regression strategy

  • Unit-test the policy that maps navigation destinations to secure/non-secure state.

  • Instrument window flags where possible, but do not treat flag presence as end-to-end compositor proof.

  • Run physical-device tests across supported API levels and representative OEMs.

  • Keep golden evidence for secure, non-secure control, transition, background/Recents, dialog, multi-window, and screen-share cases.

  • Verify crash/recreation/configuration changes reapply protection before content.

  • Add privacy review whenever a new Activity, dialog, SurfaceView, notification, or export path can show the sensitive value.

Troubleshooting checklist

  • Screenshot still shows content → confirm the visible content belongs to the flagged Window and no second Activity/dialog/window is on top.

  • Only part of video is visible/blank → inspect SurfaceView/TextureView, secure surface timing, DRM, and display security.

  • Compose screen becomes capturable after navigation → find competing effects clearing the shared window flag.

  • Support screen sharing is blank → the behavior is expected for secure windows; create an approved redacted/support flow rather than disabling protection silently.

  • Callback never fires → device must run Android 14+, permission must be declared, Activity started, callback registered, and supported hardware screenshot gesture used.

  • App crashes on older Android → guard API 34/35 calls with SDK checks and keep newer classes out of unsafe initialization paths.

A practical security decision

  • Recovery seeds, password/secret reveal, identity documents, high-risk financial authorization → default to an always-secure Activity.

  • Ordinary profile or receipt page → consider user choice, selective reveal, watermarking, or share/export designed by the app.

  • Messaging screenshot awareness → detection may fit only with clear notice, policy, and platform-limit language.

  • Remote support workflow → build an explicitly redacted support screen instead of globally clearing security.

  • Screen-share-only privacy on Android 15+ → mark sensitive views while retaining local user capture if that matches the threat model.

Dialogs, SDK screens, and multiple windows

  • Audit dialogs, permission-style education UI, authentication SDK Activities, browser/custom tabs, and payment SDK flows separately.

  • A third-party Activity owns a different Window and does not inherit the caller’s secure flag.

  • Prefer SDK-supported privacy options; do not use reflection or overlay hacks to alter foreign windows.

  • Document which vendor surfaces can display the sensitive value and include them in release tests.

Tell users what protection changes

  • Explain why screenshots or screen sharing are unavailable at the moment they matter.

  • Offer a safe export, copy, share, or redacted support path when the user has a legitimate need.

  • Avoid dead-end blank casting behavior without an actionable message on the local device.

  • Include capture limitations in security documentation so support staff do not overpromise.

Release matrix to record

For each protected flow, record minimum/target SDK, Activity and Window owner, device/API/OEM results, screenshot and recording methods, Recents behavior, screen-share behavior, accessibility review, and the product owner who accepted residual risk.

Primary references