Making your app a device owner allows you to access some features that would normally require root. You can, for example, reboot the device, hide other installed apps, or restrict access to system settings. You can even perform silent app installs or grant dangerous permissions to apps.

What you can do, slightly changed between Android versions, but most of the stuff that I show here should work on Android 6 and higher.

I've used the device-owner-related api in one of the projects I was working on heavily, because it allowed me to fulfill some of the client's requirements using regular Android devices, without rooting or performing any system customizations (and possibly voiding the guarantee).

If you're not sure how to enable a device owner app, check out my previous post.

Creating Kiosk App - LockTask Mode

Devices with Android 6 Marshmallow and later can be configured to run as corporate-owned, single-use (COSU) devices. One of the features related to COSU is the lock task mode, which you can use to create your own kiosk apps (e.g., Internet kiosk, ticket sales device, shopping center map, ...).

You can use the startLockTask() method to put your app into a mode where your user won't be able to exit it.

Before you can use this mode, you first need to add your task to a list of packages that are allowed to start the LockTask mode. You should use the setLockTaskPackages() method of DevicePolicyManager to whitelist your app for using LockTask mode.

When calling startLockTask(), also make sure that the Activity is in foreground.

fun startLockTaskMode() {

    // Get DevicePolicyManager instance
    val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager

    // Get an identifier for the component that is DeviceAdminReceiver
    // This will be used in various DevicePolicyManager methods
    val cn = ComponentName(this, DevAdminReceiver::class.java)

    // If we are a device owner, we are allowed to
    // whitelist a package for using LockTask mode
    if (dpm.isDeviceOwnerApp(packageName) && !dpm.isLockTaskPermitted(packageName)) {
        val packages = arrayOf(packageName)
        dpm.setLockTaskPackages(cn, packages)
    }

    // If we are whitelisted for LockTask mode,
    // start the mode
    if (dpm.isLockTaskPermitted(packageName)) {
        startLockTask()
    }
}

Alternatively, instead of calling startLockTask() from within your Activity, you can set the lockTaskMode attribute in your AndroidManifest.xml file to always or if_whitelisted.

Whitelisting your package for using LockTask mode will automatically allow other packages that share the same uid to use this mode.

Hiding/Blocking Other Apps (Including Pre-Installed Apps)

In addition to enforcing the use of only one "kiosk" app as in previous section, you can also restrict the usage of other apps. You can hide apps that have no use for your specific purpose and expose only a limited list of your own apps to the user.

The setApplicationHidden() method of DevicePolicyManager allows you to hide an app from user. The actual app and its data will still remain on the device, the user just won't be able to access it.

For example, to hide the phone app you can use the following code

fun hideDialer() {

    val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
    val cn = ComponentName(this, DevAdminReceiver::class.java)

    // Hide dialer here
    dpm.setApplicationHidden(cn, "com.android.dialer", true)
}

To unhide the app again later, you just need to flip the last argument of setApplicationHidden() to false.

Granting Dangerous Permissions Without User Confirmation

Another useful feature is to grant dangerous permissions to specific apps. This allows you grant the permission without confirmation dialog, which is certainly useful when you perform a remote update and require additional permissions.

For granting the WRITE_EXTERNAL_STORAGE permission, you can do the following

fun grantStoragePermission() {

    val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
    val cn = ComponentName(this, DevAdminReceiver::class.java)

    dpm.setPermissionGrantState(cn, packageName,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED)
}

Disallow User to Remove or Configure Apps

You might not want the user to clear your app's cache or change app-related settings.

To block user from modifying app settings, use the following code

fun disallowApsControl() {

    val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
    val cn = ComponentName(this, DevAdminReceiver::class.java)

    dpm.addUserRestriction(cn, UserManager.DISALLOW_APPS_CONTROL)
}

The UserManager class contains some additional keys that you can supply to the addUserRestriction() method. Additional ones that I've found useful were DISALLOW_ADD_USER, DISALLOW_FACTORY_RESET, and DISALLOW_INSTALL_APPS. Their names explain sufficiently what they do.

Later you can remove the restriction by calling clearUserRestriction().

Changing Secure Settings

With a device owner app, you are able to modify some of the settings from the Secure namespace.

The method involved is setSecureSetting() and you can use it in the following way to enable location mode or skip system hints on first app start

fun changeSecureSettings() {

    val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
    val cn = ComponentName(this, DevAdminReceiver::class.java)

    dpm.setSecureSetting(cn, Settings.Secure.LOCATION_MODE, "1")
    dpm.setSecureSetting(cn, Settings.Secure.SKIP_FIRST_USE_HINTS, "1")
}

Rebooting the Device

Since Android 7.0 (API level 24) you also have the option to reboot the device without being root. You can use the reboot() method of DevicePolicyManager for this

fun rebootMyDevice() {
    val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
    val cn = ComponentName(this, DevAdminReceiver::class.java)

    dpm.reboot(cn)
}

Checking out Goats

The heading above might not make sense to you (especially if you're not from Slovakia), so please ignore it. What I actually wanted to mention at the end are some interesting and "useful" methods I've found in UserManager class.

You should definitely check out the isUserAGoat() method and the DISALLOW_FUN constant.

What are these for?

I'll leave figuring that out as an exercise to the reader.

You can find more of these Easter eggs at the following link.

Useful Links

If you're not sure how to set up a device owner app, checkout my previous post.

For learning how to do a silent app update, check out the following post.

Additionally, if you're doing some Android device maintenance on-the-go (e.g., maintenance of your kiosk), you might checkout my Bugjaeger app, which offers some ADB functionality directly from your phone.

Previous Post

Add a comment