ScreenSync MCP guide

Testing Android Biometrics with Claude Code and MCP

Automate Android BiometricPrompt, Keystore CryptoObject, PIN fallback, and lockout handling on real devices using Claude Code and ScreenSync MCP.

A smartphone showing a glowing purple circle connects via fiber optic cables to a partially open laptop on a dark desk.

Automating biometric authentication, cryptographic hardware keystores, and secure user credential fallbacks on physical Android smartphones represents one of the most demanding engineering challenges in modern mobile software quality assurance. Mobile engineering teams building enterprise fintech applications, neobank accounts, cryptocurrency wallets, healthcare medical records, and zero-trust identity managers rely heavily on Android's standardized BiometricPrompt API. By delegating user identity verification to hardware-backed biometric sensors—including optical and ultrasonic fingerprint scanners, 3D structured-light infrared face unlock, and iris sensors—applications ensure sensitive cryptographic keys stored inside the Android Keystore or Secure Element cannot be accessed without genuine human presence. However, testing these security-critical journeys has historically created severe bottlenecks in continuous integration and automated QA pipelines. Traditional testing tools like Espresso, AndroidX UIAutomator, and Appium fail at the boundary of BiometricPrompt because modern Android sandboxes biometric authentication inside the OS-level SystemUI process (com.android.systemui). Application-level testing harnesses cannot reach across process boundaries into system security dialogs, cannot simulate physical finger taps on physical hardware sensors, and cannot easily triage cryptographic cipher state machines during runtime lockouts. By integrating Anthropic's autonomous terminal coding agent Claude Code with the Model Context Protocol (MCP) using ScreenSync MCP, mobile engineering teams can bridge desktop AI reasoning with physical Android hardware, automating end-to-end biometric modal verification, negative authentication flows, device PIN fallbacks, and real-time cryptographic logcat telemetry across physical devices without requiring root privileges.

Key Takeaways

  • SystemUI security boundary traversal: ScreenSync MCP inspects OS-level accessibility trees via get_ui_hierarchy, allowing Claude Code to parse and interact with com.android.systemui biometric prompts that standard in-app Espresso test runners cannot touch.
  • Low-latency optical verification without root: Using Android's native MediaProjection API, ScreenSync MCP streams display frames to the local desktop hub at approximately 94 milliseconds median latency, enabling AI vision models to verify biometric prompt titles, error animations, and lockouts without root access.
  • Deterministic credential fallback automation: Claude Code automates negative authentication paths, canceling biometric sheets or forcing the "Use PIN / Password" fallback through precise ADB touch injection (control_tap) and keyboard input (control_type).
  • Hardware Keystore and CryptoObject validation: Stream real-time diagnostic telemetry via get_logcat to assert that BiometricPrompt.CryptoObject initialized valid ciphers and that hardware Keystore keys unwrap correctly upon successful authentication.
  • Physical device vs. emulator parity: While emulator workflows rely on synthetic adb emu finger touch commands, ScreenSync MCP enables testing physical Android hardware to triage OEM-specific layout variations across Samsung One UI, Xiaomi HyperOS, and Google Pixel.
  • Unified 226-tool developer plane: ScreenSync MCP provides 29 mobile and desktop tools (including 12 control_*, 3 os_*, and 14 capture tools) alongside 197 browser automation tools (web_*), uniting mobile device automation and backend developer consoles under one AI agent.

The Mobile Security Boundary: Why BiometricPrompt Stalls Traditional Test Suites

Android BiometricPrompt stalls traditional test suites because modern Android isolates biometric authentication inside the SystemUI system process (com.android.systemui). Application-level testing frameworks like Espresso cannot cross application boundaries to interact with system security dialogs, while physical hardware biometric sensors cannot accept synthetic touch events without specialized mock abstraction layers or automated credential fallbacks.

To appreciate why mobile QA teams struggle to automate biometric authentication, one must examine the architectural defense-in-depth model introduced in Android 9.0 (Pie) and solidified in Android 10 (API level 29) through Android 15. Prior to BiometricPrompt, Android applications used the legacy FingerprintManager API, which allowed applications to draw custom dialog fragments directly inside the application view hierarchy. While convenient for UI automation, this legacy design created severe vulnerabilities: malicious applications could disguise credential harvesting dialogs, draw overlays over the fingerprint prompt, or intercept touch coordinates.

