Build a device policy controller (original) (raw)

This guide describes how to develop a device policy controller (DPC) for devices in an Android enterprise deployment. A DPC app, previously known as a work policy controller, controls local device policies and system applications on devices.

About DPCs

In an Android enterprise deployment, an enterprise maintains control over various aspects of user devices, such as isolating work-related information from users' personal data, pre-configuring approved apps for the environment, or disabling device capabilities (for example, the camera).

As an EMM, you develop a DPC app that can be used by your customers in conjunction with your EMM console and server. Your customer deploys the DPC to the user devices that they manage. The DPC acts as the bridge between your EMM console (and server) and the device. An admin uses the EMM console to perform a range of tasks, including configuring device settings and apps.

The DPC creates and manages the work profile on the device on which it is installed. The work profile encrypts work-related information and keeps it separate from users' personal apps and data. Before creating the work profile, the DPC can also provision a managed Google Play Account for use on the device.

This guide shows you how to develop a DPC that can create and manage work profiles.

DPC Support Library for EMMs

The DPC Support Library for EMMs comprises utility and helper classes that facilitate provisioning and management of Android devices in an enterprise environment. The library lets you take advantage of important features in your DPC apps:

Follow the steps below to download the library. The tasks detailed in this guide assume the use of the DPC Support Library.

Download the DPC Support Library

To use the DPC Support Library, download the library from the Android Enterprise EMM Provider community. You must add the library to your build.gradle file and take care of other dependencies when you build your DPC app. For example, the library requires 11.4.0Google Play Services auth client library.

  1. Add the library to the build.gradle file:

Groovy

implementation(name:'dpcsupport-yyyymmdd', ext:'aar')

Kotlin

implementation(name = "dpcsupport-yyyymmdd", ext = "aar") 2. Add 11.4.0 Google Play Services auth client library to the build.gradle file:

Groovy

implementation 'com.google.android.gms:play-services-auth:11.4.0'

Kotlin

implementation("com.google.android.gms:play-services-auth:11.4.0")

The library requires certain permissions to run, so you must add these to your DPC app’s manifest when you upload to Google Play:

In addition to these preliminary setup and deployment steps, you must also initialize the specific library functionality in your DPC code, depending on the capability you want to implement. The details are included in the relevant sections below.

Create a DPC

Build your DPC on the existing model used for device administration applications. Specifically, your app must subclass [DeviceAdminReceiver](/reference/android/app/admin/DeviceAdminReceiver) (a class from the android.app.adminpackage) as described inDevice Administration.

Create a work profile

For a sample that demonstrates how to create a basic work profile, seeBasicManagedProfile on GitHub.

To create a work profile on a device that already has a personal profile, first find out if the device can support a work profile, by checking for the existence of theFEATURE_MANAGED_USERS system feature:

Kotlin

