Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.apis.app;
     18 
     19 import com.example.android.apis.R;
     20 
     21 import android.app.ActivityManager;
     22 import android.app.AlertDialog;
     23 import android.app.admin.DeviceAdminReceiver;
     24 import android.app.admin.DevicePolicyManager;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.DialogInterface;
     28 import android.content.Intent;
     29 import android.os.Bundle;
     30 import android.preference.CheckBoxPreference;
     31 import android.preference.EditTextPreference;
     32 import android.preference.ListPreference;
     33 import android.preference.Preference;
     34 import android.preference.Preference.OnPreferenceChangeListener;
     35 import android.preference.Preference.OnPreferenceClickListener;
     36 import android.preference.PreferenceActivity;
     37 import android.preference.PreferenceCategory;
     38 import android.preference.PreferenceFragment;
     39 import android.preference.PreferenceScreen;
     40 import android.text.TextUtils;
     41 import android.util.Log;
     42 import android.widget.Toast;
     43 
     44 import java.util.List;
     45 
     46 /**
     47  * This activity provides a comprehensive UI for exploring and operating the DevicePolicyManager
     48  * api.  It consists of two primary modules:
     49  *
     50  * 1:  A device policy controller, implemented here as a series of preference fragments.  Each
     51  *     one contains code to monitor and control a particular subset of device policies.
     52  *
     53  * 2:  A DeviceAdminReceiver, to receive updates from the DevicePolicyManager when certain aspects
     54  *     of the device security status have changed.
     55  */
     56 public class DeviceAdminSample extends PreferenceActivity {
     57 
     58     // Miscellaneous utilities and definitions
     59     private static final String TAG = "DeviceAdminSample";
     60 
     61     private static final int REQUEST_CODE_ENABLE_ADMIN = 1;
     62     private static final int REQUEST_CODE_START_ENCRYPTION = 2;
     63 
     64     private static final long MS_PER_MINUTE = 60 * 1000;
     65     private static final long MS_PER_HOUR = 60 * MS_PER_MINUTE;
     66     private static final long MS_PER_DAY = 24 * MS_PER_HOUR;
     67 
     68     // The following keys are used to find each preference item
     69     private static final String KEY_ENABLE_ADMIN = "key_enable_admin";
     70     private static final String KEY_DISABLE_CAMERA = "key_disable_camera";
     71     private static final String KEY_DISABLE_NOTIFICATIONS = "key_disable_notifications";
     72     private static final String KEY_DISABLE_UNREDACTED = "key_disable_unredacted";
     73     private static final String KEY_DISABLE_TRUST_AGENTS = "key_disable_trust_agents";
     74     private static final String KEY_DISABLE_KEYGUARD_WIDGETS = "key_disable_keyguard_widgets";
     75     private static final String KEY_DISABLE_KEYGUARD_SECURE_CAMERA
     76             = "key_disable_keyguard_secure_camera";
     77 
     78     private static final String KEY_CATEGORY_QUALITY = "key_category_quality";
     79     private static final String KEY_SET_PASSWORD = "key_set_password";
     80     private static final String KEY_RESET_PASSWORD = "key_reset_password";
     81     private static final String KEY_QUALITY = "key_quality";
     82     private static final String KEY_MIN_LENGTH = "key_minimum_length";
     83     private static final String KEY_MIN_LETTERS = "key_minimum_letters";
     84     private static final String KEY_MIN_NUMERIC = "key_minimum_numeric";
     85     private static final String KEY_MIN_LOWER_CASE = "key_minimum_lower_case";
     86     private static final String KEY_MIN_UPPER_CASE = "key_minimum_upper_case";
     87     private static final String KEY_MIN_SYMBOLS = "key_minimum_symbols";
     88     private static final String KEY_MIN_NON_LETTER = "key_minimum_non_letter";
     89 
     90     private static final String KEY_CATEGORY_EXPIRATION = "key_category_expiration";
     91     private static final String KEY_HISTORY = "key_history";
     92     private static final String KEY_EXPIRATION_TIMEOUT = "key_expiration_timeout";
     93     private static final String KEY_EXPIRATION_STATUS = "key_expiration_status";
     94 
     95     private static final String KEY_CATEGORY_LOCK_WIPE = "key_category_lock_wipe";
     96     private static final String KEY_MAX_TIME_SCREEN_LOCK = "key_max_time_screen_lock";
     97     private static final String KEY_MAX_FAILS_BEFORE_WIPE = "key_max_fails_before_wipe";
     98     private static final String KEY_LOCK_SCREEN = "key_lock_screen";
     99     private static final String KEY_WIPE_DATA = "key_wipe_data";
    100     private static final String KEY_WIP_DATA_ALL = "key_wipe_data_all";
    101 
    102     private static final String KEY_CATEGORY_ENCRYPTION = "key_category_encryption";
    103     private static final String KEY_REQUIRE_ENCRYPTION = "key_require_encryption";
    104     private static final String KEY_ACTIVATE_ENCRYPTION = "key_activate_encryption";
    105 
    106     // Interaction with the DevicePolicyManager
    107     DevicePolicyManager mDPM;
    108     ComponentName mDeviceAdminSample;
    109 
    110     @Override
    111     protected void onCreate(Bundle savedInstanceState) {
    112         super.onCreate(savedInstanceState);
    113 
    114         // Prepare to work with the DPM
    115         mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
    116         mDeviceAdminSample = new ComponentName(this, DeviceAdminSampleReceiver.class);
    117     }
    118 
    119     /**
    120      * We override this method to provide PreferenceActivity with the top-level preference headers.
    121      */
    122     @Override
    123     public void onBuildHeaders(List<Header> target) {
    124         loadHeadersFromResource(R.xml.device_admin_headers, target);
    125     }
    126 
    127     /**
    128      * Helper to determine if we are an active admin
    129      */
    130     private boolean isActiveAdmin() {
    131         return mDPM.isAdminActive(mDeviceAdminSample);
    132     }
    133 
    134     @Override
    135     protected boolean isValidFragment(String fragmentName) {
    136         return GeneralFragment.class.getName().equals(fragmentName)
    137                 || QualityFragment.class.getName().equals(fragmentName)
    138                 || ExpirationFragment.class.getName().equals(fragmentName)
    139                 || LockWipeFragment.class.getName().equals(fragmentName)
    140                 || EncryptionFragment.class.getName().equals(fragmentName);
    141     }
    142 
    143     /**
    144      * Common fragment code for DevicePolicyManager access.  Provides two shared elements:
    145      *
    146      *   1.  Provides instance variables to access activity/context, DevicePolicyManager, etc.
    147      *   2.  Provides support for the "set password" button(s) shared by multiple fragments.
    148      */
    149     public static class AdminSampleFragment extends PreferenceFragment
    150             implements OnPreferenceChangeListener, OnPreferenceClickListener{
    151 
    152         // Useful instance variables
    153         protected DeviceAdminSample mActivity;
    154         protected DevicePolicyManager mDPM;
    155         protected ComponentName mDeviceAdminSample;
    156         protected boolean mAdminActive;
    157 
    158         // Optional shared UI
    159         private PreferenceScreen mSetPassword;
    160         private EditTextPreference mResetPassword;
    161 
    162         @Override
    163         public void onActivityCreated(Bundle savedInstanceState) {
    164             super.onActivityCreated(savedInstanceState);
    165 
    166             // Retrieve the useful instance variables
    167             mActivity = (DeviceAdminSample) getActivity();
    168             mDPM = mActivity.mDPM;
    169             mDeviceAdminSample = mActivity.mDeviceAdminSample;
    170             mAdminActive = mActivity.isActiveAdmin();
    171 
    172             // Configure the shared UI elements (if they exist)
    173             mResetPassword = (EditTextPreference) findPreference(KEY_RESET_PASSWORD);
    174             mSetPassword = (PreferenceScreen) findPreference(KEY_SET_PASSWORD);
    175 
    176             if (mResetPassword != null) {
    177                 mResetPassword.setOnPreferenceChangeListener(this);
    178             }
    179             if (mSetPassword != null) {
    180                 mSetPassword.setOnPreferenceClickListener(this);
    181             }
    182         }
    183 
    184         @Override
    185         public void onResume() {
    186             super.onResume();
    187             mAdminActive = mActivity.isActiveAdmin();
    188             reloadSummaries();
    189             // Resetting the password via API is available only to active admins
    190             if (mResetPassword != null) {
    191                 mResetPassword.setEnabled(mAdminActive);
    192             }
    193         }
    194 
    195         /**
    196          * Called automatically at every onResume.  Should also call explicitly any time a
    197          * policy changes that may affect other policy values.
    198          */
    199         protected void reloadSummaries() {
    200             if (mSetPassword != null) {
    201                 if (mAdminActive) {
    202                     // Show password-sufficient status under Set Password button
    203                     boolean sufficient = mDPM.isActivePasswordSufficient();
    204                     mSetPassword.setSummary(sufficient ?
    205                             R.string.password_sufficient : R.string.password_insufficient);
    206                 } else {
    207                     mSetPassword.setSummary(null);
    208                 }
    209             }
    210         }
    211 
    212         protected void postReloadSummaries() {
    213             getView().post(new Runnable() {
    214                 @Override
    215                 public void run() {
    216                     reloadSummaries();
    217                 }
    218             });
    219         }
    220 
    221         @Override
    222         public boolean onPreferenceClick(Preference preference) {
    223             if (mSetPassword != null && preference == mSetPassword) {
    224                 Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
    225                 startActivity(intent);
    226                 return true;
    227             }
    228             return false;
    229         }
    230 
    231         @Override
    232         public boolean onPreferenceChange(Preference preference, Object newValue) {
    233             if (mResetPassword != null && preference == mResetPassword) {
    234                 doResetPassword((String)newValue);
    235                 return true;
    236             }
    237             return false;
    238         }
    239 
    240         /**
    241          * This is dangerous, so we prevent automated tests from doing it, and we
    242          * remind the user after we do it.
    243          */
    244         private void doResetPassword(String newPassword) {
    245             if (alertIfMonkey(mActivity, R.string.monkey_reset_password)) {
    246                 return;
    247             }
    248             mDPM.resetPassword(newPassword, DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
    249             AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
    250             String message = mActivity.getString(R.string.reset_password_warning, newPassword);
    251             builder.setMessage(message);
    252             builder.setPositiveButton(R.string.reset_password_ok, null);
    253             builder.show();
    254         }
    255 
    256         /**
    257          * Simple helper for summaries showing local & global (aggregate) policy settings
    258          */
    259         protected String localGlobalSummary(Object local, Object global) {
    260             return getString(R.string.status_local_global, local, global);
    261         }
    262     }
    263 
    264     /**
    265      * PreferenceFragment for "general" preferences.
    266      */
    267     public static class GeneralFragment extends AdminSampleFragment
    268             implements OnPreferenceChangeListener {
    269         // UI elements
    270         private CheckBoxPreference mEnableCheckbox;
    271         private CheckBoxPreference mDisableCameraCheckbox;
    272         private CheckBoxPreference mDisableKeyguardWidgetsCheckbox;
    273         private CheckBoxPreference mDisableKeyguardSecureCameraCheckbox;
    274         private CheckBoxPreference mDisableKeyguardNotificationCheckbox;
    275         private CheckBoxPreference mDisableKeyguardTrustAgentCheckbox;
    276         private CheckBoxPreference mDisableKeyguardUnredactedCheckbox;
    277 
    278         @Override
    279         public void onCreate(Bundle savedInstanceState) {
    280             super.onCreate(savedInstanceState);
    281             addPreferencesFromResource(R.xml.device_admin_general);
    282             mEnableCheckbox = (CheckBoxPreference) findPreference(KEY_ENABLE_ADMIN);
    283             mEnableCheckbox.setOnPreferenceChangeListener(this);
    284 
    285             mDisableCameraCheckbox = (CheckBoxPreference) findPreference(KEY_DISABLE_CAMERA);
    286             mDisableCameraCheckbox.setOnPreferenceChangeListener(this);
    287 
    288             mDisableKeyguardWidgetsCheckbox =
    289                 (CheckBoxPreference) findPreference(KEY_DISABLE_KEYGUARD_WIDGETS);
    290             mDisableKeyguardWidgetsCheckbox.setOnPreferenceChangeListener(this);
    291 
    292             mDisableKeyguardSecureCameraCheckbox =
    293                 (CheckBoxPreference) findPreference(KEY_DISABLE_KEYGUARD_SECURE_CAMERA);
    294             mDisableKeyguardSecureCameraCheckbox.setOnPreferenceChangeListener(this);
    295 
    296             mDisableKeyguardNotificationCheckbox =
    297                     (CheckBoxPreference) findPreference(KEY_DISABLE_NOTIFICATIONS);
    298             mDisableKeyguardNotificationCheckbox.setOnPreferenceChangeListener(this);
    299 
    300             mDisableKeyguardUnredactedCheckbox =
    301                     (CheckBoxPreference) findPreference(KEY_DISABLE_UNREDACTED);
    302             mDisableKeyguardUnredactedCheckbox.setOnPreferenceChangeListener(this);
    303 
    304             mDisableKeyguardTrustAgentCheckbox =
    305                     (CheckBoxPreference) findPreference(KEY_DISABLE_TRUST_AGENTS);
    306             mDisableKeyguardTrustAgentCheckbox.setOnPreferenceChangeListener(this);
    307         }
    308 
    309         // At onResume time, reload UI with current values as required
    310         @Override
    311         public void onResume() {
    312             super.onResume();
    313             mEnableCheckbox.setChecked(mAdminActive);
    314             enableDeviceCapabilitiesArea(mAdminActive);
    315 
    316             if (mAdminActive) {
    317                 mDPM.setCameraDisabled(mDeviceAdminSample, mDisableCameraCheckbox.isChecked());
    318                 mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample, createKeyguardDisabledFlag());
    319                 reloadSummaries();
    320             }
    321         }
    322 
    323         int createKeyguardDisabledFlag() {
    324             int flags = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
    325             flags |= mDisableKeyguardWidgetsCheckbox.isChecked() ?
    326                     DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL : 0;
    327             flags |= mDisableKeyguardSecureCameraCheckbox.isChecked() ?
    328                     DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA : 0;
    329             flags |= mDisableKeyguardNotificationCheckbox.isChecked() ?
    330                     DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS : 0;
    331             flags |= mDisableKeyguardUnredactedCheckbox.isChecked() ?
    332                     DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS : 0;
    333             flags |= mDisableKeyguardTrustAgentCheckbox.isChecked() ?
    334                     DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS : 0;
    335             return flags;
    336         }
    337 
    338         @Override
    339         public boolean onPreferenceChange(Preference preference, Object newValue) {
    340             if (super.onPreferenceChange(preference, newValue)) {
    341                 return true;
    342             }
    343             boolean value = (Boolean) newValue;
    344             if (preference == mEnableCheckbox) {
    345                 if (value != mAdminActive) {
    346                     if (value) {
    347                         // Launch the activity to have the user enable our admin.
    348                         Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
    349                         intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminSample);
    350                         intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
    351                                 mActivity.getString(R.string.add_admin_extra_app_text));
    352                         startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN);
    353                         // return false - don't update checkbox until we're really active
    354                         return false;
    355                     } else {
    356                         mDPM.removeActiveAdmin(mDeviceAdminSample);
    357                         enableDeviceCapabilitiesArea(false);
    358                         mAdminActive = false;
    359                     }
    360                 }
    361             } else if (preference == mDisableCameraCheckbox) {
    362                 mDPM.setCameraDisabled(mDeviceAdminSample, value);
    363                 // Delay update because the change is only applied after exiting this method.
    364                 postReloadSummaries();
    365             } else if (preference == mDisableKeyguardWidgetsCheckbox
    366                     || preference == mDisableKeyguardSecureCameraCheckbox
    367                     || preference == mDisableKeyguardNotificationCheckbox
    368                     || preference == mDisableKeyguardUnredactedCheckbox
    369                     || preference == mDisableKeyguardTrustAgentCheckbox) {
    370                 // Delay update because the change is only applied after exiting this method.
    371                 getView().post(new Runnable() {
    372                     @Override
    373                     public void run() {
    374                         mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample,
    375                                 createKeyguardDisabledFlag());
    376                     }
    377                 });
    378                 postReloadSummaries();
    379             }
    380             return true;
    381         }
    382 
    383         @Override
    384         protected void reloadSummaries() {
    385             super.reloadSummaries();
    386             String cameraSummary = getString(mDPM.getCameraDisabled(mDeviceAdminSample)
    387                     ? R.string.camera_disabled : R.string.camera_enabled);
    388             mDisableCameraCheckbox.setSummary(cameraSummary);
    389 
    390             int disabled = mDPM.getKeyguardDisabledFeatures(mDeviceAdminSample);
    391 
    392             String keyguardWidgetSummary = getString(
    393                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0 ?
    394                             R.string.keyguard_widgets_disabled : R.string.keyguard_widgets_enabled);
    395             mDisableKeyguardWidgetsCheckbox.setSummary(keyguardWidgetSummary);
    396 
    397             String keyguardSecureCameraSummary = getString(
    398                 (disabled & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 ?
    399                 R.string.keyguard_secure_camera_disabled : R.string.keyguard_secure_camera_enabled);
    400             mDisableKeyguardSecureCameraCheckbox.setSummary(keyguardSecureCameraSummary);
    401 
    402             String keyguardSecureNotificationsSummary = getString(
    403                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) != 0 ?
    404                         R.string.keyguard_secure_notifications_disabled
    405                         : R.string.keyguard_secure_notifications_enabled);
    406             mDisableKeyguardNotificationCheckbox.setSummary(keyguardSecureNotificationsSummary);
    407 
    408             String keyguardUnredactedSummary = getString(
    409                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) != 0
    410                         ? R.string.keyguard_unredacted_notifications_disabled
    411                         : R.string.keyguard_unredacted_notifications_enabled);
    412             mDisableKeyguardUnredactedCheckbox.setSummary(keyguardUnredactedSummary);
    413 
    414             String keyguardEnableTrustAgentSummary = getString(
    415                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0 ?
    416                         R.string.keyguard_trust_agents_disabled
    417                         : R.string.keyguard_trust_agents_enabled);
    418             mDisableKeyguardTrustAgentCheckbox.setSummary(keyguardEnableTrustAgentSummary);
    419         }
    420 
    421         /** Updates the device capabilities area (dis/enabling) as the admin is (de)activated */
    422         private void enableDeviceCapabilitiesArea(boolean enabled) {
    423             mDisableCameraCheckbox.setEnabled(enabled);
    424             mDisableKeyguardWidgetsCheckbox.setEnabled(enabled);
    425             mDisableKeyguardSecureCameraCheckbox.setEnabled(enabled);
    426             mDisableKeyguardNotificationCheckbox.setEnabled(enabled);
    427             mDisableKeyguardUnredactedCheckbox.setEnabled(enabled);
    428             mDisableKeyguardTrustAgentCheckbox.setEnabled(enabled);
    429         }
    430     }
    431 
    432     /**
    433      * PreferenceFragment for "password quality" preferences.
    434      */
    435     public static class QualityFragment extends AdminSampleFragment
    436             implements OnPreferenceChangeListener {
    437 
    438         // Password quality values
    439         // This list must match the list found in samples/ApiDemos/res/values/arrays.xml
    440         final static int[] mPasswordQualityValues = new int[] {
    441             DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
    442             DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
    443             DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
    444             DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
    445             DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
    446             DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
    447             DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
    448         };
    449 
    450         // Password quality values (as strings, for the ListPreference entryValues)
    451         // This list must match the list found in samples/ApiDemos/res/values/arrays.xml
    452         final static String[] mPasswordQualityValueStrings = new String[] {
    453             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED),
    454             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING),
    455             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
    456             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
    457             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
    458             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC),
    459             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)
    460         };
    461 
    462         // UI elements
    463         private PreferenceCategory mQualityCategory;
    464         private ListPreference mPasswordQuality;
    465         private EditTextPreference mMinLength;
    466         private EditTextPreference mMinLetters;
    467         private EditTextPreference mMinNumeric;
    468         private EditTextPreference mMinLowerCase;
    469         private EditTextPreference mMinUpperCase;
    470         private EditTextPreference mMinSymbols;
    471         private EditTextPreference mMinNonLetter;
    472 
    473         @Override
    474         public void onCreate(Bundle savedInstanceState) {
    475             super.onCreate(savedInstanceState);
    476             addPreferencesFromResource(R.xml.device_admin_quality);
    477 
    478             mQualityCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_QUALITY);
    479             mPasswordQuality = (ListPreference) findPreference(KEY_QUALITY);
    480             mMinLength = (EditTextPreference) findPreference(KEY_MIN_LENGTH);
    481             mMinLetters = (EditTextPreference) findPreference(KEY_MIN_LETTERS);
    482             mMinNumeric = (EditTextPreference) findPreference(KEY_MIN_NUMERIC);
    483             mMinLowerCase = (EditTextPreference) findPreference(KEY_MIN_LOWER_CASE);
    484             mMinUpperCase = (EditTextPreference) findPreference(KEY_MIN_UPPER_CASE);
    485             mMinSymbols = (EditTextPreference) findPreference(KEY_MIN_SYMBOLS);
    486             mMinNonLetter = (EditTextPreference) findPreference(KEY_MIN_NON_LETTER);
    487 
    488             mPasswordQuality.setOnPreferenceChangeListener(this);
    489             mMinLength.setOnPreferenceChangeListener(this);
    490             mMinLetters.setOnPreferenceChangeListener(this);
    491             mMinNumeric.setOnPreferenceChangeListener(this);
    492             mMinLowerCase.setOnPreferenceChangeListener(this);
    493             mMinUpperCase.setOnPreferenceChangeListener(this);
    494             mMinSymbols.setOnPreferenceChangeListener(this);
    495             mMinNonLetter.setOnPreferenceChangeListener(this);
    496 
    497             // Finish setup of the quality dropdown
    498             mPasswordQuality.setEntryValues(mPasswordQualityValueStrings);
    499         }
    500 
    501         @Override
    502         public void onResume() {
    503             super.onResume();
    504             mQualityCategory.setEnabled(mAdminActive);
    505         }
    506 
    507         /**
    508          * Update the summaries of each item to show the local setting and the global setting.
    509          */
    510         @Override
    511         protected void reloadSummaries() {
    512             super.reloadSummaries();
    513             // Show numeric settings for each policy API
    514             int local, global;
    515             local = mDPM.getPasswordQuality(mDeviceAdminSample);
    516             global = mDPM.getPasswordQuality(null);
    517             mPasswordQuality.setSummary(
    518                     localGlobalSummary(qualityValueToString(local), qualityValueToString(global)));
    519             local = mDPM.getPasswordMinimumLength(mDeviceAdminSample);
    520             global = mDPM.getPasswordMinimumLength(null);
    521             mMinLength.setSummary(localGlobalSummary(local, global));
    522             local = mDPM.getPasswordMinimumLetters(mDeviceAdminSample);
    523             global = mDPM.getPasswordMinimumLetters(null);
    524             mMinLetters.setSummary(localGlobalSummary(local, global));
    525             local = mDPM.getPasswordMinimumNumeric(mDeviceAdminSample);
    526             global = mDPM.getPasswordMinimumNumeric(null);
    527             mMinNumeric.setSummary(localGlobalSummary(local, global));
    528             local = mDPM.getPasswordMinimumLowerCase(mDeviceAdminSample);
    529             global = mDPM.getPasswordMinimumLowerCase(null);
    530             mMinLowerCase.setSummary(localGlobalSummary(local, global));
    531             local = mDPM.getPasswordMinimumUpperCase(mDeviceAdminSample);
    532             global = mDPM.getPasswordMinimumUpperCase(null);
    533             mMinUpperCase.setSummary(localGlobalSummary(local, global));
    534             local = mDPM.getPasswordMinimumSymbols(mDeviceAdminSample);
    535             global = mDPM.getPasswordMinimumSymbols(null);
    536             mMinSymbols.setSummary(localGlobalSummary(local, global));
    537             local = mDPM.getPasswordMinimumNonLetter(mDeviceAdminSample);
    538             global = mDPM.getPasswordMinimumNonLetter(null);
    539             mMinNonLetter.setSummary(localGlobalSummary(local, global));
    540         }
    541 
    542         @Override
    543         public boolean onPreferenceChange(Preference preference, Object newValue) {
    544             if (super.onPreferenceChange(preference, newValue)) {
    545                 return true;
    546             }
    547             String valueString = (String)newValue;
    548             if (TextUtils.isEmpty(valueString)) {
    549                 return false;
    550             }
    551             int value = 0;
    552             try {
    553                 value = Integer.parseInt(valueString);
    554             } catch (NumberFormatException nfe) {
    555                 String warning = mActivity.getString(R.string.number_format_warning, valueString);
    556                 Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
    557             }
    558             if (preference == mPasswordQuality) {
    559                 mDPM.setPasswordQuality(mDeviceAdminSample, value);
    560             } else if (preference == mMinLength) {
    561                 mDPM.setPasswordMinimumLength(mDeviceAdminSample, value);
    562             } else if (preference == mMinLetters) {
    563                 mDPM.setPasswordMinimumLetters(mDeviceAdminSample, value);
    564             } else if (preference == mMinNumeric) {
    565                 mDPM.setPasswordMinimumNumeric(mDeviceAdminSample, value);
    566             } else if (preference == mMinLowerCase) {
    567                 mDPM.setPasswordMinimumLowerCase(mDeviceAdminSample, value);
    568             } else if (preference == mMinUpperCase) {
    569                 mDPM.setPasswordMinimumUpperCase(mDeviceAdminSample, value);
    570             } else if (preference == mMinSymbols) {
    571                 mDPM.setPasswordMinimumSymbols(mDeviceAdminSample, value);
    572             } else if (preference == mMinNonLetter) {
    573                 mDPM.setPasswordMinimumNonLetter(mDeviceAdminSample, value);
    574             }
    575             // Delay update because the change is only applied after exiting this method.
    576             postReloadSummaries();
    577             return true;
    578         }
    579 
    580         private String qualityValueToString(int quality) {
    581             for (int i=  0; i < mPasswordQualityValues.length; i++) {
    582                 if (mPasswordQualityValues[i] == quality) {
    583                     String[] qualities =
    584                         mActivity.getResources().getStringArray(R.array.password_qualities);
    585                     return qualities[i];
    586                 }
    587             }
    588             return "(0x" + Integer.toString(quality, 16) + ")";
    589         }
    590     }
    591 
    592     /**
    593      * PreferenceFragment for "password expiration" preferences.
    594      */
    595     public static class ExpirationFragment extends AdminSampleFragment
    596             implements OnPreferenceChangeListener, OnPreferenceClickListener {
    597         private PreferenceCategory mExpirationCategory;
    598         private EditTextPreference mHistory;
    599         private EditTextPreference mExpirationTimeout;
    600         private PreferenceScreen mExpirationStatus;
    601 
    602         @Override
    603         public void onCreate(Bundle savedInstanceState) {
    604             super.onCreate(savedInstanceState);
    605             addPreferencesFromResource(R.xml.device_admin_expiration);
    606 
    607             mExpirationCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_EXPIRATION);
    608             mHistory = (EditTextPreference) findPreference(KEY_HISTORY);
    609             mExpirationTimeout = (EditTextPreference) findPreference(KEY_EXPIRATION_TIMEOUT);
    610             mExpirationStatus = (PreferenceScreen) findPreference(KEY_EXPIRATION_STATUS);
    611 
    612             mHistory.setOnPreferenceChangeListener(this);
    613             mExpirationTimeout.setOnPreferenceChangeListener(this);
    614             mExpirationStatus.setOnPreferenceClickListener(this);
    615         }
    616 
    617         @Override
    618         public void onResume() {
    619             super.onResume();
    620             mExpirationCategory.setEnabled(mAdminActive);
    621         }
    622 
    623         /**
    624          * Update the summaries of each item to show the local setting and the global setting.
    625          */
    626         @Override
    627         protected void reloadSummaries() {
    628             super.reloadSummaries();
    629 
    630             int local, global;
    631             local = mDPM.getPasswordHistoryLength(mDeviceAdminSample);
    632             global = mDPM.getPasswordHistoryLength(null);
    633             mHistory.setSummary(localGlobalSummary(local, global));
    634 
    635             long localLong, globalLong;
    636             localLong = mDPM.getPasswordExpirationTimeout(mDeviceAdminSample);
    637             globalLong = mDPM.getPasswordExpirationTimeout(null);
    638             mExpirationTimeout.setSummary(localGlobalSummary(
    639                     localLong / MS_PER_MINUTE, globalLong / MS_PER_MINUTE));
    640 
    641             String expirationStatus = getExpirationStatus();
    642             mExpirationStatus.setSummary(expirationStatus);
    643         }
    644 
    645         @Override
    646         public boolean onPreferenceChange(Preference preference, Object newValue) {
    647             if (super.onPreferenceChange(preference, newValue)) {
    648                 return true;
    649             }
    650             String valueString = (String)newValue;
    651             if (TextUtils.isEmpty(valueString)) {
    652                 return false;
    653             }
    654             int value = 0;
    655             try {
    656                 value = Integer.parseInt(valueString);
    657             } catch (NumberFormatException nfe) {
    658                 String warning = mActivity.getString(R.string.number_format_warning, valueString);
    659                 Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
    660             }
    661             if (preference == mHistory) {
    662                 mDPM.setPasswordHistoryLength(mDeviceAdminSample, value);
    663             } else if (preference == mExpirationTimeout) {
    664                 mDPM.setPasswordExpirationTimeout(mDeviceAdminSample, value * MS_PER_MINUTE);
    665             }
    666             // Delay update because the change is only applied after exiting this method.
    667             postReloadSummaries();
    668             return true;
    669         }
    670 
    671         @Override
    672         public boolean onPreferenceClick(Preference preference) {
    673             if (super.onPreferenceClick(preference)) {
    674                 return true;
    675             }
    676             if (preference == mExpirationStatus) {
    677                 String expirationStatus = getExpirationStatus();
    678                 mExpirationStatus.setSummary(expirationStatus);
    679                 return true;
    680             }
    681             return false;
    682         }
    683 
    684         /**
    685          * Create a summary string describing the expiration status for the sample app,
    686          * as well as the global (aggregate) status.
    687          */
    688         private String getExpirationStatus() {
    689             // expirations are absolute;  convert to relative for display
    690             long localExpiration = mDPM.getPasswordExpiration(mDeviceAdminSample);
    691             long globalExpiration = mDPM.getPasswordExpiration(null);
    692             long now = System.currentTimeMillis();
    693 
    694             // local expiration
    695             String local;
    696             if (localExpiration == 0) {
    697                 local = mActivity.getString(R.string.expiration_status_none);
    698             } else {
    699                 localExpiration -= now;
    700                 String dms = timeToDaysMinutesSeconds(mActivity, Math.abs(localExpiration));
    701                 if (localExpiration >= 0) {
    702                     local = mActivity.getString(R.string.expiration_status_future, dms);
    703                 } else {
    704                     local = mActivity.getString(R.string.expiration_status_past, dms);
    705                 }
    706             }
    707 
    708             // global expiration
    709             String global;
    710             if (globalExpiration == 0) {
    711                 global = mActivity.getString(R.string.expiration_status_none);
    712             } else {
    713                 globalExpiration -= now;
    714                 String dms = timeToDaysMinutesSeconds(mActivity, Math.abs(globalExpiration));
    715                 if (globalExpiration >= 0) {
    716                     global = mActivity.getString(R.string.expiration_status_future, dms);
    717                 } else {
    718                     global = mActivity.getString(R.string.expiration_status_past, dms);
    719                 }
    720             }
    721             return mActivity.getString(R.string.status_local_global, local, global);
    722         }
    723     }
    724 
    725     /**
    726      * PreferenceFragment for "lock screen & wipe" preferences.
    727      */
    728     public static class LockWipeFragment extends AdminSampleFragment
    729             implements OnPreferenceChangeListener, OnPreferenceClickListener {
    730         private PreferenceCategory mLockWipeCategory;
    731         private EditTextPreference mMaxTimeScreenLock;
    732         private EditTextPreference mMaxFailures;
    733         private PreferenceScreen mLockScreen;
    734         private PreferenceScreen mWipeData;
    735         private PreferenceScreen mWipeAppData;
    736 
    737         @Override
    738         public void onCreate(Bundle savedInstanceState) {
    739             super.onCreate(savedInstanceState);
    740             addPreferencesFromResource(R.xml.device_admin_lock_wipe);
    741 
    742             mLockWipeCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_LOCK_WIPE);
    743             mMaxTimeScreenLock = (EditTextPreference) findPreference(KEY_MAX_TIME_SCREEN_LOCK);
    744             mMaxFailures = (EditTextPreference) findPreference(KEY_MAX_FAILS_BEFORE_WIPE);
    745             mLockScreen = (PreferenceScreen) findPreference(KEY_LOCK_SCREEN);
    746             mWipeData = (PreferenceScreen) findPreference(KEY_WIPE_DATA);
    747             mWipeAppData = (PreferenceScreen) findPreference(KEY_WIP_DATA_ALL);
    748 
    749             mMaxTimeScreenLock.setOnPreferenceChangeListener(this);
    750             mMaxFailures.setOnPreferenceChangeListener(this);
    751             mLockScreen.setOnPreferenceClickListener(this);
    752             mWipeData.setOnPreferenceClickListener(this);
    753             mWipeAppData.setOnPreferenceClickListener(this);
    754         }
    755 
    756         @Override
    757         public void onResume() {
    758             super.onResume();
    759             mLockWipeCategory.setEnabled(mAdminActive);
    760         }
    761 
    762         /**
    763          * Update the summaries of each item to show the local setting and the global setting.
    764          */
    765         @Override
    766         protected void reloadSummaries() {
    767             super.reloadSummaries();
    768 
    769             long localLong, globalLong;
    770             localLong = mDPM.getMaximumTimeToLock(mDeviceAdminSample);
    771             globalLong = mDPM.getMaximumTimeToLock(null);
    772             mMaxTimeScreenLock.setSummary(localGlobalSummary(
    773                     localLong / MS_PER_MINUTE, globalLong / MS_PER_MINUTE));
    774 
    775             int local, global;
    776             local = mDPM.getMaximumFailedPasswordsForWipe(mDeviceAdminSample);
    777             global = mDPM.getMaximumFailedPasswordsForWipe(null);
    778             mMaxFailures.setSummary(localGlobalSummary(local, global));
    779         }
    780 
    781         @Override
    782         public boolean onPreferenceChange(Preference preference, Object newValue) {
    783             if (super.onPreferenceChange(preference, newValue)) {
    784                 return true;
    785             }
    786             String valueString = (String)newValue;
    787             if (TextUtils.isEmpty(valueString)) {
    788                 return false;
    789             }
    790             int value = 0;
    791             try {
    792                 value = Integer.parseInt(valueString);
    793             } catch (NumberFormatException nfe) {
    794                 String warning = mActivity.getString(R.string.number_format_warning, valueString);
    795                 Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
    796             }
    797             if (preference == mMaxTimeScreenLock) {
    798                 mDPM.setMaximumTimeToLock(mDeviceAdminSample, value * MS_PER_MINUTE);
    799             } else if (preference == mMaxFailures) {
    800                 if (alertIfMonkey(mActivity, R.string.monkey_wipe_data)) {
    801                     return true;
    802                 }
    803                 mDPM.setMaximumFailedPasswordsForWipe(mDeviceAdminSample, value);
    804             }
    805             // Delay update because the change is only applied after exiting this method.
    806             postReloadSummaries();
    807             return true;
    808         }
    809 
    810         @Override
    811         public boolean onPreferenceClick(Preference preference) {
    812             if (super.onPreferenceClick(preference)) {
    813                 return true;
    814             }
    815             if (preference == mLockScreen) {
    816                 if (alertIfMonkey(mActivity, R.string.monkey_lock_screen)) {
    817                     return true;
    818                 }
    819                 mDPM.lockNow();
    820                 return true;
    821             } else if (preference == mWipeData || preference == mWipeAppData) {
    822                 if (alertIfMonkey(mActivity, R.string.monkey_wipe_data)) {
    823                     return true;
    824                 }
    825                 promptForRealDeviceWipe(preference == mWipeAppData);
    826                 return true;
    827             }
    828             return false;
    829         }
    830 
    831         /**
    832          * Wiping data is real, so we don't want it to be easy.  Show two alerts before wiping.
    833          */
    834         private void promptForRealDeviceWipe(final boolean wipeAllData) {
    835             final DeviceAdminSample activity = mActivity;
    836 
    837             AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    838             builder.setMessage(R.string.wipe_warning_first);
    839             builder.setPositiveButton(R.string.wipe_warning_first_ok,
    840                     new DialogInterface.OnClickListener() {
    841                 @Override
    842                 public void onClick(DialogInterface dialog, int which) {
    843                     AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    844                     if (wipeAllData) {
    845                         builder.setMessage(R.string.wipe_warning_second_full);
    846                     } else {
    847                         builder.setMessage(R.string.wipe_warning_second);
    848                     }
    849                     builder.setPositiveButton(R.string.wipe_warning_second_ok,
    850                             new DialogInterface.OnClickListener() {
    851                         @Override
    852                         public void onClick(DialogInterface dialog, int which) {
    853                             boolean stillActive = mActivity.isActiveAdmin();
    854                             if (stillActive) {
    855                                 mDPM.wipeData(wipeAllData
    856                                         ? DevicePolicyManager.WIPE_EXTERNAL_STORAGE : 0);
    857                             }
    858                         }
    859                     });
    860                     builder.setNegativeButton(R.string.wipe_warning_second_no, null);
    861                     builder.show();
    862                 }
    863             });
    864             builder.setNegativeButton(R.string.wipe_warning_first_no, null);
    865             builder.show();
    866         }
    867     }
    868 
    869     /**
    870      * PreferenceFragment for "encryption" preferences.
    871      */
    872     public static class EncryptionFragment extends AdminSampleFragment
    873             implements OnPreferenceChangeListener, OnPreferenceClickListener {
    874         private PreferenceCategory mEncryptionCategory;
    875         private CheckBoxPreference mRequireEncryption;
    876         private PreferenceScreen mActivateEncryption;
    877 
    878         @Override
    879         public void onCreate(Bundle savedInstanceState) {
    880             super.onCreate(savedInstanceState);
    881             addPreferencesFromResource(R.xml.device_admin_encryption);
    882 
    883             mEncryptionCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_ENCRYPTION);
    884             mRequireEncryption = (CheckBoxPreference) findPreference(KEY_REQUIRE_ENCRYPTION);
    885             mActivateEncryption = (PreferenceScreen) findPreference(KEY_ACTIVATE_ENCRYPTION);
    886 
    887             mRequireEncryption.setOnPreferenceChangeListener(this);
    888             mActivateEncryption.setOnPreferenceClickListener(this);
    889         }
    890 
    891         @Override
    892         public void onResume() {
    893             super.onResume();
    894             mEncryptionCategory.setEnabled(mAdminActive);
    895             mRequireEncryption.setChecked(mDPM.getStorageEncryption(mDeviceAdminSample));
    896         }
    897 
    898         /**
    899          * Update the summaries of each item to show the local setting and the global setting.
    900          */
    901         @Override
    902         protected void reloadSummaries() {
    903             super.reloadSummaries();
    904 
    905             boolean local, global;
    906             local = mDPM.getStorageEncryption(mDeviceAdminSample);
    907             global = mDPM.getStorageEncryption(null);
    908             mRequireEncryption.setSummary(localGlobalSummary(local, global));
    909 
    910             int deviceStatusCode = mDPM.getStorageEncryptionStatus();
    911             String deviceStatus = statusCodeToString(deviceStatusCode);
    912             String status = mActivity.getString(R.string.status_device_encryption, deviceStatus);
    913             mActivateEncryption.setSummary(status);
    914         }
    915 
    916         @Override
    917         public boolean onPreferenceChange(Preference preference, Object newValue) {
    918             if (super.onPreferenceChange(preference, newValue)) {
    919                 return true;
    920             }
    921             if (preference == mRequireEncryption) {
    922                 boolean newActive = (Boolean) newValue;
    923                 mDPM.setStorageEncryption(mDeviceAdminSample, newActive);
    924                 // Delay update because the change is only applied after exiting this method.
    925                 postReloadSummaries();
    926                 return true;
    927             }
    928             return true;
    929         }
    930 
    931         @Override
    932         public boolean onPreferenceClick(Preference preference) {
    933             if (super.onPreferenceClick(preference)) {
    934                 return true;
    935             }
    936             if (preference == mActivateEncryption) {
    937                 if (alertIfMonkey(mActivity, R.string.monkey_encryption)) {
    938                     return true;
    939                 }
    940                 // Check to see if encryption is even supported on this device (it's optional).
    941                 if (mDPM.getStorageEncryptionStatus() ==
    942                         DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
    943                     AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
    944                     builder.setMessage(R.string.encryption_not_supported);
    945                     builder.setPositiveButton(R.string.encryption_not_supported_ok, null);
    946                     builder.show();
    947                     return true;
    948                 }
    949                 // Launch the activity to activate encryption.  May or may not return!
    950                 Intent intent = new Intent(DevicePolicyManager.ACTION_START_ENCRYPTION);
    951                 startActivityForResult(intent, REQUEST_CODE_START_ENCRYPTION);
    952                 return true;
    953             }
    954             return false;
    955         }
    956 
    957         private String statusCodeToString(int newStatusCode) {
    958             int newStatus = R.string.encryption_status_unknown;
    959             switch (newStatusCode) {
    960                 case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED:
    961                     newStatus = R.string.encryption_status_unsupported;
    962                     break;
    963                 case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE:
    964                     newStatus = R.string.encryption_status_inactive;
    965                     break;
    966                 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING:
    967                     newStatus = R.string.encryption_status_activating;
    968                     break;
    969                 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
    970                     newStatus = R.string.encryption_status_active;
    971                     break;
    972             }
    973             return mActivity.getString(newStatus);
    974         }
    975     }
    976 
    977     /**
    978      * Simple converter used for long expiration times reported in mSec.
    979      */
    980     private static String timeToDaysMinutesSeconds(Context context, long time) {
    981         long days = time / MS_PER_DAY;
    982         long hours = (time / MS_PER_HOUR) % 24;
    983         long minutes = (time / MS_PER_MINUTE) % 60;
    984         return context.getString(R.string.status_days_hours_minutes, days, hours, minutes);
    985     }
    986 
    987     /**
    988      * If the "user" is a monkey, post an alert and notify the caller.  This prevents automated
    989      * test frameworks from stumbling into annoying or dangerous operations.
    990      */
    991     private static boolean alertIfMonkey(Context context, int stringId) {
    992         if (ActivityManager.isUserAMonkey()) {
    993             AlertDialog.Builder builder = new AlertDialog.Builder(context);
    994             builder.setMessage(stringId);
    995             builder.setPositiveButton(R.string.monkey_ok, null);
    996             builder.show();
    997             return true;
    998         } else {
    999             return false;
   1000         }
   1001     }
   1002 
   1003     /**
   1004      * Sample implementation of a DeviceAdminReceiver.  Your controller must provide one,
   1005      * although you may or may not implement all of the methods shown here.
   1006      *
   1007      * All callbacks are on the UI thread and your implementations should not engage in any
   1008      * blocking operations, including disk I/O.
   1009      */
   1010     public static class DeviceAdminSampleReceiver extends DeviceAdminReceiver {
   1011         void showToast(Context context, String msg) {
   1012             String status = context.getString(R.string.admin_receiver_status, msg);
   1013             Toast.makeText(context, status, Toast.LENGTH_SHORT).show();
   1014         }
   1015 
   1016         @Override
   1017         public void onReceive(Context context, Intent intent) {
   1018             if (intent.getAction() == ACTION_DEVICE_ADMIN_DISABLE_REQUESTED) {
   1019                 abortBroadcast();
   1020             }
   1021             super.onReceive(context, intent);
   1022         }
   1023 
   1024         @Override
   1025         public void onEnabled(Context context, Intent intent) {
   1026             showToast(context, context.getString(R.string.admin_receiver_status_enabled));
   1027         }
   1028 
   1029         @Override
   1030         public CharSequence onDisableRequested(Context context, Intent intent) {
   1031             return context.getString(R.string.admin_receiver_status_disable_warning);
   1032         }
   1033 
   1034         @Override
   1035         public void onDisabled(Context context, Intent intent) {
   1036             showToast(context, context.getString(R.string.admin_receiver_status_disabled));
   1037         }
   1038 
   1039         @Override
   1040         public void onPasswordChanged(Context context, Intent intent) {
   1041             showToast(context, context.getString(R.string.admin_receiver_status_pw_changed));
   1042         }
   1043 
   1044         @Override
   1045         public void onPasswordFailed(Context context, Intent intent) {
   1046             showToast(context, context.getString(R.string.admin_receiver_status_pw_failed));
   1047         }
   1048 
   1049         @Override
   1050         public void onPasswordSucceeded(Context context, Intent intent) {
   1051             showToast(context, context.getString(R.string.admin_receiver_status_pw_succeeded));
   1052         }
   1053 
   1054         @Override
   1055         public void onPasswordExpiring(Context context, Intent intent) {
   1056             DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
   1057                     Context.DEVICE_POLICY_SERVICE);
   1058             long expr = dpm.getPasswordExpiration(
   1059                     new ComponentName(context, DeviceAdminSampleReceiver.class));
   1060             long delta = expr - System.currentTimeMillis();
   1061             boolean expired = delta < 0L;
   1062             String message = context.getString(expired ?
   1063                     R.string.expiration_status_past : R.string.expiration_status_future);
   1064             showToast(context, message);
   1065             Log.v(TAG, message);
   1066         }
   1067     }
   1068 }
   1069