To eliminate these vulnerabilities, Google migrated all biometric authentication to the system-managed BiometricPrompt framework. When an application calls biometricPrompt.authenticate(promptInfo):

  1. Window Manager Decoupling: The Android Window Manager assigns window focus to a dedicated system overlay window managed by com.android.systemui. The calling application loses active input focus, and its view hierarchy is suspended beneath the system modal sheet.
  2. Process Boundary Isolation: In Espresso, tests execute inside the application's own process (com.example.banking). When the BiometricPrompt appears, Espresso's onView() matchers cannot find the "Cancel" button, the prompt title, or the subtitle because those views live in the com.android.systemui process namespace. Attempting to locate them throws a NoMatchingViewException.
  3. Hardware Sensor Physicality: On physical Android devices, biometric sensors (such as capacitive side-mounted fingerprint scanners, under-display optical sensors, or ultrasonic transducers) communicate with the Android Hardware Abstraction Layer (HAL) via dedicated low-level kernel drivers and the Trusted Execution Environment (TEE). ADB commands cannot inject electrical capacitance into an under-display sensor glass. Unless developers test on the desktop emulator—which exposes an emulator-only console command (adb -e emu finger touch <id>)—physical device test suites freeze, awaiting a physical human finger.
  4. Keystore Cryptographic Binding: Enterprise security architectures rarely rely on simple boolean authentication results. Financial transactions and master password access bind biometric authorization to a hardware-backed cryptographic object (BiometricPrompt.CryptoObject). The application initializes a Cipher, Signature, or Mac requiring user authentication (setUserAuthenticationRequired(true)). When the biometric prompt is dismissed or fails, any attempt to invoke cipher.doFinal() throws an unhandled UserNotAuthenticatedException, crashing the test runner if unhandled.
Feature / Dimension Legacy FingerprintManager Standard Espresso / UIAutomator Android Emulator Console (adb emu) ScreenSync MCP + Claude Code
Process Hosting Application Process (In-App View) Application Process (In-App Only) SystemUI Window Overlay OS-Wide SystemUI Accessibility Surface
System Dialog Visibility High (In-app layout matchers) Zero (Throws NoMatchingViewException) Blind (Requires manual visual check) Full Dual-Plane: Accessibility Node + Visual Pixel Stream
Physical Hardware Support Physical Devices Only Physical Devices & Emulators Emulators Only (adb -e emu fails on real devices) Physical Android Hardware (Android 7.0+) & Emulators
Root & Instrumentation Requires Test APK Instrumentation Requires Test APK Instrumentation Requires Android Virtual Device (AVD) Zero Root, Zero APK Instrumentation (Standard Debug Build)
Hardware Sensor Injection None (Requires Human Touch) None (Requires Mock Abstractions) Synthetic Finger ID (emu finger touch 1) Automated PIN/Password Fallbacks & Logcat Telemetry
Keystore CryptoObject Triage In-app Unit Assertions Only In-app Unit Assertions Only Emulator Console Logs Only Real-Time Hardware TEE & System Logcat (get_logcat)
Frame Latency & Capture Slow ADB Framebuffer Dumps No Native Visual Stream Emulator Window Capture Native MediaProjection (~94 ms Median LAN Latency)
Tool Integration Scope Single Test Harness In-App Android Only Shell Scripts & Terminal ADB 226 Tools: Mobile Hardware, Desktop OS, & Browser Tabs

Architecture of ScreenSync MCP: Bridging SystemUI Modals and Physical Hardware Telemetry

ScreenSync MCP bridges SystemUI modals and physical hardware telemetry by decoupling visual display streaming from input execution. The system captures system-level dialogs using Android's native MediaProjection API at approximately 94 milliseconds median latency, dispatches hardware touch events via the Android Debug Bridge, and triages cryptographic keystore events through real-time logcat inspection without requiring device root.

ScreenSync MCP resolves the biometric testing impasse by decoupling the inspection and control surfaces into three specialized, high-performance planes operating simultaneously across your local workstation and target Android device:

  • Control and Agent Plane: Claude Code runs in your workstation terminal over local stdio JSON-RPC, querying accessibility nodes from com.android.systemui, analyzing live visual frames, and executing coordinated ADB actions.
  • Desktop Hub Gateway: The ScreenSync MCP hub runs on Node.js 18 (HTTP port 3000, secured with SCREEN_SYNC_TOKEN), managing bidirectional communication between Claude Code and connected devices.
  • On-Device Display Streaming: The ScreenSync MCP Android client leverages Android's native MediaProjection API to stream display frames over local Wi-Fi or USB reverse sockets at approximately 94 ms latency without root.
  • Kernel Input and Telemetry Channel: ADB bridge sockets handle Linux input subsystem event dispatch (/dev/input/event*) and stream device logcat buffers for real-time Keystore cryptographic verification.

1. The Low-Latency MediaProjection Visual Plane