if (!packageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) { // This device does not support work profiles! }

Java

PackageManager pm = getPackageManager(); if (!pm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) { // This device does not support work profiles! }

If the device supports work profiles, create a work profile by sending an intent with an ACTION_PROVISION_MANAGED_PROFILEaction. (In some documentation, managed profile is a general term that means the same thing as _work profile_in the context of Android in the enterprise.) Include the device admin package name as an extra:

Kotlin

val provisioningActivity = getActivity()

// You'll need the package name for the DPC app. val myDPCPackageName = "com.example.myDPCApp"

// Set up the provisioning intent val adminComponent = ComponentName(provisioningActivity.applicationContext, MyAdminReceiver::class.java) provisioningIntent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, adminComponent.flattenToString()) if (provisioningIntent.resolveActivity(provisioningActivity.packageManager) == null) { // No handler for intent! Can't provision this device. // Show an error message and cancel. } else { // REQUEST_PROVISION_MANAGED_PROFILE is defined // to be a suitable request code startActivityForResult(provisioningIntent, REQUEST_PROVISION_MANAGED_PROFILE) provisioningActivity.finish() }

Java

Activity provisioningActivity = getActivity(); // You'll need the package name for the DPC app. String myDPCPackageName = "com.example.myDPCApp"; // Set up the provisioning intent Intent provisioningIntent = new Intent("android.app.action.PROVISION_MANAGED_PROFILE"); ComponentName adminComponent = new ComponentName(provisioningActivity.getApplicationContext(), MyAdminReceiver.class); provisioningIntent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, adminComponent.flattenToString()); if (provisioningIntent.resolveActivity(provisioningActivity.getPackageManager()) == null) { // No handler for intent! Can't provision this device. // Show an error message and cancel. } else { // REQUEST_PROVISION_MANAGED_PROFILE is defined // to be a suitable request code startActivityForResult(provisioningIntent, REQUEST_PROVISION_MANAGED_PROFILE); provisioningActivity.finish(); }

The system responds to this intent by doing the following:

Override [onActivityResult()](/reference/android/app/Activity#onActivityResult%28int, int, android.content.Intent%29) to see if provisioning was successful:

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { // Check if this is the result of the provisioning activity if (requestCode == REQUEST_PROVISION_MANAGED_PROFILE) { // If provisioning was successful, the result code is // Activity.RESULT_OK if (resultCode == Activity.RESULT_OK) { // Work profile created and provisioned. } else { // Provisioning failed. } return } else { // This is the result of some other activity. Call the superclass. super.onActivityResult(requestCode, resultCode, data) } }

Java

@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { // Check if this is the result of the provisioning activity if (requestCode == REQUEST_PROVISION_MANAGED_PROFILE) { // If provisioning was successful, the result code is // Activity.RESULT_OK if (resultCode == Activity.RESULT_OK) { // Work profile created and provisioned. } else { // Provisioning failed. } return; } else { // This is the result of some other activity. Call the superclass. super.onActivityResult(requestCode, resultCode, data); } }

Finish enabling the work profile

When the profile has been provisioned, the system calls the DPC app's [DeviceAdminReceiver.onProfileProvisioningComplete()](/reference/android/app/admin/DeviceAdminReceiver#onProfileProvisioningComplete%28android.content.Context, android.content.Intent%29) method. Override this callback method to finish enabling the work profile.

A typical DeviceAdminReceiver.onProfileProvisioningComplete()callback implementation does the following:

Activate the work profile

Once you have completed these tasks, call the device policy manager's[setProfileEnabled()](/reference/android/app/admin/DevicePolicyManager#setProfileEnabled%28android.content.ComponentName%29) method to activate the work profile:

Kotlin

// Get the device policy manager val myDevicePolicyMgr = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val componentName = myDeviceAdminReceiver.getComponentName(this) // Set the name for the newly created work profile. myDevicePolicyMgr.setProfileName(componentName, "My New Work Profile") // ...and enable the profile myDevicePolicyMgr.setProfileEnabled(componentName)

Java

// Get the device policy manager DevicePolicyManager myDevicePolicyMgr = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); ComponentName componentName = myDeviceAdminReceiver.getComponentName(this); // Set the name for the newly created work profile. myDevicePolicyMgr.setProfileName(componentName, "My New Work Profile"); // ...and enable the profile myDevicePolicyMgr.setProfileEnabled(componentName);

Set up device policies

The DPC app applies the device policies as set by an admin to meet an organization's requirements and constraints. For example, security policy might require that devices lock after a certain number of failed password attempts. The DPC queries the EMM console for current policies then applies the policies using the Device Administration API.

For information on how to apply device policies, seePolicies.

Apply managed configurations to work apps

Managed configurations let you provide your customers with the ability to pre-configure the apps that they’ve approved for deployment, and update those apps easily when the configuration needs to change. Configuring an app prior to deployment ensures that the organization’s security and other policies are met upon installation of the app on the target device.

The app capabilities are defined by the app developer in an XML schema (the managed configurations schema) that accompanies the app upon upload to Google Play (app developers, see Set Up Managed Configurations for details).

You retrieve this schema from the app to display for your customer admins in your EMM console, provide a UI in which the various options defined in the schema display, and enable admins to pre-configure the app’s settings. The resulting managed configuration set by the admin is typically stored on the EMM server which then uses the Play EMM API to setManagedconfigurationsfordeviceor Managedconfigurationsforuser. SeeManaged Configurations through Play for details.

Managed configurations can be applied to the app by using the Play EMM API (recommended approach) or directly from the DPC (described in Apply managed configurations directly from the DPC). Using the Play EMM API has several advantages, including easy implementation because you can use theDPC Support Library to simplify DPC tasks. In addition, the Play EMM API:

Apply managed configurations using the Play EMM API

To use the Play EMM API for managed configurations, the DPC must allow the Google Play to set configurations. The DPC Support Library takes care of this task for you by proxying the configuration sent by Google Play.

To use the Play EMM API, download the DPC Support Libraryand then enable managed configurations support in your DPC.

Enable Managed Configurations support in your DPC

Import this class in your DPC:

com.google.android.apps.work.dpcsupport.ManagedConfigurationsSupport

Initialize the managed configurations library. In this example, "admin" is the ComponentName of the DeviceAdminReceiver.

Kotlin

var managedConfigurationsSupport = ManagedConfigurationsSupport(context, admin)

Java

ManagedConfigurationsSupport managedConfigurationsSupport = new ManagedConfigurationsSupport(context, admin);

Enable managed configurations:

Kotlin

managedConfigurationsSupport.enableManagedConfigurations()

Java

managedConfigurationsSupport.enableManagedConfigurations();

With this library initialized in your DPC, you can use theGoogle Play EMM API in your EMM console and server to apply managed configurations to approved apps, instead of coding these tasks directly in the DPC. SeeManaged Configurations through Play for details.

Apply managed configurations directly from the DPC

To change an app's configuration settings directly from the DPC, call the[DevicePolicyManager.setApplicationRestrictions()](/reference/android/app/admin/DevicePolicyManager#setApplicationRestrictions%28android.content.ComponentName, java.lang.String, android.os.Bundle%29) method and pass parameters for the DPC app'sDeviceAdminReceiver, the package name of the target app, and the Bundle comprising the app’s managed configuration as set by the admin. SeeHow your DPC and EMM console interact and Set up Managed Configurations for details. However, note that this alternative approach to applying managed configurations is not recommended in managed Google Play Accounts deployments.

Managed Google Play Account provisioning support

The DPC Support Library includes support for provisioning managed Google Play Accounts. To use this support, you must first initialize the library, and then you can Ensure the working environment and Add a managed Google Play Account.

Initialize managed Google Play Accounts support in your DPC

Import this class in your DPC:

com.google.android.apps.work.dpcsupport.AndroidForWorkAccountSupport

Initialize the provisioning compatibility library. In this example, “admin” is the ComponentName of the [DeviceAdminReceiver](/reference/android/app/admin/DeviceAdminReceiver).

Kotlin

var androidForWorkAccountSupport = AndroidForWorkAccountSupport(context, admin)

Java

AndroidForWorkAccountSupport androidForWorkAccountSupport = new AndroidForWorkAccountSupport(context, admin);

Ensure the working environment for managed Google Play Accounts

After the DPC provisions a device in profile owner mode ([ACTION_PROVISION_MANAGED_PROFILE](/reference/android/app/admin/DevicePolicyManager#ACTION%5FPROVISION%5FMANAGED%5FPROFILE)) or device owner mode ([ACTION_PROVISION_MANAGED_DEVICE](/reference/android/app/admin/DevicePolicyManager#ACTION%5FPROVISION%5FMANAGED%5FDEVICE)), make sure that the device can support managed Google Play Accounts by calling:

Kotlin

androidForWorkAccountSupport.ensureWorkingEnvironment(callback)

Java

androidForWorkAccountSupport.ensureWorkingEnvironment(callback);

The callback reports the success or failure of this process. When the callback returns successfully, a managed Google Play Account can be added. If the callback reports an error, prompt the user to make sure the device has a network connection (for example, if the download fails). In other cases, report the failure to Google.

Kotlin

object : WorkingEnvironmentCallback() { override fun onSuccess() { // Can now provision the managed Google Play Account } override fun onFailure(error: Error) { // Notify user, handle error (check network connection) } }

Java

new WorkingEnvironmentCallback() { @Override public void onSuccess() { // Can now provision the managed Google Play Account }

@Override
public void onFailure(Error error) {
    // Notify user, handle error (check network connection)
}

}

Add a managed Google Play Account

The Android framework's [AccountManager](/reference/android/accounts/AccountManager)can add a managed Google Play Account to a device. To simplify interaction withAccountManager, use the helper function (shown in the example below) from the DPC Support Library. The function handles the token returned by Google Play server and facilitates provisioning the managed Google Play Account. The function returns when the managed Google Play Account is in a valid state:

Kotlin

androidForWorkAccountSupport.addAndroidForWorkAccount(token, accountAddedCallback)

Java

androidForWorkAccountSupport.addAndroidForWorkAccount(token, accountAddedCallback);

Kotlin

val workAccountAddedCallback = object : WorkAccountAddedCallback() { override fun onAccountReady(account: Account, deviceHint: String) { // Device account was successfully added to the device // and is ready to be used. }

override fun onFailure(error: Error) {
    // The account was not successfully added. Check that the token
    // provided was valid (it expires after a certain period of time).
}

}

Java

WorkAccountAddedCallback workAccountAddedCallback = new WorkAccountAddedCallback() { @Override public void onAccountReady(Account account, String deviceHint) { // Device account was successfully added to the device // and is ready to be used. }

    @Override
    public void onFailure(Error error) {
        // The account was not successfully added. Check that the token
        // provided was valid (it expires after a certain period of time).
    }

};