ScreenSync MCP guide

Testing Android Location and Geofencing with Claude Code and MCP

Automate Android FusedLocationProvider, mock GPS coordinates, geofence dwell transitions, and precise permission flows on physical devices using Claude Code and ScreenSync MCP.

A smartphone showing a digital map with geofence circles connected to a laptop on a dark desk.

Automating spatial intelligence, dynamic GPS routes, and geographic boundary triggers represents one of the most stubborn engineering frontiers in modern mobile software quality assurance. From micro-mobility fleets and rideshare dispatch engines to localized retail delivery platforms, field-service utilities, and fitness tracking apps, location awareness is often the foundational business layer of the client application. Yet, testing location-sensitive code has historically proven excruciatingly manual, unreliable, and expensive. Developers have historically been forced to either physically walk and drive predetermined outdoor test routes with tethered laptops or rely on synthetic in-memory mocks and desktop emulator controls that bypass authentic mobile operating system boundaries. In-memory stubs fail to capture the real-world complexities of Android's location subsystem, including satellite multipath interference, radio antenna warm-up delays, sensor fusion drift, and the aggressive battery optimization policies enforced by modern Android releases. When a mobile application transitions between foreground navigation and background tracking, unforeseen defects in geofence dwell thresholds, location permission downgrades, or Android Doze mode throttling frequently cause silent tracking failures in production. By combining autonomous terminal-based AI coding agents like Claude Code with the Model Context Protocol (MCP) through ScreenSync MCP, mobile engineering teams can bridge desktop agent reasoning with genuine Android hardware, executing deterministic, end-to-end GPS route simulations, geofencing transition assertions, and runtime permission validations directly on physical test smartphones without requiring device root privileges.

Key Takeaways

  • Eliminating physical field testing with mock location injection: ScreenSync MCP orchestrates ADB-level mock location providers (appops set <package> android:mock_location allow), allowing Claude Code to inject precise GPS coordinates, multi-waypoint GPX routes, and simulated transit speeds into physical devices without driving or walking real-world paths.
  • Deterministic geofencing lifecycle verification: Automate Google Play Services GeofencingClient transition triggers—including GEOFENCE_TRANSITION_ENTER, GEOFENCE_TRANSITION_DWELL, and GEOFENCE_TRANSITION_EXIT—asserting instantaneous on-device push alerts and state machine updates.
  • Triage Android 12+ Approximate vs. Precise location dialogs: ScreenSync MCP inspects OS-level accessibility trees via get_ui_hierarchy to navigate and assert Android's dual-tier location permission prompts (ACCESS_FINE_LOCATION vs. ACCESS_COARSE_LOCATION) and background location (ACCESS_BACKGROUND_LOCATION) modal sheets.
  • Doze mode and power management resilience testing: Simulate deep battery optimization states using dumpsys deviceidle force-idle to verify how background location listeners and WorkManager location sync tasks survive aggressive OS throttling.
  • Native MediaProjection visual feedback at sub-100ms latency: Stream real-time display frames from physical Android smartphones to the desktop hub at approximately 94 milliseconds median latency without consuming ADB bandwidth or requiring device rooting.
  • Unified 226-tool cross-surface automation: Leverage 29 mobile and desktop tools (12 control_*, 3 os_*, 14 capture/telemetry tools) alongside 197 web_* browser tools to verify that mobile GPS check-ins synchronize in real time with backend web dispatch consoles and dispatch queues.

The Challenge of Testing Location and Geofencing on Physical Android Hardware

Testing location-aware Android applications on physical hardware is challenging because GPS satellite signals cannot be physically moved inside laboratory environments, while synthetic software mocks in unit tests bypass Google Play Services FusedLocationProviderClient IPC mechanisms. Physical device testing exposes real-world sensor drift, antenna attenuation, Android Doze mode throttling, and dual-tier location permission dialogs.

To understand why automated location testing has eluded continuous integration pipelines for over a decade, one must examine the complex hardware and software stack responsible for determining an Android device's physical coordinates. Unlike simple user interface interactions, location coordinates do not originate inside the application process; they are synthesized by the Android operating system's LocationManagerService and Google Play Services' FusedLocationProviderClient. The fused provider continuously blends signals from Global Navigation Satellite Systems (GNSS such as GPS, GLONASS, Galileo, and BeiDou), terrestrial Wi-Fi access point scans, cellular tower triangulation, and onboard inertial measurement unit (IMU) sensors including accelerometers, barometers, and gyroscopes.

Inside a laboratory, office building, or continuous integration server rack, physical Android devices suffer from acute radio frequency attenuation and multipath signal reflections. Concrete walls and metal ceilings degrade satellite reception, causing severe Dilution of Precision (DOP) and erratic coordinate jumping. When engineers attempt to run automated integration tests under these conditions, location-dependent assertions fail randomly, not due to software bugs, but because real GPS chips cannot achieve a 3D satellite fix indoors.