Unlike traditional testing tools that attempt to capture screens via slow ADB commands (adb exec-out screencap -p), the ScreenSync MCP mobile application runs directly on Android 7.0 (API level 24) or higher. Once the user grants screen capture permission during session initialization, the application establishes a hardware-accelerated MediaProjection pipeline. Because MediaProjection captures the composite output of the Android SurfaceFlinger hardware display pipeline, it captures everything visible on the physical glass—including system alert windows, notifications, and com.android.systemui biometric prompts. These frames stream to the desktop hub over your local Wi-Fi network or USB reverse socket at approximately 94 milliseconds median latency, allowing Claude Code's multimodal vision model to inspect biometric prompt states, biometric icon animations, and error banners in real time.

2. The OS-Wide Accessibility Tree Inspection Plane

When Claude Code invokes get_ui_hierarchy, ScreenSync MCP dumps the active window hierarchy from the Android accessibility framework. Because accessibility services possess system-wide visibility, the returned XML tree encompasses both the underlying application window and the active com.android.systemui modal sheet. Claude Code can directly query the accessibility attributes of the biometric prompt:

  • package="com.android.systemui"
  • resource-id="com.android.systemui:id/biometric_prompt"
  • text="Verify your identity"
  • resource-id="com.android.systemui:id/button_negative" (Cancel or Use Password)

3. The Hardware Control and Telemetry Plane

When automated interaction is required, Claude Code uses ADB control tools (control_tap, control_type, control_key, control_swipe). When testing credential fallbacks, Claude Code taps the negative button ("Use PIN"), waits for the system Keyguard credential entry screen to appear, and dispatches the user's test PIN into the secure pin pad using control_type and control_key. Simultaneously, Claude Code monitors system and application logcat buffers using get_logcat to catch hardware TEE responses, biometric match results, and cryptographic key generation events.

Diagnostic Subsystem MCP Inspection Tool Underlying Android Architecture Primary Verification Objective
Visual Sheet Display get_latest_screenshot Android MediaProjection & SurfaceFlinger Validating biometric prompt visual appearance and error text
SystemUI Window Tree get_ui_hierarchy Accessibility Framework (AccessibilityNodeInfo) Locating button coordinates (button_negative, button_credential)
Touch Event Dispatch control_tap Linux Input Subsystem (/dev/input/event*) Dispatching hardware touch coordinates to cancel or fallback
PIN / Password Input control_type / control_key Input Method Service & Keyguard PIN Pad Entering fallback numeric PINs or alphanumeric passwords
Cryptographic Telemetry get_logcat Android Logcat (BiometricService / KeyStore) Asserting CryptoObject initialization and cipher authentication
Background Service Sync 197 web_* Tools Browser Extension Chrome/Edge Tabs Verifying backend token exchange on enterprise web dashboards

Step-by-Step Setup: Connecting Claude Code to Physical Android Hardware

Connecting Claude Code to physical Android hardware requires running the ScreenSync MCP desktop hub on Node.js 18, registering the stdio MCP server in Claude Code, installing the free ScreenSync MCP mobile application on Android 7.0+, granting MediaProjection screen capture consent, and enabling USB or wireless debugging on the workstation development environment.

Deploying this autonomous biometric testing pipeline on Windows, macOS, or Linux takes fewer than five minutes:

Step 1: Workstation Environment Preparation

Verify that your workstation development environment contains the required runtimes:

  1. Operating System: Windows 10/11, macOS (Apple Silicon or Intel), or Linux.
  2. Node.js: Node.js 18.0.0 or newer (node -v).
  3. Android Platform Tools: adb configured in your system PATH (adb version).
  4. Target Device: A real Android smartphone running Android 7.0 (API level 24) or higher, connected via a high-quality USB data cable or paired over local Wi-Fi.

Step 2: Launch the ScreenSync MCP Desktop Hub

Download the desktop hub archive from screensyncmcp.tech and extract it into a local directory. Execute the launcher script:

# On Windows (PowerShell or Command Prompt)
cd C:\tools\screensync-hub
.\start-hub.bat

# On macOS or Linux (Terminal)
cd ~/tools/screensync-hub
chmod +x start-hub.sh
./start-hub.sh

The hub initializes a local HTTP server on port 3000 to receive encrypted MediaProjection display frames and prepares the MCP stdio interface. The terminal console prints your session bearer token (SCREEN_SYNC_TOKEN) and presents a pairing QR code. For detailed setup parameters, consult the official ScreenSync MCP setup guide.

Step 3: Register ScreenSync MCP with Claude Code

In your workstation terminal, register the desktop hub with the Claude Code CLI:

