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