Furthermore, traditional software testing paradigms present severe architectural limitations:

  1. Synthetic Unit Test Stubs (MockK/Mockito): Developers frequently mock FusedLocationProviderClient in unit tests, returning static android.location.Location objects directly to coroutine flows or observers. While this verifies that internal business logic calculates distances correctly, it completely bypasses the real Android OS IPC layer, the Google Play Services daemon, background service lifecycle constraints, and location permission enforcement.
  2. Standard Desktop Android Emulators: Google APIs emulator system images include a basic extended control panel allowing developers to send static GPS points or load GPX route files via telnet geo fix commands. However, emulators run on x86 virtualized hardware that does not replicate real ARM device thermal throttling, OEM-specific battery management optimizations (such as aggressive background task killing on Samsung One UI, Xiaomi HyperOS, or OnePlus OxygenOS), or real-time camera and map OpenGL rendering pipelines.
  3. Third-Party Cloud Device Farms: Running location tests on remote cloud device farms (such as SauceLabs or BrowserStack) introduces immense network latency. Moreover, cloud devices often block mock location permissions at the enterprise MDM level or lack permission to configure low-level ADB appops, preventing developers from testing custom GPS trajectories or rapid boundary crossing sequences.

ScreenSync MCP resolves this dilemma by treating authentic, physical Android smartphones as fully controllable execution environments. Rather than requiring developers to step outside or script cumbersome emulator scripts, ScreenSync MCP interfaces directly with the device's native accessibility layer and ADB platform tools. An autonomous AI agent running in your workstation terminal—such as Claude Code—can register mock location providers, inject millisecond-precise coordinate sequences, inspect the resulting user interface via structural node trees (get_ui_hierarchy), and visually confirm vector map rendering via low-latency screen capture (get_latest_screenshot).

Location QA Dimension In-Memory Stubs (Unit Tests) Desktop Android Emulator Cloud Device Farms ScreenSync MCP + Claude Code (Physical Device)
Google Play Services IPC Execution Bypassed; returns static in-memory objects Simulated; x86 Google Play Services container Unstable; high network lag and shared accounts Native; authentic ARM Google Play Services IPC
Dynamic Waypoint Route Traversal Synthetic array iteration; no timing fidelity Telnet geo fix; brittle scripting interface Unsupported or manual upload per test run Autonomous; agent injects timestamped GPS vectors
Geofencing Dwell & Loitering Testing Mocked; cannot test real OS loitering delays Inconsistent; emulator timing clock drifts High timeout failure rate on cloud workers Deterministic; verifies loiteringDelayMs on device
Android 12 Approximate vs Precise Dialogs Cannot test; no system permission sheets Manual clicks; fragile UIAutomator scripts Flaky; regional permission variations break scripts Native; semantic node inspection via get_ui_hierarchy
Doze Mode & Power Throttling Impact Impossible; no OS battery state emulation Incomplete; synthetic battery levels Unsupported; MDM locks battery management Authentic; forces Doze states via dumpsys deviceidle
Visual Map & Polyline Assertion None; headless test runner Low frame rate software OpenGL rendering Compressed WebRTC stream with heavy lag Native MediaProjection (~94 ms latency) via get_latest_screenshot

Android Location Architecture: FusedLocationProviderClient, Mock Providers, and Runtime Permissions

Android location services rely on Google Play Services FusedLocationProviderClient to synthesize GNSS, Wi-Fi, and cellular radio signals into unified coordinates. Modern implementations must handle granular runtime permissions, including Android 12 approximate versus precise location toggles and Android 10 background access dialogs, while managing geofencing transition intents through pending broadcast receivers.

Modern Android development has long retired the legacy framework android.location.LocationManager for consumer applications in favor of the Google Play Services Location API. At the center of this subsystem is FusedLocationProviderClient, which abstracts complex sensor fusion algorithms behind clean, asynchronous interfaces. When constructing location requests, applications specify an operational priority that informs the operating system of the desired balance between battery consumption and spatial accuracy:

  • Priority.PRIORITY_HIGH_ACCURACY: Requests the most accurate location possible, primarily utilizing GNSS satellite receivers combined with Wi-Fi and cell tower triangulation. Essential for turn-by-turn navigation, geofence boundary triggers, and real-time delivery tracking.
  • Priority.PRIORITY_BALANCED_POWER_ACCURACY: Optimizes power efficiency by prioritizing Wi-Fi access point fingerprinting and cellular tower IDs, typically delivering accuracy within approximately 100 meters while consuming minimal battery.
  • Priority.PRIORITY_LOW_POWER: Employs coarse cell tower triangulation to provide city-level accuracy (~10 km) for weather widgets or regional content customization.
  • Priority.PRIORITY_PASSIVE: Consumes zero additional battery by silently listening for location updates requested by other applications on the device.

Production Kotlin Implementation: Robust Location & Geofence Manager