claude mcp add screensync node "/path/to/screensync-hub/screensync-hub.js"

If you are using Claude Desktop, open your claude_desktop_config.json configuration file (accessible via Settings > Developer > Edit Config) and define the server:

{
  "mcpServers": {
    "screensync": {
      "command": "node",
      "args": ["/path/to/screensync-hub/screensync-hub.js"],
      "env": {
        "SCREEN_SYNC_TOKEN": "your_secure_bearer_token_here"
      }
    }
  }
}

(Note: Claude Code, Claude Desktop, Cursor, and VS Code agent mode communicate with ScreenSync MCP via local stdio processes and are fully supported. The ChatGPT web application requires remote HTTPS webhooks and does not support local stdio MCP servers).

Step 4: Configure the Physical Android Smartphone

  1. On your physical Android device, download the free ScreenSync MCP application (com.screensync.mcp) directly from Google Play.
  2. Open the app. Allow mDNS discovery to automatically pair with the desktop hub over your local Wi-Fi subnet, or scan the QR code displayed in your workstation terminal.
  3. Tap Start Projection and confirm the native Android MediaProjection screen capture consent dialog.
  4. Enable Developer Options (Settings > About Phone > tap Build Number 7 times) and toggle USB Debugging to ON. If you prefer a cable-free workstation, follow our comprehensive guide on Android wireless debugging for MCP servers.
  5. Verify ADB connectivity:
    adb devices

Step 5: Android Application Biometric Implementation

In your Android application project, ensure you are utilizing the modern AndroidX Biometric library (androidx.biometric:biometric:1.1.0 or newer). Implement a robust BiometricPrompt controller with Keystore CryptoObject binding:

package com.example.screensync.security

import android.os.Bundle
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey

class BiometricAuthActivity : AppCompatActivity() {

    private val TAG = "ScreenSyncBiometricQA"
    private val KEY_NAME = "VaultMasterKey"
    private lateinit var biometricPrompt: BiometricPrompt
    private lateinit var promptInfo: BiometricPrompt.PromptInfo

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_biometric_auth)

        val executor = ContextCompat.getMainExecutor(this)
        biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                super.onAuthenticationError(errorCode, errString)
                Log.e(TAG, "AUTHENTICATION_ERROR: Code [$errorCode] - $errString")
            }

            override fun onAuthenticationFailed() {
                super.onAuthenticationFailed()
                Log.w(TAG, "AUTHENTICATION_FAILED: Biometric not recognized")
            }

            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                super.onAuthenticationSucceeded(result)
                val authenticatedCipher = result.cryptoObject?.cipher
                if (authenticatedCipher != null) {
                    Log.i(TAG, "AUTHENTICATION_SUCCESS: CryptoObject cipher unlocked successfully")
                } else {
                    Log.i(TAG, "AUTHENTICATION_SUCCESS: Biometric verified without crypto binding")
                }
            }
        })

        promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("Unlock Secure Vault")
            .setSubtitle("Confirm your fingerprint or enter device PIN")
            .setDescription("Authenticate to decrypt your private session keys")
            .setAllowedAuthenticators(Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL)
            .build()
    }

    fun initiateAuthentication() {
        val biometricManager = BiometricManager.from(this)
        val canAuth = biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG)
        Log.d(TAG, "CAN_AUTHENTICATE_RESULT: $canAuth")

        try {
            val cipher = getOrCreateCipher()
            biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
        } catch (e: Exception) {
            Log.e(TAG, "CIPHER_INIT_FAILED: ${e.message}")
            // Fallback without CryptoObject if device credentials only
            biometricPrompt.authenticate(promptInfo)
        }
    }

    private fun getOrCreateCipher(): Cipher {
        val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
        if (!keyStore.containsAlias(KEY_NAME)) {
            val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
            val spec = KeyGenParameterSpec.Builder(
                KEY_NAME,
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
            )
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                .setUserAuthenticationRequired(true)
                .build()
            keyGenerator.init(spec)
            keyGenerator.generateKey()
        }
        val secretKey = keyStore.getKey(KEY_NAME, null) as SecretKey
        val cipher = Cipher.getInstance("${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_CBC}/${KeyProperties.ENCRYPTION_PADDING_PKCS7}")
        cipher.init(Cipher.ENCRYPT_MODE, secretKey)
        return cipher
    }
}

Core QA Workflows: Simulating Touch, Enforcing PIN Fallback, and Keystore Verification

Automating biometric QA workflows requires verifying three critical authentication paths: negative cancellation flows, device PIN or password credential fallbacks, and cryptographic Keystore CryptoObject assertions. Claude Code orchestrates get_ui_hierarchy to locate SystemUI action buttons, dispatches control_tap to trigger credential fallbacks, and streams get_logcat records to assert successful Keystore cryptographic cipher decryption.

