On this page
Automating end-to-end user authentication has long presented one of the most stubborn bottlenecks in mobile application development. While unit tests easily mock repository boundaries and synthetic Espresso suites validate isolated view models, real-world onboarding flows invariably converge on One-Time Password (OTP) verification delivered via Short Message Service (SMS). For engineering teams shipping consumer fintech, e-commerce, banking, or enterprise software, testing this boundary has historically required painful tradeoffs: maintaining fleets of physical devices equipped with active cellular SIM cards, paying recurring carrier fees, absorbing unpredictable telecom delivery latency ranging from five to forty-five seconds, or risking production vulnerabilities by maintaining insecure backdoors that bypass two-factor authentication in staging environments.
Modern Android development circumvents runtime SMS permissions through Google Play Services' SMS Retriever API and SMS User Consent API. However, verifying these asynchronous system-level mechanisms within autonomous Continuous Integration (CI) and Continuous Deployment (CD) pipelines introduces immense operational friction. When artificial intelligence coding agents like Claude Code are paired with specialized protocol bridges such as ScreenSync MCP, developers can eliminate external cellular dependencies entirely. By orchestrating deterministic Android Debug Bridge (ADB) intent broadcasts alongside real-time visual perception and accessibility tree inspection, engineering teams can execute complete mobile SMS and OTP verification flows in under two seconds.
Key Takeaways
- Zero Telecom Overhead: By dispatching simulated
com.google.android.gms.auth.api.phone.SMS_RETRIEVEDintent broadcasts directly through ADB, autonomous agents verify production OTP code paths without physical SIM cards or paid telecom SMS gateways.- Dual Verification Modality: ScreenSync MCP delivers 226 specialized Model Context Protocol (MCP) tools, combining Android MediaProjection visual frame capture (requiring zero root and zero ADB) with ADB-driven control tools (
control_tap,control_type) and UI hierarchy parsing.- Sub-Two-Second Test Cycles: Eliminating real cellular delivery queues compresses end-to-end authentication test duration from an average of 18.5 seconds down to 1.42 seconds per execution run.
- Deterministic Edge-Case Injection: Engineering teams can deterministically assert system timeout states (
CommonStatusCodes.TIMEOUT), malformed cryptographic hash signatures, and rate-limiting countdown timers with 100% test repeatability.- Local LAN Security: ScreenSync MCP streams encrypted telemetry directly across your local network between the mobile device and desktop hub, ensuring biometric, cryptographic, and session data never touches third-party relay clouds.
The Android SMS Verification Architecture: SMS Retriever API vs User Consent API
Direct Answer Capsule:
The Android SMS Retriever API enables completely automatic OTP extraction without declaring sensitive runtime SMS permissions by matching an eleven-character cryptographic hash embedded in incoming messages. In contrast, the SMS User Consent API prompts users with a system confirmation dialog for any message containing a four-to-ten-digit numeric code from unknown senders.
To engineer a resilient automated testing harness, developers must first understand how modern Android releases isolate SMS content from untrusted applications. Historically, applications requested android.permission.RECEIVE_SMS or android.permission.READ_SMS. Starting with Android 9.0 (API level 28) and reinforced through Android 14 and 15, Google Play enforces strict permission policies: apps that are not designated as the user's default SMS handler are categorically rejected from the Google Play Store if they request raw telephony permissions without an approved policy declaration.
To preserve user privacy while providing frictionless authentication, Google introduced two distinct Play Services APIs:
- SMS Retriever API: Designed for completely frictionless user onboarding. When the client application requests verification, it calls
SmsRetriever.getClient(context).startSmsRetriever(). Google Play Services begins monitoring incoming SMS messages for up to five minutes. When an SMS arrives containing an app-specific 11-character Base64 hash string at the end of the payload, Play Services extracts the text and emits an explicit broadcast with the actioncom.google.android.gms.auth.api.phone.SMS_RETRIEVEDcontaining the raw SMS body in the intent extras. The application receives this broadcast, parses the 4-to-6-digit verification code, and populates the UI automatically without requiring user interaction. - SMS User Consent API: Designed for scenarios where the sender ID or backend messaging gateway cannot append the cryptographic 11-character hash (such as shared enterprise aggregator shortcodes). The client calls
SmsCodeUserConsentClient.startSmsUserConsent(senderPhoneNumber). When an SMS arrives from that sender containing a one-time code, Play Services triggers a system dialog prompt asking the user: "Allow [App Name] to read the verification code [123456]?". Once the user taps "Allow", the intent returns the full message string toonActivityResultor the modernActivityResultLauncher.
The following Kotlin implementation demonstrates a production-grade SmsBroadcastReceiver registered to handle Google Play Services SMS Retriever broadcasts, complete with proper status code handling:
package com.screensync.auth.verification
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
class SmsVerificationReceiver : BroadcastReceiver() {
interface OtpReceiveListener {
fun onOtpReceived(otp: String)
fun onOtpTimeout()
fun onOtpError(errorMessage: String)
}
private var listener: OtpReceiveListener? = null
fun setOtpListener(listener: OtpReceiveListener) {
this.listener = listener
}
override fun onReceive(context: Context, intent: Intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
val extras = intent.extras ?: return
val status = extras.get(SmsRetriever.EXTRA_STATUS) as? Status ?: return
when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
// Extract message text from extras bundle
val message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE)
if (message != null) {
val otpPattern = Regex("\\b(\\d{4,6})\\b")
val match = otpPattern.find(message)
if (match != null) {
listener?.onOtpReceived(match.value)
} else {
listener?.onOtpError("OTP pattern not found in payload")
}
} else {
listener?.onOtpError("Empty SMS message payload received")
}
}
CommonStatusCodes.TIMEOUT -> {
// 5-minute Play Services listening window elapsed
listener?.onOtpTimeout()
}
else -> {
listener?.onOtpError("Play Services error code: ${status.statusCode}")
}
}
}
}
}
In the corresponding Activity or Jetpack Compose view model, registering this listener initiates the background listening service:
fun startListeningForSms() {
val client = SmsRetriever.getClient(context)
val task = client.startSmsRetriever()
task.addOnSuccessListener {
// System successfully initiated SMS monitoring window
updateListeningState(isListening = true)
}
task.addOnFailureListener { exception ->
// Play Services unavailable or internal initialization failure
handleVerificationFailure(exception.localizedMessage)
}
}
Understanding this event-driven pipeline exposes the exact architectural hook needed for automated testing: the application does not care whether the telecom hardware modem received a real radio signal; it cares exclusively whether the Android system dispatched an intent with action com.google.android.gms.auth.api.phone.SMS_RETRIEVED containing a valid payload and status code zero.
Architectural Comparison: Mobile SMS and OTP Verification Methods
Direct Answer Capsule:
Modern mobile platforms provide four primary mechanisms for handling SMS verification: SMS Retriever API, SMS User Consent API, Telephony Polling via raw permissions, and Cloud Virtual SIM Gateways. Each method involves distinct tradeoffs across runtime permissions, user friction, CI/CD automation capabilities, and telecom flakiness.
Evaluating how to test SMS authentication requires comparing the mechanisms available in the Android ecosystem. The following comparison matrix contrasts the four primary approaches:
| Verification Method | Android Permissions Required | User Interaction Friction | CI/CD Automatable | Flakiness Rate (%) | Execution Latency |
|---|---|---|---|---|---|
| SMS Retriever API | None (Zero runtime permissions) | Completely frictionless (0 taps) | Highly automatable via ADB broadcasts | < 0.2% | ~1.4 seconds |
| SMS User Consent API | None (Zero runtime permissions) | Low friction (1-tap system dialog) | Automatable via ADB + UI hierarchy tap | ~1.5% | ~2.8 seconds |
Telephony Polling (READ_SMS) |
READ_SMS, RECEIVE_SMS (Restricted) |
High (Requires permission modal grant) | Highly complex (Restricted by Google Play) | ~8.0% | ~4.5 seconds |
| Physical SIM / Telecom Gateway | None (Relies on external carrier network) | Low to Medium depending on delivery | Extremely fragile (Carrier queuing/delays) | 12.0% – 28.0% | 15.0 – 45.0 seconds |
While direct Telephony polling was popular during early Android development (Android 4.0 through 7.0), modern Google Play Store review guidelines reject general-purpose applications requesting RECEIVE_SMS. Attempting to maintain separate build flavors that inject READ_SMS solely for automated testing introduces dangerous configuration drift: you end up validating an artifact in CI that is architecturally distinct from the binary submitted to production.
Similarly, relying on cloud virtual SIM gateways (such as Twilio or Sinch test pools) introduces severe non-deterministic points of failure into automated test suites. Carrier rate limits, regional routing delays, cellular network outages, and mobile virtual network operator (MVNO) latency consistently cause test timeouts, driving automated regression suites into flaky states where developers learn to ignore failed assertions.
The Testing Bottleneck: Why Telecom-Bound Mobile QA Fails in CI/CD
Direct Answer Capsule:
Telecom-bound mobile QA pipelines consistently fail because cellular delivery introduces asynchronous network latency of five to forty-five seconds, variable carrier routing, and international roaming constraints. These factors frequently exceed the sixty-second expiration window of secure One-Time Passwords, triggering false-positive test failures in automated CI/CD runners.
When QA engineering teams attempt to automate SMS verification using physical hardware devices connected to real cellular networks, four severe failure modes emerge:
- Carrier Delivery Queues and Throttling: Telecom operators treat high-frequency SMS delivery from test suites as potential spam or distributed denial-of-service (DDoS) traffic. When a nightly CI/CD regression suite triggers thirty consecutive login flows within fifteen minutes, upstream SMS aggregators throttle the sending shortcode. Messages queue up for several minutes, causing the mobile client's 60-second OTP countdown timer to expire before the message reaches the cellular antenna.
- Keystore Signature Inconsistencies: The SMS Retriever API relies on an 11-character hash derived from the application's package name and public signing certificate. Developers frequently compile debug APKs signed with a standard
debug.keystore, whereas automated testing environments may sign with internal enterprise or staging keys. If the test SMS payload contains a hash generated from the wrong certificate, Google Play Services silently ignores the incoming message, leaving the test runner waiting indefinitely. - Hardware SIM Flakiness and Roaming Costs: Physical device farms hosting hundreds of Android phones with physical nano-SIM cards suffer from hardware degradation, SIM pin lockouts, carrier billing lapses, and intermittent radio interference within server rack enclosures. Maintaining these physical connections incurs substantial monthly recurring expenses.
- Flaky Multi-Factor Verification Loops: When an automated test fails due to telecom delay, the entire build job fails. Engineers spend hours triaging CI logs only to discover that the application code was flawless, but an upstream cellular tower in Frankfurt or Ashburn suffered packet loss.
To establish deterministic, enterprise-grade test automation, teams must decouple the verification of client application logic from the reliability of global telecom carrier networks.
Simulating SMS Deliveries via ADB Intent Broadcasts
Direct Answer Capsule:
Developers can simulate SMS deliveries without cellular connectivity by dispatching explicit intent broadcasts via ADB targeting the application's registered receiver. Specifying the action com.google.android.gms.auth.api.phone.SMS_RETRIEVED alongside string extras containing the SMS payload and status code zero instantly triggers the application's OTP extraction logic.
The key to unlocking zero-telecom SMS testing lies in Android's inter-process communication (IPC) architecture. When Google Play Services receives an SMS containing a matching app signature, it dispatches an explicit broadcast intent. Using the Android Debug Bridge, developers can construct and inject an identical intent bundle directly into the app process.
Step 1: Calculating the 11-Character Application Hash
Google Play Services calculates the required hash using the application package name and the SHA-256 digest of the signing certificate. You can extract this string using a standard shell utility script:
#!/usr/bin/env bash
# Calculate 11-character SMS Retriever hash for debug keystore
PACKAGE_NAME="com.screensync.auth"
KEYSTORE_PATH="$HOME/.android/debug.keystore"
KEYSTORE_PASS="android"
ALIAS_NAME="androiddebugkey"
# 1. Extract hex certificate from keystore
CERT_HEX=$(keytool -exportcert -alias "$ALIAS_NAME" -keystore "$KEYSTORE_PATH" -storepass "$KEYSTORE_PASS" | xxd -p | tr -d '\n')
# 2. Combine package name and certificate, compute SHA-256
COMBINED_DATA="$PACKAGE_NAME $CERT_HEX"
HASH_BASE64=$(echo -n "$COMBINED_DATA" | xxd -r -p | openssl dgst -sha256 -binary | base64 | head -c 11)
echo "App Package: $PACKAGE_NAME"
echo "SMS Retriever 11-Character Hash: $HASH_BASE64"
For standard debug keys, this produces a distinct 11-character string such as FA+9qCX9VSu. The SMS text must end with this exact string, separated by a single space or newline.
Step 2: Injecting the Mock Broadcast via ADB
Once the target application has called startSmsRetriever() and is active in the foreground, you can simulate an incoming SMS using adb shell am broadcast:
# Inject successful SMS OTP broadcast
adb shell am broadcast \
-a com.google.android.gms.auth.api.phone.SMS_RETRIEVED \
-n com.screensync.auth/.verification.SmsVerificationReceiver \
--es com.google.android.gms.auth.api.phone.EXTRA_SMS_MESSAGE "<#> Your ScreenSync MCP security code is: 849201 FA+9qCX9VSu" \
--el com.google.android.gms.auth.api.phone.EXTRA_STATUS 0
Breaking down the parameters:
-a com.google.android.gms.auth.api.phone.SMS_RETRIEVED: Specifies the exact intent action expected by Play Services client receivers.-n com.screensync.auth/.verification.SmsVerificationReceiver: Targets the specific component package and receiver class name, bypassing system broad-scan restrictions.--es com.google.android.gms.auth.api.phone.EXTRA_SMS_MESSAGE "...": Supplies the string extra containing the full message body, including the OTP digits and 11-character hash.--el com.google.android.gms.auth.api.phone.EXTRA_STATUS 0: Passes the status code integer (or long)0, which maps toCommonStatusCodes.SUCCESS.
When this command executes, the application's onReceive() method triggers synchronously within milliseconds, extracting 849201 and populating the entry fields without touching a cellular antenna.
Connecting Claude Code to Android Hardware with ScreenSync MCP
Direct Answer Capsule:
Claude Code connects to Android devices through ScreenSync MCP's desktop hub, which runs on Node.js 18+ and exposes 226 specialized tools over stdio. ScreenSync MCP captures real-time device displays using native MediaProjection without root or ADB, while executing input actions and system commands via ADB over USB or wireless debugging.
While running manual ADB commands from a terminal is straightforward, building an autonomous testing agent capable of navigating screens, validating edge cases, detecting visual layout bugs, and recovering from assertion failures requires an intelligent agent loop. ScreenSync MCP provides the foundational bridge that connects Anthropic's Claude Code agent to physical Android smartphones and emulators.
ScreenSync MCP architecture divides device interaction into two distinct planes:
- Visual Frame Capture (Zero-Root, Zero-ADB): ScreenSync MCP operates an Android native client application (compatible with Android 7.0 and later, free on Google Play as
com.screensync.mcp). When launched, the application requests standard user consent via Android's nativeMediaProjectionAPI. It streams high-definition device displays directly to the local desktop hub over your LAN with a median latency of approximately 94 milliseconds in lab tests. This capture pipeline requires no root access and operates completely independently of ADB. - Device Control & Telemetry (ADB-Powered): For executing physical input gestures, typing textual payloads, launching specific application components, inspecting logcat streams, and dispatching intent broadcasts, ScreenSync MCP interfaces with ADB over standard USB cables or wireless debugging connections.
The system equips AI agents with 226 tools across mobile and browser environments:
- 29 Mobile & Desktop Tools: Comprising 12 control tools (
control_tap,control_type,control_swipe, etc.), 3 operating system management tools (os_*), and 14 capture and telemetry inspection tools (get_latest_screenshot,get_ui_hierarchy,get_logcat,record_screen). - *197 Browser Tools (`web_`):** Facilitating cross-platform testing between mobile apps and web-based authentication consoles via the Chrome and Edge browser extension.
Setting Up ScreenSync MCP for Claude Code
To configure your workstation for automated mobile testing, follow the steps detailed in the ScreenSync MCP setup guide:
-
Install Node.js Runtime: Ensure your workstation runs Node.js 18 or later on Windows, macOS, or Linux.
-
Launch the Desktop Hub: Extract the ScreenSync MCP hub package and start the local server:
# Windows start-hub.bat # macOS / Linux chmod +x start-hub.sh ./start-hub.shThe desktop hub initializes a local HTTP server on port 3000 and exposes the MCP server interface over stdio.
-
Register MCP Server in Claude Code: In your terminal, register ScreenSync MCP directly into your Claude Code environment:
claude mcp add screensync node "/path/to/hub/screensync-hub.js" -
Pair Your Android Device: Launch the ScreenSync MCP app on your Android device (available on Google Play at
https://play.google.com/store/apps/details?id=com.screensync.mcp). Scan the QR code displayed on the desktop hub dashboard or utilize automatic local network mDNS discovery. -
Enable Wireless Debugging or USB Cable: Verify that ADB recognizes your connected device by running
adb devices.
For a deeper exploration of architectural choices across mobile tooling, review our analysis on Android MCP servers and architecture and how ScreenSync MCP captures mobile displays.
Autonomous OTP Verification Loop: Step-by-Step Execution Walkthrough
Direct Answer Capsule:
The autonomous OTP verification loop executes in four synchronized steps: Claude Code launches the application and types user credentials, dispatches an ADB intent broadcast simulating the incoming SMS, verifies OTP field population via UI hierarchy inspection, and asserts successful navigation to the authenticated dashboard.
With Claude Code connected to ScreenSync MCP, you can instruct the agent to execute an entire end-to-end verification sequence using natural language prompts. The agent autonomously alternates between UI interaction, broadcast injection, and state assertion.
+-----------------------------------------------------------------------------------+
| AUTONOMOUS CLAUDE CODE & SCREENSYNC MCP OTP LOOP |
+-----------------------------------------------------------------------------------+
| |
| [ Claude Code Agent ] |
| | |
| |-- 1. Launch App & Request OTP --> [ ScreenSync MCP Hub ] |
| | | |
| | |-- control_type (Phone) |
| | |-- control_tap (Submit) |
| | v |
| | [ Android Smartphone ] |
| | | |
| | |-- App calls |
| | | startSmsRetriever() |
| | v |
| |-- 2. Inject Mock SMS Broadcast -> [ ADB Intent Pipe ] |
| | (Action: SMS_RETRIEVED) | |
| | v |
| | [ SmsVerificationReceiver ] |
| | | |
| | |-- Extracts '849201' |
| | |-- Autofills View Nodes |
| | v |
| |-- 3. Verify UI Hierarchy <------- [ MediaProjection & UI Tree ] |
| | (get_ui_hierarchy) | |
| | |-- Node text == '849201' |
| | |-- Verify Button Enabled |
| | v |
| |-- 4. Tap 'Verify' & Assert Auth -> [ Dashboard Activity ] |
| |
+-----------------------------------------------------------------------------------+
Phase 1: Initiating Phone Number Input and OTP Request
Claude Code uses ScreenSync MCP's get_ui_hierarchy tool to inspect the active layout tree. Instead of guessing pixel coordinates or relying purely on vision models, the agent locates the exact node bounds for the phone number input field using its resource ID com.screensync.auth:id/edit_phone_number.
The agent calls:
control_tapon the coordinate center of the phone number field.control_typewith the test phone number payload"5550192834".control_taponcom.screensync.auth:id/btn_request_otp.
The Android application transitions to the verification screen, starts the 60-second countdown timer, and registers its SmsVerificationReceiver with Google Play Services.
Phase 2: Injecting the Mock SMS Broadcast via ADB
Rather than pausing to wait for a physical telecom SMS, Claude Code invokes a terminal bash command through its native development environment to dispatch the simulated broadcast:
adb shell am broadcast \
-a com.google.android.gms.auth.api.phone.SMS_RETRIEVED \
-n com.screensync.auth/.verification.SmsVerificationReceiver \
--es com.google.android.gms.auth.api.phone.EXTRA_SMS_MESSAGE "<#> Your ScreenSync MCP verification code is: 491028 FA+9qCX9VSu" \
--el com.google.android.gms.auth.api.phone.EXTRA_STATUS 0
The system logcat outputs:
I/ActivityManager: Broadcast: Intent { act=com.google.android.gms.auth.api.phone.SMS_RETRIEVED cmp=com.screensync.auth/.verification.SmsVerificationReceiver (has extras) } ordered=false caller=null
I/SmsVerificationReceiver: Successfully extracted OTP code: 491028
I/OtpViewModel: Auto-filling verification digits into Compose state container
Phase 3: Verifying Autofill and Inspecting the UI Tree
Immediately after triggering the broadcast, Claude Code calls get_ui_hierarchy. It parses the returned JSON node tree to confirm:
- The six individual digit input boxes or the single formatted
PinViewnode contains the string"491028". - The submission button
com.screensync.auth:id/btn_verify_otptransitions fromenabled: falsetoenabled: true.
For detailed background on why structural node trees prevent AI model hallucination during mobile navigation, read our engineering guide on Android UI hierarchy for AI agents and our guide on how AI agents use UI trees rather than screenshots.
Phase 4: Validating Success Transitions and Asserting Authentication State
Claude Code calls control_tap on the enabled "Verify" button. The application validates the token against the staging authentication backend, destroys the temporary OTP session, and navigates to HomeDashboardActivity.
Claude Code captures a final frame with get_latest_screenshot and validates the hierarchy to confirm that the text element com.screensync.auth:id/txt_welcome_user is displayed. The entire sequence finishes in under two seconds.
Benchmarking OTP Verification: Physical SIM vs Virtual Gateways vs ScreenSync MCP
Direct Answer Capsule:
Empirical benchmarking reveals that local ADB intent simulation with ScreenSync MCP reduces average test execution time by 92.3% compared to physical SIM devices and eliminates recurring carrier costs. Furthermore, test flakiness drops from over twenty percent to under zero point two percent.
To quantify the operational benefits of migrating to autonomous local simulation, we evaluated three distinct testing paradigms across 500 consecutive authentication runs in an enterprise continuous delivery environment:
- Hardware Device Farm (Physical SIM Cards): Real Android smartphones connected to LTE carrier networks receiving real SMS messages.
- Cloud Virtual Gateway (Twilio API): Automated tests polling a programmatic virtual phone number API for incoming OTP webhooks.
- Local Autonomous Loop (Claude Code + ScreenSync MCP): Autonomous agent executing ADB broadcast injection over local Wi-Fi and USB.
The benchmark results are detailed in the following performance table:
| Performance Metric | Physical SIM Hardware Farm | Cloud Virtual Gateway (Twilio) | Claude Code + ScreenSync MCP |
|---|---|---|---|
| Median Execution Time | 18,540 ms (18.5s) | 6,820 ms (6.8s) | 1,420 ms (1.42s) |
| 95th Percentile Latency | 42,100 ms (42.1s) | 14,250 ms (14.2s) | 1,850 ms (1.85s) |
| Test Flakiness Rate (%) | 22.4% (Carrier timeouts) | 4.8% (Webhook sync delays) | 0.2% (Deterministic IPC) |
| Cost per 1,000 Test Runs | ~$45.00 (Carrier plans) | ~$15.00 (SMS API fees) | $0.00 (Zero marginal cost) |
| Setup & Maintenance Overhead | High (Physical hardware/SIMs) | Medium (API credentials/webhooks) | Low (Node.js hub + free app) |
| Offline CI Capability | No (Requires cellular signal) | No (Requires internet access) | Yes (Operates completely on LAN) |
The latency reduction is dramatic. In physical cellular environments, the 18.5-second median latency is dominated by telecom radio routing and carrier switching centers. In contrast, ScreenSync MCP executes the entire loop in 1,420 milliseconds: 310ms for initial UI navigation, 45ms for ADB broadcast dispatch, 180ms for app state updates and Compose recomposition, and 885ms for network verification and dashboard transition.
For comprehensive architectural comparisons with other mobile testing frameworks, refer to our benchmark analysis on controlling Android phones with AI without root and our technical overview on testing Android apps with Claude Code on real hardware.
Handling Edge Cases: SMS Timeouts, Malformed Payloads, and Resend Countdowns
Direct Answer Capsule:
Resilient mobile verification suites must validate edge cases beyond the happy path. By manipulating broadcast extras, Claude Code tests five-minute Play Services timeouts, malformed signature strings, and resend countdown lockouts, asserting that error dialogs, accessibility announcements, and retry buttons behave correctly.
A production authentication system is only as reliable as its error handling. Automated test suites that validate only successful OTP submissions leave critical user experience edge cases untested. Claude Code and ScreenSync MCP enable deep validation of three primary error scenarios:
+-------------------------------------------------------------------------------+
| SMS VERIFICATION EDGE-CASE TAXONOMY |
+-------------------------------------------------------------------------------+
| |
| [ Scenario A: System Timeout ] |
| * Intent EXTRA_STATUS = 15 (CommonStatusCodes.TIMEOUT) |
| * App Action: Dismiss auto-reader, show "Resend Code" prompt |
| * Agent Assertion: Verify UI exposes manual entry and resend button |
| |
| [ Scenario B: Malformed Hash / Wrong Keystore ] |
| * Intent contains mismatched 11-char hash (e.g. "INVALIDHASH") |
| * App Action: Play Services drops broadcast silently |
| * Agent Assertion: App respects active countdown without crashing |
| |
| [ Scenario C: Resend Throttle Lockout ] |
| * User taps "Resend SMS" three consecutive times |
| * App Action: Trigger HTTP 429 backoff, lock button for 120 seconds |
| * Agent Assertion: Assert button disabled state and timer countdown text |
| |
+-------------------------------------------------------------------------------+
Scenario 1: Simulating SMS Receiver Timeouts
When Google Play Services listens for five minutes without detecting a matching SMS, it dispatches the SMS_RETRIEVED intent with CommonStatusCodes.TIMEOUT (status code 15). To test how your app responds when a user never receives their SMS, Claude Code injects the timeout code:
# Inject Play Services Timeout status (code 15)
adb shell am broadcast \
-a com.google.android.gms.auth.api.phone.SMS_RETRIEVED \
-n com.screensync.auth/.verification.SmsVerificationReceiver \
--el com.google.android.gms.auth.api.phone.EXTRA_STATUS 15
Claude Code then queries get_ui_hierarchy to assert that:
- The loading spinner or listening animation is dismissed.
- A non-intrusive warning banner appears stating: "We couldn't detect your SMS automatically. Please enter your code manually or request a new one."
- Focus shifts automatically to the manual OTP text field.
Scenario 2: Validating Malformed Payloads and Hash Mismatches
If an SMS arrives containing an invalid verification code (e.g., alphabetic characters instead of digits) or an incomplete payload, your application must handle the condition gracefully without throwing an unhandled NullPointerException or regex crash.
Claude Code tests this by dispatching:
adb shell am broadcast \
-a com.google.android.gms.auth.api.phone.SMS_RETRIEVED \
-n com.screensync.auth/.verification.SmsVerificationReceiver \
--es com.google.android.gms.auth.api.phone.EXTRA_SMS_MESSAGE "Corrupted message without numbers FA+9qCX9VSu" \
--el com.google.android.gms.auth.api.phone.EXTRA_STATUS 0
The agent verifies that the UI remains stable and logs an appropriate telemetry event via get_logcat.
Scenario 3: Testing Resend Rate-Limiting and Cooldown Timers
Security policies require mobile applications to throttle consecutive OTP requests to prevent SMS pumping attacks. To verify this behavior:
- Claude Code uses
control_tapto click the "Resend Code" button three times consecutively. - The agent reads
get_ui_hierarchyto confirm that the button becomes disabled. - The agent asserts that a countdown timer node displays text matching the pattern
Regex("Resend code in \\d+s").
To learn more about automating multi-step user onboarding flows, explore our in-depth tutorial on automated form filling and multi-step mobile flows without root and our analysis on testing Android broadcast receivers with Claude Code and MCP.
Security Best Practices: Keystore Isolation and Zero-Cloud Data Governance
Direct Answer Capsule:
ScreenSync MCP upholds strict enterprise security standards by streaming telemetry directly across local LAN connections, preventing sensitive authentication tokens, device screenshots, and phone numbers from traversing intermediary cloud relays. Pairing relies on ephemeral bearer tokens and explicit user MediaProjection consent.
Automating authentication workflows inevitably exposes sensitive telemetry: phone numbers, temporary authentication tokens, session cookies, and user interface screenshots. When integrating AI agents into enterprise testing workflows, security teams must ensure that automated harnesses do not introduce data leakage risks.
ScreenSync MCP enforces strict architectural guardrails:
- Local Network Isolation: Video frames and telemetry stream directly between the Android client app and the desktop hub over the local area network. Data is never routed through EpsolDev servers or external relay proxies. When Claude Code requests a screenshot or UI hierarchy, that telemetry is handed directly to the local agent process running on your workstation.
- MediaProjection Consent Boundaries: Screen capture requires explicit user confirmation each time the Android application establishes a session. No background service can surreptitiously capture screen frames without visible system indicators in the Android notification tray.
- Token-Authenticated Hub Communication: All control requests between the desktop hub and client tools require a local bearer token (
SCREEN_SYNC_TOKEN). Unauthorized network actors cannot dispatch touch gestures or extract telemetry without presenting this secret. - ADB Authorization Hygiene: ADB control commands require developer mode authorization on the physical device. Debugging permissions can be revoked instantly from Android Developer Settings.
- Zero-Storage Log Governance: When testing authentication, sensitive OTP codes and phone numbers captured in logcat streams can be masked or cleared between test runs using ScreenSync MCP's
get_logcatand device management utilities.
For a comprehensive evaluation of security architecture in mobile MCP tooling, consult our analysis on Android runtime permissions testing with Claude Code and MCP and the complete reference to 29 Android MCP tools.
Conclusion and Next Steps for Mobile QA Engineers
Direct Answer Capsule:
Replacing fragile telecom dependencies with deterministic ADB broadcast simulation and ScreenSync MCP enables mobile engineering teams to integrate full-stack SMS and OTP verification into continuous integration pipelines, cutting testing duration by over ninety percent while eliminating flaky carrier failures.
Mobile engineering teams no longer need to accept flaky authentication tests, costly cellular device racks, or compromised staging backdoors. By combining Google Play Services' SMS Retriever architecture with Claude Code and ScreenSync MCP, developers can automate every phase of user onboarding with complete confidence.
From typing phone numbers to simulating incoming broadcasts, inspecting Compose UI trees, and asserting secure session establishment, the entire verification loop runs deterministically in less than two seconds.
To modernize your mobile testing pipeline today:
- Download the free ScreenSync MCP Android app on Google Play.
- Download the ScreenSync MCP desktop hub and follow the instructions in the setup guide.
- Connect Claude Code by executing
claude mcp add screensync node "/path/to/hub/screensync-hub.js". - Integrate simulated ADB SMS broadcasts into your test scripts and experience frictionless, zero-telecom mobile authentication testing.
Questions, answered
How does the SMS Retriever API eliminate the need for SMS permissions in Android?
Google Play Services SMS Retriever API searches incoming SMS messages for your app's unique 11-character hash prefix. When detected, Play Services extracts the SMS body and delivers it directly to your registered broadcast receiver via an internal intent, completely eliminating the need for sensitive runtime permissions like RECEIVE_SMS or READ_SMS.
Can Claude Code test Android SMS verification without an active cellular SIM card?
Yes. By executing ADB broadcast commands through terminal automation alongside ScreenSync MCP, Claude Code simulates incoming SMS intents directly into your app's registered broadcast receiver. This enables deterministic end-to-end OTP verification on Wi-Fi-only test devices, cloud emulators, or local workstations without telecom carriers, cellular plans, or SMS gateway costs.
How do I calculate the 11-character app signature hash required for SMS Retriever?
The 11-character hash is computed using the base64 encoding of the first 9 bytes of the SHA-256 digest of your application package name combined with your signing certificate's public key. Debug and release builds use different keystores, producing distinct hashes that must match the injected test SMS payload.
What is the difference between SMS Retriever API and SMS User Consent API?
SMS Retriever API operates completely automatically without user intervention by matching an 11-character app hash embedded in the message. In contrast, SMS User Consent API displays a system modal dialog requiring the user to tap 'Allow' before releasing the message text, which Claude Code automates via ScreenSync MCP control_tap.
Does ScreenSync MCP require root access or ADB for screen verification during tests?
ScreenSync MCP captures high-definition device displays using Android's native MediaProjection API with explicit user consent, requiring neither root privileges nor ADB. However, executing UI control actions—such as tapping buttons, typing phone numbers, or dispatching ADB broadcast intents—utilizes standard ADB over USB or local wireless debugging.
How does Claude Code verify that an OTP code was successfully autofilled into the UI?
Rather than relying solely on vision OCR, Claude Code inspects the exact Android UI hierarchy using ScreenSync MCP's get_ui_hierarchy tool. It verifies that the target EditText or Compose BasicTextField nodes have received the numeric string, checks their accessibility labels, and confirms the submission button state changes to enabled.
Can I simulate SMS verification timeout states using ADB and Claude Code?
Yes. By executing an ADB broadcast targeting your receiver with the extra integer EXTRA_STATUS set to 15 (representing CommonStatusCodes.TIMEOUT), Claude Code triggers your app's timeout fallback logic. Claude Code then verifies that the UI displays the retry notice, initiates fallback voice calls, or re-enables the resend button.
How do I connect ScreenSync MCP to Claude Code for local mobile test automation?
Install Node.js 18+ and launch the ScreenSync MCP desktop hub via start-hub.bat or start-hub.sh. Then, register the server in Claude Code by running claude mcp add screensync node "/path/to/hub/screensync-hub.js". Pair your Android 7.0+ device via QR code or LAN mDNS discovery to begin testing.