The following production-ready Kotlin class encapsulates modern Android location tracking, dynamic geofence registration, and comprehensive permission state handling using FusedLocationProviderClient and GeofencingClient:

package com.example.screensync.location

import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.location.Location
import android.os.Looper
import com.google.android.gms.location.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.tasks.await

sealed class LocationState {
    object Idle : LocationState()
    object RequestingPermissions : LocationState()
    data class LocationUpdate(val location: Location, val isMock: Boolean) : LocationState()
    data class GeofenceTriggered(val geofenceId: String, val transitionType: Int) : LocationState()
    data class Error(val message: String, val exception: Exception? = null) : LocationState()
}

class LocationTrackingManager(private val context: Context) {

    private val fusedLocationClient: FusedLocationProviderClient =
        LocationServices.getFusedLocationProviderClient(context)
    private val geofencingClient: GeofencingClient =
        LocationServices.getGeofencingClient(context)

    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(context, GeofenceBroadcastReceiver::class.java)
        PendingIntent.getBroadcast(
            context,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
        )
    }

    @SuppressLint("MissingPermission")
    fun requestLocationUpdates(
        intervalMs: Long = 5000L,
        minDistanceMeters: Float = 5.0f
    ): Flow<Location> = callbackFlow {
        val locationRequest = LocationRequest.Builder(
            Priority.PRIORITY_HIGH_ACCURACY,
            intervalMs
        ).apply {
            setMinUpdateDistanceMeters(minDistanceMeters)
            setMinUpdateIntervalMillis(intervalMs / 2)
            setGranularity(Granularity.GRANULARITY_FINE)
            setWaitForAccurateLocation(false)
        }.build()

        val locationCallback = object : LocationCallback() {
            override fun onLocationResult(result: LocationResult) {
                result.lastLocation?.let { location ->
                    trySend(location)
                }
            }

            override fun onLocationAvailability(availability: LocationAvailability) {
                if (!availability.isLocationAvailable) {
                    // Logcat warning: Device location provider temporarily unavailable
                }
            }
        }

        fusedLocationClient.requestLocationUpdates(
            locationRequest,
            locationCallback,
            Looper.getMainLooper()
        )

        awaitClose {
            fusedLocationClient.removeLocationUpdates(locationCallback)
        }
    }

    @SuppressLint("MissingPermission")
    suspend fun registerGeofence(
        geofenceId: String,
        latitude: Double,
        longitude: Double,
        radiusMeters: Float = 100.0f,
        loiteringDelayMs: Int = 30000
    ): Boolean {
        val geofence = Geofence.Builder()
            .setRequestId(geofenceId)
            .setCircularRegion(latitude, longitude, radiusMeters)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .setTransitionTypes(
                Geofence.GEOFENCE_TRANSITION_ENTER or
                Geofence.GEOFENCE_TRANSITION_DWELL or
                Geofence.GEOFENCE_TRANSITION_EXIT
            )
            .setLoiteringDelay(loiteringDelayMs)
            .build()

        val geofencingRequest = GeofencingRequest.Builder().apply {
            setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            addGeofence(geofence)
        }.build()

        return try {
            geofencingClient.addGeofences(geofencingRequest, geofencePendingIntent).await()
            true
        } catch (e: Exception) {
            false
        }
    }

    suspend fun removeGeofence(geofenceId: String): Boolean {
        return try {
            geofencingClient.removeGeofences(listOf(geofenceId)).await()
            true
        } catch (e: Exception) {
            false
        }
    }
}

Navigating the Android 12+ Permission Hierarchy

Starting in Android 12 (API 31), Google introduced a major user privacy paradigm shift: users are empowered to grant applications either "Precise" or "Approximate" location access. When an application requests both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION, the system inflates a split visual dialog. If the user selects "Approximate", the operating system withholds exact GPS coordinates and instead delivers an artificially jittered coordinate rounded to approximately 1.6 kilometers. Applications that crash or refuse to function when granted only approximate location fail Google Play Store quality guidelines. Testing this interaction requires verifying that the user interface degrades gracefully, disabling turn-by-turn routing while still permitting regional search. For teams building robust permission handling, review our companion guide on testing Android runtime permissions with Claude Code and MCP.

Permission Level Android Manifest Declaration User Prompt Presentation Delivered Accuracy High-Risk Failure Mode Caught by QA
Approximate Location android.permission.ACCESS_COARSE_LOCATION "Approximate" bubble selected ~1.6 km offset grid App crashes with SecurityException expecting fine coords
Precise Location android.permission.ACCESS_FINE_LOCATION "Precise" bubble selected ~3 to 10 meters Map markers jump or turn-by-turn guidance fails
Background Location android.permission.ACCESS_BACKGROUND_LOCATION Separate system settings screen Dependent on OS Doze state App repeatedly re-prompts, violating Google Play policy
Mock Location Permission Granted via ADB: appops set <pkg> android:mock_location allow Developer Options setting Exact injected latitude/longitude In-app fraud engines reject mock provider flags