Workflow 1: Testing Negative Cancellation and Back-Button Dismissal

Applications must handle user cancellations gracefully without crashing, freezing, or locking the user out of the application.

Developer Prompt:
"Claude, launch our application com.example.screensync.security, trigger the biometric login flow, assert that the BiometricPrompt modal appears with correct title 'Unlock Secure Vault', tap the 'Cancel' negative button, and verify that the app returns to the landing screen with an error banner."

Step 1: Launch Activity and Trigger Biometric Prompt

Claude Code invokes control_launch_app with package com.example.screensync.security. Once the initial screen renders, Claude Code calls control_tap on the "Login with Biometrics" button.

Step 2: Inspect the SystemUI Modal via Accessibility Tree

Claude Code invokes get_ui_hierarchy to inspect the newly presented system window:

<node class="android.widget.FrameLayout" package="com.android.systemui">
  <node class="android.widget.LinearLayout" resource-id="com.android.systemui:id/biometric_prompt">
    <node class="android.widget.TextView" resource-id="com.android.systemui:id/title" text="Unlock Secure Vault" bounds="[72,1420][1008,1490]" />
    <node class="android.widget.TextView" resource-id="com.android.systemui:id/subtitle" text="Confirm your fingerprint or enter device PIN" bounds="[72,1502][1008,1552]" />
    <node class="android.widget.Button" resource-id="com.android.systemui:id/button_negative" text="Cancel" bounds="[72,1980][480,2100]" />
  </node>
</node>

Step 3: Visual Frame Assertion

Claude Code calls get_latest_screenshot to confirm that the biometric fingerprint glyph is rendered clearly and is not obscured by system bars or keyboard dialogs. For a deeper understanding of why structural node trees must accompany visual pixels, read our architectural review on Android UI hierarchy vs computer vision: why LLMs need structural node trees to prevent drift.

Step 4: Dispatch Tap on Negative Action Button

Using the calculated center coordinates of button_negative (bounds="[72,1980][480,2100]" -> x: 276, y: 2040), Claude Code invokes:

control_tap { x: 276, y: 2040 }

Step 5: Triage Cancellation in Logcat

Claude Code streams runtime logs via get_logcat with filter ScreenSyncBiometricQA:

E ScreenSyncBiometricQA: AUTHENTICATION_ERROR: Code [13] - Cancelled by user
D ScreenSyncBiometricQA: Navigating back to LandingActivity

Claude Code confirms that error code 13 (BiometricPrompt.ERROR_USER_CANCELED) was received and that the application safely dismissed the sheet.

Workflow 2: Testing Credential Fallback (Device PIN / Password)

In modern secure apps configured with Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL, users who cannot authenticate biometrically can select "Use PIN / Pattern / Password".

Developer Prompt:
"Claude, trigger biometric authentication, tap 'Use PIN' in the prompt, enter our test device PIN '4892' on the Keyguard screen, and verify that authentication succeeds and navigates to the Home dashboard."

Execution Sequence:

  1. Trigger Authentication: Claude Code calls control_tap on the login action.
  2. Locate Credential Fallback Button: In get_ui_hierarchy, Claude Code detects:
    <node class="android.widget.Button" resource-id="com.android.systemui:id/button_use_credential" text="Use PIN" bounds="[540,1980][1008,2100]" />
  3. Trigger PIN Dialog: Claude Code calls control_tap { x: 774, y: 2040 }. The Android OS switches from the biometric sheet to the full-screen Keyguard credential entry screen.
  4. Enter Device PIN: Claude Code calls control_type { text: "4892" } and dispatches control_key { key: 66 } (KEYCODE_ENTER).
  5. Verify Success State: Claude Code calls get_ui_hierarchy and get_latest_screenshot to assert that HomeActivity rendered with the user's dashboard cards, confirming that fallback credentials successfully satisfied application security requirements. If you are also auditing permission gates in your app, consult our detailed walkthrough on testing Android runtime permissions with Claude Code and MCP.

Workflow 3: Validating Keystore CryptoObject Cryptographic Unlocking

In financial and healthcare apps, simple boolean authentication is insufficient; the app must prove that the hardware key held inside the Secure Element unlocked and decrypted the user's session master token.

Developer Prompt:
"Claude, execute the biometric authentication flow for the VaultMasterKey, monitor logcat for CryptoObject confirmation, and assert that the decrypted token payload matches our test hash."

Execution and Telemetry Capture:

