blob: 71badcdf77ae4ddf06e669f4483de8ca158da39b [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.apis.app;
import com.example.android.apis.R;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This activity provides a comprehensive UI for exploring and operating the DevicePolicyManager
* api. It consists of two primary modules:
*
* 1: A device policy controller, implemented here as a series of preference fragments. Each
* one contains code to monitor and control a particular subset of device policies.
*
* 2: A DeviceAdminReceiver, to receive updates from the DevicePolicyManager when certain aspects
* of the device security status have changed.
*/
public class DeviceAdminSample extends PreferenceActivity {
// Miscellaneous utilities and definitions
private static final String TAG = "DeviceAdminSample";
private static final int REQUEST_CODE_ENABLE_ADMIN = 1;
private static final int REQUEST_CODE_START_ENCRYPTION = 2;
private static final long MS_PER_MINUTE = 60 * 1000;
private static final long MS_PER_HOUR = 60 * MS_PER_MINUTE;
private static final long MS_PER_DAY = 24 * MS_PER_HOUR;
// The following keys are used to find each preference item
private static final String KEY_ENABLE_ADMIN = "key_enable_admin";
private static final String KEY_DISABLE_CAMERA = "key_disable_camera";
private static final String KEY_DISABLE_NOTIFICATIONS = "key_disable_notifications";
private static final String KEY_DISABLE_UNREDACTED = "key_disable_unredacted";
private static final String KEY_DISABLE_TRUST_AGENTS = "key_disable_trust_agents";
private static final String KEY_TRUST_AGENT_COMPONENT = "key_trust_agent_component";
private static final String KEY_TRUST_AGENT_FEATURES = "key_trust_agent_features";
private static final String KEY_DISABLE_KEYGUARD_WIDGETS = "key_disable_keyguard_widgets";
private static final String KEY_DISABLE_KEYGUARD_SECURE_CAMERA
= "key_disable_keyguard_secure_camera";
private static final String KEY_CATEGORY_QUALITY = "key_category_quality";
private static final String KEY_SET_PASSWORD = "key_set_password";
private static final String KEY_RESET_PASSWORD = "key_reset_password";
private static final String KEY_QUALITY = "key_quality";
private static final String KEY_MIN_LENGTH = "key_minimum_length";
private static final String KEY_MIN_LETTERS = "key_minimum_letters";
private static final String KEY_MIN_NUMERIC = "key_minimum_numeric";
private static final String KEY_MIN_LOWER_CASE = "key_minimum_lower_case";
private static final String KEY_MIN_UPPER_CASE = "key_minimum_upper_case";
private static final String KEY_MIN_SYMBOLS = "key_minimum_symbols";
private static final String KEY_MIN_NON_LETTER = "key_minimum_non_letter";
private static final String KEY_CATEGORY_EXPIRATION = "key_category_expiration";
private static final String KEY_HISTORY = "key_history";
private static final String KEY_EXPIRATION_TIMEOUT = "key_expiration_timeout";
private static final String KEY_EXPIRATION_STATUS = "key_expiration_status";
private static final String KEY_CATEGORY_LOCK_WIPE = "key_category_lock_wipe";
private static final String KEY_MAX_TIME_SCREEN_LOCK = "key_max_time_screen_lock";
private static final String KEY_MAX_FAILS_BEFORE_WIPE = "key_max_fails_before_wipe";
private static final String KEY_LOCK_SCREEN = "key_lock_screen";
private static final String KEY_WIPE_DATA = "key_wipe_data";
private static final String KEY_WIP_DATA_ALL = "key_wipe_data_all";
private static final String KEY_CATEGORY_ENCRYPTION = "key_category_encryption";
private static final String KEY_REQUIRE_ENCRYPTION = "key_require_encryption";
private static final String KEY_ACTIVATE_ENCRYPTION = "key_activate_encryption";
// Interaction with the DevicePolicyManager
DevicePolicyManager mDPM;
ComponentName mDeviceAdminSample;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Prepare to work with the DPM
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mDeviceAdminSample = new ComponentName(this, DeviceAdminSampleReceiver.class);
}
/**
* We override this method to provide PreferenceActivity with the top-level preference headers.
*/
@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.device_admin_headers, target);
}
/**
* Helper to determine if we are an active admin
*/
private boolean isActiveAdmin() {
return mDPM.isAdminActive(mDeviceAdminSample);
}
@Override
protected boolean isValidFragment(String fragmentName) {
return GeneralFragment.class.getName().equals(fragmentName)
|| QualityFragment.class.getName().equals(fragmentName)
|| ExpirationFragment.class.getName().equals(fragmentName)
|| LockWipeFragment.class.getName().equals(fragmentName)
|| EncryptionFragment.class.getName().equals(fragmentName);
}
/**
* Common fragment code for DevicePolicyManager access. Provides two shared elements:
*
* 1. Provides instance variables to access activity/context, DevicePolicyManager, etc.
* 2. Provides support for the "set password" button(s) shared by multiple fragments.
*/
public static class AdminSampleFragment extends PreferenceFragment
implements OnPreferenceChangeListener, OnPreferenceClickListener{
// Useful instance variables
protected DeviceAdminSample mActivity;
protected DevicePolicyManager mDPM;
protected ComponentName mDeviceAdminSample;
protected boolean mAdminActive;
// Optional shared UI
private PreferenceScreen mSetPassword;
private EditTextPreference mResetPassword;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Retrieve the useful instance variables
mActivity = (DeviceAdminSample) getActivity();
mDPM = mActivity.mDPM;
mDeviceAdminSample = mActivity.mDeviceAdminSample;
mAdminActive = mActivity.isActiveAdmin();
// Configure the shared UI elements (if they exist)
mResetPassword = (EditTextPreference) findPreference(KEY_RESET_PASSWORD);
mSetPassword = (PreferenceScreen) findPreference(KEY_SET_PASSWORD);
if (mResetPassword != null) {
mResetPassword.setOnPreferenceChangeListener(this);
}
if (mSetPassword != null) {
mSetPassword.setOnPreferenceClickListener(this);
}
}
@Override
public void onResume() {
super.onResume();
mAdminActive = mActivity.isActiveAdmin();
reloadSummaries();
// Resetting the password via API is available only to active admins
if (mResetPassword != null) {
mResetPassword.setEnabled(mAdminActive);
}
}
/**
* Called automatically at every onResume. Should also call explicitly any time a
* policy changes that may affect other policy values.
*/
protected void reloadSummaries() {
if (mSetPassword != null) {
if (mAdminActive) {
// Show password-sufficient status under Set Password button
boolean sufficient = mDPM.isActivePasswordSufficient();
mSetPassword.setSummary(sufficient ?
R.string.password_sufficient : R.string.password_insufficient);
} else {
mSetPassword.setSummary(null);
}
}
}
protected void postReloadSummaries() {
getView().post(new Runnable() {
@Override
public void run() {
reloadSummaries();
}
});
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (mSetPassword != null && preference == mSetPassword) {
Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
startActivity(intent);
return true;
}
return false;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mResetPassword != null && preference == mResetPassword) {
doResetPassword((String)newValue);
return true;
}
return false;
}
/**
* This is dangerous, so we prevent automated tests from doing it, and we
* remind the user after we do it.
*/
private void doResetPassword(String newPassword) {
if (alertIfMonkey(mActivity, R.string.monkey_reset_password)) {
return;
}
mDPM.resetPassword(newPassword, DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
String message = mActivity.getString(R.string.reset_password_warning, newPassword);
builder.setMessage(message);
builder.setPositiveButton(R.string.reset_password_ok, null);
builder.show();
}
/**
* Simple helper for summaries showing local & global (aggregate) policy settings
*/
protected String localGlobalSummary(Object local, Object global) {
return getString(R.string.status_local_global, local, global);
}
}
/**
* PreferenceFragment for "general" preferences.
*/
public static class GeneralFragment extends AdminSampleFragment
implements OnPreferenceChangeListener {
// UI elements
private CheckBoxPreference mEnableCheckbox;
private CheckBoxPreference mDisableCameraCheckbox;
private CheckBoxPreference mDisableKeyguardWidgetsCheckbox;
private CheckBoxPreference mDisableKeyguardSecureCameraCheckbox;
private CheckBoxPreference mDisableKeyguardNotificationCheckbox;
private CheckBoxPreference mDisableKeyguardTrustAgentCheckbox;
private CheckBoxPreference mDisableKeyguardUnredactedCheckbox;
private EditTextPreference mTrustAgentComponent;
private EditTextPreference mTrustAgentFeatures;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.device_admin_general);
mEnableCheckbox = (CheckBoxPreference) findPreference(KEY_ENABLE_ADMIN);
mEnableCheckbox.setOnPreferenceChangeListener(this);
mDisableCameraCheckbox = (CheckBoxPreference) findPreference(KEY_DISABLE_CAMERA);
mDisableCameraCheckbox.setOnPreferenceChangeListener(this);
mDisableKeyguardWidgetsCheckbox =
(CheckBoxPreference) findPreference(KEY_DISABLE_KEYGUARD_WIDGETS);
mDisableKeyguardWidgetsCheckbox.setOnPreferenceChangeListener(this);
mDisableKeyguardSecureCameraCheckbox =
(CheckBoxPreference) findPreference(KEY_DISABLE_KEYGUARD_SECURE_CAMERA);
mDisableKeyguardSecureCameraCheckbox.setOnPreferenceChangeListener(this);
mDisableKeyguardNotificationCheckbox =
(CheckBoxPreference) findPreference(KEY_DISABLE_NOTIFICATIONS);
mDisableKeyguardNotificationCheckbox.setOnPreferenceChangeListener(this);
mDisableKeyguardUnredactedCheckbox =
(CheckBoxPreference) findPreference(KEY_DISABLE_UNREDACTED);
mDisableKeyguardUnredactedCheckbox.setOnPreferenceChangeListener(this);
mDisableKeyguardTrustAgentCheckbox =
(CheckBoxPreference) findPreference(KEY_DISABLE_TRUST_AGENTS);
mDisableKeyguardTrustAgentCheckbox.setOnPreferenceChangeListener(this);
mTrustAgentComponent =
(EditTextPreference) findPreference(KEY_TRUST_AGENT_COMPONENT);
mTrustAgentComponent.setOnPreferenceChangeListener(this);
mTrustAgentFeatures =
(EditTextPreference) findPreference(KEY_TRUST_AGENT_FEATURES);
mTrustAgentFeatures.setOnPreferenceChangeListener(this);
}
// At onResume time, reload UI with current values as required
@Override
public void onResume() {
super.onResume();
mEnableCheckbox.setChecked(mAdminActive);
enableDeviceCapabilitiesArea(mAdminActive);
if (mAdminActive) {
mDPM.setCameraDisabled(mDeviceAdminSample, mDisableCameraCheckbox.isChecked());
mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample, createKeyguardDisabledFlag());
reloadSummaries();
}
}
int createKeyguardDisabledFlag() {
int flags = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
flags |= mDisableKeyguardWidgetsCheckbox.isChecked() ?
DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL : 0;
flags |= mDisableKeyguardSecureCameraCheckbox.isChecked() ?
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA : 0;
flags |= mDisableKeyguardNotificationCheckbox.isChecked() ?
DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS : 0;
flags |= mDisableKeyguardUnredactedCheckbox.isChecked() ?
DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS : 0;
flags |= mDisableKeyguardTrustAgentCheckbox.isChecked() ?
DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS : 0;
return flags;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (super.onPreferenceChange(preference, newValue)) {
return true;
}
if (preference == mEnableCheckbox) {
boolean value = (Boolean) newValue;
if (value != mAdminActive) {
if (value) {
// Launch the activity to have the user enable our admin.
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminSample);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
mActivity.getString(R.string.add_admin_extra_app_text));
startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN);
// return false - don't update checkbox until we're really active
return false;
} else {
mDPM.removeActiveAdmin(mDeviceAdminSample);
enableDeviceCapabilitiesArea(false);
mAdminActive = false;
}
}
} else if (preference == mDisableCameraCheckbox) {
boolean value = (Boolean) newValue;
mDPM.setCameraDisabled(mDeviceAdminSample, value);
// Delay update because the change is only applied after exiting this method.
postReloadSummaries();
} else if (preference == mDisableKeyguardWidgetsCheckbox
|| preference == mDisableKeyguardSecureCameraCheckbox
|| preference == mDisableKeyguardNotificationCheckbox
|| preference == mDisableKeyguardUnredactedCheckbox
|| preference == mDisableKeyguardTrustAgentCheckbox
|| preference == mTrustAgentComponent
|| preference == mTrustAgentFeatures) {
postUpdateDpmDisableFeatures();
postReloadSummaries();
}
return true;
}
private void postUpdateDpmDisableFeatures() {
getView().post(new Runnable() {
@Override
public void run() {
mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample,
createKeyguardDisabledFlag());
String component = mTrustAgentComponent.getText();
if (component != null) {
ComponentName agent = ComponentName.unflattenFromString(component);
if (agent != null) {
String featureString = mTrustAgentFeatures.getText();
if (featureString != null) {
PersistableBundle bundle = new PersistableBundle();
bundle.putStringArray("features", featureString.split(","));
mDPM.setTrustAgentConfiguration(mDeviceAdminSample, agent, bundle);
}
} else {
Log.w(TAG, "Invalid component: " + component);
}
}
}
});
}
@Override
protected void reloadSummaries() {
super.reloadSummaries();
String cameraSummary = getString(mDPM.getCameraDisabled(mDeviceAdminSample)
? R.string.camera_disabled : R.string.camera_enabled);
mDisableCameraCheckbox.setSummary(cameraSummary);
int disabled = mDPM.getKeyguardDisabledFeatures(mDeviceAdminSample);
String keyguardWidgetSummary = getString(
(disabled & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0 ?
R.string.keyguard_widgets_disabled : R.string.keyguard_widgets_enabled);
mDisableKeyguardWidgetsCheckbox.setSummary(keyguardWidgetSummary);
String keyguardSecureCameraSummary = getString(
(disabled & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 ?
R.string.keyguard_secure_camera_disabled : R.string.keyguard_secure_camera_enabled);
mDisableKeyguardSecureCameraCheckbox.setSummary(keyguardSecureCameraSummary);
String keyguardSecureNotificationsSummary = getString(
(disabled & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) != 0 ?
R.string.keyguard_secure_notifications_disabled
: R.string.keyguard_secure_notifications_enabled);
mDisableKeyguardNotificationCheckbox.setSummary(keyguardSecureNotificationsSummary);
String keyguardUnredactedSummary = getString(
(disabled & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) != 0
? R.string.keyguard_unredacted_notifications_disabled
: R.string.keyguard_unredacted_notifications_enabled);
mDisableKeyguardUnredactedCheckbox.setSummary(keyguardUnredactedSummary);
String keyguardEnableTrustAgentSummary = getString(
(disabled & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0 ?
R.string.keyguard_trust_agents_disabled
: R.string.keyguard_trust_agents_enabled);
mDisableKeyguardTrustAgentCheckbox.setSummary(keyguardEnableTrustAgentSummary);
final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
final boolean trustDisabled =
(disabled & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
String component = prefs.getString(mTrustAgentComponent.getKey(), null);
mTrustAgentComponent.setSummary(component);
mTrustAgentComponent.setEnabled(trustDisabled);
String features = prefs.getString(mTrustAgentFeatures.getKey(), null);
mTrustAgentFeatures.setSummary(features);
mTrustAgentFeatures.setEnabled(trustDisabled);
}
/** Updates the device capabilities area (dis/enabling) as the admin is (de)activated */
private void enableDeviceCapabilitiesArea(boolean enabled) {
mDisableCameraCheckbox.setEnabled(enabled);
mDisableKeyguardWidgetsCheckbox.setEnabled(enabled);
mDisableKeyguardSecureCameraCheckbox.setEnabled(enabled);
mDisableKeyguardNotificationCheckbox.setEnabled(enabled);
mDisableKeyguardUnredactedCheckbox.setEnabled(enabled);
mDisableKeyguardTrustAgentCheckbox.setEnabled(enabled);
mTrustAgentComponent.setEnabled(enabled);
mTrustAgentFeatures.setEnabled(enabled);
}
}
/**
* PreferenceFragment for "password quality" preferences.
*/
public static class QualityFragment extends AdminSampleFragment
implements OnPreferenceChangeListener {
// Password quality values
// This list must match the list found in samples/ApiDemos/res/values/arrays.xml
final static int[] mPasswordQualityValues = new int[] {
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
};
// Password quality values (as strings, for the ListPreference entryValues)
// This list must match the list found in samples/ApiDemos/res/values/arrays.xml
final static String[] mPasswordQualityValueStrings = new String[] {
String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED),
String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING),
String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC),
String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)
};
// UI elements
private PreferenceCategory mQualityCategory;
private ListPreference mPasswordQuality;
private EditTextPreference mMinLength;
private EditTextPreference mMinLetters;
private EditTextPreference mMinNumeric;
private EditTextPreference mMinLowerCase;
private EditTextPreference mMinUpperCase;
private EditTextPreference mMinSymbols;
private EditTextPreference mMinNonLetter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.device_admin_quality);
mQualityCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_QUALITY);
mPasswordQuality = (ListPreference) findPreference(KEY_QUALITY);
mMinLength = (EditTextPreference) findPreference(KEY_MIN_LENGTH);
mMinLetters = (EditTextPreference) findPreference(KEY_MIN_LETTERS);
mMinNumeric = (EditTextPreference) findPreference(KEY_MIN_NUMERIC);
mMinLowerCase = (EditTextPreference) findPreference(KEY_MIN_LOWER_CASE);
mMinUpperCase = (EditTextPreference) findPreference(KEY_MIN_UPPER_CASE);
mMinSymbols = (EditTextPreference) findPreference(KEY_MIN_SYMBOLS);
mMinNonLetter = (EditTextPreference) findPreference(KEY_MIN_NON_LETTER);
mPasswordQuality.setOnPreferenceChangeListener(this);
mMinLength.setOnPreferenceChangeListener(this);
mMinLetters.setOnPreferenceChangeListener(this);
mMinNumeric.setOnPreferenceChangeListener(this);
mMinLowerCase.setOnPreferenceChangeListener(this);
mMinUpperCase.setOnPreferenceChangeListener(this);
mMinSymbols.setOnPreferenceChangeListener(this);
mMinNonLetter.setOnPreferenceChangeListener(this);
// Finish setup of the quality dropdown
mPasswordQuality.setEntryValues(mPasswordQualityValueStrings);
}
@Override
public void onResume() {
super.onResume();
mQualityCategory.setEnabled(mAdminActive);
}
/**
* Update the summaries of each item to show the local setting and the global setting.
*/
@Override
protected void reloadSummaries() {
super.reloadSummaries();
// Show numeric settings for each policy API
int local, global;
local = mDPM.getPasswordQuality(mDeviceAdminSample);
global = mDPM.getPasswordQuality(null);
mPasswordQuality.setSummary(
localGlobalSummary(qualityValueToString(local), qualityValueToString(global)));
local = mDPM.getPasswordMinimumLength(mDeviceAdminSample);
global = mDPM.getPasswordMinimumLength(null);
mMinLength.setSummary(localGlobalSummary(local, global));
local = mDPM.getPasswordMinimumLetters(mDeviceAdminSample);
global = mDPM.getPasswordMinimumLetters(null);
mMinLetters.setSummary(localGlobalSummary(local, global));
local = mDPM.getPasswordMinimumNumeric(mDeviceAdminSample);
global = mDPM.getPasswordMinimumNumeric(null);
mMinNumeric.setSummary(localGlobalSummary(local, global));
local = mDPM.getPasswordMinimumLowerCase(mDeviceAdminSample);
global = mDPM.getPasswordMinimumLowerCase(null);
mMinLowerCase.setSummary(localGlobalSummary(local, global));
local = mDPM.getPasswordMinimumUpperCase(mDeviceAdminSample);
global = mDPM.getPasswordMinimumUpperCase(null);
mMinUpperCase.setSummary(localGlobalSummary(local, global));
local = mDPM.getPasswordMinimumSymbols(mDeviceAdminSample);
global = mDPM.getPasswordMinimumSymbols(null);
mMinSymbols.setSummary(localGlobalSummary(local, global));
local = mDPM.getPasswordMinimumNonLetter(mDeviceAdminSample);
global = mDPM.getPasswordMinimumNonLetter(null);
mMinNonLetter.setSummary(localGlobalSummary(local, global));
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (super.onPreferenceChange(preference, newValue)) {
return true;
}
String valueString = (String)newValue;
if (TextUtils.isEmpty(valueString)) {
return false;
}
int value = 0;
try {
value = Integer.parseInt(valueString);
} catch (NumberFormatException nfe) {
String warning = mActivity.getString(R.string.number_format_warning, valueString);
Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
}
if (preference == mPasswordQuality) {
mDPM.setPasswordQuality(mDeviceAdminSample, value);
} else if (preference == mMinLength) {
mDPM.setPasswordMinimumLength(mDeviceAdminSample, value);
} else if (preference == mMinLetters) {
mDPM.setPasswordMinimumLetters(mDeviceAdminSample, value);
} else if (preference == mMinNumeric) {
mDPM.setPasswordMinimumNumeric(mDeviceAdminSample, value);
} else if (preference == mMinLowerCase) {
mDPM.setPasswordMinimumLowerCase(mDeviceAdminSample, value);
} else if (preference == mMinUpperCase) {
mDPM.setPasswordMinimumUpperCase(mDeviceAdminSample, value);
} else if (preference == mMinSymbols) {
mDPM.setPasswordMinimumSymbols(mDeviceAdminSample, value);
} else if (preference == mMinNonLetter) {
mDPM.setPasswordMinimumNonLetter(mDeviceAdminSample, value);
}
// Delay update because the change is only applied after exiting this method.
postReloadSummaries();
return true;
}
private String qualityValueToString(int quality) {
for (int i= 0; i < mPasswordQualityValues.length; i++) {
if (mPasswordQualityValues[i] == quality) {
String[] qualities =
mActivity.getResources().getStringArray(R.array.password_qualities);
return qualities[i];
}
}
return "(0x" + Integer.toString(quality, 16) + ")";
}
}
/**
* PreferenceFragment for "password expiration" preferences.
*/
public static class ExpirationFragment extends AdminSampleFragment
implements OnPreferenceChangeListener, OnPreferenceClickListener {
private PreferenceCategory mExpirationCategory;
private EditTextPreference mHistory;
private EditTextPreference mExpirationTimeout;
private PreferenceScreen mExpirationStatus;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.device_admin_expiration);
mExpirationCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_EXPIRATION);
mHistory = (EditTextPreference) findPreference(KEY_HISTORY);
mExpirationTimeout = (EditTextPreference) findPreference(KEY_EXPIRATION_TIMEOUT);
mExpirationStatus = (PreferenceScreen) findPreference(KEY_EXPIRATION_STATUS);
mHistory.setOnPreferenceChangeListener(this);
mExpirationTimeout.setOnPreferenceChangeListener(this);
mExpirationStatus.setOnPreferenceClickListener(this);
}
@Override
public void onResume() {
super.onResume();
mExpirationCategory.setEnabled(mAdminActive);
}
/**
* Update the summaries of each item to show the local setting and the global setting.
*/
@Override
protected void reloadSummaries() {
super.reloadSummaries();
int local, global;
local = mDPM.getPasswordHistoryLength(mDeviceAdminSample);
global = mDPM.getPasswordHistoryLength(null);
mHistory.setSummary(localGlobalSummary(local, global));
long localLong, globalLong;
localLong = mDPM.getPasswordExpirationTimeout(mDeviceAdminSample);
globalLong = mDPM.getPasswordExpirationTimeout(null);
mExpirationTimeout.setSummary(localGlobalSummary(
localLong / MS_PER_MINUTE, globalLong / MS_PER_MINUTE));
String expirationStatus = getExpirationStatus();
mExpirationStatus.setSummary(expirationStatus);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (super.onPreferenceChange(preference, newValue)) {
return true;
}
String valueString = (String)newValue;
if (TextUtils.isEmpty(valueString)) {
return false;
}
int value = 0;
try {
value = Integer.parseInt(valueString);
} catch (NumberFormatException nfe) {
String warning = mActivity.getString(R.string.number_format_warning, valueString);
Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
}
if (preference == mHistory) {
mDPM.setPasswordHistoryLength(mDeviceAdminSample, value);
} else if (preference == mExpirationTimeout) {
mDPM.setPasswordExpirationTimeout(mDeviceAdminSample, value * MS_PER_MINUTE);
}
// Delay update because the change is only applied after exiting this method.
postReloadSummaries();
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (super.onPreferenceClick(preference)) {
return true;
}
if (preference == mExpirationStatus) {
String expirationStatus = getExpirationStatus();
mExpirationStatus.setSummary(expirationStatus);
return true;
}
return false;
}
/**
* Create a summary string describing the expiration status for the sample app,
* as well as the global (aggregate) status.
*/
private String getExpirationStatus() {
// expirations are absolute; convert to relative for display
long localExpiration = mDPM.getPasswordExpiration(mDeviceAdminSample);
long globalExpiration = mDPM.getPasswordExpiration(null);
long now = System.currentTimeMillis();
// local expiration
String local;
if (localExpiration == 0) {
local = mActivity.getString(R.string.expiration_status_none);
} else {
localExpiration -= now;
String dms = timeToDaysMinutesSeconds(mActivity, Math.abs(localExpiration));
if (localExpiration >= 0) {
local = mActivity.getString(R.string.expiration_status_future, dms);
} else {
local = mActivity.getString(R.string.expiration_status_past, dms);
}
}
// global expiration
String global;
if (globalExpiration == 0) {
global = mActivity.getString(R.string.expiration_status_none);
} else {
globalExpiration -= now;
String dms = timeToDaysMinutesSeconds(mActivity, Math.abs(globalExpiration));
if (globalExpiration >= 0) {
global = mActivity.getString(R.string.expiration_status_future, dms);
} else {
global = mActivity.getString(R.string.expiration_status_past, dms);
}
}
return mActivity.getString(R.string.status_local_global, local, global);
}
}
/**
* PreferenceFragment for "lock screen & wipe" preferences.
*/
public static class LockWipeFragment extends AdminSampleFragment
implements OnPreferenceChangeListener, OnPreferenceClickListener {
private PreferenceCategory mLockWipeCategory;
private EditTextPreference mMaxTimeScreenLock;
private EditTextPreference mMaxFailures;
private PreferenceScreen mLockScreen;
private PreferenceScreen mWipeData;
private PreferenceScreen mWipeAppData;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.device_admin_lock_wipe);
mLockWipeCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_LOCK_WIPE);
mMaxTimeScreenLock = (EditTextPreference) findPreference(KEY_MAX_TIME_SCREEN_LOCK);
mMaxFailures = (EditTextPreference) findPreference(KEY_MAX_FAILS_BEFORE_WIPE);
mLockScreen = (PreferenceScreen) findPreference(KEY_LOCK_SCREEN);
mWipeData = (PreferenceScreen) findPreference(KEY_WIPE_DATA);
mWipeAppData = (PreferenceScreen) findPreference(KEY_WIP_DATA_ALL);
mMaxTimeScreenLock.setOnPreferenceChangeListener(this);
mMaxFailures.setOnPreferenceChangeListener(this);
mLockScreen.setOnPreferenceClickListener(this);
mWipeData.setOnPreferenceClickListener(this);
mWipeAppData.setOnPreferenceClickListener(this);
}
@Override
public void onResume() {
super.onResume();
mLockWipeCategory.setEnabled(mAdminActive);
}
/**
* Update the summaries of each item to show the local setting and the global setting.
*/
@Override
protected void reloadSummaries() {
super.reloadSummaries();
long localLong, globalLong;
localLong = mDPM.getMaximumTimeToLock(mDeviceAdminSample);
globalLong = mDPM.getMaximumTimeToLock(null);
mMaxTimeScreenLock.setSummary(localGlobalSummary(
localLong / MS_PER_MINUTE, globalLong / MS_PER_MINUTE));
int local, global;
local = mDPM.getMaximumFailedPasswordsForWipe(mDeviceAdminSample);
global = mDPM.getMaximumFailedPasswordsForWipe(null);
mMaxFailures.setSummary(localGlobalSummary(local, global));
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (super.onPreferenceChange(preference, newValue)) {
return true;
}
String valueString = (String)newValue;
if (TextUtils.isEmpty(valueString)) {
return false;
}
int value = 0;
try {
value = Integer.parseInt(valueString);
} catch (NumberFormatException nfe) {
String warning = mActivity.getString(R.string.number_format_warning, valueString);
Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
}
if (preference == mMaxTimeScreenLock) {
mDPM.setMaximumTimeToLock(mDeviceAdminSample, value * MS_PER_MINUTE);
} else if (preference == mMaxFailures) {
if (alertIfMonkey(mActivity, R.string.monkey_wipe_data)) {
return true;
}
mDPM.setMaximumFailedPasswordsForWipe(mDeviceAdminSample, value);
}
// Delay update because the change is only applied after exiting this method.
postReloadSummaries();
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (super.onPreferenceClick(preference)) {
return true;
}
if (preference == mLockScreen) {
if (alertIfMonkey(mActivity, R.string.monkey_lock_screen)) {
return true;
}
mDPM.lockNow();
return true;
} else if (preference == mWipeData || preference == mWipeAppData) {
if (alertIfMonkey(mActivity, R.string.monkey_wipe_data)) {
return true;
}
promptForRealDeviceWipe(preference == mWipeAppData);
return true;
}
return false;
}
/**
* Wiping data is real, so we don't want it to be easy. Show two alerts before wiping.
*/
private void promptForRealDeviceWipe(final boolean wipeAllData) {
final DeviceAdminSample activity = mActivity;
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.wipe_warning_first);
builder.setPositiveButton(R.string.wipe_warning_first_ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
if (wipeAllData) {
builder.setMessage(R.string.wipe_warning_second_full);
} else {
builder.setMessage(R.string.wipe_warning_second);
}
builder.setPositiveButton(R.string.wipe_warning_second_ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
boolean stillActive = mActivity.isActiveAdmin();
if (stillActive) {
mDPM.wipeData(wipeAllData
? DevicePolicyManager.WIPE_EXTERNAL_STORAGE : 0);
}
}
});
builder.setNegativeButton(R.string.wipe_warning_second_no, null);
builder.show();
}
});
builder.setNegativeButton(R.string.wipe_warning_first_no, null);
builder.show();
}
}
/**
* PreferenceFragment for "encryption" preferences.
*/
public static class EncryptionFragment extends AdminSampleFragment
implements OnPreferenceChangeListener, OnPreferenceClickListener {
private PreferenceCategory mEncryptionCategory;
private CheckBoxPreference mRequireEncryption;
private PreferenceScreen mActivateEncryption;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.device_admin_encryption);
mEncryptionCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_ENCRYPTION);
mRequireEncryption = (CheckBoxPreference) findPreference(KEY_REQUIRE_ENCRYPTION);
mActivateEncryption = (PreferenceScreen) findPreference(KEY_ACTIVATE_ENCRYPTION);
mRequireEncryption.setOnPreferenceChangeListener(this);
mActivateEncryption.setOnPreferenceClickListener(this);
}
@Override
public void onResume() {
super.onResume();
mEncryptionCategory.setEnabled(mAdminActive);
mRequireEncryption.setChecked(mDPM.getStorageEncryption(mDeviceAdminSample));
}
/**
* Update the summaries of each item to show the local setting and the global setting.
*/
@Override
protected void reloadSummaries() {
super.reloadSummaries();
boolean local, global;
local = mDPM.getStorageEncryption(mDeviceAdminSample);
global = mDPM.getStorageEncryption(null);
mRequireEncryption.setSummary(localGlobalSummary(local, global));
int deviceStatusCode = mDPM.getStorageEncryptionStatus();
String deviceStatus = statusCodeToString(deviceStatusCode);
String status = mActivity.getString(R.string.status_device_encryption, deviceStatus);
mActivateEncryption.setSummary(status);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (super.onPreferenceChange(preference, newValue)) {
return true;
}
if (preference == mRequireEncryption) {
boolean newActive = (Boolean) newValue;
mDPM.setStorageEncryption(mDeviceAdminSample, newActive);
// Delay update because the change is only applied after exiting this method.
postReloadSummaries();
return true;
}
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (super.onPreferenceClick(preference)) {
return true;
}
if (preference == mActivateEncryption) {
if (alertIfMonkey(mActivity, R.string.monkey_encryption)) {
return true;
}
// Check to see if encryption is even supported on this device (it's optional).
if (mDPM.getStorageEncryptionStatus() ==
DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setMessage(R.string.encryption_not_supported);
builder.setPositiveButton(R.string.encryption_not_supported_ok, null);
builder.show();
return true;
}
// Launch the activity to activate encryption. May or may not return!
Intent intent = new Intent(DevicePolicyManager.ACTION_START_ENCRYPTION);
startActivityForResult(intent, REQUEST_CODE_START_ENCRYPTION);
return true;
}
return false;
}
private String statusCodeToString(int newStatusCode) {
int newStatus = R.string.encryption_status_unknown;
switch (newStatusCode) {
case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED:
newStatus = R.string.encryption_status_unsupported;
break;
case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE:
newStatus = R.string.encryption_status_inactive;
break;
case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING:
newStatus = R.string.encryption_status_activating;
break;
case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
newStatus = R.string.encryption_status_active;
break;
}
return mActivity.getString(newStatus);
}
}
/**
* Simple converter used for long expiration times reported in mSec.
*/
private static String timeToDaysMinutesSeconds(Context context, long time) {
long days = time / MS_PER_DAY;
long hours = (time / MS_PER_HOUR) % 24;
long minutes = (time / MS_PER_MINUTE) % 60;
return context.getString(R.string.status_days_hours_minutes, days, hours, minutes);
}
/**
* If the "user" is a monkey, post an alert and notify the caller. This prevents automated
* test frameworks from stumbling into annoying or dangerous operations.
*/
private static boolean alertIfMonkey(Context context, int stringId) {
if (ActivityManager.isUserAMonkey()) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(stringId);
builder.setPositiveButton(R.string.monkey_ok, null);
builder.show();
return true;
} else {
return false;
}
}
/**
* Sample implementation of a DeviceAdminReceiver. Your controller must provide one,
* although you may or may not implement all of the methods shown here.
*
* All callbacks are on the UI thread and your implementations should not engage in any
* blocking operations, including disk I/O.
*/
public static class DeviceAdminSampleReceiver extends DeviceAdminReceiver {
void showToast(Context context, String msg) {
String status = context.getString(R.string.admin_receiver_status, msg);
Toast.makeText(context, status, Toast.LENGTH_SHORT).show();
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == ACTION_DEVICE_ADMIN_DISABLE_REQUESTED) {
abortBroadcast();
}
super.onReceive(context, intent);
}
@Override
public void onEnabled(Context context, Intent intent) {
showToast(context, context.getString(R.string.admin_receiver_status_enabled));
}
@Override
public CharSequence onDisableRequested(Context context, Intent intent) {
return context.getString(R.string.admin_receiver_status_disable_warning);
}
@Override
public void onDisabled(Context context, Intent intent) {
showToast(context, context.getString(R.string.admin_receiver_status_disabled));
}
@Override
public void onPasswordChanged(Context context, Intent intent) {
showToast(context, context.getString(R.string.admin_receiver_status_pw_changed));
}
@Override
public void onPasswordFailed(Context context, Intent intent) {
showToast(context, context.getString(R.string.admin_receiver_status_pw_failed));
}
@Override
public void onPasswordSucceeded(Context context, Intent intent) {
showToast(context, context.getString(R.string.admin_receiver_status_pw_succeeded));
}
@Override
public void onPasswordExpiring(Context context, Intent intent) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
long expr = dpm.getPasswordExpiration(
new ComponentName(context, DeviceAdminSampleReceiver.class));
long delta = expr - System.currentTimeMillis();
boolean expired = delta < 0L;
String message = context.getString(expired ?
R.string.expiration_status_past : R.string.expiration_status_future);
showToast(context, message);
Log.v(TAG, message);
}
}
}