How ScreenSync MCP Bridges Claude Code to Physical GPS Simulation

ScreenSync MCP bridges Claude Code to physical Android devices by coupling ADB shell commands for mock location registration with OS-level UI hierarchy inspection and low-latency video streaming. Claude Code injects realistic geographic coordinates, verifies visual map rendering, and asserts system permission dialog states without requiring root privileges or third-party cloud farms.

ScreenSync MCP establishes a high-throughput, low-latency bridge between developer workstation AI agents and physical mobile devices. Rather than relying on cloud proxies or slow polling mechanisms, ScreenSync MCP operates across three tightly integrated architecture layers:

  1. Reasoning Agent Layer (Terminal & Stdio): Claude Code executes locally in your command prompt, communicating with the ScreenSync desktop hub via the standard Model Context Protocol (MCP) JSON-RPC protocol over stdio. This ensures sub-millisecond command dispatch latency.
  2. Visual Capture Layer (MediaProjection & HTTP): The physical Android smartphone runs the ScreenSync MCP mobile application (available on Google Play as com.screensync.mcp). When projection starts, the app leverages Android's native MediaProjection API (Android 7.0+) to stream display frames directly to the desktop hub on local HTTP port 3000 at a median latency of approximately 94 milliseconds. Video capture requires zero device rooting and consumes zero ADB bandwidth.
  3. OS Control & Telemetry Layer (ADB Platform Tools): Control actions—including coordinate injection, touch taps, gestures, UI dumps, and system logcat extraction—are dispatched from the hub to the phone through ADB over high-speed USB or wireless debugging sockets.

1. Programmatic Mock Location Injection via ADB

To test location without stepping outside, the physical Android device must accept simulated coordinates into its LocationManagerService. ScreenSync MCP facilitates this by executing privileged ADB shell commands from the hub:

# Step A: Grant mock location permission to the target testing harness
adb shell appops set com.example.screensync.location android:mock_location allow

# Step B: Enable mock location provider inside the Android Location Subsystem
adb shell cmd location send-extra-command com.example.screensync.location set_mock_location true

Once granted, Claude Code can drive a companion test harness or execute terminal commands that feed geographic coordinates into the system mock provider. Every call to fusedLocationClient.lastLocation or requestLocationUpdates immediately receives the synthetic latitude, longitude, altitude, accuracy, and speed parameters.

2. Inspecting Map Elements with get_ui_hierarchy

When an application renders a map using Google Maps Android SDK, Mapbox, or OpenStreetMap, UI elements are rendered inside an OpenGL or Vulkan surface view. However, surrounding overlay elements—such as distance counters, turn-by-turn direction text, ETA cards, speed limit warnings, and geofence check-in buttons—are standard Android view nodes.

When Claude Code calls get_ui_hierarchy, ScreenSync MCP queries the Android Accessibility Framework at the operating system level, returning the active window's complete XML node structure:

<node index="0" package="com.example.screensync.location" class="android.widget.FrameLayout">
  <node index="0" class="android.widget.LinearLayout" resource-id="com.example.screensync.location:id/navigation_card">
    <node index="0" class="android.widget.TextView" resource-id="com.example.screensync.location:id/text_eta" text="12 mins (4.2 miles)" />
    <node index="1" class="android.widget.TextView" resource-id="com.example.screensync.location:id/text_next_turn" text="Turn right on Market St" />
    <node index="2" class="android.widget.TextView" resource-id="com.example.screensync.location:id/text_current_speed" text="35 mph" />
    <node index="3" class="android.widget.Button" resource-id="com.example.screensync.location:id/btn_arrive" text="Check In at Depot" clickable="true" bounds="[80,1850][1000,1980]" />
  </node>
</node>

Claude Code analyzes this structure semantically. As the simulated route advances, Claude Code continuously asserts that text_eta decreases proportionally, that text_next_turn updates at scheduled waypoints, and that btn_arrive transitions from disabled to clickable as soon as the injected coordinate enters the depot geofence radius.

3. The 226-Tool Ecosystem Advantage

ScreenSync MCP provides an extensive tool catalog designed for comprehensive end-to-end testing:

  • 29 Phone & Desktop Tools: Includes 12 direct device control tools (control_tap, control_swipe, control_type, control_key, control_open_url, control_launch_app), 3 desktop operating system tools (os_mouse_click, os_type, os_hotkey), and 14 capture and telemetry tools (get_latest_screenshot, get_ui_hierarchy, get_logcat, record_screen, get_device_status).
  • 197 Browser Tools: The complete web_* suite operates through an authenticated Chrome/Edge browser extension. In omnichannel delivery architectures, a mobile courier checking in via GPS must update the dispatcher's web dashboard simultaneously. Claude Code can inject mock GPS coordinates on the physical Android smartphone and immediately use web_navigate, web_get_by, and web_eval on your workstation's Chrome browser to confirm that the live fleet map on the web console updates in real time. For a full inventory of available capabilities, explore our complete Android MCP tool reference deep dive.