Claude Code streams logcat output through get_logcat during the authentication sequence, capturing hardware cryptographic transitions:

D ScreenSyncBiometricQA: CAN_AUTHENTICATE_RESULT: 0 (BIOMETRIC_SUCCESS)
D AndroidKeyStore: Loading key VaultMasterKey from keystore2 daemon
I BiometricService: Authenticated user: uid=10283, modality=2 (FINGERPRINT)
D KeyStore: KeyStore.grantAuthorization: auth token passed to TEE
I ScreenSyncBiometricQA: AUTHENTICATION_SUCCESS: CryptoObject cipher unlocked successfully
I ScreenSyncBiometricQA: Decrypted master session hash: e891dcf9427b

Claude Code parses the logcat output, verifies that the cryptographic cipher unwrapped without throwing UserNotAuthenticatedException, and reports confirmed hardware security integrity. For hybrid architectures that embed web checkouts alongside biometric authorization, developers can review testing Android WebViews and hybrid apps with Claude Code and MCP.

Biometric QA Scenario ScreenSync MCP Tools Primary Detection Mechanism Critical Vulnerability Caught
Negative Cancellation get_ui_hierarchy, control_tap SystemUI button coordinate matching Application freezes upon user cancellation
Device PIN Fallback control_tap, control_type Keyguard PIN pad automation Crash when DEVICE_CREDENTIAL is requested
CryptoObject Binding get_logcat, get_latest_screenshot Cryptographic Keystore logcat triage UserNotAuthenticatedException crashing app
Back Button Dismissal control_key (Back), get_logcat OS back press event verification Sheet dismisses but app state remains locked
Temporary Lockout get_logcat, get_ui_hierarchy ERROR_LOCKOUT log assertion App fails to display 30-second cooldown timer
Permanent Lockout get_logcat, control_tap ERROR_LOCKOUT_PERMANENT triage Biometric permanently locked; PIN fallback disabled
Cross-Device UI Shift get_latest_screenshot, get_ui_hierarchy Multi-OEM coordinate translation Sensor prompt clipped or off-screen on OEM skins

Triaging Biometric Failure Modes: Lockouts, Missing Enrolments, and Sensor Errors

Triaging biometric failure modes requires parsing system error codes emitted by Android's BiometricManager and AuthenticationCallback listeners. Claude Code captures diagnostic logcat signatures for temporary lockouts (ERROR_LOCKOUT), permanent lockouts (ERROR_LOCKOUT_PERMANENT), un-enrolled hardware (ERROR_NONE_ENROLLED), and user cancellations, validating that the mobile client gracefully falls back to master credentials without crashing or leaking sensitive user session tokens.

When biometric authentication fails in production, applications receive specific integer error codes through BiometricPrompt.AuthenticationCallback.onAuthenticationError(errorCode, errString). By capturing these events through get_logcat, Claude Code can diagnose and triage root causes autonomously:

1. Triaging ERROR_LOCKOUT (Code 7) and Cooldown Intervals

When five consecutive failed biometric recognition attempts occur, Android's biometric subsystem triggers a temporary lockout (BiometricPrompt.ERROR_LOCKOUT, code 7). During this interval, the biometric sensor is disabled for 30 seconds.

  • Diagnostic Logcat Signature:
    E BiometricPrompt: onAuthenticationError: 7, Too many attempts. Try again later.
    W FingerprintService: Client com.example.screensync.security locked out for 30000ms
  • Autonomous Agent Action: Claude Code detects error code 7, verifies that the application displays a friendly cooldown countdown or offers an immediate "Use Password" alternative, and waits for the cooldown period before attempting re-authentication.

2. Triaging ERROR_LOCKOUT_PERMANENT (Code 9)

If the user continues to fail attempts after previous lockouts, Android enforces ERROR_LOCKOUT_PERMANENT (code 9). Biometric authentication is disabled until the user unlocks the device with strong credentials (PIN, pattern, or password).

  • Diagnostic Logcat Signature:
    E BiometricPrompt: onAuthenticationError: 9, Too many attempts. Fingerprint sensor disabled.
  • Autonomous Agent Action: Claude Code asserts that the application does not become trapped in an infinite retry loop and immediately presents the device credential prompt or user password field.

3. Triaging BIOMETRIC_ERROR_NONE_ENROLLED (Code 11)

When testing on fresh device images, corporate devices, or secondary user profiles where the tester has not registered fingerprints, calling biometricManager.canAuthenticate() returns BIOMETRIC_ERROR_NONE_ENROLLED.

  • Diagnostic Logcat Signature:
    W ScreenSyncBiometricQA: CAN_AUTHENTICATE_RESULT: 11 (BIOMETRIC_ERROR_NONE_ENROLLED)
  • Autonomous Agent Action: Claude Code verifies that the application redirects the user to Android Settings (Settings.ACTION_BIOMETRIC_ENROLL or Settings.ACTION_SECURITY_SETTINGS) rather than crashing with an unhandled state exception.
