From https://newcircle.com/s/post/1291/android_device_policy_administration_tutorial
Device Administration Overview
The Android Device Administration API, introduced in Android 2.2, allows you to create security-aware applications that are useful in enterprise settings, such as:
-
Email clients
-
Security applications that do remote wipe
-
Device management services and applications
You use the Device Administration API to write device admin applications that users install on their devices. The device admin application enforces desired security policies. Here’s how it works:
-
A system administrator writes a device admin application that enforces remote/local device security policies.
-
The application is installed on a user’s device.
-
The system prompts the user to enable the device admin application.
-
Once the users enable the device admin application, they are subject to its policies.
When enabled, in addition to enforcing security policies, the admin application can:
-
Prompt the user to set a new password
-
Lock the device immediately
-
Perform a factory reset on the device, wiping the user data (if it has permission)
If a device contains multiple enabled admin applications, the strictest policy is enforced.
If users do not enable the device admin app, it remains on the device, but in an inactive state.
-
Users will not be subject to its policies, but the application may disable some or all of its functionality.
If a user fails to comply with the policies (for example, if a user sets a password that violates the guidelines), it is up to the application to decide how to handle this.
-
For example, the application may prompt the user to set a new password or disable some or all of its functionality.
To uninstall an existing device admin application, users need to first deactivate the application as a device administrator.
-
Upon deactivation, the application may disable some or all of its functionality, delete its data, and/or perform a factory reset (if it has permission).
Security Policies
An admin application may enforce security policies regarding the device’s screen lock PIN/password, including:
-
The maximum inactivity time to trigger the screen lock
-
The minimum number of PIN/password characters
-
The maximum number of failed password attempts
-
The minimum number of uppercase letters, lowercase letters, digits, and/or special password characters (Android 3.0)
-
The password expiration period (Android 3.0)
-
A password history restriction, preventing users from reusing the last n unique passwords (Android 3.0)
Additionally, a security policy can require device storage encryption as of Android 3.0 and disabling of camera as for Android 4.0.
The Device Administration Classes
The Device Administration API includes the following classes:
-
DeviceAdminReceiver
-
Base class for implementing a device administration component. This class provides a convenience for interpreting the raw intent actions that are sent by the system. Your Device Administration application must include a DeviceAdminReceiver subclass.
DevicePolicyManager
-
A class for managing policies enforced on a device. Most clients of this class must have published a DeviceAdminReceiver that the user has currently enabled. The DevicePolicyManager manages policies for one or more DeviceAdminReceiver instances.
DeviceAdminInfo
-
This class is used to specify metadata for a device administrator component.
Creating the Manifest
The manifest of your admin application must register your DeviceAdminReceiver as a <receiver>.
The <receiver> should set android:permission="android.permission.BIND_DEVICE_ADMIN" to ensure that only the system is allowed to interact with the broadcast receiver.
The <receiver> must have an <intent-filter> child element including one or more of the following<action>s, as defined in the DeviceAdminReceiver class:
-
ACTION_DEVICE_ADMIN_ENABLED
-
(Required) This is the primary action that a device administrator must implement to be allowed to manage a device. This is sent to a device administrator when the user enables it for administration.
ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
-
Action sent to a device administrator when the user has requested to disable it, but before this has actually been done.
ACTION_DEVICE_ADMIN_DISABLED
-
Action sent to a device administrator when the user has disabled it.
ACTION_PASSWORD_CHANGED
-
Action sent to a device administrator when the user has changed the password of their device.
ACTION_PASSWORD_EXPIRING
-
Action periodically sent to a device administrator when the device password is expiring.
ACTION_PASSWORD_FAILED
-
Action sent to a device administrator when the user has failed at attempted to enter the password.
ACTION_PASSWORD_SUCCEEDED
-
Action sent to a device administrator when the user has successfully entered their password, after failing one or more times.
<receiver android:name="MyDeviceAdminReceiver" android:permission="android.permission.BIND_DEVICE_ADMIN"> <intent-filter> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> <action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" /> <action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLED" /> </intent-filter> <!-- ... --> </receiver>
Your <receiver> element must also include a <meta-data> child element specifying an XML resource declaring the policies used by your admin application.
-
The android:name attribute must be android.app.device_admin.
-
The android:resource must reference an XML resource in your application.
-
For example:
<meta-data android:name="android.app.device_admin" android:resource="@xml/device_admin_sample" />
An example XML resource requesting all policies would be:
<device-admin xmlns:android="http://schemas.android.com/apk/res/android"> <uses-policies> <limit-password /> <watch-login /> <reset-password /> <force-lock /> <wipe-data /> <expire-password /> <encrypted-storage /> <disable-camera /> </uses-policies> </device-admin>
Your application needs to list only those policies it actually uses.
The DeviceAdminReceiver Class
The DeviceAdminReceiver class defines a set of methods that you can override to handle the device administration events broadcast by the system:
-
void onEnabled(Context context, Intent intent)
-
Called after the administrator is first enabled, as a result of receivingACTION_DEVICE_ADMIN_ENABLED. At this point you can use DevicePolicyManager to set your desired policies.
CharSequence onDisableRequested(Context context, Intent intent)
-
Called when the user has asked to disable the administrator, as a result of receivingACTION_DEVICE_ADMIN_DISABLE_REQUESTED. You may return a warning message to display to the user before being disabled, or null for no message.
void onDisabled(Context context, Intent intent)
-
Called prior to the administrator being disabled, as a result of receivingACTION_DEVICE_ADMIN_DISABLED. Upon return, you can no longer use the protected parts of the DevicePolicyManager API.
void onPasswordChanged(Context context, Intent intent)
-
Called after the user has changed their password, as a result of receivingACTION_PASSWORD_CHANGED.
void onPasswordExpiring(Context context, Intent intent)
-
Called periodically when the password is about to expire or has expired, as a result of receivingACTION_PASSWORD_EXPIRING. (API 11)
void onPasswordFailed(Context context, Intent intent)
-
Called after the user has failed at entering their current password, as a result of receivingACTION_PASSWORD_FAILED.
void onPasswordSucceeded(Context context, Intent intent)
-
Called after the user has succeeded at entering their current password, as a result of receivingACTION_PASSWORD_SUCCEEDED.
Testing Whether the Admin Application is Enabled
You can query the DevicePolicyManager to test if your admin application is enabled:
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); ComponentName deviceAdminComponentName = new ComponentName(this, MyDeviceAdminReceiver.class); boolean isActive = devicePolicyManager.isAdminActive(deviceAdminComponentName);
You could then enable or disable features of your application depending on whether it is an active device administrator.
Enabling the Application
Your application must explicitly request the user to enable it for device administration. To do so:
-
Create an implicit Intent with the DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN action:
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
-
Add an extra identifying your DeviceAdminReceiver component:
ComponentName deviceAdminComponentName = new ComponentName(this, MyDeviceAdminReceiver.class); intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, deviceAdminComponentName);
-
Optionally, provide an explanation as to why the user should activate the admin application:
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Your boss told you to do this");
-
Use the Intent with startActivityForResult() to display the activation dialog:
startActivityForResult(intent, ACTIVATION_REQUEST);
-
You can test for successful activation in your Activity’s onActivityResult() method:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case ACTIVATION_REQUEST: if (resultCode == Activity.RESULT_OK) { Log.i("DeviceAdminSample", "Administration enabled!"); } else { Log.i("DeviceAdminSample", "Administration enable FAILED!"); } return; } super.onActivityResult(requestCode, resultCode, data); }
Setting Password Quality Policies
DevicePolicyManager includes APIs for setting and enforcing the device screen lock password policy.
-
The setPasswordQuality() lets you set basic password requirements for your admin application, using these constants:
-
PASSWORD_QUALITY_ALPHABETIC
-
The user must enter a password containing at least alphabetic (or other symbol) characters.
PASSWORD_QUALITY_ALPHANUMERIC
-
The user must enter a password containing at least both numeric and alphabetic (or other symbol) characters.
PASSWORD_QUALITY_NUMERIC
-
The user must enter a password containing at least numeric characters.
PASSWORD_QUALITY_SOMETHING
-
The policy requires some kind of password, but doesn’t care what it is.
PASSWORD_QUALITY_UNSPECIFIED
-
The policy has no requirements for the password.
PASSWORD_QUALITY_COMPLEX
-
(API 11) The user must have entered a password containing at least a letter, a numerical digit and a special symbol.
-
-
Once you have set the password quality, you may also specify a minimum length (except withPASSWORD_QUALITY_SOMETHING and PASSWORD_QUALITY_UNSPECIFIED) using thesetPasswordMinimumLength() method.
-
For example:
devicePolicyManager.setPasswordQuality(deviceAdminComponentName, PASSWORD_QUALITY_ALPHANUMERIC); devicePolicyManager.setPasswordMinimumLength(deviceAdminComponentName, 6);
Your application’s policy metadata resource must request the <limit-password /> policy to control password quality; otherwise these methods throw a security exception.
Setting Password Quality Policies, API 11
Beginning with Android 3.0, the DevicePolicyManager class includes methods that give you greater control over the contents of the password. Here are the methods for fine-tuning a password’s contents:
-
setPasswordMinimumLetters()
-
setPasswordMinimumLowerCase()
-
setPasswordMinimumUpperCase()
-
setPasswordMinimumNonLetter()
-
setPasswordMinimumNumeric()
-
setPasswordMinimumSymbols()
You can also set the password expiration timeout, and prevent users from reusing the last n unique passwords:
-
setPasswordExpirationTimeout()
-
setPasswordHistoryLength()
Additionally, Android 3.0 introduced support for a policy requiring the user to encrypt the device, which you can set with:
-
setStorageEncryption()
Your application’s policy metadata resource must request the <limit-password /> policy to control password quality; otherwise these methods throw a security exception.
Similarly, it must request the <expire-password /> and <encrypted-storage /> policies to control those features without throwing a security exception.
Setting the Device Password
You can test if the current device password meets the quality requirements by callingDevicePolicyManager.isActivePasswordSufficient(), which returns a boolean result.
If necessary, you can start an activity prompting the user to set a password as follows:
Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); startActivity(intent);
Your application can also perform a password reset on the device usingDevicePolicyManager.resetPassword(). This can be useful if your application is designed to support remote administration, with a new password being provided from a central administration system.
Your application’s policy metadata resource must request the <reset-password /> policy to reset the password; otherwise resetPassword() throws a security exception.
Locking and Wiping the Device
Your application can lock the device programmatically using DevicePolicyManager.lockNow().
You can wipe the user data of the device, performing a factory reset, usingDevicePolicyManager.wipeData().
Additionally, you can set the maximum number of allowed failed password attempts before the device is wiped automatically by callingDevicePolicyManager.setMaximumFailedPasswordsForWipe()
Your application’s policy metadata resource must request the <wipe-data /> policy to wipe the data either explicitly or set the maximum failed passwords for wipe; otherwise a security exception is thrown. setMaximumFailedPasswordsForWipe() also requires the <watch-login /> policy.
The lockNow() method requires your application to request the <force-lock /> policy to avoid throwing a security exception.
Device Administration Demo
In this example app, you will see how to write an application that requests to device administration privileges, and once it gets them, allows user to lock or reset the device.
We are going to look at the following files:
-
Android Manifest File
-
XML Resource File
-
Device Admin Receiver Component
-
Activity
The source code for this project is available athttps://thenewcircle.com/static/courseware/android/DevicePolicyDemo.zip
Android Manifest File
This is where we register our device administration receiver component. It appears as another receiver declaration.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.marakana.android.devicepolicydemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".DevicePolicyDemoActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- This is where we register our receiver --> <receiver android:name=".DemoDeviceAdminReceiver" android:permission="android.permission.BIND_DEVICE_ADMIN" > <intent-filter> <!-- This action is required --> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> </intent-filter> <!-- This is required this receiver to become device admin component. --> <meta-data android:name="android.app.device_admin" android:resource="@xml/device_admin_sample" /> </receiver> </application> </manifest>
Notice that <receiver> element now includes requiredandroid:permission="android.permission.BIND_DEVICE_ADMIN" permission declaration.
We also must include the appropriate intent action filterandroid.app.action.DEVICE_ADMIN_ENABLED as well as the <meta-data/> element that specifies that this receiver users @xml/device_admin_sample resource, which we’ll look at next.
XML Resource File
This XML resource file, referenced from AndroidManifest.xml specifies what policies we are interested in.
<device-admin xmlns:android="http://schemas.android.com/apk/res/android"> <uses-policies> <limit-password /> <watch-login /> <reset-password /> <force-lock /> <wipe-data /> <expire-password /> <encrypted-storage /> </uses-policies> </device-admin>
In this example, we ask for most of the available policies merely to illustrate what is available. In a real-world example, you should only ask for policies that you really require.
Device Admin Receiver Component
This is the main device administration component. It is basically a specialized BroadcastReceiverclass that implements some callbacks specific to device administration.
package com.marakana.android.devicepolicydemo; import android.app.admin.DeviceAdminReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.Toast; /** * This is the component that is responsible for actual device administration. * It becomes the receiver when a policy is applied. It is important that we * subclass DeviceAdminReceiver class here and to implement its only required * method onEnabled(). */ public class DemoDeviceAdminReceiver extends DeviceAdminReceiver { static final String TAG = "DemoDeviceAdminReceiver"; /** Called when this application is approved to be a device administrator. */ @Override public void onEnabled(Context context, Intent intent) { super.onEnabled(context, intent); Toast.makeText(context, R.string.device_admin_enabled, Toast.LENGTH_LONG).show(); Log.d(TAG, "onEnabled"); } /** Called when this application is no longer the device administrator. */ @Override public void onDisabled(Context context, Intent intent) { super.onDisabled(context, intent); Toast.makeText(context, R.string.device_admin_disabled, Toast.LENGTH_LONG).show(); Log.d(TAG, "onDisabled"); } @Override public void onPasswordChanged(Context context, Intent intent) { super.onPasswordChanged(context, intent); Log.d(TAG, "onPasswordChanged"); } @Override public void onPasswordFailed(Context context, Intent intent) { super.onPasswordFailed(context, intent); Log.d(TAG, "onPasswordFailed"); } @Override public void onPasswordSucceeded(Context context, Intent intent) { super.onPasswordSucceeded(context, intent); Log.d(TAG, "onPasswordSucceeded"); } }
Notice that we subclass DeviceAdminReceiver class. This is the required for this component to be able to receive policy notifications.
We also must implement the required onEnabled() method that is called when the policy administration is first enabled.
We don’t really do much here other than log what happened to visually illustrate the execution of this code.
Activity
The activity acts as our demo client in this case. The significant methods are onClick(),onCheckedChanged() and onActivityResult().
package com.marakana.android.devicepolicydemo; import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.Toast; import android.widget.ToggleButton; public class DevicePolicyDemoActivity extends Activity implements OnCheckedChangeListener { static final String TAG = "DevicePolicyDemoActivity"; static final int ACTIVATION_REQUEST = 47; // identifies our request id DevicePolicyManager devicePolicyManager; ComponentName demoDeviceAdmin; ToggleButton toggleButton; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); toggleButton = (ToggleButton) super .findViewById(R.id.toggle_device_admin); toggleButton.setOnCheckedChangeListener(this); // Initialize Device Policy Manager service and our receiver class devicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); demoDeviceAdmin = new ComponentName(this, DemoDeviceAdminReceiver.class); } /** * Called when a button is clicked on. We have Lock Device and Reset Device * buttons that could invoke this method. */ public void onClick(View v) { switch (v.getId()) { case R.id.button_lock_device: // We lock the screen Toast.makeText(this, "Locking device...", Toast.LENGTH_LONG).show(); Log.d(TAG, "Locking device now"); devicePolicyManager.lockNow(); break; case R.id.button_reset_device: // We reset the device - this will erase entire /data partition! Toast.makeText(this, "Locking device...", Toast.LENGTH_LONG).show(); Log.d(TAG, "RESETing device now - all user data will be ERASED to factory settings"); devicePolicyManager.wipeData(ACTIVATION_REQUEST); break; } } /** * Called when the state of toggle button changes. In this case, we send an * intent to activate the device policy administration. */ public void onCheckedChanged(CompoundButton button, boolean isChecked) { if (isChecked) { // Activate device administration Intent intent = new Intent( DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, demoDeviceAdmin); intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Your boss told you to do this"); startActivityForResult(intent, ACTIVATION_REQUEST); } Log.d(TAG, "onCheckedChanged to: " + isChecked); } /** * Called when startActivityForResult() call is completed. The result of * activation could be success of failure, mostly depending on user okaying * this app's request to administer the device. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case ACTIVATION_REQUEST: if (resultCode == Activity.RESULT_OK) { Log.i(TAG, "Administration enabled!"); toggleButton.setChecked(true); } else { Log.i(TAG, "Administration enable FAILED!"); toggleButton.setChecked(false); } return; } super.onActivityResult(requestCode, resultCode, data); } }
onCheckedChanged() is invoked when the toggle button changes state. It sends an intent requesting that this application be granted device administration permissions on this device. User has to allow this request. The result of user’s action is then passed to onActivityResult() method.
onClick() method processes button clicks for lock and reset buttons. Note that its calls toDevicePolicyManager will lock and wipe out the device user data partition, respectively.