SecuritySettings.java revision 971b79e30667ce24b3e53db021edeb19df4748a9
1/*
2 * Copyright (C) 2007 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.settings;
18
19
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.app.Dialog;
23import android.content.ContentQueryMap;
24import android.content.ContentResolver;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.pm.PackageManager.NameNotFoundException;
29import android.database.Cursor;
30import android.location.LocationManager;
31import android.os.Bundle;
32import android.preference.CheckBoxPreference;
33import android.preference.EditTextPreference;
34import android.preference.Preference;
35import android.preference.PreferenceActivity;
36import android.preference.PreferenceCategory;
37import android.preference.PreferenceGroup;
38import android.preference.PreferenceScreen;
39import android.provider.Settings;
40import android.security.Keystore;
41import android.text.Html;
42import android.text.TextUtils;
43import android.text.method.LinkMovementMethod;
44import android.view.View;
45import android.widget.TextView;
46import android.widget.Toast;
47
48import com.android.internal.widget.LockPatternUtils;
49import android.telephony.TelephonyManager;
50
51import java.util.ArrayList;
52import java.util.List;
53import java.util.Observable;
54import java.util.Observer;
55
56/**
57 * Gesture lock pattern settings.
58 */
59public class SecuritySettings extends PreferenceActivity implements
60        DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
61
62    // Lock Settings
63
64    private static final String KEY_LOCK_ENABLED = "lockenabled";
65    private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
66    private static final String KEY_TACTILE_FEEDBACK_ENABLED = "tactilefeedback";
67    private static final int CONFIRM_PATTERN_THEN_DISABLE_AND_CLEAR_REQUEST_CODE = 55;
68
69    private LockPatternUtils mLockPatternUtils;
70    private CheckBoxPreference mLockEnabled;
71    private CheckBoxPreference mVisiblePattern;
72    private CheckBoxPreference mTactileFeedback;
73    private Preference mChoosePattern;
74
75    private CheckBoxPreference mShowPassword;
76
77    // Location Settings
78    private static final String LOCATION_CATEGORY = "location_category";
79    private static final String LOCATION_NETWORK = "location_network";
80    private static final String LOCATION_GPS = "location_gps";
81    private static final String ASSISTED_GPS = "assisted_gps";
82
83    // Credential storage
84    public static final String ACTION_ADD_CREDENTIAL =
85            "android.security.ADD_CREDENTIAL";
86    public static final String ACTION_UNLOCK_CREDENTIAL_STORAGE =
87            "android.security.UNLOCK_CREDENTIAL_STORAGE";
88    private static final String KEY_CSTOR_TYPE_NAME = "typeName";
89    private static final String KEY_CSTOR_ITEM = "item";
90    private static final String KEY_CSTOR_NAMESPACE = "namespace";
91    private static final String KEY_CSTOR_DESCRIPTION = "description";
92    private static final int CSTOR_MIN_PASSWORD_LENGTH = 8;
93
94    private static final int CSTOR_INIT_DIALOG = 1;
95    private static final int CSTOR_CHANGE_PASSWORD_DIALOG = 2;
96    private static final int CSTOR_UNLOCK_DIALOG = 3;
97    private static final int CSTOR_RESET_DIALOG = 4;
98    private static final int CSTOR_NAME_CREDENTIAL_DIALOG = 5;
99
100    private CstorHelper mCstorHelper = new CstorHelper();
101
102    // Vendor specific
103    private static final String GSETTINGS_PROVIDER = "com.google.android.providers.settings";
104    private static final String USE_LOCATION = "use_location";
105    private static final String KEY_DONE_USE_LOCATION = "doneLocation";
106    private CheckBoxPreference mUseLocation;
107    private boolean mOkClicked;
108    private Dialog mUseLocationDialog;
109
110    private CheckBoxPreference mNetwork;
111    private CheckBoxPreference mGps;
112    private CheckBoxPreference mAssistedGps;
113
114    // These provide support for receiving notification when Location Manager settings change.
115    // This is necessary because the Network Location Provider can change settings
116    // if the user does not confirm enabling the provider.
117    private ContentQueryMap mContentQueryMap;
118    private final class SettingsObserver implements Observer {
119        public void update(Observable o, Object arg) {
120            updateToggles();
121        }
122    }
123
124    @Override
125    protected void onCreate(Bundle savedInstanceState) {
126        super.onCreate(savedInstanceState);
127        addPreferencesFromResource(R.xml.security_settings);
128
129        mLockPatternUtils = new LockPatternUtils(getContentResolver());
130
131        createPreferenceHierarchy();
132
133        mNetwork = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_NETWORK);
134        mGps = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_GPS);
135        mAssistedGps = (CheckBoxPreference) getPreferenceScreen().findPreference(ASSISTED_GPS);
136        mUseLocation = (CheckBoxPreference) getPreferenceScreen().findPreference(USE_LOCATION);
137
138        // Vendor specific
139        try {
140            if (mUseLocation != null
141                    && getPackageManager().getPackageInfo(GSETTINGS_PROVIDER, 0) == null) {
142                ((PreferenceGroup)findPreference(LOCATION_CATEGORY))
143                        .removePreference(mUseLocation);
144            }
145        } catch (NameNotFoundException nnfe) {
146        }
147        updateToggles();
148
149        // listen for Location Manager settings changes
150        Cursor settingsCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, null,
151                "(" + Settings.System.NAME + "=?)",
152                new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
153                null);
154        mContentQueryMap = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, null);
155        mContentQueryMap.addObserver(new SettingsObserver());
156        boolean doneUseLocation = savedInstanceState != null
157                && savedInstanceState.getBoolean(KEY_DONE_USE_LOCATION, true);
158        if (getIntent().getBooleanExtra("SHOW_USE_LOCATION", false) && !doneUseLocation) {
159            showUseLocationDialog(true);
160        }
161
162        mCstorHelper.handleCstorIntents(getIntent());
163    }
164
165    private PreferenceScreen createPreferenceHierarchy() {
166        // Root
167        PreferenceScreen root = this.getPreferenceScreen();
168
169        // Inline preferences
170        PreferenceCategory inlinePrefCat = new PreferenceCategory(this);
171        inlinePrefCat.setTitle(R.string.lock_settings_title);
172        root.addPreference(inlinePrefCat);
173
174        // autolock toggle
175        mLockEnabled = new LockEnabledPref(this);
176        mLockEnabled.setTitle(R.string.lockpattern_settings_enable_title);
177        mLockEnabled.setSummary(R.string.lockpattern_settings_enable_summary);
178        mLockEnabled.setKey(KEY_LOCK_ENABLED);
179        inlinePrefCat.addPreference(mLockEnabled);
180
181        // visible pattern
182        mVisiblePattern = new CheckBoxPreference(this);
183        mVisiblePattern.setKey(KEY_VISIBLE_PATTERN);
184        mVisiblePattern.setTitle(R.string.lockpattern_settings_enable_visible_pattern_title);
185        inlinePrefCat.addPreference(mVisiblePattern);
186
187        // tactile feedback
188        mTactileFeedback = new CheckBoxPreference(this);
189        mTactileFeedback.setKey(KEY_TACTILE_FEEDBACK_ENABLED);
190        mTactileFeedback.setTitle(R.string.lockpattern_settings_enable_tactile_feedback_title);
191        inlinePrefCat.addPreference(mTactileFeedback);
192
193        // change pattern lock
194        Intent intent = new Intent();
195        intent.setClassName("com.android.settings",
196                    "com.android.settings.ChooseLockPatternTutorial");
197        mChoosePattern = getPreferenceManager().createPreferenceScreen(this);
198        mChoosePattern.setIntent(intent);
199        inlinePrefCat.addPreference(mChoosePattern);
200
201        int activePhoneType = TelephonyManager.getDefault().getPhoneType();
202
203        // do not display SIM lock for CDMA phone
204        if (TelephonyManager.PHONE_TYPE_CDMA != activePhoneType)
205        {
206            PreferenceScreen simLockPreferences = getPreferenceManager()
207                    .createPreferenceScreen(this);
208            simLockPreferences.setTitle(R.string.sim_lock_settings_category);
209            // Intent to launch SIM lock settings
210            intent = new Intent();
211            intent.setClassName("com.android.settings", "com.android.settings.IccLockSettings");
212            simLockPreferences.setIntent(intent);
213
214            PreferenceCategory simLockCat = new PreferenceCategory(this);
215            simLockCat.setTitle(R.string.sim_lock_settings_title);
216            root.addPreference(simLockCat);
217            simLockCat.addPreference(simLockPreferences);
218        }
219
220        // Passwords
221        PreferenceCategory passwordsCat = new PreferenceCategory(this);
222        passwordsCat.setTitle(R.string.security_passwords_title);
223        root.addPreference(passwordsCat);
224
225        CheckBoxPreference showPassword = mShowPassword = new CheckBoxPreference(this);
226        showPassword.setKey("show_password");
227        showPassword.setTitle(R.string.show_password);
228        showPassword.setSummary(R.string.show_password_summary);
229        showPassword.setPersistent(false);
230        passwordsCat.addPreference(showPassword);
231
232        // Credential storage
233        PreferenceCategory credStoreCat = new PreferenceCategory(this);
234        credStoreCat.setTitle(R.string.cstor_settings_category);
235        root.addPreference(credStoreCat);
236        credStoreCat.addPreference(mCstorHelper.createAccessCheckBox());
237        credStoreCat.addPreference(mCstorHelper.createSetPasswordPreference());
238        credStoreCat.addPreference(mCstorHelper.createResetPreference());
239
240        return root;
241    }
242
243    @Override
244    protected void onResume() {
245        super.onResume();
246
247        boolean patternExists = mLockPatternUtils.savedPatternExists();
248        mLockEnabled.setEnabled(patternExists);
249        mVisiblePattern.setEnabled(patternExists);
250        mTactileFeedback.setEnabled(patternExists);
251
252        mLockEnabled.setChecked(mLockPatternUtils.isLockPatternEnabled());
253        mVisiblePattern.setChecked(mLockPatternUtils.isVisiblePatternEnabled());
254        mTactileFeedback.setChecked(mLockPatternUtils.isTactileFeedbackEnabled());
255
256        int chooseStringRes = mLockPatternUtils.savedPatternExists() ?
257                R.string.lockpattern_settings_change_lock_pattern :
258                R.string.lockpattern_settings_choose_lock_pattern;
259        mChoosePattern.setTitle(chooseStringRes);
260
261        mShowPassword
262                .setChecked(Settings.System.getInt(getContentResolver(),
263                Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
264    }
265
266    @Override
267    public void onPause() {
268        if (mUseLocationDialog != null && mUseLocationDialog.isShowing()) {
269            mUseLocationDialog.dismiss();
270        }
271        mUseLocationDialog = null;
272        super.onPause();
273    }
274
275    @Override
276    public void onSaveInstanceState(Bundle icicle) {
277        if (mUseLocationDialog != null && mUseLocationDialog.isShowing()) {
278            icicle.putBoolean(KEY_DONE_USE_LOCATION, false);
279        }
280        super.onSaveInstanceState(icicle);
281    }
282
283    @Override
284    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
285            Preference preference) {
286        final String key = preference.getKey();
287
288        if (KEY_LOCK_ENABLED.equals(key)) {
289            mLockPatternUtils.setLockPatternEnabled(isToggled(preference));
290        } else if (KEY_VISIBLE_PATTERN.equals(key)) {
291            mLockPatternUtils.setVisiblePatternEnabled(isToggled(preference));
292        } else if (KEY_TACTILE_FEEDBACK_ENABLED.equals(key)) {
293            mLockPatternUtils.setTactileFeedbackEnabled(isToggled(preference));
294        } else if (preference == mShowPassword) {
295            Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD,
296                    mShowPassword.isChecked() ? 1 : 0);
297        } else if (preference == mNetwork) {
298            Settings.Secure.setLocationProviderEnabled(getContentResolver(),
299                    LocationManager.NETWORK_PROVIDER, mNetwork.isChecked());
300        } else if (preference == mGps) {
301            boolean enabled = mGps.isChecked();
302            Settings.Secure.setLocationProviderEnabled(getContentResolver(),
303                    LocationManager.GPS_PROVIDER, enabled);
304            mAssistedGps.setEnabled(enabled);
305        } else if (preference == mAssistedGps) {
306            Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED,
307                    mAssistedGps.isChecked() ? 1 : 0);
308        } else if (preference == mUseLocation) {
309            //normally called on the toggle click
310            if (mUseLocation.isChecked()) {
311                showUseLocationDialog(false);
312            } else {
313                updateUseLocation();
314            }
315        }
316
317        return false;
318    }
319
320    private void showPrivacyPolicy() {
321        Intent intent = new Intent("android.settings.TERMS");
322        startActivity(intent);
323    }
324
325    private void showUseLocationDialog(boolean force) {
326        // Show a warning to the user that location data will be shared
327        mOkClicked = false;
328        if (force) {
329            mUseLocation.setChecked(true);
330        }
331
332        CharSequence msg = getResources().getText(R.string.use_location_warning_message);
333        mUseLocationDialog = new AlertDialog.Builder(this).setMessage(msg)
334                .setTitle(R.string.use_location_title)
335                .setIcon(android.R.drawable.ic_dialog_alert)
336                .setPositiveButton(R.string.agree, this)
337                .setNegativeButton(R.string.disagree, this)
338                .show();
339        ((TextView)mUseLocationDialog.findViewById(android.R.id.message))
340                .setMovementMethod(LinkMovementMethod.getInstance());
341        mUseLocationDialog.setOnDismissListener(this);
342    }
343
344    /*
345     * Creates toggles for each available location provider
346     */
347    private void updateToggles() {
348        ContentResolver res = getContentResolver();
349        boolean gpsEnabled = Settings.Secure.isLocationProviderEnabled(
350                res, LocationManager.GPS_PROVIDER);
351        mNetwork.setChecked(Settings.Secure.isLocationProviderEnabled(
352                res, LocationManager.NETWORK_PROVIDER));
353        mGps.setChecked(gpsEnabled);
354        mAssistedGps.setChecked(Settings.Secure.getInt(res,
355                Settings.Secure.ASSISTED_GPS_ENABLED, 2) == 1);
356        mAssistedGps.setEnabled(gpsEnabled);
357        mUseLocation.setChecked(Settings.Secure.getInt(res,
358                Settings.Secure.USE_LOCATION_FOR_SERVICES, 2) == 1);
359    }
360
361    private boolean isToggled(Preference pref) {
362        return ((CheckBoxPreference) pref).isChecked();
363    }
364
365    private void updateUseLocation() {
366        boolean use = mUseLocation.isChecked();
367        Settings.Secure.putInt(getContentResolver(),
368                Settings.Secure.USE_LOCATION_FOR_SERVICES, use ? 1 : 0);
369    }
370
371
372    /**
373     * For the user to disable keyguard, we first make them verify their
374     * existing pattern.
375     */
376    private class LockEnabledPref extends CheckBoxPreference {
377
378        public LockEnabledPref(Context context) {
379            super(context);
380        }
381
382        @Override
383        protected void onClick() {
384            if (mLockPatternUtils.savedPatternExists() && isChecked()) {
385                confirmPatternThenDisableAndClear();
386            } else {
387                super.onClick();
388            }
389        }
390    }
391
392    /**
393     * Launch screen to confirm the existing lock pattern.
394     * @see #onActivityResult(int, int, android.content.Intent)
395     */
396    private void confirmPatternThenDisableAndClear() {
397        final Intent intent = new Intent();
398        intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
399        startActivityForResult(intent, CONFIRM_PATTERN_THEN_DISABLE_AND_CLEAR_REQUEST_CODE);
400    }
401
402    /**
403     * @see #confirmPatternThenDisableAndClear
404     */
405    @Override
406    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
407        super.onActivityResult(requestCode, resultCode, data);
408
409        final boolean resultOk = resultCode == Activity.RESULT_OK;
410
411        if ((requestCode == CONFIRM_PATTERN_THEN_DISABLE_AND_CLEAR_REQUEST_CODE)
412                && resultOk) {
413            mLockPatternUtils.setLockPatternEnabled(false);
414            mLockPatternUtils.saveLockPattern(null);
415        }
416    }
417
418    public void onClick(DialogInterface dialog, int which) {
419        if (which == DialogInterface.BUTTON_POSITIVE) {
420            //updateProviders();
421            mOkClicked = true;
422        } else {
423            // Reset the toggle
424            mUseLocation.setChecked(false);
425        }
426        updateUseLocation();
427    }
428
429    public void onDismiss(DialogInterface dialog) {
430        // Assuming that onClick gets called first
431        if (!mOkClicked) {
432            mUseLocation.setChecked(false);
433        }
434    }
435
436    @Override
437    protected Dialog onCreateDialog (int id) {
438        switch (id) {
439            case CSTOR_INIT_DIALOG:
440            case CSTOR_CHANGE_PASSWORD_DIALOG:
441                return mCstorHelper.createSetPasswordDialog(id);
442
443            case CSTOR_UNLOCK_DIALOG:
444                return mCstorHelper.createUnlockDialog();
445
446            case CSTOR_RESET_DIALOG:
447                return mCstorHelper.createResetDialog();
448
449            case CSTOR_NAME_CREDENTIAL_DIALOG:
450                return mCstorHelper.createNameCredentialDialog();
451
452            default:
453                return null;
454        }
455    }
456
457    private class CstorHelper implements
458            DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
459        private Keystore mKeystore = Keystore.getInstance();
460        private View mView;
461        private int mDialogId;
462        private boolean mConfirm = true;
463
464        private CheckBoxPreference mAccessCheckBox;
465        private Preference mResetButton;
466
467        private Intent mSpecialIntent;
468        private CstorAddCredentialHelper mCstorAddCredentialHelper;
469
470        void handleCstorIntents(Intent intent) {
471            if (intent == null) return;
472            String action = intent.getAction();
473
474            if (ACTION_ADD_CREDENTIAL.equals(action)) {
475                mCstorAddCredentialHelper = new CstorAddCredentialHelper(intent);
476                showDialog(CSTOR_NAME_CREDENTIAL_DIALOG);
477            } else if (ACTION_UNLOCK_CREDENTIAL_STORAGE.equals(action)) {
478                mSpecialIntent = intent;
479                showDialog(mCstorHelper.isCstorInitialized()
480                        ? CSTOR_UNLOCK_DIALOG
481                        : CSTOR_INIT_DIALOG);
482            }
483        }
484
485        private boolean isCstorUnlocked() {
486            return (mKeystore.getState() == Keystore.UNLOCKED);
487        }
488
489        private boolean isCstorInitialized() {
490            return (mKeystore.getState() != Keystore.UNINITIALIZED);
491        }
492
493        private void lockCstor() {
494            mKeystore.lock();
495            mAccessCheckBox.setChecked(false);
496        }
497
498        private int unlockCstor(String passwd) {
499            int ret = mKeystore.unlock(passwd);
500            if (ret == -1) resetCstor();
501            if (ret == 0) {
502                Toast.makeText(SecuritySettings.this, R.string.cstor_is_enabled,
503                        Toast.LENGTH_SHORT).show();
504            }
505            return ret;
506        }
507
508        private int changeCstorPassword(String oldPasswd, String newPasswd) {
509            int ret = mKeystore.changePassword(oldPasswd, newPasswd);
510            if (ret == -1) resetCstor();
511            return ret;
512        }
513
514        private void initCstor(String passwd) {
515            mKeystore.setPassword(passwd);
516            enablePreferences(true);
517            mAccessCheckBox.setChecked(true);
518            Toast.makeText(SecuritySettings.this, R.string.cstor_is_enabled,
519                    Toast.LENGTH_SHORT).show();
520        }
521
522        private void resetCstor() {
523            mKeystore.reset();
524            enablePreferences(false);
525            mAccessCheckBox.setChecked(false);
526        }
527
528        private void addCredential() {
529            String formatString = mCstorAddCredentialHelper.saveToStorage() < 0
530                    ? getString(R.string.cstor_add_error)
531                    : getString(R.string.cstor_is_added);
532            String message = String.format(formatString,
533                    mCstorAddCredentialHelper.getName());
534            Toast.makeText(SecuritySettings.this, message, Toast.LENGTH_SHORT)
535                    .show();
536        }
537
538        public void onClick(DialogInterface dialog, int which) {
539            if (which == DialogInterface.BUTTON_NEGATIVE) {
540                if (mCstorAddCredentialHelper != null) {
541                    // release the object here so that it doesn't get triggerred in
542                    // onDismiss()
543                    mCstorAddCredentialHelper = null;
544                    finish();
545                }
546                return;
547            }
548
549            switch (mDialogId) {
550                case CSTOR_INIT_DIALOG:
551                case CSTOR_CHANGE_PASSWORD_DIALOG:
552                    mConfirm = checkPasswords((Dialog) dialog);
553                    break;
554
555                case CSTOR_UNLOCK_DIALOG:
556                    mConfirm = checkUnlockPassword((Dialog) dialog);
557                    break;
558
559                case CSTOR_RESET_DIALOG:
560                    resetCstor();
561                    break;
562
563                case CSTOR_NAME_CREDENTIAL_DIALOG:
564                    mConfirm = checkAddCredential();
565                    break;
566            }
567        }
568
569        public void onDismiss(DialogInterface dialog) {
570            if (!mConfirm) {
571                mConfirm = true;
572                showDialog(mDialogId);
573            } else {
574                removeDialog(mDialogId);
575
576                if (mCstorAddCredentialHelper != null) {
577                    if (!isCstorInitialized()) {
578                        showDialog(CSTOR_INIT_DIALOG);
579                    } else if (!isCstorUnlocked()) {
580                        showDialog(CSTOR_UNLOCK_DIALOG);
581                    } else {
582                        addCredential();
583                        finish();
584                    }
585                } else if (mSpecialIntent != null) {
586                    finish();
587                }
588            }
589        }
590
591        private void showResetWarning(int count) {
592            TextView v = showError(count <= 3
593                    ? R.string.cstor_password_error_reset_warning
594                    : R.string.cstor_password_error);
595            if (count <= 3) {
596                if (count == 1) {
597                    v.setText(R.string.cstor_password_error_reset_warning);
598                } else {
599                    String format = getString(
600                            R.string.cstor_password_error_reset_warning_plural);
601                    v.setText(String.format(format, count));
602                }
603            }
604        }
605
606        private boolean checkAddCredential() {
607            hideError();
608
609            String name = getText(R.id.cstor_credential_name);
610            if (TextUtils.isEmpty(name)) {
611                showError(R.string.cstor_name_empty_error);
612                return false;
613            }
614
615            for (int i = 0, len = name.length(); i < len; i++) {
616                if (!Character.isLetterOrDigit(name.charAt(i))) {
617                    showError(R.string.cstor_name_char_error);
618                    return false;
619                }
620            }
621
622            mCstorAddCredentialHelper.setName(name);
623            return true;
624        }
625
626        // returns true if the password is long enough and does not contain
627        // characters that we don't like
628        private boolean verifyPassword(String passwd) {
629            if (passwd == null) {
630                showError(R.string.cstor_passwords_empty_error);
631                return false;
632            } else if ((passwd.length() < CSTOR_MIN_PASSWORD_LENGTH)
633                    || passwd.contains(" ")) {
634                showError(R.string.cstor_password_verification_error);
635                return false;
636            } else {
637                return true;
638            }
639        }
640
641        // returns true if the password is ok
642        private boolean checkUnlockPassword(Dialog d) {
643            hideError();
644
645            String passwd = getText(R.id.cstor_password);
646            if (TextUtils.isEmpty(passwd)) {
647                showError(R.string.cstor_password_empty_error);
648                return false;
649            }
650
651            int count = unlockCstor(passwd);
652            if (count > 0) {
653                showResetWarning(count);
654                return false;
655            } else {
656                // done or reset
657                return true;
658            }
659        }
660
661        // returns true if the passwords are ok
662        private boolean checkPasswords(Dialog d) {
663            hideError();
664
665            String oldPasswd = getText(R.id.cstor_old_password);
666            String newPasswd = getText(R.id.cstor_new_password);
667            String confirmPasswd = getText(R.id.cstor_confirm_password);
668
669            if ((mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG)
670                    && TextUtils.isEmpty(oldPasswd)) {
671                showError(R.string.cstor_password_empty_error);
672                return false;
673            }
674
675            if (TextUtils.isEmpty(newPasswd)
676                    && TextUtils.isEmpty(confirmPasswd)) {
677                showError(R.string.cstor_passwords_empty_error);
678                return false;
679            }
680
681            if (!verifyPassword(newPasswd)) {
682                return false;
683            } else if (!newPasswd.equals(confirmPasswd)) {
684                showError(R.string.cstor_passwords_error);
685                return false;
686            }
687
688            if (mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG) {
689                int count = changeCstorPassword(oldPasswd, newPasswd);
690                if (count > 0) {
691                    showResetWarning(count);
692                    return false;
693                } else {
694                    // done or reset
695                    return true;
696                }
697            } else {
698                initCstor(newPasswd);
699                return true;
700            }
701        }
702
703        private TextView showError(int messageId) {
704            TextView v = (TextView) mView.findViewById(R.id.cstor_error);
705            v.setText(messageId);
706            if (v != null) v.setVisibility(View.VISIBLE);
707            return v;
708        }
709
710        private void hide(int viewId) {
711            View v = mView.findViewById(viewId);
712            if (v != null) v.setVisibility(View.GONE);
713        }
714
715        private void hideError() {
716            hide(R.id.cstor_error);
717        }
718
719        private String getText(int viewId) {
720            return ((TextView) mView.findViewById(viewId)).getText().toString();
721        }
722
723        private void setText(int viewId, String text) {
724            TextView v = (TextView) mView.findViewById(viewId);
725            if (v != null) v.setText(text);
726        }
727
728        private void setText(int viewId, int textId) {
729            TextView v = (TextView) mView.findViewById(viewId);
730            if (v != null) v.setText(textId);
731        }
732
733        private void enablePreferences(boolean enabled) {
734            mAccessCheckBox.setEnabled(enabled);
735            mResetButton.setEnabled(enabled);
736        }
737
738        private Preference createAccessCheckBox() {
739            CheckBoxPreference pref = new CheckBoxPreference(
740                    SecuritySettings.this);
741            pref.setTitle(R.string.cstor_access_title);
742            pref.setSummary(R.string.cstor_access_summary);
743            pref.setChecked(isCstorUnlocked());
744            pref.setOnPreferenceChangeListener(
745                    new Preference.OnPreferenceChangeListener() {
746                        public boolean onPreferenceChange(
747                                Preference pref, Object value) {
748                            if (((Boolean) value)) {
749                                showDialog(isCstorInitialized()
750                                        ? CSTOR_UNLOCK_DIALOG
751                                        : CSTOR_INIT_DIALOG);
752                            } else {
753                                lockCstor();
754                            }
755                            return true;
756                        }
757                    });
758            pref.setEnabled(isCstorInitialized());
759            mAccessCheckBox = pref;
760            return pref;
761        }
762
763        private Preference createSetPasswordPreference() {
764            Preference pref = new Preference(SecuritySettings.this);
765            pref.setTitle(R.string.cstor_set_passwd_title);
766            pref.setSummary(R.string.cstor_set_passwd_summary);
767            pref.setOnPreferenceClickListener(
768                    new Preference.OnPreferenceClickListener() {
769                        public boolean onPreferenceClick(Preference pref) {
770                            showDialog(isCstorInitialized()
771                                    ? CSTOR_CHANGE_PASSWORD_DIALOG
772                                    : CSTOR_INIT_DIALOG);
773                            return true;
774                        }
775                    });
776            return pref;
777        }
778
779        private Preference createResetPreference() {
780            Preference pref = new Preference(SecuritySettings.this);
781            pref.setTitle(R.string.cstor_reset_title);
782            pref.setSummary(R.string.cstor_reset_summary);
783            pref.setOnPreferenceClickListener(
784                    new Preference.OnPreferenceClickListener() {
785                        public boolean onPreferenceClick(Preference pref) {
786                            showDialog(CSTOR_RESET_DIALOG);
787                            return true;
788                        }
789                    });
790            pref.setEnabled(isCstorInitialized());
791            mResetButton = pref;
792            return pref;
793        }
794
795        private Dialog createUnlockDialog() {
796            mDialogId = CSTOR_UNLOCK_DIALOG;
797            mView = View.inflate(SecuritySettings.this,
798                    R.layout.cstor_unlock_dialog_view, null);
799            hideError();
800
801            // show extra hint only when the action comes from outside
802            if ((mSpecialIntent == null)
803                    && (mCstorAddCredentialHelper == null)) {
804                hide(R.id.cstor_access_dialog_hint_from_action);
805            }
806
807            Dialog d = new AlertDialog.Builder(SecuritySettings.this)
808                    .setView(mView)
809                    .setTitle(R.string.cstor_access_dialog_title)
810                    .setPositiveButton(android.R.string.ok, this)
811                    .setNegativeButton(android.R.string.cancel, this)
812                    .setCancelable(false)
813                    .create();
814            d.setOnDismissListener(this);
815            return d;
816        }
817
818        private Dialog createSetPasswordDialog(int id) {
819            mDialogId = id;
820            mView = View.inflate(SecuritySettings.this,
821                    R.layout.cstor_set_password_dialog_view, null);
822            hideError();
823
824            // show extra hint only when the action comes from outside
825            if ((mSpecialIntent != null)
826                    || (mCstorAddCredentialHelper != null)) {
827                setText(R.id.cstor_first_time_hint,
828                        R.string.cstor_first_time_hint_from_action);
829            }
830
831            switch (id) {
832                case CSTOR_INIT_DIALOG:
833                    mView.findViewById(R.id.cstor_old_password_block)
834                            .setVisibility(View.GONE);
835                    break;
836
837                case CSTOR_CHANGE_PASSWORD_DIALOG:
838                    mView.findViewById(R.id.cstor_first_time_hint)
839                            .setVisibility(View.GONE);
840                    break;
841
842                default:
843                    throw new RuntimeException(
844                            "Unknown dialog id: " + mDialogId);
845            }
846
847            Dialog d = new AlertDialog.Builder(SecuritySettings.this)
848                    .setView(mView)
849                    .setTitle(R.string.cstor_set_passwd_dialog_title)
850                    .setPositiveButton(android.R.string.ok, this)
851                    .setNegativeButton(android.R.string.cancel, this)
852                    .setCancelable(false)
853                    .create();
854            d.setOnDismissListener(this);
855            return d;
856        }
857
858        private Dialog createResetDialog() {
859            mDialogId = CSTOR_RESET_DIALOG;
860            return new AlertDialog.Builder(SecuritySettings.this)
861                    .setTitle(android.R.string.dialog_alert_title)
862                    .setIcon(android.R.drawable.ic_dialog_alert)
863                    .setMessage(R.string.cstor_reset_hint)
864                    .setPositiveButton(getString(android.R.string.ok), this)
865                    .setNegativeButton(getString(android.R.string.cancel), this)
866                    .create();
867        }
868
869        private Dialog createNameCredentialDialog() {
870            mDialogId = CSTOR_NAME_CREDENTIAL_DIALOG;
871            mView = View.inflate(SecuritySettings.this,
872                    R.layout.cstor_name_credential_dialog_view, null);
873            hideError();
874
875            setText(R.id.cstor_credential_name_title,
876                    R.string.cstor_credential_name);
877            setText(R.id.cstor_credential_info_title,
878                    R.string.cstor_credential_info);
879            setText(R.id.cstor_credential_info,
880                    mCstorAddCredentialHelper.getDescription().toString());
881
882            Dialog d = new AlertDialog.Builder(SecuritySettings.this)
883                    .setView(mView)
884                    .setTitle(R.string.cstor_name_credential_dialog_title)
885                    .setPositiveButton(android.R.string.ok, this)
886                    .setNegativeButton(android.R.string.cancel, this)
887                    .setCancelable(false)
888                    .create();
889            d.setOnDismissListener(this);
890            return d;
891        }
892    }
893
894    private class CstorAddCredentialHelper {
895        private String mTypeName;
896        private List<byte[]> mItemList;
897        private List<String> mNamespaceList;
898        private String mDescription;
899        private String mName;
900
901        CstorAddCredentialHelper(Intent intent) {
902            parse(intent);
903        }
904
905        String getTypeName() {
906            return mTypeName;
907        }
908
909        CharSequence getDescription() {
910            return Html.fromHtml(mDescription);
911        }
912
913        void setName(String name) {
914            mName = name;
915        }
916
917        String getName() {
918            return mName;
919        }
920
921        int saveToStorage() {
922            Keystore ks = Keystore.getInstance();
923            for (int i = 0, count = mItemList.size(); i < count; i++) {
924                byte[] blob = mItemList.get(i);
925                int ret = ks.put(mNamespaceList.get(i), mName, new String(blob));
926                if (ret < 0) return ret;
927            }
928            return 0;
929        }
930
931        private void parse(Intent intent) {
932            mTypeName = intent.getStringExtra(KEY_CSTOR_TYPE_NAME);
933            mItemList = new ArrayList<byte[]>();
934            mNamespaceList = new ArrayList<String>();
935            for (int i = 0; ; i++) {
936                byte[] blob = intent.getByteArrayExtra(KEY_CSTOR_ITEM + i);
937                if (blob == null) break;
938                mItemList.add(blob);
939                mNamespaceList.add(intent.getStringExtra(
940                        KEY_CSTOR_NAMESPACE + i));
941            }
942
943            // build description string
944            StringBuilder sb = new StringBuilder();
945            for (int i = 0; ; i++) {
946                String s = intent.getStringExtra(KEY_CSTOR_DESCRIPTION + i);
947                if (s == null) break;
948                sb.append(s).append("<br>");
949            }
950            mDescription = sb.toString();
951        }
952    }
953}
954