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
17package com.android.email.activity.setup;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
22import android.app.DialogFragment;
23import android.app.FragmentManager;
24import android.app.LoaderManager;
25import android.app.admin.DevicePolicyManager;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.Loader;
30import android.content.res.Resources;
31import android.net.Uri;
32import android.os.AsyncTask;
33import android.os.Bundle;
34import android.os.Handler;
35import android.text.TextUtils;
36
37import com.android.email.DebugUtils;
38import com.android.email.R;
39import com.android.email.SecurityPolicy;
40import com.android.emailcommon.provider.Account;
41import com.android.emailcommon.provider.EmailContent;
42import com.android.emailcommon.provider.HostAuth;
43import com.android.emailcommon.provider.Policy;
44import com.android.emailcommon.utility.IntentUtilities;
45import com.android.mail.ui.MailAsyncTaskLoader;
46import 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 */
59public 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