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