This document describes the current repository as a starting point for future development. It is based on the implementation in the repository, not only on the intended product behavior.
The app enrolls an Android device into a MeshCentral server and exposes a mobile-specific subset of the MeshCentral agent protocol. Its main capabilities are:
mc:// deep link, or manual
pairing-link entry.The remote desktop implementation is currently view only. Protocol handlers for keyboard, mouse, Unicode key, and input-lock messages exist, but they are no-ops. This app does not currently provide general remote input control.
| Item | Current value |
|---|---|
| Project type | Single-module Android application, Kotlin and XML views |
| Module | app |
| Application ID | com.meshcentral.agent2 |
| Kotlin namespace | com.meshcentral.agent |
| Minimum Android SDK | 23 (Android 6.0) |
| Compile/target SDK | 35 (Android 15) |
| Version | 1.0.23 (versionCode 30) |
| Kotlin | 1.9.10 |
| Android Gradle Plugin | 8.6.1 |
| Gradle wrapper | 8.7 |
| Java/Kotlin target | JVM 17 |
The package namespace and installed application ID intentionally differ in the current build configuration. Keep that distinction in mind when working with Firebase configuration, manifests, or package-sensitive integrations.
flowchart LR
User[Device user] --> UI[MainActivity and fragments]
UI --> Pairing[Pairing link and preferences]
Pairing --> Agent[MeshAgent control channel]
FCM[Firebase Cloud Messaging] --> Push[MeshFirebaseMessagingService]
Push --> UI
Push --> Agent
Agent <-->|WSS /agent.ashx| Server[MeshCentral server]
Agent --> Tunnel[MeshTunnel relay channels]
Tunnel <-->|WSS /meshrelay.ashx| Server
Tunnel --> Capture[ScreenCaptureService]
Tunnel --> Media[MediaStore and shared storage]
Capture --> Projection[Android MediaProjection]
This is a compact, stateful application rather than a layered Android
architecture. MainActivity.kt declares process-wide top-level variables for
the active activity, fragments, server link, agent, tunnels, screen-capture
service, desktop settings, and pending 2FA request. The principal classes refer
to this shared state directly.
MainActivity.kt is the application coordinator. It loads pairing data,
generates or loads the agent identity, requests runtime permissions, starts
and stops the agent, manages reconnect behavior, launches MediaProjection,
displays local alerts/notifications, and applies settings.MainFragment.kt is the home/status screen. It shows pairing and connection
state, server branding, and users with active tunnel sessions.ScannerFragment.kt scans and validates mc:// pairing QR codes. Pairing can
also be entered manually or received through the manifest’s mc deep-link
intent filter.AuthFragment.kt displays a decoded 2FA code with approve and reject actions.
Requests expire after approximately 60 seconds.SettingsFragment.kt exposes automatic connection and automatic screen-share
consent preferences.WebViewFragment.kt provides an in-app browser used by the openurl console
command. A separate openbrowser command launches the system browser.nav_graph.xml defines navigation from the home screen to scanner, browser,
authentication, and settings screens.MeshAgent.kt owns the long-lived control connection to
wss://<server>/agent.ashx using OkHttp. Its state values are:
| State | Meaning |
|---|---|
| 0 | Disconnected |
| 1 | Connecting |
| 2 | Authenticating |
| 3 | Connected and authenticated |
The handshake uses a locally generated 2048-bit RSA key pair and self-signed X.509 certificate. The app exchanges nonces, validates the server identity from the pairing link, signs the handshake, and then sends Android agent metadata and capabilities. Once connected, the control channel:
The connection advertises MeshCentral capabilities for files and console access in addition to the mobile/desktop-view capability.
MeshTunnel.kt creates a separate pinned WebSocket for each MeshCentral relay
session. Implemented tunnel usages are:
The virtual roots presented to MeshCentral are Sdcard, Images, Audio, and
Videos. Behavior differs across Android versions because Android 10 and later
use scoped MediaStore APIs while older versions use public storage paths.
ScreenCaptureService.kt is a foreground service with the mediaProjection
service type. The flow is:
MainActivity launches Android’s
MediaProjection consent UI.ImageReader.Captured data is broadcast to all connected remote-desktop tunnels. Capture is stopped when the user stops it, the agent disconnects, or no desktop tunnel remains.
MeshFirebaseMessagingService.kt receives FCM tokens and messages. Incoming
messages are accepted only after their abbreviated server hash matches the
stored pairing link. Depending on payload, the service can:
2fa:// URL to the approval UI.MainActivity to show a standard Android notification, optionally with a
URL action.flash, netinfo, sysinfo, and
vibrate) and send a response through FCM.For 2FA, AuthFragment extracts and Base64-decodes the code query parameter.
The user’s decision is sent over the authenticated control WebSocket by
MeshAgent.send2faAuth.
The connected agent channel supports commands for:
alert, toast, openurl, openbrowser, uiclose, and uistate.dial, flash, and vibrate.battery, netinfo, storageinfo, and sysinfo.kvmstart, kvmstop, serverlog, and help.Commands and relevant actions are logged back to the server. Some actions open Android UI or require an active activity and therefore are not silent background management operations.
mc://host,serverHash,deviceGroupId link.qrmsh in the meshagent SharedPreferences file.MeshAgent connects to /agent.ashx and authenticates both sides using the
pairing data, TLS certificate hash, nonces, and signatures.The build also supports a source-level hardCodedServerLink. When populated,
users cannot replace or clear the configured server.
ScreenCaptureService sends changed image regions through every active
desktop tunnel.Persistent state is split between two SharedPreferences stores:
meshagent: pairing link, agent certificate, and private key.Most live state is held in process-wide Kotlin variables. There is no database, repository layer, dependency injection container, or persistent background work scheduler. A process restart reconstructs state from preferences and FCM, then reconnects only when the current settings and Android lifecycle permit it.
The manifest declares:
INTERNET, VIBRATE, and CAMERA.POST_NOTIFICATIONS.READ_MEDIA_IMAGES, READ_MEDIA_AUDIO, and
READ_MEDIA_VIDEO.READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE,
with legacy external-storage behavior requested by the application.FOREGROUND_SERVICE and
FOREGROUND_SERVICE_MEDIA_PROJECTION.The camera is optional hardware. The manifest registers MainActivity, the FCM
service, and ScreenCaptureService. It also removes Google Mobile Ads’ AD_ID
permission during manifest merging.
The main external dependencies are:
Some declared lifecycle dependencies are not central to the current global-state architecture. WebRTC and WorkManager dependencies are present only as commented experiments.
app/
build.gradle Android module configuration
google-services.json Firebase application configuration
proguard-rules.pro Release shrinker rules
src/main/
AndroidManifest.xml Permissions, activity, and services
java/com/meshcentral/agent/
MainActivity.kt App lifecycle and coordinator
MainFragment.kt Home and connection status
ScannerFragment.kt QR pairing
AuthFragment.kt 2FA approval
SettingsFragment.kt Preference screen
WebViewFragment.kt In-app browser
MeshAgent.kt Authenticated control channel
MeshTunnel.kt Desktop/file relay channels
ScreenCaptureService.kt MediaProjection screen encoder
MeshFirebaseMessagingService.kt FCM handling
NotificationUtils.kt Foreground-service notification
res/ Layouts, navigation, strings, themes, icons
Use JDK 17 or Android Studio’s bundled JDK 21 for the current Android Gradle
Plugin 8.6.1 and Gradle 8.7 combination. Confirm that JAVA_HOME and
java -version select one of those JDKs before building. Java 24 is not
supported by this wrapper and fails during Gradle settings evaluation with
Unsupported class file major version 68.
From the repository root on Windows:
.\gradlew.bat assembleDebug
Useful related tasks include:
.\gradlew.bat lintDebug
.\gradlew.bat clean
The repository currently has no active unit or instrumentation test dependencies
and no test source tree. The test declarations in app/build.gradle are
commented out, so changes currently rely on compilation, lint, and manual testing
against Android devices and a MeshCentral server.
For device-level verification, exercise at least:
These are current implementation characteristics to review before broad changes, not a complete security audit:
app/release/app-release.aab is checked into the tree.
Decide whether release artifacts should remain versioned.