visit
At GoDaddy, we manage 300k+ Smart Payment Terminals using our in-house Device Management Solution. Here, I wanted to share some lessons from my experience and a few established open-source methods that Android OS offers to accomplish Device Management.
App and Content Control: Push educational apps and block social media or entertainment apps to ensure that the use of the tablet is for learning purposes.
Classroom Mode: The teacher can see the activities going on in the tablet, push content, and lock the device during exams or study hours.
Software Updates: Remotely push OS updates and patches to all devices continuously for security and functionality.
Remote Management: Allow IT administrators to diagnose problems, monitor usage, and address device malfunction from a distance.
Create an enrollment token
POST //androidmanagement.googleapis.com/v1/{parent=enterprises/*}/enrollmentTokens
Input:
userAccountIdentifier
: This field allows you to associate a specific user account with the enrolled device. If not specified, the API will generate a unique user account for each device.{
"applications": [
{
"packageName": "com.myawesome.devicemanager",
"installType": "REQUIRED_FOR_SETUP"
}
],
"setupActions": [
{
"title": {
"defaultMessage": "Setup"
},
"launchApp": {
"packageName": "com.myawesome.devicemanager"
}
}
],
"allowPersonalUsage": false
}
Output:
enrollmentToken
Object: Contains the enrollmentTokenId
and a QRCode
link, which is then used by the end user for device provisioning. This token is crucial for enrolling COTS devices under management without manual configuration.
Refer here to explore other options: //developers.google.com/android/management/provision-device
{
"android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME": "com.myawesome.devicemanager/.receiver.DeviceAdminReceiver",
"android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM": "I5YvS0O5hXY46mb01BlRjq4oJJGs2kuUcHvVkAPEXlg",
"android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION": "//play.google.com/managed/downloadManagingApp?identifier=setup",
"android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE": {
"com.google.android.apps.work.clouddpc.EXTRA_ENROLLMENT_TOKEN": "{enrollment-token}"
}
}
/data/system/device_owner_2.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<root>
<device-owner
package="com.myawesome.devicemanager"
name="Device Manager"
component="com.myawesome.devicemanager/.receiver.DeviceAdminReceiver"
userRestrictionsMigrated="true"
canAccessDeviceIds="true" />
<device-owner-context userId="0" />
</root>
/data/system/device_policies.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<policies setup-complete="true" provisioning-state="3">
<admin name="com.myawesome.devicemanager/.receiver.DeviceAdminReceiver">
<policies flags="17" />
<strong-auth-unlock-timeout value="0" />
<user-restrictions no_add_managed_profile="true" />
<default-enabled-user-restrictions>
<restriction value="no_add_managed_profile" />
</default-enabled-user-restrictions>
<cross-profile-calendar-packages />
</admin>
<lock-task-features value="16" />
</policies>
<!-- In your AndroidManifest.xml -->
<receiver
android:name=".MyDeviceAdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_policies" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
<!-- res/xml/device_policies.xml -->
<device-admin xmlns:android="//schemas.android.com/apk/res/android">
<uses-policies>
<force-lock />
<wipe-data />
<reset-password />
<reboot />
<grant-policies />
</uses-policies>
</device-admin>
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
fun rebootDevice(context: Context, adminComponent: ComponentName) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
devicePolicyManager.reboot(adminComponent)
}
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
fun requestBugReport(context: Context, adminComponent: ComponentName) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
devicePolicyManager.requestBugreport(adminComponent)
}
Here’s how to implement the BugReportReceiver
to retrieve the bug report:
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class BugReportReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Retrieve the file path of the generated bug report zip file
if (intent.action == DevicePolicyManager.ACTION_BUGREPORT_SHARE) {
val bugReportUri = intent.getData()
if (bugReportUri != null) {
Log.d("BugReportReceiver", "Bug report saved at: $bugReportUri")
// Process the bug report file as needed, e.g., upload to a server or save it locally
}
} else {
Log.e("BugReportReceiver", "Bug report generation failed")
}
}
}
<receiver android:name=".BugReportReceiver">
<intent-filter>
<action android:name="android.app.action.BUGREPORT_SHARE" />
</intent-filter>
</receiver>
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.os.UserHandle
fun grantPermissionToApp(context: Context, adminComponent: ComponentName, packageName: String, permission: String) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
devicePolicyManager.setPermissionGrantState(adminComponent, packageName, permission, DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED)
}
For example, if you want to grant CAMERA
permission to an app:
grantPermissionToApp(context, adminComponent, "com.app.cameraapp", android.Manifest.permission.CAMERA)
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
fun wipeDeviceData(context: Context, adminComponent: ComponentName) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
devicePolicyManager.wipeData(DevicePolicyManager.WIPE_EXTERNAL_STORAGE or DevicePolicyManager.WIPE_RESET_PROTECTION_DATA)
}
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
fun setCameraDisabled(context: Context, adminComponent: ComponentName, disabled: Boolean) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
devicePolicyManager.setCameraDisabled(adminComponent, disabled)
}
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
fun enforcePasswordQuality(context: Context, adminComponent: ComponentName) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
devicePolicyManager.setPasswordQuality(adminComponent, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC)
}
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
fun hideAppPackage(context: Context, adminComponent: ComponentName, packageName: String, hide: Boolean) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val success = devicePolicyManager.setApplicationHidden(adminComponent, packageName, hide)
if (success) {
if (hide) {
println("Package $packageName has been hidden.")
} else {
println("Package $packageName has been unhidden.")
}
} else {
println("Failed to change the visibility of $packageName.")
}
}
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import java.io.File
fun installApk(context: Context, apkFile: File, adminComponent: ComponentName) {
val devicePolicyManager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
// Install the APK using the DevicePolicyManager
devicePolicyManager.installPackage(adminComponent, apkFile.toURI(), false)
}