Biometric Error Code Constant Name Cause Description Engineering Remediation
Code 1 ERROR_HW_UNAVAILABLE Biometric hardware is busy, suspended, or uninitialized Implement exponential backoff retry; check camera/sensor HAL
Code 2 ERROR_UNABLE_TO_PROCESS Sensor could not read biometric (e.g. wet or dirty finger) Keep prompt active; display helper text instructing clean sensor
Code 3 ERROR_TIMEOUT Prompt timed out waiting for user interaction (typically 30s) Dismiss prompt gracefully; allow user to tap retry button
Code 4 ERROR_NO_SPACE Insufficient hardware storage to complete operation Clear obsolete device credentials; inspect Keystore space
Code 5 ERROR_CANCELED Biometric operation canceled by another system process Prevent concurrent BiometricPrompt.authenticate() calls
Code 7 ERROR_LOCKOUT 5 failed attempts; sensor locked out for 30 seconds Display cooldown timer; enable immediate PIN/password fallback
Code 9 ERROR_LOCKOUT_PERMANENT Excessive lockouts; sensor disabled until strong unlock Require device PIN/pattern unlock before re-enabling biometrics
Code 10 ERROR_USER_CANCELED User tapped outside modal or pressed OS back button Return to landing screen; do not display error banner
Code 11 ERROR_NO_BIOMETRICS No fingerprints or faces registered in system settings Route user to Android Security Settings enrollment intent
Code 12 ERROR_HW_NOT_PRESENT Physical device lacks biometric hardware sensors Hide biometric option; default to password/PIN authentication
Code 13 ERROR_NEGATIVE_BUTTON User pressed explicit negative button ("Cancel" or "Use PIN") Trigger configured fallback flow or return to login view
Code 14 ERROR_NO_DEVICE_CREDENTIAL App requested DEVICE_CREDENTIAL but device has no PIN Prompt user to configure secure screen lock in OS settings

Production Checklist for Automated Android Biometric Testing

  • [ ] Host workstation runs Node.js 18+ with Android Platform Tools adb configured in system PATH.
  • [ ] ScreenSync MCP desktop hub launched and active on local HTTP port 3000 (start-hub.bat or start-hub.sh).
  • [ ] ScreenSync MCP registered in Claude Code via claude mcp add screensync node "/path/to/screensync-hub.js".
  • [ ] Free ScreenSync MCP client (version 2.5.7+) installed on physical Android 7.0+ smartphone from Google Play.
  • [ ] Physical device and workstation connected to the same local Wi-Fi subnet; mDNS discovery or QR code pairing authenticated.
  • [ ] MediaProjection screen capture consent confirmed on device for ~94 ms low-latency visual streaming without root.
  • [ ] USB debugging or wireless debugging enabled on target Android device (adb devices confirms authorized status).
  • [ ] Application uses modern androidx.biometric:biometric:1.1.0 or higher to guarantee unified BiometricPrompt behavior.
  • [ ] PromptInfo explicitly declares setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL) to enable automated PIN fallbacks.
  • [ ] Keystore cryptographic keys declare setUserAuthenticationRequired(true) and handle UserNotAuthenticatedException defensively.
  • [ ] Claude Code verifies SystemUI window hierarchy elements (com.android.systemui:id/biometric_prompt) via get_ui_hierarchy.
  • [ ] Telemetry assertions actively stream and filter get_logcat logs for ScreenSyncBiometricQA, BiometricService, and KeyStore.
Component / Layer Verification Target MCP Tool / Command Acceptance Criteria
Visual Stream Sub-100ms Display Mirroring get_latest_screenshot Frame received in ~94 ms over LAN
SystemUI Overlay Prompt Modal Detection get_ui_hierarchy com.android.systemui package bounds resolved
Negative Button Dismissal & State Reset control_tap on button_negative ERROR_NEGATIVE_BUTTON captured in logcat
Credential Fallback PIN Pad Entry control_type on Keyguard input PIN entered; HomeActivity launched
CryptoObject Cipher Keystore Key Decryption get_logcat (filter: ScreenSyncBiometricQA) Cipher unlocks without UserNotAuthenticatedException
Temporary Lockout Cooldown Enforcement get_logcat (filter: BiometricPrompt) Error code 7 logged; retry disabled for 30s
Missing Enrollment OS Enrollment Intent get_ui_hierarchy Application launches Security Settings intent
Cross-Platform Sync Backend Session Integrity 197 web_* Extension Tools Mobile biometric auth updates cloud dashboard

