Official Android components for OpenMDM - the embeddable Mobile Device Management SDK.
Caution
This project is under active development and is NOT ready for production use. APIs may change without notice. Use at your own risk.
This repository contains:
| Module | Description |
|---|---|
:agent |
Full-featured MDM agent app - fork this for customization |
:library |
Core MDM library - embed in your own Android app |
Best for: Building a branded MDM agent with full functionality.
- Fork this repository
- Customize branding in
agent/src/main/res/ - Update
agent/build.gradle.ktswith your app ID - Configure your server URL in the app
- Build and distribute
git clone https://github.com/YOUR_ORG/openmdm-android
cd openmdm-android
./gradlew :agent:assembleReleaseBest for: Adding MDM capabilities to an existing app.
Step 1: Add JitPack repository
// settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}Or in your root build.gradle.kts:
allprojects {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}Step 2: Add the dependency
// app/build.gradle.kts
dependencies {
implementation("com.github.azoila.openmdm-android:library:0.1.0")
}Note: Replace
0.1.0with the latest release version or usemain-SNAPSHOTfor the latest development version.
import com.openmdm.library.device.DeviceManager
// Create DeviceManager with your DeviceAdminReceiver
val deviceManager = DeviceManager.create(
context = applicationContext,
adminReceiverClass = MyDeviceAdminReceiver::class.java
)
// Check capabilities
if (deviceManager.isDeviceOwner()) {
// Full MDM capabilities available
// Silent app installation
deviceManager.installApkSilently(
apkUrl = "https://example.com/app.apk",
packageName = "com.example.app"
)
// Grant permissions
deviceManager.grantCommonPermissions("com.example.app")
// Kiosk mode
deviceManager.startLockTaskMode("com.example.app")
}
if (deviceManager.isDeviceAdmin()) {
// Basic admin capabilities
deviceManager.lockDevice()
deviceManager.setCameraDisabled(true)
}import com.openmdm.library.MDMClient
import com.openmdm.library.api.*
// Create client
val mdmClient = MDMClient.Builder()
.serverUrl("https://mdm.example.com")
.deviceSecret("your-shared-secret")
.debug(BuildConfig.DEBUG)
.onTokenRefresh { token, refreshToken ->
// Save tokens for persistence
prefs.saveTokens(token, refreshToken)
}
.onEnrollmentLost {
// Handle re-enrollment
navigateToEnrollment()
}
.build()
// Enroll device
val timestamp = Instant.now().toString()
val signature = mdmClient.generateEnrollmentSignature(
model = Build.MODEL,
manufacturer = Build.MANUFACTURER,
osVersion = Build.VERSION.RELEASE,
serialNumber = getSerialNumber(),
imei = null,
macAddress = getMacAddress(),
androidId = getAndroidId(),
method = "app-only",
timestamp = timestamp
)
val enrollmentRequest = EnrollmentRequest(
model = Build.MODEL,
manufacturer = Build.MANUFACTURER,
osVersion = Build.VERSION.RELEASE,
sdkVersion = Build.VERSION.SDK_INT,
serialNumber = getSerialNumber(),
imei = null,
macAddress = getMacAddress(),
androidId = getAndroidId(),
agentVersion = BuildConfig.VERSION_NAME,
agentPackage = packageName,
method = "app-only",
timestamp = timestamp,
signature = signature
)
val result = mdmClient.enroll(enrollmentRequest)
result.onSuccess { response ->
// Device enrolled successfully
Log.i("MDM", "Enrolled as device: ${response.deviceId}")
}
// Send heartbeat
val heartbeatRequest = HeartbeatRequest(
deviceId = mdmClient.getDeviceId()!!,
timestamp = Instant.now().toString(),
batteryLevel = getBatteryLevel(),
isCharging = isCharging(),
// ... other telemetry
)
val heartbeatResult = mdmClient.heartbeat(heartbeatRequest)
heartbeatResult.onSuccess { response ->
// Process pending commands
response.pendingCommands?.forEach { command ->
processCommand(command)
}
// Apply policy updates
response.policyUpdate?.let { policy ->
applyPolicy(policy)
}
}To enable full MDM capabilities, the agent must be set as Device Owner.
adb shell dpm set-device-owner com.openmdm.agent/.receiver.MDMDeviceAdminReceiverConfigure your devices through Android Zero-Touch or Samsung Knox.
Generate a provisioning QR code using the OpenMDM CLI:
npx openmdm enroll qr --output enrollment.pngopenmdm-android/
├── agent/ # Full-featured MDM agent app
│ ├── src/main/
│ │ ├── java/.../
│ │ │ ├── receiver/ # DeviceAdminReceiver, BootReceiver
│ │ │ ├── service/ # MDMService, FCM service
│ │ │ ├── ui/ # Compose UI screens
│ │ │ └── util/ # Utilities
│ │ └── res/ # Resources (customize for branding)
│ └── build.gradle.kts
│
├── library/ # Core MDM library
│ ├── src/main/java/.../
│ │ ├── api/ # API models and Retrofit interface
│ │ ├── device/ # DeviceManager
│ │ └── MDMClient.kt # High-level client
│ └── build.gradle.kts
│
├── docs/ # Documentation
├── build.gradle.kts # Root build file
└── settings.gradle.kts # Module configuration
- App Icon: Replace files in
agent/src/main/res/mipmap-*/ - App Name: Edit
agent/src/main/res/values/strings.xml - Colors: Edit
agent/src/main/res/values/colors.xml - Package Name: Update
applicationIdinagent/build.gradle.kts
// In your MDMService or command processor
when (command.type) {
"custom" -> {
val customType = command.payload?.get("customType") as? String
when (customType) {
"myCustomCommand" -> handleMyCustomCommand(command.payload)
else -> CommandResult(false, "Unknown custom command")
}
}
}class MyDeviceAdminReceiver : DeviceAdminReceiver() {
override fun onEnabled(context: Context, intent: Intent) {
super.onEnabled(context, intent)
// Custom initialization
}
override fun onDisabled(context: Context, intent: Intent) {
super.onDisabled(context, intent)
// Cleanup
}
}# Build everything
./gradlew build
# Build agent APK
./gradlew :agent:assembleRelease
# Build library AAR
./gradlew :library:assembleRelease
# Run tests
./gradlew testThis Android agent is designed to work with OpenMDM Server v0.2.0+.
The API protocol is defined in the @openmdm/client TypeScript package. Keep the Kotlin models in sync when updating.
The library module is published via JitPack.
Releases are automatically available on JitPack when a GitHub release is created:
implementation("com.github.azoila.openmdm-android:library:0.1.0")You can also use any commit hash:
implementation("com.github.azoila.openmdm-android:library:abc1234")For the latest unreleased changes from the main branch:
implementation("com.github.azoila.openmdm-android:library:main-SNAPSHOT")Note: SNAPSHOT versions are cached for 24 hours. Use
--refresh-dependenciesto force update.
Check the JitPack build status at: https://jitpack.io/#azoila/openmdm-android
Contributions welcome! Please read the Contributing Guide.
MIT License - see LICENSE for details.
Part of the OpenMDM project.