On this page
Testing Android passkey authentication, FIDO2 cryptographic handshakes, and Credential Manager bottom sheets on physical mobile hardware represents one of the most critical yet structurally elusive disciplines in modern mobile quality assurance. In the modern Android operating system, passwordless authentication has transitioned from an experimental convenience into a mandatory architectural standard. Google's Credential Manager API (androidx.credentials:credentials:1.3.0 and newer) has unified disparate authentication mechanisms—including legacy Smart Lock for Passwords, FIDO2 WebAuthn public key credentials, federated Identity Providers like Sign-In with Google, and device-bound biometric authenticators—into a single, consolidated user experience. However, testing these passwordless journeys under real-world operating system conditions has historically paralyzed continuous integration pipelines. Conventional automated testing frameworks like Espresso and Robolectric fail completely when evaluating Credential Manager because the authentication bottom sheet, account picker, and biometric verification dialogs are rendered out-of-process by Google Play Services (com.google.android.gms) and the Android System UI (com.android.systemui). In-process test harnesses cannot inspect out-of-process view trees, cannot dispatch touch events to system modals without encountering security sandbox violations, and cannot evaluate hardware-backed Trusted Execution Environment (TEE) cryptographic attestation. 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. This architecture automates end-to-end passkey registration, selects credentials from Google Play Services bottom sheets, simulates biometric cancellations, and analyzes real-time cryptographic telemetry without requiring device root privileges.
Key Takeaways
- Out-of-process credential sheet inspection: ScreenSync MCP inspects system-level accessibility node trees across process boundaries using
get_ui_hierarchy, enabling Claude Code to locate and assert Google Play Services passkey bottom sheets that standard Espresso runners cannot touch. - Low-latency non-invasive visual verification: Using Android's native MediaProjection API, ScreenSync MCP streams display frames to the workstation hub at approximately 94 milliseconds median latency without root or ADB overhead, allowing Claude Code's visual models to verify cryptographic shield icons and biometric prompts.
- Deterministic passkey touch dispatch: Claude Code drives Google Play Services account choosers and passkey confirmation dialogs directly across physical Android hardware using coordinate and element-based touch tools like
control_tap. - Real-time WebAuthn diagnostic telemetry: Claude Code monitors real-time
get_logcattelemetry during passkey creation and assertion, triaging public key challenge mismatches, Digital Asset Links errors, and FIDO provider cancellations. - Hardware-in-the-loop Keystore and TEE validation: Testing on physical Android smartphones running Android 7.0 or higher uncovers OEM-specific hardware Keystore and StrongBox cryptographic storage bugs across Samsung One UI, Xiaomi HyperOS, and Google Pixel that virtual emulators fail to reproduce.
- Unified 226-tool developer automation plane: ScreenSync MCP unites 29 mobile and desktop tools (including 12
control_*, 3os_*, and 14 capture tools) with 197 browser automation tools (web_*), empowering AI agents to verify mobile passkey logins against cloud WebAuthn relying party servers within a single test execution loop.
The Passkey Testing Dilemma: Why Credential Manager Breaks In-Process Suites
Android Credential Manager breaks in-process testing suites because authentication bottom sheets execute within Google Play Services rather than the application process. Frameworks like Espresso cannot query out-of-process view trees, intercept biometric verification challenges, or automate cross-window system dialogs without encountering security sandbox exceptions or test suite deadlocks.
To understand why mobile engineering teams struggle to automate passkey testing, developers must analyze the architectural security boundaries separating modern Android applications from the Credential Manager subsystem. When an Android application implements androidx.credentials, the local application code does not render the account selection modal or handle the cryptographic private key directly. Instead, the application invokes CredentialManager.getCredential() or CredentialManager.createCredential(), passing a cryptographic challenge payload generated by the backend Relying Party (RP) server.
The Android framework intercepts this request and initiates an Inter-Process Communication (IPC) transaction with Google Play Services (com.google.android.gms.auth.api.credentials). Google Play Services evaluates the calling application's cryptographic signature, verifies the domain relationship against the server's Digital Asset Links (/.well-known/assetlinks.json), retrieves stored public key credentials from Google Password Manager or on-device hardware-backed security modules, and renders a system-level modal bottom sheet directly on top of the active display window.
This architecture introduces insurmountable barriers for traditional Android automated testing harnesses:
- Window Isolation and Security Sandboxing: Frameworks such as Espresso and Robolectric execute strictly within the host application process and bind to the active
Activitywindow. Because the Credential Manager bottom sheet is rendered by an external system process (com.google.android.gms), Espresso'sViewMatcherAPIs cannot traverse the view hierarchy. Attempting to click a "Continue" button or select a passkey account throws an immediateNoMatchingViewException. - BiometricPrompt Modal Interception: Passkey creation (
CreatePublicKeyCredentialRequest) and assertion (GetPublicKeyCredentialOption) frequently mandate user verification via biometric sensors (fingerprint or 3D face unlock) or device screen lock (PIN, pattern, or password). TheBiometricPromptUI is rendered bySystemUIinside a secure system overlay window. In-process test runners freeze or time out because they possess no mechanism to interact with the system lock screen or biometric hardware daemon. - Digital Asset Links Domain Validation: Android enforces strict domain binding between the native Android app package name, its signing certificate SHA-256 fingerprint, and the Relying Party identifier (RP ID) hosted at
https://example.com/.well-known/assetlinks.json. Virtual emulators lacking Google Play Services or executing in disconnected CI containers cannot validate these asset links, generating false-positiveCreateCredentialCustomExceptionfailures that do not reflect production behavior. - Hardware-Backed Keystore vs Software Mocks: Production passkeys rely on hardware-backed cryptographic modules—such as Android Keystore, StrongBox Keymaster, and ARM TrustZone Trusted Execution Environments (TEE). Synthetic test runners like Robolectric mock cryptographic key generation in the host Java Virtual Machine. These JVM mocks completely bypass hardware key attestation checks, hardware rate limiting, and OEM-specific cryptographic driver quirks found on physical Qualcomm, MediaTek, and Samsung Exynos silicon.
| Evaluation Metric / Feature | JVM Unit Tests (Robolectric) | Standard Espresso / UIAutomator | Android Emulator Console (adb emu) |
ScreenSync MCP + Claude Code (Physical Hardware) |
|---|---|---|---|---|
| Execution Surface | Local JVM / Mocked Android Framework | In-App Process Instrumentation | Virtual AOSP System Image | Authentic Physical Android Device (Android 7.0+) |
| Credential Bottom Sheet Access | Completely Mocked (No OS IPC) | Blind (Google Play Services Isolated) | ADB Shell Only (No Agent Cognition) | Full System Access via get_ui_hierarchy |
| Biometric Challenge Triage | Mocked Callback Invocations | Unsupported (SystemUI Blocked) | Manual CLI Fingerprint Injection | End-to-End Triage: UI Trees + get_logcat Telemetry |
| Digital Asset Links Validation | Bypassed / Mocked | Incomplete in Local CI | Variable (Depends on Google Play Image) | Real-World Validation against Production RP Servers |
| Hardware TEE / StrongBox Fidelity | Zero (Host Software Crypto) | Low (Software Keystore Emulation) | Generic Goldfish / Ranchu Drivers | Authentic Silicon TEE (Qualcomm, MediaTek, Exynos) |
| Display Capture Overhead | Zero Visual Plane | In-Memory Bitmap Scraping | High-Latency Virtual Framebuffer | ~94 ms Median LAN Latency via Native MediaProjection |
| Diagnostic Logcat Analysis | Console STDOUT Only | Post-Mortem Crash Logs | Uncoordinated Shell Scripts | Real-Time Logcat Filtering & State Assertion (get_logcat) |
| Tool Ecosystem Breadth | In-Memory Assertions Only | Android Matcher Matchers Only | Command-Line ADB Only | 226 Tools: Mobile Hardware, Desktop OS, & Browser Suite |
Architecture of ScreenSync MCP: Orchestrating Passkey and FIDO2 Verification
ScreenSync MCP orchestrates Android passkey verification by bridging workstation AI reasoning with physical Android devices over Model Context Protocol stdio streams. Android native MediaProjection streams display frames at approximately 94 milliseconds median latency without root, while system accessibility node dumping and ADB control channels automate out-of-process credential selection.
ScreenSync MCP resolves the passkey testing bottleneck by organizing developer automation across four synchronized operational planes operating between your workstation and connected mobile hardware:
- Autonomous Agent Reasoning Plane: Claude Code executes in your workstation terminal over local stdio JSON-RPC, parsing test directives, inspecting visual display feeds, and analyzing structured accessibility trees without human intervention.
- Workstation Desktop Hub Gateway: The ScreenSync MCP hub runs on Node.js 18 or newer (serving HTTP on port 3000 and authenticated with
SCREEN_SYNC_TOKEN), brokering high-throughput communication between Claude Code, device sockets, and browser extensions. - Native MediaProjection Visual Plane: The ScreenSync MCP mobile application runs on Android 7.0 (API level 24) or newer. Upon user consent during session initialization, it captures the hardware SurfaceFlinger composite display and streams frames over local Wi-Fi or USB at approximately 94 ms median latency without requiring device root privileges.
- Android Debug Bridge Telemetry Plane: ADB bridge channels execute shell commands, inject touch events (
control_tap) into out-of-process bottom sheets, and stream real-time logcat buffers (get_logcat) directly into Claude Code's context window.
1. Cross-Window Accessibility Node Inspection
When Claude Code invokes get_ui_hierarchy, ScreenSync MCP dumps the active window tree using Android's system accessibility framework. Unlike Espresso, which is strictly bounded by the host application's active activity window, Android's accessibility layer spans system overlay windows. Claude Code can inspect bottom sheets rendered by com.google.android.gms, locate the "Continue with Passkey" action button, assert the presence of specific user account emails or usernames, and verify security disclaimer text. To understand why structural node trees must accompany visual pixels to prevent UI coordinate drift during automated sweeps, consult our detailed analysis on Android UI hierarchy vs computer vision: why LLMs need structural node trees to prevent drift.
2. High-Speed SurfaceFlinger Frame Capture
While accessibility node trees provide structured text assertions, cryptographic quality assurance requires verifying that visual security cues render correctly for the end user. ScreenSync MCP utilizes Android's native MediaProjection API. Because this API intercepts frames directly from the hardware display compositor (SurfaceFlinger), it captures the entire screen surface, including Google Play Services bottom sheets, fingerprint glyph animations, and lock screen security prompts. The capture engine operates with zero root requirements and zero ADB overhead, delivering live visual telemetry to Claude Code with minimal CPU overhead on the mobile device.
3. Out-of-Process Diagnostic Telemetry via Logcat
When passkey authentication fails, the error frequently occurs inside the Google Play Services daemon before reaching the application callback. ScreenSync MCP provides get_logcat, enabling Claude Code to stream operating system log buffers continuously. Claude Code monitors core security tags:
CredentialManager: Tracks credential request creation, provider routing, and client response packaging.Fido2PendingIntent: Monitors WebAuthn public key challenge generation, cryptographic signature validation, and Digital Asset Links verification.PasskeyCredentialHelper: Validates JSON serialization ofauthenticatorData,clientDataJSON, andsignatureblocks.BiometricPrompt: Tracks biometric sensor activation, user authentication success, and cancellation signals.
| Passkey Subsystem | MCP Inspection Tool | Underlying Android Primitive | Primary QA Verification Objective |
|---|---|---|---|
| Credential Bottom Sheet | get_ui_hierarchy |
Accessibility Node Hierarchy (AccessibilityNodeInfo) |
Locating passkey account rows, username labels, and cancel buttons |
| Visual Verification | get_latest_screenshot |
Native MediaProjection & SurfaceFlinger | Asserting visual rendering of biometric glyphs and security shields |
| Passkey Selection | control_tap |
Linux Input Subsystem (/dev/input/event*) |
Tapping Google Play Services credential accounts and continue buttons |
| WebAuthn Challenge Triage | get_logcat |
Android Logcat Daemon (logd) |
Asserting challenge signing, clientDataJSON, and FIDO provider codes |
| Biometric Challenge Injection | ADB Shell Commands | BiometricService & FingerprintService | Simulating biometric acceptance or sensor cancellation |
| Backend RP Validation | 197 web_* Extension Tools |
Chrome DevTools Protocol & Web Tabs | Verifying registered passkeys in cloud authentication dashboards |
Step-by-Step Setup: Connecting Claude Code to Physical Android Hardware
Connecting Claude Code to physical Android hardware requires deploying 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 establishing ADB connectivity over USB or wireless debugging.
Setting up an automated passkey testing pipeline on Windows, macOS, or Linux requires fewer than five minutes:
Step 1: Workstation Environment Preparation
Ensure that your development workstation contains the necessary runtimes and command-line tools:
- Operating System: Windows 10/11, macOS (Intel or Apple Silicon), or Linux.
- Node.js Runtime: Node.js 18.0.0 or higher (
node --version). - Android Platform Tools: Android Debug Bridge (
adb) installed and available in systemPATH(adb version). - Physical Android Device: A real smartphone running Android 7.0 (API level 24) or higher, connected via USB data cable or paired over local Wi-Fi.
Step 2: Launch the ScreenSync MCP Desktop Hub
Download the desktop hub distribution package from screensyncmcp.tech and extract the contents to a dedicated directory. Launch the hub using the platform-specific launcher:
# 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 starts a local HTTP server listening on port 3000 to receive encrypted MediaProjection display frames and initializes the MCP stdio interface. The terminal displays your session bearer token (SCREEN_SYNC_TOKEN) and presents an ASCII pairing QR code. For detailed parameter reference, 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 add the server entry:
{
"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
- On your physical Android device, download and install the free ScreenSync MCP mobile application (
com.screensync.mcp) from Google Play. - 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.
- Tap Start Projection and confirm the native Android MediaProjection screen capture consent dialog.
- Enable Developer Options (Settings > About Phone > tap Build Number 7 times) and turn on USB Debugging. If you prefer a cable-free testing environment, review our detailed guide on Android wireless debugging for MCP servers.
- Verify ADB connectivity in your terminal:
adb devices
Step 5: Android Application Passkey Implementation
In your Android application, implement modern passkey registration and authentication using androidx.credentials:credentials:1.3.0 and androidx.credentials:credentials-play-services-auth:1.3.0. The following Kotlin class encapsulates passkey creation and assertion workflows using coroutines:
package com.example.screensync.security
import android.content.Context
import android.util.Log
import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.CreatePublicKeyCredentialResponse
import androidx.credentials.CredentialManager
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import androidx.credentials.GetPublicKeyCredentialOption
import androidx.credentials.PublicKeyCredential
import androidx.credentials.exceptions.CreateCredentialCancellationException
import androidx.credentials.exceptions.CreateCredentialCustomException
import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.GetCredentialCancellationException
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.NoCredentialException
class PasskeyAuthManager(private val context: Context) {
private val credentialManager = CredentialManager.create(context)
companion object {
const val TAG = "ScreenSyncPasskeyQA"
}
suspend fun registerPasskey(
creationJsonPayload: String
): Result<CreatePublicKeyCredentialResponse> {
Log.i(TAG, "Initiating passkey registration request")
val request = CreatePublicKeyCredentialRequest(creationJsonPayload)
return try {
val response = credentialManager.createCredential(
context = context,
request = request
) as CreatePublicKeyCredentialResponse
Log.i(TAG, "Passkey registration successful: ${response.registrationResponseJson}")
Result.success(response)
} catch (e: CreateCredentialCancellationException) {
Log.w(TAG, "Passkey creation cancelled by user: ${e.message}")
Result.failure(e)
} catch (e: CreateCredentialCustomException) {
Log.e(TAG, "Custom credential exception: ${e.type} - ${e.message}", e)
Result.failure(e)
} catch (e: CreateCredentialException) {
Log.e(TAG, "Passkey creation failed: ${e.message}", e)
Result.failure(e)
}
}
suspend fun authenticateWithPasskey(
requestJsonPayload: String
): Result<String> {
Log.i(TAG, "Initiating passkey authentication request")
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(requestJsonPayload)
val getCredRequest = GetCredentialRequest(
listOf(getPublicKeyCredentialOption)
)
return try {
val result: GetCredentialResponse = credentialManager.getCredential(
context = context,
request = getCredRequest
)
val credential = result.credential
if (credential is PublicKeyCredential) {
val responseJson = credential.authenticationResponseJson
Log.i(TAG, "Passkey authentication successful: $responseJson")
Result.success(responseJson)
} else {
Log.e(TAG, "Unexpected credential type returned: ${credential.type}")
Result.failure(IllegalStateException("Unsupported credential type"))
}
} catch (e: GetCredentialCancellationException) {
Log.w(TAG, "User cancelled passkey selection: ${e.message}")
Result.failure(e)
} catch (e: NoCredentialException) {
Log.w(TAG, "No passkey credentials available on device: ${e.message}")
Result.failure(e)
} catch (e: GetCredentialException) {
Log.e(TAG, "Passkey authentication error: ${e.message}", e)
Result.failure(e)
}
}
}
When building apps that pair passkey authentication with biometric prompts or hardware-backed keystores, explore our companion guide on testing Android biometrics with Claude Code and MCP. If your application requires camera-based QR code passkey cross-device pairing, review our in-depth analysis on testing Android runtime permissions with Claude Code and MCP.
Core QA Workflows: Automating Registration, Passkey Selection, and Biometric Fallback
Automating Android passkey quality assurance requires validating three essential flows: initial passkey registration with WebAuthn challenge signing, 1-tap passkey authentication from the Google Play Services bottom sheet, and graceful fallback to device screen lock or password autofill when biometric hardware verification fails.
Workflow 1: Validating New Passkey Creation Flow
Validating new passkey creation requires asserting that the application receives a cryptographic challenge from the server, triggers the Google Play Services passkey enrollment bottom sheet, prompts the user to save the passkey to Google Password Manager, and returns valid WebAuthn clientDataJSON and attestationObject payloads.
Developer Instruction:
"Claude, launch com.example.screensync.security, navigate to the account settings screen, tap 'Create Passkey', inspect the Google Play Services bottom sheet using get_ui_hierarchy, tap 'Continue', stream get_logcat to verify that registrationResponseJson was received, and assert that the UI displays 'Passkey Registered'."
Step 1: Launch Application and Trigger Passkey Enrollment
Claude Code invokes control_launch_app { package: "com.example.screensync.security" }. Upon screen rendering, Claude Code dispatches control_tap on the "Create Passkey" button, triggering PasskeyAuthManager.registerPasskey() with a mock WebAuthn challenge:
{
"challenge": "dGVzdF9jaGFsbGVuZ2Vfbm9uY2VfMTI4Yml0cw==",
"rp": {
"name": "ScreenSync Security Lab",
"id": "screensyncmcp.tech"
},
"user": {
"id": "dXNlcl84OTQyMWFiYw==",
"name": "[email protected]",
"displayName": "Staff Mobile Engineer"
},
"pubKeyCredParams": [
{ "type": "public-key", "alg": -7 },
{ "type": "public-key", "alg": -257 }
],
"timeout": 60000,
"attestation": "none"
}
Step 2: Assert Google Play Services Bottom Sheet via UI Hierarchy
Claude Code invokes get_ui_hierarchy to inspect the out-of-process modal:
<node class="android.widget.FrameLayout" package="com.google.android.gms">
<node class="android.widget.TextView" resource-id="com.google.android.gms:id/header_title" text="Create a passkey" />
<node class="android.widget.TextView" resource-id="com.google.android.gms:id/account_email" text="[email protected]" />
<node class="android.widget.TextView" resource-id="com.google.android.gms:id/provider_name" text="Saved to Google Password Manager" />
<node class="android.widget.Button" resource-id="com.google.android.gms:id/continue_button" text="Continue" />
</node>
Step 3: Tap Continue and Stream Logcat Verification
Claude Code calls control_tap on the coordinates of the continue_button. It immediately streams get_logcat with tag ScreenSyncPasskeyQA:
I ScreenSyncPasskeyQA: Initiating passkey registration request
D Fido2PendingIntent: Constructing MakeCredentialRequest for RP: screensyncmcp.tech
D KeymasterUtils: Hardware keypair generated in StrongBox Keymaster (Algorithm: EC, Curve: P-256)
I ScreenSyncPasskeyQA: Passkey registration successful: {"id":"AbCdEf123...","rawId":"...","response":{"clientDataJSON":"...","attestationObject":"..."}}
Claude Code calls get_ui_hierarchy to assert that the application displays "Passkey Registered" with a green verification badge.
Workflow 2: Automating Passkey Sign-In and Account Selection
Automating passkey sign-in requires validating that Credential Manager queries local and synchronized passkeys, displays an interactive account picker bottom sheet, handles user account selection, and securely signs the server challenge.
Developer Instruction:
"Claude, log out of com.example.screensync.security, tap 'Sign in with Passkey', verify that the Google Play Services account picker appears with get_ui_hierarchy, select the account '[email protected]', and assert that authentication succeeds."
Step 1: Trigger Sign-In Flow
Claude Code navigates to the login screen and taps "Sign in with Passkey". The application issues GetCredentialRequest with GetPublicKeyCredentialOption:
{
"challenge": "YXV0aF9jaGFsbGVuZ2Vfbm9uY2VfMjU2Yml0cw==",
"rpId": "screensyncmcp.tech",
"timeout": 60000,
"userVerification": "required"
}
Step 2: Inspect Account Picker Bottom Sheet
Claude Code executes get_ui_hierarchy to parse the account selection list rendered by com.google.android.gms:
<node class="android.widget.LinearLayout" package="com.google.android.gms" resource-id="com.google.android.gms:id/credential_container">
<node class="android.widget.TextView" resource-id="com.google.android.gms:id/title" text="Choose a passkey" />
<node class="android.widget.RelativeLayout" resource-id="com.google.android.gms:id/credential_item">
<node class="android.widget.TextView" resource-id="com.google.android.gms:id/user_name" text="[email protected]" />
<node class="android.widget.TextView" resource-id="com.google.android.gms:id/display_name" text="Staff Mobile Engineer" />
</node>
</node>
Step 3: Select Credential and Assert WebAuthn Signature
Claude Code calls control_tap directly on the credential row. It reads get_logcat:
I ScreenSyncPasskeyQA: Initiating passkey authentication request
D Fido2PendingIntent: Processing GetAssertionRequest for RP: screensyncmcp.tech
D KeymasterUtils: Hardware signature generated via Keystore TEE
I ScreenSyncPasskeyQA: Passkey authentication successful: {"id":"AbCdEf123...","response":{"authenticatorData":"...","clientDataJSON":"...","signature":"MEQCICx4...","userHandle":"..."}}
Claude Code captures get_latest_screenshot to confirm that the user has been routed to the authenticated home dashboard. To understand how to control physical hardware seamlessly without rooting, explore our foundational guide on how to control an Android phone with AI without root.
Workflow 3: Testing Biometric Cancellation, Timeout, and Password Fallback
Robust mobile applications must handle real-world edge cases: users dismissing the passkey bottom sheet, biometric sensor timeouts, and hardware authentication failures. Claude Code validates that the application catches GetCredentialCancellationException and presents an immediate fallback to traditional password or PIN fields.
Developer Instruction:
"Claude, trigger passkey sign-in, simulate user cancellation by tapping outside the Google Play Services modal, assert via get_logcat that GetCredentialCancellationException was caught, and verify that the UI falls back to manual email and password input fields."
Step 1: Trigger Sign-In and Dismiss Modal
Claude Code triggers passkey login, rendering the Google Play Services bottom sheet. Claude Code dispatches control_tap on coordinates outside the modal bounds (e.g., top margin (W*0.5, H*0.1)), simulating a user tap-to-dismiss gesture.
Step 2: Assert Cancellation Exception in Logcat
Claude Code streams get_logcat:
W ScreenSyncPasskeyQA: User cancelled passkey selection: androidx.credentials.exceptions.GetCredentialCancellationException: User cancelled the modal dialog
I LoginActivity: Credential Manager cancelled; displaying fallback password login form
Step 3: Assert Fallback Form via UI Hierarchy
Claude Code calls get_ui_hierarchy to assert that the application revealed the manual credential inputs:
<node class="android.widget.EditText" resource-id="com.example.screensync.security:id/password_input" />
<node class="android.widget.Button" resource-id="com.example.screensync.security:id/submit_password_button" text="Sign In with Password" />
| Passkey QA Scenario | ScreenSync MCP Tools | Primary Detection Mechanism | Critical Vulnerability Caught |
|---|---|---|---|
| Passkey Registration | get_ui_hierarchy, get_logcat |
Accessibility tree match + registrationResponseJson |
Server challenge rejected or missing RP ID configuration |
| Passkey Sign-In | control_tap, get_logcat |
Bottom sheet tap + authenticationResponseJson |
Signature assertion mismatch or expired WebAuthn token |
| User Dismissal | control_tap, get_ui_hierarchy |
Logcat GetCredentialCancellationException |
App freezes indefinitely when user taps outside bottom sheet |
| No Stored Passkeys | get_logcat, get_ui_hierarchy |
Logcat NoCredentialException handling |
App fails to render password fallback for new users |
| Digital Asset Links Error | get_logcat |
Logcat TYPE_CREATE_CREDENTIAL_PROVIDER_CONFIGURATION |
SHA-256 fingerprint missing from assetlinks.json |
| Biometric Sensor Lockout | ADB Shell, get_logcat |
Logcat BiometricService: ERROR_LOCKOUT |
App crashes when too many invalid biometric attempts occur |
| Hybrid Web-to-App Login | 197 web_* Extension Tools |
Browser tab check + mobile passkey state | Cross-device QR code handshake fails to complete |
Triaging Passkey Failures: Digital Asset Links, User Cancellation, and OEM KeyStore Bugs
Triaging Android passkey failures requires diagnosing system error signatures emitted by Google Play Services, Digital Asset Links verifiers, and OEM KeyStore daemons. Claude Code captures real-time logcat traces for domain mismatches, cryptographic origin validation failures, and biometric timeouts, verifying that mobile applications handle authentication exceptions gracefully.
When passkey operations fail on physical devices, the underlying cause is rarely an application runtime exception. Instead, failure modes originate within the operating system security daemons. By analyzing telemetry through get_logcat, Claude Code autonomously triages and remediates common failure modes:
1. Triaging Digital Asset Links Mismatches
The most prevalent passkey failure in continuous deployment pipelines is the CreateCredentialCustomException triggered by asset links validation failure. When the signing certificate used for the local debug APK does not match the SHA-256 fingerprint registered in the server's assetlinks.json, Google Play Services refuses to associate the domain with the app.
- Diagnostic Logcat Signature:
E CredentialManager: Provider failure: androidx.credentials.TYPE_CREATE_CREDENTIAL_PROVIDER_CONFIGURATION_EXCEPTION E Fido2PendingIntent: Caller package com.example.screensync.security is not linked to origin screensyncmcp.tech E Fido2PendingIntent: Asset Links verification failed: SHA-256 [14:8B:2A:...] not found in assetlinks.json - Autonomous Remediation Action: Claude Code detects the configuration mismatch, extracts the debug keystore's SHA-256 fingerprint via
keytool -list -v -keystore ~/.android/debug.keystore, and outputs the updatedassetlinks.jsonsnippet required for the server. For applications that embed authentication within WebViews, review our guide on testing Android WebViews and hybrid apps with Claude Code and MCP.
2. Triaging Hardware TEE KeyStore Lockups on Custom OEM Skins
Custom OEM distributions (such as Samsung One UI running Knox or Xiaomi HyperOS) enforce proprietary cryptographic rate-limiting policies within their hardware Keymaster daemons. If an automated test suite rapidly generates and discards passkeys during regression testing, the hardware TEE enters a temporary lockout state.
- Diagnostic Logcat Signature:
E KeymasterUtils: Hardware keypair creation aborted: KeymasterResponseCode(-28) KM_ERROR_OPERATION_CANCELLED W KeyStore: System Keystore rate limit exceeded. TEE lock active for 30 seconds. E ScreenSyncPasskeyQA: Passkey creation failed: androidx.credentials.exceptions.CreateCredentialInterruptedException - Autonomous Remediation Action: Claude Code identifies the hardware TEE rate-limiting signature, inserts an exponential backoff sleep loop into the test harness, and prevents the continuous integration worker from marking the test as an application defect.
3. Triaging Biometric Sensor Lockouts
When evaluating fallback flows, repeated failed biometric attempts cause the Android BiometricService to enforce temporary or permanent sensor lockouts (BIOMETRIC_ERROR_LOCKOUT_PERMANENT).
- Diagnostic Logcat Signature:
W BiometricService: Too many failed attempts. Fingerprint sensor locked. E CredentialManager: Biometric prompt returned ERROR_LOCKOUT. Falling back to device credential. - Autonomous Remediation Action: Claude Code dispatches an ADB command to reset the biometric sensor lockout state or provides device PIN credentials to unlock the Keystore daemon. To learn more about securing communications between the workstation hub and mobile devices, read our architecture review on security and privacy in mobile screen MCP servers.
| Passkey Error Signature | Subsystem / Component | Root Cause Description | Engineering Remediation |
|---|---|---|---|
| ProviderConfigurationException | Google Play Services / FIDO2 | Calling app signature missing from server assetlinks.json |
Register debug keystore SHA-256 in /.well-known/assetlinks.json |
| GetCredentialCancellationException | CredentialManager Framework | User dismissed account bottom sheet or tapped backdrop | Display fallback password or magic link authentication UI |
| NoCredentialException | Google Password Manager | No matching passkeys or credentials found for RP ID | Prompt user to register a new passkey or use password autofill |
| CreateCredentialInterruptedException | Hardware Keymaster / TEE | Hardware Keystore rate limit or concurrent crypto lock | Implement test retry backoff; avoid rapid key generation bursts |
| BiometricErrorLockout | BiometricService / SystemUI | Too many invalid biometric verification attempts | Unlock device with primary device PIN/password via ADB |
| WebAuthnClientDataJsonMismatch | Server RP Middleware | Challenge nonce expired or RP ID domain mismatch | Ensure challenge timestamp is valid (< 5 min) and matches origin |
| UnsupportedCredentialTypeException | CredentialManager Client | App received password or federation credential unexpectedly | Implement multi-credential handlers (CustomCredential, PasswordCredential) |
Production Checklist for Automated Android Passkey Testing
Automating Android passkey testing requires verifying workstation runtime environments, local desktop hub pairing, mobile application MediaProjection screen capture consent, ADB shell access, and Digital Asset Links configurations before deploying autonomous mobile testing pipelines across physical continuous integration device fleets.
- [ ] Workstation environment runs Node.js 18+ with Android Platform Tools
adbconfigured in systemPATH. - [ ] ScreenSync MCP desktop hub launched and actively listening on HTTP port 3000 (
start-hub.batorstart-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.
- [ ] Target device and workstation paired over local Wi-Fi subnet; mDNS discovery or QR code pairing authenticated.
- [ ] MediaProjection screen capture consent granted on device for ~94 ms low-latency visual streaming without root.
- [ ] USB debugging or wireless debugging authorized on target Android hardware (
adb devicesconfirms authorized status). - [ ] Application implements
androidx.credentials:credentials:1.3.0andcredentials-play-services-auth:1.3.0. - [ ] Digital Asset Links (
/.well-known/assetlinks.json) deployed on production and staging RP domains with correct SHA-256 fingerprints. - [ ] Claude Code monitors
CredentialManager,Fido2PendingIntent, andBiometricPromptlogcat tags usingget_logcat. - [ ] Test scripts simulate user cancellation and verify that the UI gracefully reveals manual email/password fallbacks.
- [ ] Test harness evaluates hardware Keystore resilience across genuine physical devices rather than virtual emulators.
| Architecture Layer | Verification Objective | MCP Command / Diagnostic Tool | Acceptance Criteria |
|---|---|---|---|
| Visual Capture Plane | Low-Latency Display Stream | get_latest_screenshot |
Frame delivered in ~94 ms over LAN via MediaProjection |
| Bottom Sheet Modal | Credential Chooser Display | get_ui_hierarchy |
Modal rendered by com.google.android.gms with valid account list |
| Touch Event Dispatch | Account Row Selection | control_tap on account row |
Account item triggers WebAuthn public key signing |
| Biometric Challenge | Visual Sensor Glyph | get_latest_screenshot |
Biometric fingerprint or face glyph visible without graphical tearing |
| Logcat Telemetry | Cryptographic Assertion | get_logcat (filter: ScreenSyncPasskeyQA) |
authenticationResponseJson returned with valid signature |
| Fallback Form State | Graceful Degradation | get_ui_hierarchy |
Password fields rendered upon GetCredentialCancellationException |
| Full-Stack Assertion | Cloud Dashboard Sync | 197 web_* Extension Tools |
Cloud identity portal confirms passkey token creation and login session |
Conclusion
Automating Android passkey authentication, FIDO2 WebAuthn cryptographic verification, and Credential Manager bottom sheets with Claude Code and ScreenSync MCP bridges a critical divide in modern mobile quality assurance. By combining non-invasive MediaProjection screen capture, system-wide accessibility hierarchy inspection across Google Play Services windows, deterministic touch event dispatch via ADB, and out-of-process logcat telemetry, engineering teams can validate complex passwordless journeys across genuine physical Android hardware.
ScreenSync MCP provides the ScreenSync MCP Android app, ScreenSync MCP hub, and ScreenSync MCP browser extension for engineering teams worldwide. Explore our comprehensive technical documentation on our developer portal, consult the setup guide to deploy your local workstation gateway, and download the free mobile application directly: Get it on Google Play to start automating real physical Android hardware today.
Questions, answered
Why do traditional Android test frameworks fail to test Credential Manager passkeys?
Traditional frameworks like Espresso fail because the Credential Manager bottom sheet and biometric verification dialogs are rendered out-of-process by Google Play Services and System UI. In-process test runners cannot access external window hierarchies, cannot click system modals, and cannot evaluate hardware-backed Trusted Execution Environment operations.
How does ScreenSync MCP inspect Google Play Services passkey bottom sheets without root?
ScreenSync MCP inspects Google Play Services bottom sheets by querying Android's system-level accessibility framework via get_ui_hierarchy. Unlike Espresso, Android's accessibility node trees span system overlay windows, allowing AI agents to locate passkey account rows, usernames, and action buttons without device root privileges.
Can Claude Code automate passkey account selection on physical Android devices?
Yes, Claude Code automates passkey selection by combining get_ui_hierarchy to locate the coordinates of target account rows with control_tap to dispatch Linux touch events via the desktop hub. This allows agents to choose specific passkey accounts and tap continue buttons deterministically.
What is the role of Digital Asset Links in Android passkey authentication testing?
Digital Asset Links verify domain binding between the native Android app package, its signing certificate SHA-256 fingerprint, and the server Relying Party ID. If the debug keystore fingerprint is missing from assetlinks.json, Google Play Services throws an immediate ProviderConfigurationException.
How does ScreenSync MCP stream mobile screen frames during passkey testing without ADB overhead?
ScreenSync MCP streams screen frames using Android's native MediaProjection API after session initialization consent. The mobile client captures hardware SurfaceFlinger composite frames and streams them directly to the workstation desktop hub over local Wi-Fi or USB at approximately 94 milliseconds median latency without consuming ADB bandwidth.
How does Claude Code detect passkey cancellation and fallback to password login?
Claude Code detects cancellation by streaming logcat output via get_logcat, asserting the presence of GetCredentialCancellationException when the modal is dismissed, and immediately executing get_ui_hierarchy to confirm that the application UI dynamically reveals traditional email and password input fields.
Why is testing passkeys on physical Android hardware superior to testing on emulators?
Physical hardware testing is superior because production passkeys rely on hardware-backed KeyStore modules and StrongBox Trusted Execution Environments. Virtual emulators often lack Google Play Services or use synthetic software mocks that mask hardware driver timing issues, cryptographic rate limiting, and OEM-specific battery killers.
Which desktop operating systems and AI clients are supported by ScreenSync MCP?
The ScreenSync MCP desktop hub requires Node.js 18 or newer on Windows, macOS, or Linux, communicating over local stdio JSON-RPC. It supports Claude Code, Claude Desktop, Cursor, and VS Code agent mode. Web-based LLM interfaces requiring remote HTTPS webhooks are not supported.




