Skip to content

rcarmo/go-rdp-android

Repository files navigation

go-rdp-android

go-rdp-android icon

A native Android RDP server experiment written in Kotlin and Go.

go-rdp-android is exploring how far a normal installed Android app can go toward exposing the Android screen over RDP without using ADB as the runtime architecture, building on rcarmo/go-rdp as the original core protocol implementation and reference. The app uses Android MediaProjection for screen capture, an AccessibilityService landing path for input, and a Go RDP server core bridged into Android with gomobile.

The current implementation is a CI-first prototype: it can build Go-backed APK/AAB artifacts, launch the APK in an Android emulator, grant MediaProjection, connect to the embedded Go RDP server over forwarded TCP, render RDP screenshots, exercise keyboard/mouse/touch input scripts, and generate a Gherkin/Playwright UX PDF report.

Feature list

Implemented or validated today:

  • Native Android Kotlin shell with MainActivity, RdpForegroundService, MediaProjection consent/denial flow, serialized foreground-service mode switching, notification/UI stop actions, credential-refusal cleanup, network-address notification refresh, non-sticky service restart policy, non-secret settings persistence, Android controls for security mode and failed-auth policy, compact UI health/diagnostic sharing, and AccessibilityService declaration.
  • Go RDP server core with TPKT, X.224, MCS, GCC server core/security/network data, TLS-only Client Info authentication, Hybrid/NLA CredSSP/NTLMv2 authentication via rcarmo/go-rdp, TLS certificate persistence/rotation/fingerprint support, access-policy controls, failed-auth backoff/lockout, Demand Active/Confirm Active finalization, FontMap, slow-path bitmap updates, and slow-path/Fast-Path input decoding.
  • gomobile bind integration via mobile.aar, with Kotlin reflection backend and logging fallback when the AAR is absent.
  • Android MediaProjection capture pipeline using VirtualDisplay + ImageReader RGBA frames.
  • Synthetic test-pattern frame source for emulator/CI validation without capture permission.
  • RDP 24-bit BGR bitmap update tiling sized for safe TPKT/PER envelopes.
  • Dirty-tile suppression for post-initial streamed frames.
  • Layered capture pacing/backpressure: Android adaptive capture interval, bounded Go queue drops, and server-side queued-frame coalescing.
  • Optional MediaProjection downscale mode (capture_scale / emulator_capture_scale).
  • Keyboard, mouse, pointer, wheel decode/degrade, and RDPEI touch validation in CI using scripted emulator input and synthetic dynamic-channel touch packets.
  • Gherkin-style UX stories under features/ux/ and a Playwright-based PDF report generator.
  • GitHub Actions coverage for Go tests, race tests, fuzz smoke, classic and NLA authentication smokes, Android APK builds, gomobile AAR/API checks, FreeRDP compatibility probes, emulator capture tests, and UX PDF artifacts.
  • Tag-driven CI/CD policy for build, UX, and release tag classes, including signed APK/AAB staging, SBOM, checksum, and release-note artifacts for v* tags.
  • Security documentation in docs/THREAT_MODEL.md plus user-facing privacy/security copy in docs/PRIVACY.md covering capture, listening state, credentials, remote input, diagnostics, and recommended defaults.

Partially implemented / experimental:

  • Real-client RDP compatibility. The mock server/probe path is stable, and the FreeRDP CI gate now requires /sec:rdp, /sec:tls, and /sec:nla bitmap fallback plus /sec:nla /gfx RDPGFX to reach active state, handle Fast-Path input, capture a screenshot, and stay connected until CI terminates the client; Microsoft-client compatibility is still pending.
  • Accessibility input injection. Pointer taps/drags and frame-aware RDPEI touch contacts now reach bounded Accessibility gesture paths with continuation/multi-stroke fallback; richer keyboard/text, secondary-button behavior, gesture failure handling, and physical-device validation still need hardening.
  • Performance. RDPGFX over drdynvc is implemented as the default compressed graphics path using Planar no-alpha RLE, while slow-path 24-bit bitmap transport remains the measured fallback. Experimental Android MediaCodec H.264 capture and RDPGFX AVC420 emission scaffolding exists, with CI artifacts proving forced server-side emission but not true client compatibility yet; Android health now exposes h264Status so testers can distinguish disabled, unsupported, forced, and ready paths. Physical-device performance comparison is still pending.

