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