Home | History | Annotate | Download | only in setup
      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 
     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      */
    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      */
    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
    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
    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
    198     protected void onPause() {
    199         super.onPause();
    200         mActivityResumed = false;
    201     }
    202 
    203     @Override
    204     protected void onResume() {
    205         super.onResume();
    206         mActivityResumed = true;
    207         tickleAccountAndPolicyLoader();
    208     }
    209 
    210     protected boolean isActivityResumed() {
    211         return mActivityResumed;
    212     }
    213 
    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 
    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
    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
    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
    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 
    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
    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
    311         protected void onDiscardResult(Account result) {}
    312     }
    313 
    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
    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      */
    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      */
    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
    509         public SecurityNeededDialog() {}
    510 
    511         /**
    512          * Create a new dialog.
    513          */
    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
    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
    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          */
    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
    590         public PasswordExpirationDialog() {}
    591 
    592         /**
    593          * Note, this actually creates two slightly different dialogs (for expiring vs. expired)
    594          */
    595         @Override
    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
    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