Known limitations

  • Physical Android-device validation is still pending; current automated evidence comes from CI, emulator, Go tests, and FreeRDP probes.
  • Microsoft Remote Desktop active-streaming validation is still pending, especially NLA behavior and certificate-warning UX.
  • Accessibility input depends on Android service availability and user consent; richer keyboard/text, secondary-button, and gesture-failure handling remain limited.
  • Graphics default to RDPGFX Planar when the client supports it, with raw 24-bit bitmap updates retained as compatibility fallback. Bitmap RLE is available only as an experimental opt-in fallback (GO_RDP_ANDROID_ENABLE_BITMAP_RLE=1) with diagnostics/saved-byte evidence, not as a negotiated/default release path. NSCodec/JPEG/PNG/RemoteFX SurfaceBits emitters are also experimental/opt-in and report write/raw/saved/percentage diagnostics when exercised; RemoteFX has a production single-tile encoder but still requires client advertisement and remains non-default. RDPGFX ClearCodec, Progressive/ProgressiveV2, and AVC444/AVC444v2 have partial/minimal opt-in production encoders plus fixture hooks, but none are release defaults. H.264/AVC over RDPGFX AVC420 is experimental and not a release compatibility claim until a client advertises support without force-mode probes; use h264Status in diagnostics to classify fallback reasons, and keep physical-device/Microsoft-client performance and compatibility evidence pending.
  • Public release defaults should prefer nla-required; tls-only is for non-NLA clients, and plain RDP is only for isolated compatibility testing.

Package and version

  • SemVer: 0.1.2
  • Android namespace/application ID: io.carmo.go.rdp.android
  • Android versionCode: 3
  • Go module: github.com/rcarmo/go-rdp-android

Android package IDs cannot contain hyphens. The project name go-rdp-android is represented as the Android package io.carmo.go.rdp.android.

Repository layout

android/app/                     Native Android Kotlin app
cmd/mock-server/                 Desktop mock RDP server for protocol experiments
cmd/probe/                       Scriptable RDP probe/client used by CI
features/ux/                     Gherkin-style UX user stories
internal/frame/                  Frame source abstractions and test patterns
internal/input/                  Input sink abstractions
internal/rdpserver/              Go RDP server core
mobile/                          gomobile-facing Go bridge API
scripts/                         CI helpers, artifact checks, UX report generator
docs/                            Architecture, testing, performance and release docs

Documentation

Build and test locally

Go checks:

make test
make build-go
make coverage

Run the desktop mock server and probe:

# terminal 1
make run-mock-pattern

# terminal 2
make probe

# or run both as a smoke test
make smoke

Build Android locally if the Android SDK and Gradle are available:

make android-build

Build the Go-backed Android APK:

make gomobile-init   # first time only
make android-build-go

Generate a UX report from an existing emulator-artifacts/ directory:

npm ci
npx playwright install --with-deps chromium
make ux-report

CI/CD quick reference

Default push/PR CI runs:

  • Go vet/build/test/coverage.
  • Race tests and parser fuzz smoke.
  • Mock server + probe artifact generation.
  • Android debug APK build and inspection.
  • gomobile AAR build, API verification, and Go-backed APK/AAB builds with standalone artifact uploads.
  • Blocking FreeRDP compatibility probe requiring bitmap/update streaming evidence.

Current blocking FreeRDP compatibility signals are tracked in docs/STATUS.md:

Mode Active Bitmap/update Fast-Path input Screenshot Current expected exit
/sec:rdp 131 non-timeout shutdown after capture
/sec:tls 131 non-timeout shutdown after capture
/sec:nla 131 non-timeout shutdown after capture

