• 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                                 Log.w(TAG, "setTrustAgentConfigurat() is disabled");
410                                 // mDPM.setTrustAgentConfiguration(mDeviceAdminSample, agent, bundle);
411                             }
412                         } else {
413                             Log.w(TAG, "Invalid component: " + component);
414                         }
415                     }
416                 }
417             });
418         }
419 
420         @Override
reloadSummaries()421         protected void reloadSummaries() {
422             super.reloadSummaries();
423             String cameraSummary = getString(mDPM.getCameraDisabled(mDeviceAdminSample)
424                     ? R.string.camera_disabled : R.string.camera_enabled);
425             mDisableCameraCheckbox.setSummary(cameraSummary);
426 
427             int disabled = mDPM.getKeyguardDisabledFeatures(mDeviceAdminSample);
428 
429             String keyguardWidgetSummary = getString(
430                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0 ?
431                             R.string.keyguard_widgets_disabled : R.string.keyguard_widgets_enabled);
432             mDisableKeyguardWidgetsCheckbox.setSummary(keyguardWidgetSummary);
433 
434             String keyguardSecureCameraSummary = getString(
435                 (disabled & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 ?
436                 R.string.keyguard_secure_camera_disabled : R.string.keyguard_secure_camera_enabled);
437             mDisableKeyguardSecureCameraCheckbox.setSummary(keyguardSecureCameraSummary);
438 
439             String keyguardSecureNotificationsSummary = getString(
440                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) != 0 ?
441                         R.string.keyguard_secure_notifications_disabled
442                         : R.string.keyguard_secure_notifications_enabled);
443             mDisableKeyguardNotificationCheckbox.setSummary(keyguardSecureNotificationsSummary);
444 
445             String keyguardUnredactedSummary = getString(
446                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) != 0
447                         ? R.string.keyguard_unredacted_notifications_disabled
448                         : R.string.keyguard_unredacted_notifications_enabled);
449             mDisableKeyguardUnredactedCheckbox.setSummary(keyguardUnredactedSummary);
450 
451             String keyguardEnableTrustAgentSummary = getString(
452                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0 ?
453                         R.string.keyguard_trust_agents_disabled
454                         : R.string.keyguard_trust_agents_enabled);
455             mDisableKeyguardTrustAgentCheckbox.setSummary(keyguardEnableTrustAgentSummary);
456 
457             final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
458             final boolean trustDisabled =
459                     (disabled & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
460             String component = prefs.getString(mTrustAgentComponent.getKey(), null);
461             mTrustAgentComponent.setSummary(component);
462             mTrustAgentComponent.setEnabled(trustDisabled);
463 
464             String features = prefs.getString(mTrustAgentFeatures.getKey(), null);
465             mTrustAgentFeatures.setSummary(features);
466             mTrustAgentFeatures.setEnabled(trustDisabled);
467         }
468 
469         /** Updates the device capabilities area (dis/enabling) as the admin is (de)activated */
enableDeviceCapabilitiesArea(boolean enabled)470         private void enableDeviceCapabilitiesArea(boolean enabled) {
471             mDisableCameraCheckbox.setEnabled(enabled);
472             mDisableKeyguardWidgetsCheckbox.setEnabled(enabled);
473             mDisableKeyguardSecureCameraCheckbox.setEnabled(enabled);
474             mDisableKeyguardNotificationCheckbox.setEnabled(enabled);
475             mDisableKeyguardUnredactedCheckbox.setEnabled(enabled);
476             mDisableKeyguardTrustAgentCheckbox.setEnabled(enabled);
477             mTrustAgentComponent.setEnabled(enabled);
478             mTrustAgentFeatures.setEnabled(enabled);
479         }
480     }
481 
482     /**
483      * PreferenceFragment for "password quality" preferences.
484      */
485     public static class QualityFragment extends AdminSampleFragment
486             implements OnPreferenceChangeListener {
487 
488         // Password quality values
489         // This list must match the list found in samples/ApiDemos/res/values/arrays.xml
490         final static int[] mPasswordQualityValues = new int[] {
491             DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
492             DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
493             DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
494             DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
495             DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
496             DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
497             DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
498         };
499 
500         // Password quality values (as strings, for the ListPreference entryValues)
501         // This list must match the list found in samples/ApiDemos/res/values/arrays.xml
502         final static String[] mPasswordQualityValueStrings = new String[] {
503             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED),
504             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING),
505             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
506             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
507             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
508             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC),
509             String.valueOf(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)
510         };
511 
512         // UI elements
513         private PreferenceCategory mQualityCategory;
514         private ListPreference mPasswordQuality;
515         private EditTextPreference mMinLength;
516         private EditTextPreference mMinLetters;
517         private EditTextPreference mMinNumeric;
518         private EditTextPreference mMinLowerCase;
519         private EditTextPreference mMinUpperCase;
520         private EditTextPreference mMinSymbols;
521         private EditTextPreference mMinNonLetter;
522 
523         @Override
onCreate(Bundle savedInstanceState)524         public void onCreate(Bundle savedInstanceState) {
525             super.onCreate(savedInstanceState);
526             addPreferencesFromResource(R.xml.device_admin_quality);
527 
528             mQualityCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_QUALITY);
529             mPasswordQuality = (ListPreference) findPreference(KEY_QUALITY);
530             mMinLength = (EditTextPreference) findPreference(KEY_MIN_LENGTH);
531             mMinLetters = (EditTextPreference) findPreference(KEY_MIN_LETTERS);
532             mMinNumeric = (EditTextPreference) findPreference(KEY_MIN_NUMERIC);
533             mMinLowerCase = (EditTextPreference) findPreference(KEY_MIN_LOWER_CASE);
534             mMinUpperCase = (EditTextPreference) findPreference(KEY_MIN_UPPER_CASE);
535             mMinSymbols = (EditTextPreference) findPreference(KEY_MIN_SYMBOLS);
536             mMinNonLetter = (EditTextPreference) findPreference(KEY_MIN_NON_LETTER);
537 
538             mPasswordQuality.setOnPreferenceChangeListener(this);
539             mMinLength.setOnPreferenceChangeListener(this);
540             mMinLetters.setOnPreferenceChangeListener(this);
541             mMinNumeric.setOnPreferenceChangeListener(this);
542             mMinLowerCase.setOnPreferenceChangeListener(this);
543             mMinUpperCase.setOnPreferenceChangeListener(this);
544             mMinSymbols.setOnPreferenceChangeListener(this);
545             mMinNonLetter.setOnPreferenceChangeListener(this);
546 
547             // Finish setup of the quality dropdown
548             mPasswordQuality.setEntryValues(mPasswordQualityValueStrings);
549         }
550 
551         @Override
onResume()552         public void onResume() {
553             super.onResume();
554             mQualityCategory.setEnabled(mAdminActive);
555         }
556 
557         /**
558          * Update the summaries of each item to show the local setting and the global setting.
559          */
560         @Override
reloadSummaries()561         protected void reloadSummaries() {
562             super.reloadSummaries();
563             // Show numeric settings for each policy API
564             int local, global;
565             local = mDPM.getPasswordQuality(mDeviceAdminSample);
566             global = mDPM.getPasswordQuality(null);
567             mPasswordQuality.setSummary(
568                     localGlobalSummary(qualityValueToString(local), qualityValueToString(global)));
569             local = mDPM.getPasswordMinimumLength(mDeviceAdminSample);
570             global = mDPM.getPasswordMinimumLength(null);
571             mMinLength.setSummary(localGlobalSummary(local, global));
572             local = mDPM.getPasswordMinimumLetters(mDeviceAdminSample);
573             global = mDPM.getPasswordMinimumLetters(null);
574             mMinLetters.setSummary(localGlobalSummary(local, global));
575             local = mDPM.getPasswordMinimumNumeric(mDeviceAdminSample);
576             global = mDPM.getPasswordMinimumNumeric(null);
577             mMinNumeric.setSummary(localGlobalSummary(local, global));
578             local = mDPM.getPasswordMinimumLowerCase(mDeviceAdminSample);
579             global = mDPM.getPasswordMinimumLowerCase(null);
580             mMinLowerCase.setSummary(localGlobalSummary(local, global));
581             local = mDPM.getPasswordMinimumUpperCase(mDeviceAdminSample);
582             global = mDPM.getPasswordMinimumUpperCase(null);
583             mMinUpperCase.setSummary(localGlobalSummary(local, global));
584             local = mDPM.getPasswordMinimumSymbols(mDeviceAdminSample);
585             global = mDPM.getPasswordMinimumSymbols(null);
586             mMinSymbols.setSummary(localGlobalSummary(local, global));
587             local = mDPM.getPasswordMinimumNonLetter(mDeviceAdminSample);
588             global = mDPM.getPasswordMinimumNonLetter(null);
589             mMinNonLetter.setSummary(localGlobalSummary(local, global));
590         }
591 
592         @Override
onPreferenceChange(Preference preference, Object newValue)593         public boolean onPreferenceChange(Preference preference, Object newValue) {
594             if (super.onPreferenceChange(preference, newValue)) {
595                 return true;
596             }
597             String valueString = (String)newValue;
598             if (TextUtils.isEmpty(valueString)) {
599                 return false;
600             }
601             int value = 0;
602             try {
603                 value = Integer.parseInt(valueString);
604             } catch (NumberFormatException nfe) {
605                 String warning = mActivity.getString(R.string.number_format_warning, valueString);
606                 Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
607             }
608             if (preference == mPasswordQuality) {
609                 mDPM.setPasswordQuality(mDeviceAdminSample, value);
610             } else if (preference == mMinLength) {
611                 mDPM.setPasswordMinimumLength(mDeviceAdminSample, value);
612             } else if (preference == mMinLetters) {
613                 mDPM.setPasswordMinimumLetters(mDeviceAdminSample, value);
614             } else if (preference == mMinNumeric) {
615                 mDPM.setPasswordMinimumNumeric(mDeviceAdminSample, value);
616             } else if (preference == mMinLowerCase) {
617                 mDPM.setPasswordMinimumLowerCase(mDeviceAdminSample, value);
618             } else if (preference == mMinUpperCase) {
619                 mDPM.setPasswordMinimumUpperCase(mDeviceAdminSample, value);
620             } else if (preference == mMinSymbols) {
621                 mDPM.setPasswordMinimumSymbols(mDeviceAdminSample, value);
622             } else if (preference == mMinNonLetter) {
623                 mDPM.setPasswordMinimumNonLetter(mDeviceAdminSample, value);
624             }
625             // Delay update because the change is only applied after exiting this method.
626             postReloadSummaries();
627             return true;
628         }
629 
qualityValueToString(int quality)630         private String qualityValueToString(int quality) {
631             for (int i=  0; i < mPasswordQualityValues.length; i++) {
632                 if (mPasswordQualityValues[i] == quality) {
633                     String[] qualities =
634                         mActivity.getResources().getStringArray(R.array.password_qualities);
635                     return qualities[i];
636                 }
637             }
638             return "(0x" + Integer.toString(quality, 16) + ")";
639         }
640     }
641 
642     /**
643      * PreferenceFragment for "password expiration" preferences.
644      */
645     public static class ExpirationFragment extends AdminSampleFragment
646             implements OnPreferenceChangeListener, OnPreferenceClickListener {
647         private PreferenceCategory mExpirationCategory;
648         private EditTextPreference mHistory;
649         private EditTextPreference mExpirationTimeout;
650         private PreferenceScreen mExpirationStatus;
651 
652         @Override
onCreate(Bundle savedInstanceState)653         public void onCreate(Bundle savedInstanceState) {
654             super.onCreate(savedInstanceState);
655             addPreferencesFromResource(R.xml.device_admin_expiration);
656 
657             mExpirationCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_EXPIRATION);
658             mHistory = (EditTextPreference) findPreference(KEY_HISTORY);
659             mExpirationTimeout = (EditTextPreference) findPreference(KEY_EXPIRATION_TIMEOUT);
660             mExpirationStatus = (PreferenceScreen) findPreference(KEY_EXPIRATION_STATUS);
661 
662             mHistory.setOnPreferenceChangeListener(this);
663             mExpirationTimeout.setOnPreferenceChangeListener(this);
664             mExpirationStatus.setOnPreferenceClickListener(this);
665         }
666 
667         @Override
onResume()668         public void onResume() {
669             super.onResume();
670             mExpirationCategory.setEnabled(mAdminActive);
671         }
672 
673         /**
674          * Update the summaries of each item to show the local setting and the global setting.
675          */
676         @Override
reloadSummaries()677         protected void reloadSummaries() {
678             super.reloadSummaries();
679 
680             int local, global;
681             local = mDPM.getPasswordHistoryLength(mDeviceAdminSample);
682             global = mDPM.getPasswordHistoryLength(null);
683             mHistory.setSummary(localGlobalSummary(local, global));
684 
685             long localLong, globalLong;
686             localLong = mDPM.getPasswordExpirationTimeout(mDeviceAdminSample);
687             globalLong = mDPM.getPasswordExpirationTimeout(null);
688             mExpirationTimeout.setSummary(localGlobalSummary(
689                     localLong / MS_PER_MINUTE, globalLong / MS_PER_MINUTE));
690 
691             String expirationStatus = getExpirationStatus();
692             mExpirationStatus.setSummary(expirationStatus);
693         }
694 
695         @Override
onPreferenceChange(Preference preference, Object newValue)696         public boolean onPreferenceChange(Preference preference, Object newValue) {
697             if (super.onPreferenceChange(preference, newValue)) {
698                 return true;
699             }
700             String valueString = (String)newValue;
701             if (TextUtils.isEmpty(valueString)) {
702                 return false;
703             }
704             int value = 0;
705             try {
706                 value = Integer.parseInt(valueString);
707             } catch (NumberFormatException nfe) {
708                 String warning = mActivity.getString(R.string.number_format_warning, valueString);
709                 Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
710             }
711             if (preference == mHistory) {
712                 mDPM.setPasswordHistoryLength(mDeviceAdminSample, value);
713             } else if (preference == mExpirationTimeout) {
714                 mDPM.setPasswordExpirationTimeout(mDeviceAdminSample, value * MS_PER_MINUTE);
715             }
716             // Delay update because the change is only applied after exiting this method.
717             postReloadSummaries();
718             return true;
719         }
720 
721         @Override
onPreferenceClick(Preference preference)722         public boolean onPreferenceClick(Preference preference) {
723             if (super.onPreferenceClick(preference)) {
724                 return true;
725             }
726             if (preference == mExpirationStatus) {
727                 String expirationStatus = getExpirationStatus();
728                 mExpirationStatus.setSummary(expirationStatus);
729                 return true;
730             }
731             return false;
732         }
733 
734         /**
735          * Create a summary string describing the expiration status for the sample app,
736          * as well as the global (aggregate) status.
737          */
getExpirationStatus()738         private String getExpirationStatus() {
739             // expirations are absolute;  convert to relative for display
740             long localExpiration = mDPM.getPasswordExpiration(mDeviceAdminSample);
741             long globalExpiration = mDPM.getPasswordExpiration(null);
742             long now = System.currentTimeMillis();
743 
744             // local expiration
745             String local;
746             if (localExpiration == 0) {
747                 local = mActivity.getString(R.string.expiration_status_none);
748             } else {
749                 localExpiration -= now;
750                 String dms = timeToDaysMinutesSeconds(mActivity, Math.abs(localExpiration));
751                 if (localExpiration >= 0) {
752                     local = mActivity.getString(R.string.expiration_status_future, dms);
753                 } else {
754                     local = mActivity.getString(R.string.expiration_status_past, dms);
755                 }
756             }
757 
758             // global expiration
759             String global;
760             if (globalExpiration == 0) {
761                 global = mActivity.getString(R.string.expiration_status_none);
762             } else {
763                 globalExpiration -= now;
764                 String dms = timeToDaysMinutesSeconds(mActivity, Math.abs(globalExpiration));
765                 if (globalExpiration >= 0) {
766                     global = mActivity.getString(R.string.expiration_status_future, dms);
767                 } else {
768                     global = mActivity.getString(R.string.expiration_status_past, dms);
769                 }
770             }
771             return mActivity.getString(R.string.status_local_global, local, global);
772         }
773     }
774 
775     /**
776      * PreferenceFragment for "lock screen & wipe" preferences.
777      */
778     public static class LockWipeFragment extends AdminSampleFragment
779             implements OnPreferenceChangeListener, OnPreferenceClickListener {
780         private PreferenceCategory mLockWipeCategory;
781         private EditTextPreference mMaxTimeScreenLock;
782         private EditTextPreference mMaxFailures;
783         private PreferenceScreen mLockScreen;
784         private PreferenceScreen mWipeData;
785         private PreferenceScreen mWipeAppData;
786 
787         @Override
onCreate(Bundle savedInstanceState)788         public void onCreate(Bundle savedInstanceState) {
789             super.onCreate(savedInstanceState);
790             addPreferencesFromResource(R.xml.device_admin_lock_wipe);
791 
792             mLockWipeCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_LOCK_WIPE);
793             mMaxTimeScreenLock = (EditTextPreference) findPreference(KEY_MAX_TIME_SCREEN_LOCK);
794             mMaxFailures = (EditTextPreference) findPreference(KEY_MAX_FAILS_BEFORE_WIPE);
795             mLockScreen = (PreferenceScreen) findPreference(KEY_LOCK_SCREEN);
796             mWipeData = (PreferenceScreen) findPreference(KEY_WIPE_DATA);
797             mWipeAppData = (PreferenceScreen) findPreference(KEY_WIP_DATA_ALL);
798 
799             mMaxTimeScreenLock.setOnPreferenceChangeListener(this);
800             mMaxFailures.setOnPreferenceChangeListener(this);
801             mLockScreen.setOnPreferenceClickListener(this);
802             mWipeData.setOnPreferenceClickListener(this);
803             mWipeAppData.setOnPreferenceClickListener(this);
804         }
805 
806         @Override
onResume()807         public void onResume() {
808             super.onResume();
809             mLockWipeCategory.setEnabled(mAdminActive);
810         }
811 
812         /**
813          * Update the summaries of each item to show the local setting and the global setting.
814          */
815         @Override
reloadSummaries()816         protected void reloadSummaries() {
817             super.reloadSummaries();
818 
819             long localLong, globalLong;
820             localLong = mDPM.getMaximumTimeToLock(mDeviceAdminSample);
821             globalLong = mDPM.getMaximumTimeToLock(null);
822             mMaxTimeScreenLock.setSummary(localGlobalSummary(
823                     localLong / MS_PER_MINUTE, globalLong / MS_PER_MINUTE));
824 
825             int local, global;
826             local = mDPM.getMaximumFailedPasswordsForWipe(mDeviceAdminSample);
827             global = mDPM.getMaximumFailedPasswordsForWipe(null);
828             mMaxFailures.setSummary(localGlobalSummary(local, global));
829         }
830 
831         @Override
onPreferenceChange(Preference preference, Object newValue)832         public boolean onPreferenceChange(Preference preference, Object newValue) {
833             if (super.onPreferenceChange(preference, newValue)) {
834                 return true;
835             }
836             String valueString = (String)newValue;
837             if (TextUtils.isEmpty(valueString)) {
838                 return false;
839             }
840             int value = 0;
841             try {
842                 value = Integer.parseInt(valueString);
843             } catch (NumberFormatException nfe) {
844                 String warning = mActivity.getString(R.string.number_format_warning, valueString);
845                 Toast.makeText(mActivity, warning, Toast.LENGTH_SHORT).show();
846             }
847             if (preference == mMaxTimeScreenLock) {
848                 mDPM.setMaximumTimeToLock(mDeviceAdminSample, value * MS_PER_MINUTE);
849             } else if (preference == mMaxFailures) {
850                 if (alertIfMonkey(mActivity, R.string.monkey_wipe_data)) {
851                     return true;
852                 }
853                 mDPM.setMaximumFailedPasswordsForWipe(mDeviceAdminSample, value);
854             }
855             // Delay update because the change is only applied after exiting this method.
856             postReloadSummaries();
857             return true;
858         }
859 
860         @Override
onPreferenceClick(Preference preference)861         public boolean onPreferenceClick(Preference preference) {
862             if (super.onPreferenceClick(preference)) {
863                 return true;
864             }
865             if (preference == mLockScreen) {
866                 if (alertIfMonkey(mActivity, R.string.monkey_lock_screen)) {
867                     return true;
868                 }
869                 mDPM.lockNow();
870                 return true;
871             } else if (preference == mWipeData || preference == mWipeAppData) {
872                 if (alertIfMonkey(mActivity, R.string.monkey_wipe_data)) {
873                     return true;
874                 }
875                 promptForRealDeviceWipe(preference == mWipeAppData);
876                 return true;
877             }
878             return false;
879         }
880 
881         /**
882          * Wiping data is real, so we don't want it to be easy.  Show two alerts before wiping.
883          */
promptForRealDeviceWipe(final boolean wipeAllData)884         private void promptForRealDeviceWipe(final boolean wipeAllData) {
885             final DeviceAdminSample activity = mActivity;
886 
887             AlertDialog.Builder builder = new AlertDialog.Builder(activity);
888             builder.setMessage(R.string.wipe_warning_first);
889             builder.setPositiveButton(R.string.wipe_warning_first_ok,
890                     new DialogInterface.OnClickListener() {
891                 @Override
892                 public void onClick(DialogInterface dialog, int which) {
893                     AlertDialog.Builder builder = new AlertDialog.Builder(activity);
894                     if (wipeAllData) {
895                         builder.setMessage(R.string.wipe_warning_second_full);
896                     } else {
897                         builder.setMessage(R.string.wipe_warning_second);
898                     }
899                     builder.setPositiveButton(R.string.wipe_warning_second_ok,
900                             new DialogInterface.OnClickListener() {
901                         @Override
902                         public void onClick(DialogInterface dialog, int which) {
903                             boolean stillActive = mActivity.isActiveAdmin();
904                             if (stillActive) {
905                                 mDPM.wipeData(wipeAllData
906                                         ? DevicePolicyManager.WIPE_EXTERNAL_STORAGE : 0);
907                             }
908                         }
909                     });
910                     builder.setNegativeButton(R.string.wipe_warning_second_no, null);
911                     builder.show();
912                 }
913             });
914             builder.setNegativeButton(R.string.wipe_warning_first_no, null);
915             builder.show();
916         }
917     }
918 
919     /**
920      * PreferenceFragment for "encryption" preferences.
921      */
922     public static class EncryptionFragment extends AdminSampleFragment
923             implements OnPreferenceChangeListener, OnPreferenceClickListener {
924         private PreferenceCategory mEncryptionCategory;
925         private CheckBoxPreference mRequireEncryption;
926         private PreferenceScreen mActivateEncryption;
927 
928         @Override
onCreate(Bundle savedInstanceState)929         public void onCreate(Bundle savedInstanceState) {
930             super.onCreate(savedInstanceState);
931             addPreferencesFromResource(R.xml.device_admin_encryption);
932 
933             mEncryptionCategory = (PreferenceCategory) findPreference(KEY_CATEGORY_ENCRYPTION);
934             mRequireEncryption = (CheckBoxPreference) findPreference(KEY_REQUIRE_ENCRYPTION);
935             mActivateEncryption = (PreferenceScreen) findPreference(KEY_ACTIVATE_ENCRYPTION);
936 
937             mRequireEncryption.setOnPreferenceChangeListener(this);
938             mActivateEncryption.setOnPreferenceClickListener(this);
939         }
940 
941         @Override
onResume()942         public void onResume() {
943             super.onResume();
944             mEncryptionCategory.setEnabled(mAdminActive);
945             mRequireEncryption.setChecked(mDPM.getStorageEncryption(mDeviceAdminSample));
946         }
947 
948         /**
949          * Update the summaries of each item to show the local setting and the global setting.
950          */
951         @Override
reloadSummaries()952         protected void reloadSummaries() {
953             super.reloadSummaries();
954 
955             boolean local, global;
956             local = mDPM.getStorageEncryption(mDeviceAdminSample);
957             global = mDPM.getStorageEncryption(null);
958             mRequireEncryption.setSummary(localGlobalSummary(local, global));
959 
960             int deviceStatusCode = mDPM.getStorageEncryptionStatus();
961             String deviceStatus = statusCodeToString(deviceStatusCode);
962             String status = mActivity.getString(R.string.status_device_encryption, deviceStatus);
963             mActivateEncryption.setSummary(status);
964         }
965 
966         @Override
onPreferenceChange(Preference preference, Object newValue)967         public boolean onPreferenceChange(Preference preference, Object newValue) {
968             if (super.onPreferenceChange(preference, newValue)) {
969                 return true;
970             }
971             if (preference == mRequireEncryption) {
972                 boolean newActive = (Boolean) newValue;
973                 mDPM.setStorageEncryption(mDeviceAdminSample, newActive);
974                 // Delay update because the change is only applied after exiting this method.
975                 postReloadSummaries();
976                 return true;
977             }
978             return true;
979         }
980 
981         @Override
onPreferenceClick(Preference preference)982         public boolean onPreferenceClick(Preference preference) {
983             if (super.onPreferenceClick(preference)) {
984                 return true;
985             }
986             if (preference == mActivateEncryption) {
987                 if (alertIfMonkey(mActivity, R.string.monkey_encryption)) {
988                     return true;
989                 }
990                 // Check to see if encryption is even supported on this device (it's optional).
991                 if (mDPM.getStorageEncryptionStatus() ==
992                         DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
993                     AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
994                     builder.setMessage(R.string.encryption_not_supported);
995                     builder.setPositiveButton(R.string.encryption_not_supported_ok, null);
996                     builder.show();
997                     return true;
998                 }
999                 // Launch the activity to activate encryption.  May or may not return!
1000                 Intent intent = new Intent(DevicePolicyManager.ACTION_START_ENCRYPTION);
1001                 startActivityForResult(intent, REQUEST_CODE_START_ENCRYPTION);
1002                 return true;
1003             }
1004             return false;
1005         }
1006 
statusCodeToString(int newStatusCode)1007         private String statusCodeToString(int newStatusCode) {
1008             int newStatus = R.string.encryption_status_unknown;
1009             switch (newStatusCode) {
1010                 case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED:
1011                     newStatus = R.string.encryption_status_unsupported;
1012                     break;
1013                 case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE:
1014                     newStatus = R.string.encryption_status_inactive;
1015                     break;
1016                 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING:
1017                     newStatus = R.string.encryption_status_activating;
1018                     break;
1019                 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
1020                     newStatus = R.string.encryption_status_active;
1021                     break;
1022             }
1023             return mActivity.getString(newStatus);
1024         }
1025     }
1026 
1027     /**
1028      * Simple converter used for long expiration times reported in mSec.
1029      */
timeToDaysMinutesSeconds(Context context, long time)1030     private static String timeToDaysMinutesSeconds(Context context, long time) {
1031         long days = time / MS_PER_DAY;
1032         long hours = (time / MS_PER_HOUR) % 24;
1033         long minutes = (time / MS_PER_MINUTE) % 60;
1034         return context.getString(R.string.status_days_hours_minutes, days, hours, minutes);
1035     }
1036 
1037     /**
1038      * If the "user" is a monkey, post an alert and notify the caller.  This prevents automated
1039      * test frameworks from stumbling into annoying or dangerous operations.
1040      */
alertIfMonkey(Context context, int stringId)1041     private static boolean alertIfMonkey(Context context, int stringId) {
1042         if (ActivityManager.isUserAMonkey()) {
1043             AlertDialog.Builder builder = new AlertDialog.Builder(context);
1044             builder.setMessage(stringId);
1045             builder.setPositiveButton(R.string.monkey_ok, null);
1046             builder.show();
1047             return true;
1048         } else {
1049             return false;
1050         }
1051     }
1052 
1053     /**
1054      * Sample implementation of a DeviceAdminReceiver.  Your controller must provide one,
1055      * although you may or may not implement all of the methods shown here.
1056      *
1057      * All callbacks are on the UI thread and your implementations should not engage in any
1058      * blocking operations, including disk I/O.
1059      */
1060     public static class DeviceAdminSampleReceiver extends DeviceAdminReceiver {
showToast(Context context, String msg)1061         void showToast(Context context, String msg) {
1062             String status = context.getString(R.string.admin_receiver_status, msg);
1063             Toast.makeText(context, status, Toast.LENGTH_SHORT).show();
1064         }
1065 
1066         @Override
onReceive(Context context, Intent intent)1067         public void onReceive(Context context, Intent intent) {
1068             if (intent.getAction() == ACTION_DEVICE_ADMIN_DISABLE_REQUESTED) {
1069                 abortBroadcast();
1070             }
1071             super.onReceive(context, intent);
1072         }
1073 
1074         @Override
onEnabled(Context context, Intent intent)1075         public void onEnabled(Context context, Intent intent) {
1076             showToast(context, context.getString(R.string.admin_receiver_status_enabled));
1077         }
1078 
1079         @Override
onDisableRequested(Context context, Intent intent)1080         public CharSequence onDisableRequested(Context context, Intent intent) {
1081             return context.getString(R.string.admin_receiver_status_disable_warning);
1082         }
1083 
1084         @Override
onDisabled(Context context, Intent intent)1085         public void onDisabled(Context context, Intent intent) {
1086             showToast(context, context.getString(R.string.admin_receiver_status_disabled));
1087         }
1088 
1089         @Override
onPasswordChanged(Context context, Intent intent)1090         public void onPasswordChanged(Context context, Intent intent) {
1091             showToast(context, context.getString(R.string.admin_receiver_status_pw_changed));
1092         }
1093 
1094         @Override
onPasswordFailed(Context context, Intent intent)1095         public void onPasswordFailed(Context context, Intent intent) {
1096             showToast(context, context.getString(R.string.admin_receiver_status_pw_failed));
1097         }
1098 
1099         @Override
onPasswordSucceeded(Context context, Intent intent)1100         public void onPasswordSucceeded(Context context, Intent intent) {
1101             showToast(context, context.getString(R.string.admin_receiver_status_pw_succeeded));
1102         }
1103 
1104         @Override
onPasswordExpiring(Context context, Intent intent)1105         public void onPasswordExpiring(Context context, Intent intent) {
1106             DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
1107                     Context.DEVICE_POLICY_SERVICE);
1108             long expr = dpm.getPasswordExpiration(
1109                     new ComponentName(context, DeviceAdminSampleReceiver.class));
1110             long delta = expr - System.currentTimeMillis();
1111             boolean expired = delta < 0L;
1112             String message = context.getString(expired ?
1113                     R.string.expiration_status_past : R.string.expiration_status_future);
1114             showToast(context, message);
1115             Log.v(TAG, message);
1116         }
1117     }
1118 }
1119