Step-by-Step Setup: Mock Location Permissions, Desktop Hub, and Claude Code

Configuring an automated Android location testing environment requires enabling Developer Options, granting mock location app privileges via ADB, launching the ScreenSync MCP desktop hub on Node.js 18, and adding the stdio MCP server to Claude Code. Setting up USB reverse port forwarding ensures reliable frame capture across wireless radio tests.

Engineering teams can deploy this automated testing harness on Windows, macOS, or Linux through five sequential steps:

Step 1: Enable Developer Options & Grant Mock Location App Access

  1. Connect your physical Android smartphone (running Android 7.0 or higher) to your development workstation with a high-quality USB data cable.
  2. On the smartphone, navigate to Settings > About Phone and tap Build Number 7 times until the toast notification announces "You are now a developer!".
  3. Open Settings > System > Developer options, enable USB Debugging, and accept the RSA host fingerprint verification key prompt.
  4. If your testing suite uses a companion mock location provider APK (or an internal debug flavor of your application), tap Select mock location app in Developer Options and select your test app. Alternatively, grant mock location privileges directly from the terminal via ADB:
adb shell appops set com.example.screensync.location android:mock_location allow

Step 2: Launch the ScreenSync MCP Desktop Hub

Download the ScreenSync MCP desktop hub package from the official ScreenSync MCP homepage and unpack the archive into a dedicated folder on your workstation. Launch the hub using the appropriate platform 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 desktop hub initializes a local HTTP server on port 3000 to receive low-latency MediaProjection video frames and starts a Model Context Protocol server over stdio. The console output displays your session bearer token (SCREEN_SYNC_TOKEN). For detailed network troubleshooting and configuration parameters, consult the official ScreenSync MCP setup guide.

Step 3: Register ScreenSync MCP with Claude Code and Claude Desktop

Register the desktop hub with the Claude Code CLI in your terminal:

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

For developers collaborating through Claude Desktop, open your claude_desktop_config.json configuration file (Settings > Developer > Edit Config) and register the stdio server:

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

(Note: ScreenSync MCP integrates with local stdio MCP clients including Claude Code, Claude Desktop, Cursor, and VS Code agent mode. Cloud-hosted web agents like ChatGPT web require remote HTTPS endpoints and do not support local stdio processes).

Step 4: Install the Android App and Authorize MediaProjection

  1. Download the free ScreenSync MCP mobile application (package com.screensync.mcp) from Google Play.
  2. Launch the application. The app automatically scans your local Wi-Fi subnet using mDNS discovery to connect with your desktop hub, or you can scan the pairing QR code displayed in the hub console.
  3. Tap Start Projection on the mobile screen and accept Android's native MediaProjection consent prompt. Real-time screen mirroring begins streaming immediately at ~94 ms median latency. If you prefer a completely untethered setup without cables, review our guide on pairing Android wireless debugging to an MCP server without USB.

Step 5: Establish USB Reverse Port Forwarding

To ensure uninterrupted video frame streaming even when running tests that disable Wi-Fi or toggle mobile radio states, establish reverse port forwarding over the USB link:

adb reverse tcp:3000 tcp:3000

With reverse port forwarding active, MediaProjection display frames travel directly across the physical USB cable to localhost:3000, isolating your test harness from external network variations.

Practical QA Workflows: Dynamic Route Traversal, Geofence Triggers, and Permission Triage

Practical location QA workflows validate dynamic route navigation, geofence boundary crossings, and runtime permission downgrades. Claude Code automates these scenarios by sequentially dispatching mock GPS coordinates, asserting UI state transitions through accessibility node trees, and verifying background broadcast receiver activations directly on authentic Android hardware.

Workflow 1: Dynamic Route Waypoint Traversal & Speed Simulation

Simulating a delivery courier navigating through a metropolitan corridor requires injecting consecutive coordinates along a roadway while calculating simulated speed, bearing, and elapsed time.

Developer Prompt to Claude Code:
"Claude, launch our delivery app com.example.screensync.location. Simulate a driver moving along Market Street in San Francisco starting at 37.7925, -122.3958 and ending at 37.7880, -122.4015 across 6 waypoints at 25 mph. At each waypoint, verify that the speedometer displays 25 mph, the ETA card recalculates remaining distance, and the vector map centers on the current coordinate. Capture a screenshot at the final waypoint."

Step 1: Launch and Initialize

Claude Code invokes control_launch_app with package com.example.screensync.location. It calls get_ui_hierarchy to assert that the main map activity is loaded and that the user's initial position marker is visible.

Step 2: Inject Sequential Waypoints

Claude Code iterates through the waypoint array, sending mock location updates via ADB commands:

# Waypoint 1: Market St & 1st St
adb shell cmd location send-extra-command com.example.screensync.location set_location 37.7925,-122.3958,15.0,25.0
# Waypoint 2: Market St & 2nd St
adb shell cmd location send-extra-command com.example.screensync.location set_location 37.7915,-122.3970,15.0,25.0
# Waypoint 3: Market St & 3rd St
adb shell cmd location send-extra-command com.example.screensync.location set_location 37.7905,-122.3985,15.0,25.0

Step 3: Assert UI Hierarchy Updates

After each injection, Claude Code calls get_ui_hierarchy to assert:

  • Node text_current_speed displays 25 mph.
  • Node text_eta updates smoothly without showing negative or NaN numbers.
  • No ANR (Application Not Responding) dialog appears in the view hierarchy.

Step 4: Visual Confirmation

At the destination waypoint, Claude Code calls get_latest_screenshot to verify that the map route polyline rendered smoothly without visual artifacts or misaligned turn icons. For comprehensive end-to-end framework patterns, refer to our autonomous mobile app testing Claude Code guide.

Workflow 2: Geofence Transition Testing (ENTER, DWELL, EXIT)

Geofence testing verifies that crossing a virtual perimeter triggers business events promptly. A major testing hurdle is validating the GEOFENCE_TRANSITION_DWELL trigger, which requires remaining inside the geofence perimeter for a designated loitering duration (e.g., 30 seconds).

Developer Prompt to Claude Code:
"Claude, test our retail store geofence. The geofence center is at 37.7850, -122.4050 with a 100-meter radius and 15-second loitering delay. First, inject a coordinate 300 meters away. Then inject the store center coordinate. Wait 16 seconds to satisfy the loitering duration. Assert that a high-priority check-in notification appears on screen and that the app displays 'Welcome to Union Square Store! Special Coupon Unlocked'. Finally, inject a coordinate 400 meters away and assert the checkout exit modal."

Execution Steps:

  1. Initial State (Outside): Claude Code injects coordinate 37.7880, -122.4050 (outside perimeter). It asserts get_ui_hierarchy confirms no active store check-in card.
  2. Geofence Entry (ENTER & DWELL): Claude Code injects coordinate 37.7850, -122.4050 (inside perimeter). It observes the device clock for 16 seconds.
  3. Dwell Trigger Assertion: Claude Code calls get_ui_hierarchy and observes the Android notification shade:
    <node package="com.android.systemui" class="android.widget.TextView" text="Welcome to Union Square Store! Special Coupon Unlocked" />
  4. App View State Assertion: Claude Code taps the notification using control_tap, opening the store view. It inspects get_ui_hierarchy to assert that the promotional coupon discount badge is active.
  5. Geofence Exit (EXIT): Claude Code injects coordinate 37.7810, -122.4050 (outside). It asserts that the coupon sheet auto-dismisses and logs show GEOFENCE_TRANSITION_EXIT.

Workflow 3: Android 12+ Approximate vs Precise Permission Triage

Testing how an application behaves when a user chooses "Approximate" location ensures compliance with Google Play Store guidelines.

Developer Prompt to Claude Code:
"Claude, clear app storage for com.example.screensync.location. Launch the app to trigger the Android location permission dialog. In the system dialog, select 'Approximate' and tap 'While using the app'. Assert that the app displays a banner: 'Turn on Precise Location for navigation' while still displaying general city weather. Now tap 'Turn on', select 'Precise' in the system dialog, and verify that navigation mode activates."

Execution Steps:

  1. Claude Code clears app data via adb shell pm clear com.example.screensync.location and launches the app.
  2. The Android OS inflates the com.android.permissioncontroller dialog. Claude Code queries get_ui_hierarchy and locates:
    <node package="com.android.permissioncontroller" resource-id="com.android.permissioncontroller:id/permission_location_accuracy_coarse_radio_button" text="Approximate" />
  3. Claude Code calls control_tap on the Approximate radio button, then taps the While using the app button (permission_allow_foreground_only_button).
  4. Claude Code inspects get_ui_hierarchy inside the app. It asserts that the app did not crash with SecurityException, that the weather card is visible, and that the precise upgrade prompt renders cleanly.
  5. Claude Code clicks the upgrade button, selects Precise in the resulting system dialog, and asserts that turn-by-turn navigation initializes.
Location QA Scenario ScreenSync MCP Tools Used Critical Assertion Mechanism High-Risk Defect Caught
Route Waypoint Traversal control_launch_app, get_ui_hierarchy, get_latest_screenshot Node text_eta & speedometer sync App freezes main thread during map polyline recalculation
Geofence DWELL Transition control_tap, get_ui_hierarchy, get_logcat System notification + coupon view Loitering timer fails; push alert fires multiple duplicate times
Geofence EXIT Boundary get_ui_hierarchy, get_logcat Geofence state reset in database App keeps background GPS active indefinitely after user leaves area
Approximate Location Downgrade control_tap, get_ui_hierarchy Degraded UI state without crash App crashes immediately when denied ACCESS_FINE_LOCATION
Airplane Mode / Radio Loss ADB shell connectivity, get_ui_hierarchy Banner: Searching for GPS... App throws uncaught null location pointer on disconnected socket
Doze Mode Battery Throttling ADB shell dumpsys deviceidle, get_logcat WorkManager deferred sync log App fails to persist unsynced route coordinates during sleep
Cold Relaunch Position Restoration control_launch_app, get_ui_hierarchy Restored map camera & destination App wipes active navigation state upon process termination