Run the local graphics encoding matrix when changing transport code or collecting release-candidate evidence:

make encoding-matrix

That matrix covers slow-path bitmap fallback, opt-in bitmap RLE fallback with saved-byte evidence, opt-in NSCodec/JPEG/PNG/RemoteFX SurfaceBits probes with selected/write/raw/saved/percentage fields, RDPGFX Planar, RDPGFX uncompressed diagnostics, production-vs-fixture probes for ClearCodec/Progressive/ProgressiveV2/AVC444/AVC444v2, forced /gfx:AVC420, and forced /gfx H.264 smoke cases. It explicitly separates release defaults from experimental emitters, fixture hooks, partial production encoders, and missing client proof. See docs/GRAPHICS_CODECS.md for the codec coverage and decision matrix.

Manual emulator UX run:

gh workflow run CI \
  --ref main \
  -f emulator_api_level=35 \
  -f emulator_go_backed=true \
  -f emulator_capture=true \
  -f emulator_capture_scale=2

Tag behavior:

Tag pattern Behavior
*-ux Full emulator UX validation and Playwright PDF report.
*-build Build/test/artifact production.
vX.X.X Release tag: Go-backed release APK/AAB artifacts plus UX PDF report staged for signed release files.

Feature roadmap

  1. RDP compatibility hardening

    • Improve real-client compatibility beyond the current probe/mock path.
    • Expand GCC/security/licensing/capability handling.
    • Keep expanding the now-blocking FreeRDP compatibility gate beyond bitmap/update streaming toward full clean-session behavior.
  2. Input injection completion

    • Map RDP pointer, keyboard, Unicode, and touch events into robust Accessibility gestures and text input.
    • Add coordinate transforms for downscaled capture and rotation.
    • Extend CI scripts and future device tests for more input workflows.
  3. Performance workstreams

    • Continue dirty-tile suppression improvements.
    • Keep single RDP sessions open for UX navigation and incremental metrics.
    • Capture pacing/backpressure now has the first production-oriented layers in place: Android adaptive capture interval, bounded queue drops, and server-side queued-frame coalescing; remaining validation is on real devices and constrained networks.
    • Expand downscale/quality modes.
    • Investigate compressed bitmap/RDPGFX updates.
    • Continue H.264/AVC with Android hardware encoding: encoder/queue/RDPGFX AVC420 scaffolding exists, but client proof and physical performance validation remain pending.
  4. Security and release readiness

    • Security mode, failed-auth backoff controls, and copyable TLS fingerprint are now surfaced in Android UI; release guidance recommends nla-required first and reserves rdp-only for isolated compatibility testing. CIDR/user allowlists remain server-core/mock-server-only for the first polished APK; continue with TLS rotation controls.
    • Continue hardening TLS Client Info and Hybrid/NLA CredSSP authentication paths against real clients.
    • Validate signed release APK/AAB staging with production secrets.
    • Version/tag consistency is enforced by release preflight; validate the controlled vX.X.X tag path after production signing secrets are confirmed.
  5. Physical-device validation

    • Validate MediaProjection, AccessibilityService behavior, network reachability, rotation, latency, and sustained capture on real Android devices.

Current limitations

  • The app is not production-ready and should not be exposed to untrusted networks.
  • The RDP server profile is intentionally minimal and not yet compatible with every client.
  • Hybrid/NLA CredSSP passes current FreeRDP CI gates, but Microsoft Remote Desktop compatibility is still not guaranteed.
  • Audio, clipboard, drive redirection, and full multi-monitor semantics are out of scope for the current prototype. Dynamic virtual channels are implemented only for the bounded drdynvc/RDPEI touch-input subset.
  • MediaProjection cannot capture protected content.
  • Accessibility input injection is more restricted than shell/ADB input injection.

About

An RDP server for Android devices, because I wanted it done "right"

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors