Build Your Own Android RTK Integration

This guide covers the Android implementation layers behind a custom SCOUT Series RTK app. It assumes you have already selected either in-app direct use or Android Mock Location system-level injection on the architecture overview page.

Both architectures share the same core loop: USB serial access, NTRIP corrections, RTCM write-back, NMEA parsing, RTK state handling, and recovery from common failures. A validation checklist near the end covers USB reconnects, network loss, stale corrections, and background operation.

Architecture context

Review the Android RTK Architecture & Data Flow page before implementation. That page separates the common RTK data loop from the two main consumption models: in-app direct use and Android Mock Location system-level injection.

Architecture A or B

The overview page defines two top-level ways to consume the RTK solution. Keep that decision stable while implementing the shared USB and NTRIP layers below.

Architecture A · In-app direct use

Build an all-in-one RTK business app

The same app manages USB, NTRIP, RTCM write-back, RTK state, and the business workflow. This is the pattern used by RTK-capable field apps: no Android Mock Location layer is required.

Architecture B · System-level injection

Publish RTK position through Mock Location

A dedicated bridge app manages the receiver and correction service, then publishes corrected coordinates through Android Mock Location so compatible third-party apps can reuse the system location.

Optional enterprise extension: private SDK or IPC

A private SDK or IPC interface is not a third top-level architecture. It is an optional extension when in-house apps need raw NMEA, RTK state, correction age, or diagnostics that Android Mock Location cannot carry. Add it beside Architecture A or Architecture B according to your product boundary.

Shared implementation layers

The following layers are common to both architectures. Keep them separate so that USB reconnects, correction-service failures, and UI state changes do not become entangled.

USB session manager

Discover the receiver, request USB permission, open the serial port, process attach and detach events, and restore the connection after a cable reconnect.

NTRIP client

Authenticate with the caster, upload GGA when required, receive RTCM as a binary stream, and apply retry and backoff behavior when the network or correction service is unavailable.

GNSS parser and RTK state model

Parse NMEA incrementally, validate sentence boundaries and checksums, track RTK Fix / Float / DGNSS / Single state, and expose correction age and solution health to the UI and business layer.

Output adapter

Route the corrected solution either into the same app, through Android Mock Location, or through a private bound-service / SDK interface for other in-house applications.

1. Prepare the Android project

Declare USB host support and Internet access. A normal Android application should not add android.permission.ACCESS_MOCK_LOCATION to the manifest. Mock Location is granted by selecting the app in Android Developer Options.

View manifest example
<uses-feature android:name="android.hardware.usb.host" android:required="true" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- Add only when the app itself needs Android location access --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Required when the app runs a foreground service --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Required for apps targeting Android 14+ when using connectedDevice --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" /> <application ...> <service android:name=".RtkBridgeService" android:exported="false" android:foregroundServiceType="connectedDevice" /> </application>

Manifest notes

  • Do not add android.permission.ACCESS_MOCK_LOCATION to a normal app manifest. Select the mock-location app in Android Developer Options.
  • Add FOREGROUND_SERVICE only when the app runs a foreground service. For apps targeting Android 14+, also declare the matching service type and type-specific permission.
  • For a long-running USB bridge, start with connectedDevice. Request USB permission before opening the session, and add other service types only when required.

2. Open the USB serial session

Android acts as the USB host, powers the bus, enumerates the receiver, and requires user permission before an app communicates with the device. Keep serial I/O off the main thread and close the connection cleanly after a USB detach event.

01

Use UsbManager for permission and lifecycle handling

  • Detect an attached USB device and validate the target VID / PID or interface profile.
  • Call UsbManager.requestPermission() when the app does not already have access.
  • Open the port on a worker thread and monitor attach / detach events.
  • Release the interface and close the connection after disconnect or app shutdown.
02

Use a USB serial library or implement the driver layer

The open-source usb-serial-for-android project is a practical starting point. It provides raw serial read() and write() access through Android USB Host Mode and includes support for Qinheng CH340 / CH341A converters.

View USB serial session skeleton
val usbManager = getSystemService(Context.USB_SERVICE) as UsbManager val drivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager) val driver = drivers.firstOrNull() ?: return if (!usbManager.hasPermission(driver.device)) { usbManager.requestPermission(driver.device, permissionIntent) return } val connection = usbManager.openDevice(driver.device) ?: return val port = driver.ports.first() port.open(connection) port.setParameters( 115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE )

Use the SCOUT Series default serial profile only as the initial setting. Keep the serial parameters configurable so the app can match future firmware or product-specific profiles.

3. Implement the common RTK data loop

Separate the text-based NMEA parser from the binary RTCM write-back path. RTCM bytes received from the caster must be forwarded to the receiver without being treated as text.

USB RX → incremental NMEA parser → latest GGA / RMC / RTK state latest GGA → NTRIP caster when required by the service NTRIP RTCM bytes → USB TX write-back USB RX after RTCM write-back → updated NMEA output / RTK Fix state
01

Build the NTRIP client as a long-running state machine

  • Connect to the caster and request the mountpoint with the required authentication method.
  • Upload GGA only when required by the selected correction service.
  • Write RTCM bytes back to the receiver immediately and preserve byte ordering.
  • Track connection state, correction age, byte counters, retry count, and the last successful RTCM timestamp.
  • Retry with bounded exponential backoff after network loss or caster rejection.
02

Expose a solution state model, not only latitude and longitude

State field Why it matters Recommended use
Position and altitude Core business result. Publish to the UI, business logic, or Mock Location output adapter.
RTK state Distinguishes Single, DGNSS, Float, and Fix. Gate high-precision workflows and show a clear status indicator.
Correction age Shows whether the RTK solution is using fresh corrections. Warn when the correction stream becomes stale or disconnected.
Satellite and quality metrics Supports diagnostics during poor sky view or multipath conditions. Expose HDOP / VDOP, satellite count, SNR summaries, and parsing health when available.

4. Add the required output adapter

The USB and NTRIP loop is shared. The output adapter is where the product architecture diverges.

A

Direct in-app use

Route the parsed RTK solution directly into the business workflow. This is the cleanest path for a dedicated field app, ground station, control interface, or surveying application. Android Mock Location is not needed.

B

Android Mock Location bridge

Add a test provider and publish valid Android Location objects from the corrected RTK solution. The app must be selected as the mock-location app in Android Developer Options. Do not hard-code an artificial 1 cm accuracy value; publish an estimate derived from the current solution state.

View Mock Location publication skeleton
val provider = LocationManager.GPS_PROVIDER val mockLocation = Location(provider).apply { latitude = solution.latitude longitude = solution.longitude altitude = solution.altitudeMeters accuracy = solution.horizontalAccuracyMeters time = System.currentTimeMillis() elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() } locationManager.setTestProviderLocation(provider, mockLocation)

Register the test provider during service setup and handle API-level differences in a compatibility wrapper. Locations submitted through a test provider are detectable as mock locations. Some downstream apps may intentionally reject them. Validate the target Android version, device ROM, and downstream app. If your compatibility wrapper creates Location objects outside the test-provider path, use LocationCompat.setMock(...) where appropriate; do not treat it as a substitute for test-provider registration.

Optional

Private SDK or IPC extension

This is not a third top-level architecture. Add it beside Architecture A or B when another in-house app needs raw NMEA, correction age, satellite metrics, or RTK diagnostics that Mock Location does not carry. Expose a controlled bound service, AIDL interface, SDK callback, or local socket.

Avoid global implicit broadcasts for continuous raw NMEA

Continuous raw GNSS output can be frequent and may contain sensitive operational data. Prefer an app-scoped bound service or explicit IPC contract. Use explicit broadcasts only for low-rate, non-sensitive events such as connection-state changes.

5. Design for long-running operation

A bridge app is not a one-shot configuration utility. It must survive screen-off operation, network changes, Android lifecycle transitions, and USB reconnects.

Foreground service lifecycle

Start the user-visible bridge session from a visible screen, show a persistent notification, and declare the foreground-service type that matches the real use case. Android 12+ restricts background starts. Apps targeting Android 14+ must declare a service type and the matching type-specific permission; a USB bridge commonly uses connectedDevice.

USB detach and reconnect

Close the port after detach, clear stale parser buffers, update the UI, and attempt a controlled reconnect after a new device attachment.

Network recovery

Monitor connectivity changes, reconnect the NTRIP session with bounded backoff, and distinguish authentication failures from temporary network interruptions.

Credential security

Do not hard-code caster credentials in the application. Store secrets securely, separate development and production accounts, and use TLS when the correction service supports it.

Validation checklist

Test failure recovery, not only the nominal RTK Fix path.

Test case Expected behavior What to observe
USB cable unplug and reconnect The app releases the old port and restores the session after reconnect. No stale state, duplicate reader thread, or application crash.
Mobile network interruption The NTRIP client retries with bounded backoff. Correction age increases, the UI reports degraded state, and recovery is visible.
Invalid caster credentials The app stops aggressive retries and reports authentication failure. Clear operator message and actionable correction steps.
Poor sky view The solution may fall from Fix to Float, DGNSS, or Single. State transition is visible and business logic does not assume centimeter accuracy.
Screen off and app backgrounded The active bridge session remains stable according to Android foreground-service rules. USB session, correction stream, and output adapter continue operating as intended.

Implementation references

Use the official Android documentation as the source of truth for platform behavior. Treat the snippets above as architecture sketches rather than a complete application.

Need help validating an Android integration architecture? Contact Kalmix support →

Product selection

Choosing between SCOUT and SCOUT PRO?

Compare the two receiver configurations and choose the right starting point for your integration.