Diagnosing Location Drift, Doze Mode Throttling, and Logcat Telemetry

Diagnosing location failures requires capturing real-time logcat telemetry to isolate GNSS provider exceptions, geofencing service status codes, and Android Doze mode throttling. Claude Code forces battery optimization states via ADB, monitors background location frequency adjustments, and inspects system logs to identify battery leaks and stalled location listeners.

Automating coordinate injection is only effective if failures can be diagnosed autonomously. When location updates stop, geofences fail to trigger, or map tiles freeze, the root cause often lies deep within system service power management or missing error handling.

1. Isolating System Service Logs via get_logcat

Claude Code uses ScreenSync MCP's get_logcat tool with targeted tag filters to observe the Android location daemon in real time:

# Claude Code inspects location daemon and Google Play Services logs
adb logcat -d -s LocationManagerService:V FusedLocationProviderApi:V GeofenceManager:D

Key logcat signatures Claude Code diagnoses:

  • LocationSettingsStatusCodes.RESOLUTION_REQUIRED: The device's global location toggle is switched off. Claude Code detects this status and automates the system resolution dialog.
  • GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE: The device has exceeded the operating system's limit of 100 active geofences per app or Google Play Services location cache is full. Claude Code flags the resource leak in your code.
  • LocationAvailability: isLocationAvailable=false: The client lost satellite visibility. Claude Code verifies whether your UI provides clear user feedback or stalls silently.

2. Testing Android Doze Mode and App Standby Buckets

To conserve battery life, Android aggressively restricts background tasks when a device is unplugged and stationary with the screen turned off. Under Doze mode, the operating system suspends network access, delays WorkManager jobs, and throttles background location updates to once every few minutes.

Claude Code tests your app's resilience under these extreme power constraints:

# Step 1: Simulate unplugging the battery charger
adb shell dumpsys battery unplug

# Step 2: Force the Android OS into Deep Doze Mode
adb shell dumpsys deviceidle force-idle

Claude Code calls get_logcat to verify how your background tracking service behaves. A resilient architecture handles Doze mode by batching location coordinates in a local Room database and synchronizing them once the maintenance window opens. For architecture patterns on background data persistence, explore our guide on testing Android offline mode and sync with Claude Code and MCP.

# Step 3: Exit Doze Mode and restore charging state
adb shell dumpsys deviceidle unforce
adb shell dumpsys battery reset

3. Cross-Surface Synchronization with ScreenSync MCP Browser Tools

In modern logistics and ride-hailing architectures, mobile location data feeds enterprise dispatch web applications in real time. Because ScreenSync MCP includes *197 `web_` browser automation tools**, Claude Code can execute an entire end-to-end integration test spanning both mobile hardware and desktop web browsers:

  1. Claude Code injects dynamic vehicle coordinates onto the physical Android smartphone.
  2. Concurrently, Claude Code uses web_navigate to open your company's dispatch console in Google Chrome.
  3. Using web_get_by and web_eval, Claude Code verifies that the vehicle's live marker pin on the web dashboard updates its geographic coordinates and heading angle within the specified SLA latency.

Implementation Checklist for Automated Android Location and Geofencing QA

The Android location and geofencing implementation checklist provides engineering teams with an actionable verification matrix spanning mock provider permissions, desktop hub configuration, coordinate injection, and battery optimization tests. Following these structured criteria guarantees that mobile applications handle location drift, geofence boundaries, and permission downgrades gracefully prior to production release.

  • [ ] Connect an authentic physical Android device running Android 7.0 or higher to your workstation via high-speed USB data cable.
  • [ ] Enable Developer Options and turn on USB Debugging with RSA host key verification.
  • [ ] Grant mock location privileges to your test suite via adb shell appops set <package> android:mock_location allow.
  • [ ] Download and launch the ScreenSync MCP desktop hub on Node.js 18+ using start-hub.bat (Windows) or start-hub.sh (macOS/Linux).
  • [ ] Register ScreenSync MCP with Claude Code CLI via claude mcp add screensync node "/path/to/screensync-hub.js".
  • [ ] Install the free ScreenSync MCP mobile application (package com.screensync.mcp) from Google Play onto the physical phone.
  • [ ] Authorize native MediaProjection screen capture consent in the mobile app for sub-100ms (~94 ms) local display mirroring.
  • [ ] Configure USB reverse port forwarding via adb reverse tcp:3000 tcp:3000 to isolate video capture from network radio tests.
  • [ ] Verify that the application declares ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, and ACCESS_BACKGROUND_LOCATION properly in AndroidManifest.xml.
  • [ ] Test Android 12+ Approximate Location selection and verify graceful UI degradation without throwing SecurityException.
  • [ ] Execute sequential multi-waypoint GPS route simulations via mock location injection, asserting speedometer and ETA card updates.
  • [ ] Verify GEOFENCE_TRANSITION_ENTER, GEOFENCE_TRANSITION_DWELL, and GEOFENCE_TRANSITION_EXIT triggers with authentic loitering delays.
  • [ ] Force Android Doze mode via adb shell dumpsys deviceidle force-idle to validate background location batching and battery compliance.
  • [ ] Inspect real-time system logs with get_logcat to detect geofence registration limits (GEOFENCE_NOT_AVAILABLE) and provider timeouts.
