• 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.android.email.activity.setup;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.Dialog;
22 import android.app.DialogFragment;
23 import android.app.FragmentManager;
24 import android.app.LoaderManager;
25 import android.app.admin.DevicePolicyManager;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.Loader;
30 import android.content.res.Resources;
31 import android.net.Uri;
32 import android.os.AsyncTask;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.text.TextUtils;
36 
37 import com.android.email.DebugUtils;
38 import com.android.email.R;
39 import com.android.email.SecurityPolicy;
40 import com.android.emailcommon.provider.Account;
41 import com.android.emailcommon.provider.EmailContent;
42 import com.android.emailcommon.provider.HostAuth;
43 import com.android.emailcommon.provider.Policy;
44 import com.android.emailcommon.utility.IntentUtilities;
45 import com.android.mail.ui.MailAsyncTaskLoader;
46 import com.android.mail.utils.LogUtils;
47 
48 /**
49  * Psuedo-activity (no UI) to bootstrap the user up to a higher desired security level.  This
50  * bootstrap requires the following steps.
51  *
52  * 1.  Confirm the account of interest has any security policies defined - exit early if not
53  * 2.  If not actively administrating the device, ask Device Policy Manager to start that
54  * 3.  When we are actively administrating, check current policies and see if they're sufficient
55  * 4.  If not, set policies
56  * 5.  If necessary, request for user to update device password
57  * 6.  If necessary, request for user to activate device encryption
58  */
59 public class AccountSecurity extends Activity {
60     private static final String TAG = "Email/AccountSecurity";
61 
62     private static final boolean DEBUG = false;  // Don't ship with this set to true
63 
64     private static final String EXTRA_ACCOUNT_ID = "ACCOUNT_ID";
65     private static final String EXTRA_SHOW_DIALOG = "SHOW_DIALOG";
66     private static final String EXTRA_PASSWORD_EXPIRING = "EXPIRING";
67     private static final String EXTRA_PASSWORD_EXPIRED = "EXPIRED";
68 
69     private static final String SAVESTATE_INITIALIZED_TAG = "initialized";
70     private static final String SAVESTATE_TRIED_ADD_ADMINISTRATOR_TAG = "triedAddAdministrator";
71     private static final String SAVESTATE_TRIED_SET_PASSWORD_TAG = "triedSetpassword";
72     private static final String SAVESTATE_TRIED_SET_ENCRYPTION_TAG = "triedSetEncryption";
73     private static final String SAVESTATE_ACCOUNT_TAG = "account";
74 
75     private static final int REQUEST_ENABLE = 1;
76     private static final int REQUEST_PASSWORD = 2;
77     private static final int REQUEST_ENCRYPTION = 3;
78 
79     private boolean mTriedAddAdministrator;
80     private boolean mTriedSetPassword;
81     private boolean mTriedSetEncryption;
82 
83     private Account mAccount;
84 
85     protected boolean mInitialized;
86 
87     private Handler mHandler;
88     private boolean mActivityResumed;
89 
90     private static final int ACCOUNT_POLICY_LOADER_ID = 0;
91     private AccountAndPolicyLoaderCallbacks mAPLoaderCallbacks;
92     private Bundle mAPLoaderArgs;
93 
getUpdateSecurityUri(final long accountId, final boolean showDialog)94     public static Uri getUpdateSecurityUri(final long accountId, final boolean showDialog) {
95         final Uri.Builder baseUri = Uri.parse("auth://" + EmailContent.EMAIL_PACKAGE_NAME +
96                 ".ACCOUNT_SECURITY/").buildUpon();
97         IntentUtilities.setAccountId(baseUri, accountId);
98         baseUri.appendQueryParameter(EXTRA_SHOW_DIALOG, Boolean.toString(showDialog));
99         return baseUri.build();
100     }
101 
102     /**
103      * Used for generating intent for this activity (which is intended to be launched
104      * from a notification.)
105      *
106      * @param context Calling context for building the intent
107      * @param accountId The account of interest
108      * @param showDialog If true, a simple warning dialog will be shown before kicking off
109      * the necessary system settings.  Should be true anywhere the context of the security settings
110      * is not clear (e.g. any time after the account has been set up).
111      * @return an Intent which can be used to view that account
112      */
actionUpdateSecurityIntent(Context context, long accountId, boolean showDialog)113     public static Intent actionUpdateSecurityIntent(Context context, long accountId,
114             boolean showDialog) {
115         Intent intent = new Intent(context, AccountSecurity.class);
116         intent.putExtra(EXTRA_ACCOUNT_ID, accountId);
117         intent.putExtra(EXTRA_SHOW_DIALOG, showDialog);
118         return intent;
119     }
120 
121     /**
122      * Used for generating intent for this activity (which is intended to be launched
123      * from a notification.)  This is a special mode of this activity which exists only
124      * to give the user a dialog (for context) about a device pin/password expiration event.
125      */
actionDevicePasswordExpirationIntent(Context context, long accountId, boolean expired)126     public static Intent actionDevicePasswordExpirationIntent(Context context, long accountId,
127             boolean expired) {
128         Intent intent = new ForwardingIntent(context, AccountSecurity.class);
129         intent.putExtra(EXTRA_ACCOUNT_ID, accountId);
130         intent.putExtra(expired ? EXTRA_PASSWORD_EXPIRED : EXTRA_PASSWORD_EXPIRING, true);
131         return intent;
132     }
133 
134     @Override
onCreate(Bundle savedInstanceState)135     public void onCreate(Bundle savedInstanceState) {
136         super.onCreate(savedInstanceState);
137 
138         mHandler = new Handler();
139 
140         final Intent i = getIntent();
141         final long accountId;
142         Bundle extras = i.getExtras();
143         if (extras == null) {
144             // We have been invoked via a uri. We need to get our parameters from the URI instead
145             // of looking in the intent extras.
146             extras = new Bundle();
147             accountId = IntentUtilities.getAccountIdFromIntent(i);
148             extras.putLong(EXTRA_ACCOUNT_ID, accountId);
149             boolean showDialog = false;
150             final String value = i.getData().getQueryParameter(EXTRA_SHOW_DIALOG);
151             if (!TextUtils.isEmpty(value)) {
152                 showDialog = Boolean.getBoolean(value);
153             }
154             extras.putBoolean(EXTRA_SHOW_DIALOG, showDialog);
155         } else {
156             accountId = i.getLongExtra(EXTRA_ACCOUNT_ID, -1);
157             extras = i.getExtras();
158         }
159 
160         final SecurityPolicy security = SecurityPolicy.getInstance(this);
161         security.clearNotification();
162         if (accountId == -1) {
163             finish();
164             return;
165         }
166 
167         if (savedInstanceState != null) {
168             mInitialized = savedInstanceState.getBoolean(SAVESTATE_INITIALIZED_TAG, false);
169 
170             mTriedAddAdministrator =
171                     savedInstanceState.getBoolean(SAVESTATE_TRIED_ADD_ADMINISTRATOR_TAG, false);
172             mTriedSetPassword =
173                     savedInstanceState.getBoolean(SAVESTATE_TRIED_SET_PASSWORD_TAG, false);
174             mTriedSetEncryption =
175                     savedInstanceState.getBoolean(SAVESTATE_TRIED_SET_ENCRYPTION_TAG, false);
176 
177             mAccount = savedInstanceState.getParcelable(SAVESTATE_ACCOUNT_TAG);
178         }
179 
180         if (!mInitialized) {
181             startAccountAndPolicyLoader(extras);
182         }
183     }
184 
185     @Override
onSaveInstanceState(final Bundle outState)186     protected void onSaveInstanceState(final Bundle outState) {
187         super.onSaveInstanceState(outState);
188         outState.putBoolean(SAVESTATE_INITIALIZED_TAG, mInitialized);
189 
190         outState.putBoolean(SAVESTATE_TRIED_ADD_ADMINISTRATOR_TAG, mTriedAddAdministrator);
191         outState.putBoolean(SAVESTATE_TRIED_SET_PASSWORD_TAG, mTriedSetPassword);
192         outState.putBoolean(SAVESTATE_TRIED_SET_ENCRYPTION_TAG, mTriedSetEncryption);
193 
194         outState.putParcelable(SAVESTATE_ACCOUNT_TAG, mAccount);
195     }
196 
197     @Override
onPause()198     protected void onPause() {
199         super.onPause();
200         mActivityResumed = false;
201     }
202 
203     @Override
onResume()204     protected void onResume() {
205         super.onResume();
206         mActivityResumed = true;
207         tickleAccountAndPolicyLoader();
208     }
209 
isActivityResumed()210     protected boolean isActivityResumed() {
211         return mActivityResumed;
212     }
213 
tickleAccountAndPolicyLoader()214     private void tickleAccountAndPolicyLoader() {
215         // If we're already initialized we don't need to tickle.
216         if (!mInitialized) {
217             getLoaderManager().initLoader(ACCOUNT_POLICY_LOADER_ID, mAPLoaderArgs,
218                     mAPLoaderCallbacks);
219         }
220     }
221 
startAccountAndPolicyLoader(final Bundle args)222     private void startAccountAndPolicyLoader(final Bundle args) {
223         mAPLoaderArgs = args;
224         mAPLoaderCallbacks = new AccountAndPolicyLoaderCallbacks();
225         tickleAccountAndPolicyLoader();
226     }
227 
228     private class AccountAndPolicyLoaderCallbacks
229             implements LoaderManager.LoaderCallbacks<Account> {
230         @Override
onCreateLoader(final int id, final Bundle args)231         public Loader<Account> onCreateLoader(final int id, final Bundle args) {
232             final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
233             final boolean showDialog = args.getBoolean(EXTRA_SHOW_DIALOG, false);
234             final boolean passwordExpiring =
235                     args.getBoolean(EXTRA_PASSWORD_EXPIRING, false);
236             final boolean passwordExpired =
237                     args.getBoolean(EXTRA_PASSWORD_EXPIRED, false);
238 
239             return new AccountAndPolicyLoader(getApplicationContext(), accountId,
240                     showDialog, passwordExpiring, passwordExpired);
241         }
242 
243         @Override
onLoadFinished(final Loader<Account> loader, final Account account)244         public void onLoadFinished(final Loader<Account> loader, final Account account) {
245             mHandler.post(new Runnable() {
246                 @Override
247                 public void run() {
248                     final AccountSecurity activity = AccountSecurity.this;
249                     if (!activity.isActivityResumed()) {
250                         return;
251                     }
252 
253                     if (account == null || (account.mPolicyKey != 0 && account.mPolicy == null)) {
254                         activity.finish();
255                         LogUtils.d(TAG, "could not load account or policy in AccountSecurity");
256                         return;
257                     }
258 
259                     if (!activity.mInitialized) {
260                         activity.mInitialized = true;
261 
262                         final AccountAndPolicyLoader apLoader = (AccountAndPolicyLoader) loader;
263                         activity.completeCreate(account, apLoader.mShowDialog,
264                                 apLoader.mPasswordExpiring, apLoader.mPasswordExpired);
265                     }
266                 }
267             });
268         }
269 
270         @Override
onLoaderReset(Loader<Account> loader)271         public void onLoaderReset(Loader<Account> loader) {}
272     }
273 
274     private static class AccountAndPolicyLoader extends MailAsyncTaskLoader<Account> {
275         private final long mAccountId;
276         public final boolean mShowDialog;
277         public final boolean mPasswordExpiring;
278         public final boolean mPasswordExpired;
279 
280         private final Context mContext;
281 
AccountAndPolicyLoader(final Context context, final long accountId, final boolean showDialog, final boolean passwordExpiring, final boolean passwordExpired)282         AccountAndPolicyLoader(final Context context, final long accountId,
283                 final boolean showDialog, final boolean passwordExpiring,
284                 final boolean passwordExpired) {
285             super(context);
286             mContext = context;
287             mAccountId = accountId;
288             mShowDialog = showDialog;
289             mPasswordExpiring = passwordExpiring;
290             mPasswordExpired = passwordExpired;
291         }
292 
293         @Override
loadInBackground()294         public Account loadInBackground() {
295             final Account account = Account.restoreAccountWithId(mContext, mAccountId);
296             if (account == null) {
297                 return null;
298             }
299 
300             final long policyId = account.mPolicyKey;
301             if (policyId != 0) {
302                 account.mPolicy = Policy.restorePolicyWithId(mContext, policyId);
303             }
304 
305             account.getOrCreateHostAuthRecv(mContext);
306 
307             return account;
308         }
309 
310         @Override
onDiscardResult(Account result)311         protected void onDiscardResult(Account result) {}
312     }
313 
completeCreate(final Account account, final boolean showDialog, final boolean passwordExpiring, final boolean passwordExpired)314     protected void completeCreate(final Account account, final boolean showDialog,
315             final boolean passwordExpiring, final boolean passwordExpired) {
316         mAccount = account;
317 
318         // Special handling for password expiration events
319         if (passwordExpiring || passwordExpired) {
320             FragmentManager fm = getFragmentManager();
321             if (fm.findFragmentByTag("password_expiration") == null) {
322                 PasswordExpirationDialog dialog =
323                     PasswordExpirationDialog.newInstance(mAccount.getDisplayName(),
324                             passwordExpired);
325                 if (DebugUtils.DEBUG || DEBUG) {
326                     LogUtils.d(TAG, "Showing password expiration dialog");
327                 }
328                 dialog.show(fm, "password_expiration");
329             }
330             return;
331         }
332         // Otherwise, handle normal security settings flow
333         if (mAccount.mPolicyKey != 0) {
334             // This account wants to control security
335             if (showDialog) {
336                 // Show dialog first, unless already showing (e.g. after rotation)
337                 FragmentManager fm = getFragmentManager();
338                 if (fm.findFragmentByTag("security_needed") == null) {
339                     SecurityNeededDialog dialog =
340                         SecurityNeededDialog.newInstance(mAccount.getDisplayName());
341                     if (DebugUtils.DEBUG || DEBUG) {
342                         LogUtils.d(TAG, "Showing security needed dialog");
343                     }
344                     dialog.show(fm, "security_needed");
345                 }
346             } else {
347                 // Go directly to security settings
348                 tryAdvanceSecurity(mAccount);
349             }
350             return;
351         }
352         finish();
353     }
354 
355     /**
356      * After any of the activities return, try to advance to the "next step"
357      */
358     @Override
onActivityResult(int requestCode, int resultCode, Intent data)359     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
360         tryAdvanceSecurity(mAccount);
361         super.onActivityResult(requestCode, resultCode, data);
362     }
363 
364     /**
365      * Walk the user through the required steps to become an active administrator and with
366      * the requisite security settings for the given account.
367      *
368      * These steps will be repeated each time we return from a given attempt (e.g. asking the
369      * user to choose a device pin/password).  In a typical activation, we may repeat these
370      * steps a few times.  It may go as far as step 5 (password) or step 6 (encryption), but it
371      * will terminate when step 2 (isActive()) succeeds.
372      *
373      * If at any point we do not advance beyond a given user step, (e.g. the user cancels
374      * instead of setting a password) we simply repost the security notification, and exit.
375      * We never want to loop here.
376      */
tryAdvanceSecurity(Account account)377     private void tryAdvanceSecurity(Account account) {
378         SecurityPolicy security = SecurityPolicy.getInstance(this);
379         // Step 1.  Check if we are an active device administrator, and stop here to activate
380         if (!security.isActiveAdmin()) {
381             if (mTriedAddAdministrator) {
382                 if (DebugUtils.DEBUG || DEBUG) {
383                     LogUtils.d(TAG, "Not active admin: repost notification");
384                 }
385                 repostNotification(account, security);
386                 finish();
387             } else {
388                 mTriedAddAdministrator = true;
389                 // retrieve name of server for the format string
390                 final HostAuth hostAuth = account.mHostAuthRecv;
391                 if (hostAuth == null) {
392                     if (DebugUtils.DEBUG || DEBUG) {
393                         LogUtils.d(TAG, "No HostAuth: repost notification");
394                     }
395                     repostNotification(account, security);
396                     finish();
397                 } else {
398                     if (DebugUtils.DEBUG || DEBUG) {
399                         LogUtils.d(TAG, "Not active admin: post initial notification");
400                     }
401                     // try to become active - must happen here in activity, to get result
402                     Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
403                     intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
404                             security.getAdminComponent());
405                     intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
406                             this.getString(R.string.account_security_policy_explanation_fmt,
407                                     hostAuth.mAddress));
408                     startActivityForResult(intent, REQUEST_ENABLE);
409                 }
410             }
411             return;
412         }
413 
414         // Step 2.  Check if the current aggregate security policy is being satisfied by the
415         // DevicePolicyManager (the current system security level).
416         if (security.isActive(null)) {
417             if (DebugUtils.DEBUG || DEBUG) {
418                 LogUtils.d(TAG, "Security active; clear holds");
419             }
420             Account.clearSecurityHoldOnAllAccounts(this);
421             security.syncAccount(account);
422             security.clearNotification();
423             finish();
424             return;
425         }
426 
427         // Step 3.  Try to assert the current aggregate security requirements with the system.
428         security.setActivePolicies();
429 
430         // Step 4.  Recheck the security policy, and determine what changes are needed (if any)
431         // to satisfy the requirements.
432         int inactiveReasons = security.getInactiveReasons(null);
433 
434         // Step 5.  If password is needed, try to have the user set it
435         if ((inactiveReasons & SecurityPolicy.INACTIVE_NEED_PASSWORD) != 0) {
436             if (mTriedSetPassword) {
437                 if (DebugUtils.DEBUG || DEBUG) {
438                     LogUtils.d(TAG, "Password needed; repost notification");
439                 }
440                 repostNotification(account, security);
441                 finish();
442             } else {
443                 if (DebugUtils.DEBUG || DEBUG) {
444                     LogUtils.d(TAG, "Password needed; request it via DPM");
445                 }
446                 mTriedSetPassword = true;
447                 // launch the activity to have the user set a new password.
448                 Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
449                 startActivityForResult(intent, REQUEST_PASSWORD);
450             }
451             return;
452         }
453 
454         // Step 6.  If encryption is needed, try to have the user set it
455         if ((inactiveReasons & SecurityPolicy.INACTIVE_NEED_ENCRYPTION) != 0) {
456             if (mTriedSetEncryption) {
457                 if (DebugUtils.DEBUG || DEBUG) {
458                     LogUtils.d(TAG, "Encryption needed; repost notification");
459                 }
460                 repostNotification(account, security);
461                 finish();
462             } else {
463                 if (DebugUtils.DEBUG || DEBUG) {
464                     LogUtils.d(TAG, "Encryption needed; request it via DPM");
465                 }
466                 mTriedSetEncryption = true;
467                 // launch the activity to start up encryption.
468                 Intent intent = new Intent(DevicePolicyManager.ACTION_START_ENCRYPTION);
469                 startActivityForResult(intent, REQUEST_ENCRYPTION);
470             }
471             return;
472         }
473 
474         // Step 7.  No problems were found, so clear holds and exit
475         if (DebugUtils.DEBUG || DEBUG) {
476             LogUtils.d(TAG, "Policies enforced; clear holds");
477         }
478         Account.clearSecurityHoldOnAllAccounts(this);
479         security.syncAccount(account);
480         security.clearNotification();
481         finish();
482     }
483 
484     /**
485      * Mark an account as not-ready-for-sync and post a notification to bring the user back here
486      * eventually.
487      */
repostNotification(final Account account, final SecurityPolicy security)488     private static void repostNotification(final Account account, final SecurityPolicy security) {
489         if (account == null) return;
490         new AsyncTask<Void, Void, Void>() {
491             @Override
492             protected Void doInBackground(Void... params) {
493                 security.policiesRequired(account.mId);
494                 return null;
495             }
496         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
497     }
498 
499     /**
500      * Dialog briefly shown in some cases, to indicate the user that a security update is needed.
501      * If the user clicks OK, we proceed into the "tryAdvanceSecurity" flow.  If the user cancels,
502      * we repost the notification and finish() the activity.
503      */
504     public static class SecurityNeededDialog extends DialogFragment
505             implements DialogInterface.OnClickListener {
506         private static final String BUNDLE_KEY_ACCOUNT_NAME = "account_name";
507 
508         // Public no-args constructor needed for fragment re-instantiation
SecurityNeededDialog()509         public SecurityNeededDialog() {}
510 
511         /**
512          * Create a new dialog.
513          */
newInstance(String accountName)514         public static SecurityNeededDialog newInstance(String accountName) {
515             final SecurityNeededDialog dialog = new SecurityNeededDialog();
516             Bundle b = new Bundle();
517             b.putString(BUNDLE_KEY_ACCOUNT_NAME, accountName);
518             dialog.setArguments(b);
519             return dialog;
520         }
521 
522         @Override
onCreateDialog(Bundle savedInstanceState)523         public Dialog onCreateDialog(Bundle savedInstanceState) {
524             final String accountName = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME);
525 
526             final Context context = getActivity();
527             final Resources res = context.getResources();
528             final AlertDialog.Builder b = new AlertDialog.Builder(context);
529             b.setTitle(R.string.account_security_dialog_title);
530             b.setIconAttribute(android.R.attr.alertDialogIcon);
531             b.setMessage(res.getString(R.string.account_security_dialog_content_fmt, accountName));
532             b.setPositiveButton(android.R.string.ok, this);
533             b.setNegativeButton(android.R.string.cancel, this);
534             if (DebugUtils.DEBUG || DEBUG) {
535                 LogUtils.d(TAG, "Posting security needed dialog");
536             }
537             return b.create();
538         }
539 
540         @Override
onClick(DialogInterface dialog, int which)541         public void onClick(DialogInterface dialog, int which) {
542             dismiss();
543             AccountSecurity activity = (AccountSecurity) getActivity();
544             if (activity.mAccount == null) {
545                 // Clicked before activity fully restored - probably just monkey - exit quickly
546                 activity.finish();
547                 return;
548             }
549             switch (which) {
550                 case DialogInterface.BUTTON_POSITIVE:
551                     if (DebugUtils.DEBUG || DEBUG) {
552                         LogUtils.d(TAG, "User accepts; advance to next step");
553                     }
554                     activity.tryAdvanceSecurity(activity.mAccount);
555                     break;
556                 case DialogInterface.BUTTON_NEGATIVE:
557                     if (DebugUtils.DEBUG || DEBUG) {
558                         LogUtils.d(TAG, "User declines; repost notification");
559                     }
560                     AccountSecurity.repostNotification(
561                             activity.mAccount, SecurityPolicy.getInstance(activity));
562                     activity.finish();
563                     break;
564             }
565         }
566     }
567 
568     /**
569      * Dialog briefly shown in some cases, to indicate the user that the PIN/Password is expiring
570      * or has expired.  If the user clicks OK, we launch the password settings screen.
571      */
572     public static class PasswordExpirationDialog extends DialogFragment
573             implements DialogInterface.OnClickListener {
574         private static final String BUNDLE_KEY_ACCOUNT_NAME = "account_name";
575         private static final String BUNDLE_KEY_EXPIRED = "expired";
576 
577         /**
578          * Create a new dialog.
579          */
newInstance(String accountName, boolean expired)580         public static PasswordExpirationDialog newInstance(String accountName, boolean expired) {
581             final PasswordExpirationDialog dialog = new PasswordExpirationDialog();
582             Bundle b = new Bundle();
583             b.putString(BUNDLE_KEY_ACCOUNT_NAME, accountName);
584             b.putBoolean(BUNDLE_KEY_EXPIRED, expired);
585             dialog.setArguments(b);
586             return dialog;
587         }
588 
589         // Public no-args constructor needed for fragment re-instantiation
PasswordExpirationDialog()590         public PasswordExpirationDialog() {}
591 
592         /**
593          * Note, this actually creates two slightly different dialogs (for expiring vs. expired)
594          */
595         @Override
onCreateDialog(Bundle savedInstanceState)596         public Dialog onCreateDialog(Bundle savedInstanceState) {
597             final String accountName = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME);
598             final boolean expired = getArguments().getBoolean(BUNDLE_KEY_EXPIRED);
599             final int titleId = expired
600                     ? R.string.password_expired_dialog_title
601                     : R.string.password_expire_warning_dialog_title;
602             final int contentId = expired
603                     ? R.string.password_expired_dialog_content_fmt
604                     : R.string.password_expire_warning_dialog_content_fmt;
605 
606             final Context context = getActivity();
607             final Resources res = context.getResources();
608             return new AlertDialog.Builder(context)
609                     .setTitle(titleId)
610                     .setIconAttribute(android.R.attr.alertDialogIcon)
611                     .setMessage(res.getString(contentId, accountName))
612                     .setPositiveButton(android.R.string.ok, this)
613                     .setNegativeButton(android.R.string.cancel, this)
614                     .create();
615         }
616 
617         @Override
onClick(DialogInterface dialog, int which)618         public void onClick(DialogInterface dialog, int which) {
619             dismiss();
620             AccountSecurity activity = (AccountSecurity) getActivity();
621             if (which == DialogInterface.BUTTON_POSITIVE) {
622                 Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
623                 activity.startActivity(intent);
624             }
625             activity.finish();
626         }
627     }
628 }
629