Conclusion

Automating Android biometric authentication and Keystore verification with Claude Code and ScreenSync MCP breaks through one of the most stubborn security boundaries in mobile software quality assurance. By combining non-invasive MediaProjection screen mirroring, OS-wide accessibility hierarchy inspection into com.android.systemui, deterministic hardware touch injection, and real-time cryptographic logcat telemetry, development teams can validate end-to-end authentication journeys, negative flows, and credential fallbacks across genuine physical Android hardware. To learn more about controlling physical devices without invasive modifications, explore our foundational guide on how to control an Android phone with AI without root. For applications transitioning from raw biometrics to passwordless FIDO2 credentials, explore our complete guide on testing Android passkeys with Claude Code and MCP.

ScreenSync MCP provides the ScreenSync MCP Android app, ScreenSync MCP hub, and ScreenSync MCP browser extension for development teams worldwide. Explore our comprehensive technical documentation at screensyncmcp.tech, contact our engineering group directly at [email protected], and Get it on Google Play to start automating real physical Android hardware today.

FAQ

Questions, answered

Why do standard Android UI test frameworks fail to automate BiometricPrompt dialogs?

Standard Android UI test frameworks fail on BiometricPrompt dialogs because modern Android renders biometric authentication prompts inside the isolated SystemUI system process (com.android.systemui). Application-level instrumentation frameworks lack process permissions to query or interact with system-level security dialogs without elevated accessibility services or external ADB bridge coordination.

How does ScreenSync MCP capture BiometricPrompt system dialogs without rooting the device?

ScreenSync MCP captures BiometricPrompt dialogs using Android's native MediaProjection API after one-time user authorization during session launch. High-definition display frames stream directly to the desktop hub at approximately 94 milliseconds median latency, granting AI agents visual verification of system-level biometric sheets without device rooting or slow framebuffer scraping.

Can Claude Code simulate biometric fingerprint recognition on physical Android smartphones?

Physical Android smartphones require genuine biometric capacitive or optical sensor stimulation, so ADB commands like adb emu finger touch only function on Android emulators. For physical hardware testing, Claude Code automates negative credential fallback flows using device PIN or password entry via control_tap and control_type.

How does Claude Code verify that an Android Keystore CryptoObject unlocked successfully?

Claude Code verifies Keystore CryptoObject unlocks by streaming device logcat output using get_logcat with application-specific security tags. When authentication succeeds, the application's BiometricPrompt.AuthenticationCallback receives the authenticated CryptoObject, executes cryptographic operations using the unlocked hardware key, and logs confirmation tokens that Claude Code detects and validates in real time.

What causes BiometricPrompt ERROR_LOCKOUT and how can automated AI agents triage it?

BiometricPrompt.ERROR_LOCKOUT occurs when Android detects five consecutive failed biometric attempts, triggering a thirty-second hardware security lockout. Claude Code diagnoses lockout states by capturing logcat error code 7, asserting that the BiometricPrompt modal displays the lockout notice, and automating the fallback flow to verify device PIN or pattern recovery.

How does ScreenSync MCP inspect the SystemUI view hierarchy during biometric authentication?

ScreenSync MCP inspects the active display hierarchy using the get_ui_hierarchy tool, which queries Android's accessibility node subsystem. Because accessibility node dumping spans the active window manager surface, Claude Code parses the com.android.systemui node tree to inspect biometric titles, subtitles, negative action buttons, and credential fallback triggers.

Can Claude Code test biometric authentication across custom OEM Android distributions like Samsung One UI?

Yes, Claude Code tests biometric authentication across custom OEM skins like Samsung One UI, Xiaomi HyperOS, and Google Pixel. While OEM implementations position fingerprint icons and biometric sheets differently, ScreenSync MCP pairs low-latency visual frames with accessibility node trees, allowing Claude Code to locate interactive buttons regardless of proprietary OEM themes.

Which AI coding assistants and MCP clients support automated biometric testing with ScreenSync MCP?

ScreenSync MCP supports any Model Context Protocol client operating over local stdio processes, including Claude Code CLI, Claude Desktop, Cursor, and VS Code agent mode. Cloud-hosted web assistants such as ChatGPT's web interface are unsupported because they require public HTTPS webhooks rather than local workstation stdio processes.

Written by

ScreenSync MCP team

The ScreenSync MCP team at EpsolDev builds the Android app, the desktop MCP hub and the browser extension that let AI agents see and control a real phone. These guides come out of that work.