Verification Stage Command / ScreenSync Tool Primary Assertion Target Operational Success Criteria
Hardware & Hub Link adb devices & get_device_status Device model, battery level, USB state Device detected, battery > 20%, hub active on port 3000
Mock Provider Authorization appops set android:mock_location Android OS AppOps state Status returns allow; app successfully injects coordinates
Visual Frame Pipeline get_latest_screenshot Native MediaProjection stream Screen rendered at ~94 ms latency with zero ADB overhead
Approximate Permission Triage control_tap & get_ui_hierarchy System permission controller dialog Approximate mode selected; app functions without crashing
Dynamic Waypoint Injection cmd location send-extra-command Real-time map view & speedometer Map centers on coordinate; speed and distance update cleanly
Geofence Dwell Assertion get_ui_hierarchy & get_logcat Notification shade & coupon card Loitering delay triggers high-priority store check-in alert
Doze Mode Power Resilience dumpsys deviceidle force-idle Background WorkManager batch sync Location updates safely buffered in Room database
Omnichannel Web Sync web_navigate & web_get_by Desktop fleet dispatch dashboard Mobile GPS coordinates reflect on live web map in real time
FAQ

Questions, answered

How does ScreenSync MCP inject mock GPS coordinates into physical Android hardware without root?

ScreenSync MCP utilizes Android's native mock location framework by configuring ADB AppOps privileges (`adb shell appops set <package> android:mock_location allow`). This allows developer testing harnesses to feed synthetic latitude, longitude, and accuracy coordinates into Google Play Services Location APIs on authentic hardware without modifying system partitions or rooting the device.

Why does Google Play Services FusedLocationProviderClient behave differently on physical devices than emulators?

Physical Android devices execute genuine ARM Google Play Services binaries, interact with physical GPS and cellular hardware modems, and enforce OEM-specific battery optimization governors. Emulators run virtualized x86 containers that lack real sensor fusion, physical radio sleep cycles, and accurate power management behaviors.

How do I test Android 12 approximate versus precise location permissions using Claude Code?

Claude Code calls `get_ui_hierarchy` when the Android permission dialog appears to inspect the system dialog structure. The agent clicks either the Approximate or Precise radio button using `control_tap`, asserting that the application adapts gracefully without throwing unhandled `SecurityException` errors.

Can Claude Code test geofence dwell transitions with loitering delays deterministically?

Yes. Claude Code injects coordinates within the defined geofence boundary, pauses execution for the duration of `setLoiteringDelay()`, and inspects `get_ui_hierarchy` and `get_logcat` to confirm that the `GEOFENCE_TRANSITION_DWELL` intent triggers the expected notification and in-app state updates.

How does Android Doze mode affect background location updates during automated testing?

Under Doze mode, Android suspends network access and throttles background location updates to periodic maintenance windows. By triggering `dumpsys deviceidle force-idle` via ADB, Claude Code verifies that background services batch location updates locally in SQLite or Room databases rather than crashing.

Will automated mock location testing trigger anti-spoofing flags in my mobile application?

Android's `Location.isMock()` flag returns `true` during mock testing. For QA and integration testing, developers configure debug builds to bypass mock detection flags, allowing end-to-end verification of business workflows while keeping production fraud prevention mechanisms intact in release configurations.

Why is USB reverse port forwarding recommended when testing location and network dropouts?

Running `adb reverse tcp:3000 tcp:3000` routes MediaProjection display frames directly over the physical USB cable to the workstation hub. This ensures that when testing cellular radio loss, tunnel transit, or airplane mode, screen capture streaming remains completely uninterrupted.

Can ScreenSync MCP test location updates across both Android mobile apps and web dispatch dashboards?

Yes. ScreenSync MCP pairs 29 mobile and desktop tools with 197 `web_*` browser automation tools. Claude Code can inject simulated GPS coordinates on an Android smartphone and simultaneously navigate a web dispatch console in Google Chrome, verifying omnichannel synchronization within a single automated session.

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.