SecuritySettings.java revision acedb4b501ebc1c1d06753b2915ed04e1ed43f24
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) finish();
541                return;
542            }
543
544            switch (mDialogId) {
545                case CSTOR_INIT_DIALOG:
546                case CSTOR_CHANGE_PASSWORD_DIALOG:
547                    mConfirm = checkPasswords((Dialog) dialog);
548                    break;
549
550                case CSTOR_UNLOCK_DIALOG:
551                    mConfirm = checkUnlockPassword((Dialog) dialog);
552                    break;
553
554                case CSTOR_RESET_DIALOG:
555                    resetCstor();
556                    break;
557
558                case CSTOR_NAME_CREDENTIAL_DIALOG:
559                    mConfirm = checkAddCredential();
560                    break;
561            }
562        }
563
564        public void onDismiss(DialogInterface dialog) {
565            if (!mConfirm) {
566                mConfirm = true;
567                showDialog(mDialogId);
568            } else {
569                removeDialog(mDialogId);
570
571                if (mCstorAddCredentialHelper != null) {
572                    if (!isCstorInitialized()) {
573                        showDialog(CSTOR_INIT_DIALOG);
574                    } else if (!isCstorUnlocked()) {
575                        showDialog(CSTOR_UNLOCK_DIALOG);
576                    } else {
577                        addCredential();
578                        finish();
579                    }
580                } else if (mSpecialIntent != null) {
581                    finish();
582                }
583            }
584        }
585
586        private void showResetWarning(int count) {
587            TextView v = showError(count <= 3
588                    ? R.string.cstor_password_error_reset_warning
589                    : R.string.cstor_password_error);
590            if (count <= 3) {
591                if (count == 1) {
592                    v.setText(getString(
593                            R.string.cstor_password_error_reset_warning));
594                } else {
595                    String format = getString(
596                            R.string.cstor_password_error_reset_warning_plural);
597                    v.setText(String.format(format, count));
598                }
599            }
600        }
601
602        private boolean checkAddCredential() {
603            hideError();
604
605            String name = getText(R.id.cstor_credential_name);
606            if (TextUtils.isEmpty(name)) {
607                showError(R.string.cstor_name_empty_error);
608                return false;
609            }
610
611            for (int i = 0, len = name.length(); i < len; i++) {
612                if (!Character.isLetterOrDigit(name.charAt(i))) {
613                    showError(R.string.cstor_name_char_error);
614                    return false;
615                }
616            }
617
618            mCstorAddCredentialHelper.setName(name);
619            return true;
620        }
621
622        // returns true if the password is long enough and does not contain
623        // characters that we don't like
624        private boolean verifyPassword(String passwd) {
625            if (passwd == null) {
626                showError(R.string.cstor_passwords_empty_error);
627                return false;
628            } else if ((passwd.length() < CSTOR_MIN_PASSWORD_LENGTH)
629                    || passwd.contains(" ")) {
630                showError(R.string.cstor_password_verification_error);
631                return false;
632            } else {
633                return true;
634            }
635        }
636
637        // returns true if the password is ok
638        private boolean checkUnlockPassword(Dialog d) {
639            hideError();
640
641            String passwd = getText(R.id.cstor_password);
642            if (TextUtils.isEmpty(passwd)) {
643                showError(R.string.cstor_password_empty_error);
644                return false;
645            }
646
647            int count = unlockCstor(passwd);
648            if (count > 0) {
649                showResetWarning(count);
650                return false;
651            } else {
652                // done or reset
653                return true;
654            }
655        }
656
657        // returns true if the passwords are ok
658        private boolean checkPasswords(Dialog d) {
659            hideError();
660
661            String oldPasswd = getText(R.id.cstor_old_password);
662            String newPasswd = getText(R.id.cstor_new_password);
663            String confirmPasswd = getText(R.id.cstor_confirm_password);
664
665            if ((mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG)
666                    && TextUtils.isEmpty(oldPasswd)) {
667                showError(R.string.cstor_password_empty_error);
668                return false;
669            }
670
671            if (TextUtils.isEmpty(newPasswd)
672                    && TextUtils.isEmpty(confirmPasswd)) {
673                showError(R.string.cstor_passwords_empty_error);
674                return false;
675            }
676
677            if (!verifyPassword(newPasswd)) {
678                return false;
679            } else if (!newPasswd.equals(confirmPasswd)) {
680                showError(R.string.cstor_passwords_error);
681                return false;
682            }
683
684            if (mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG) {
685                int count = changeCstorPassword(oldPasswd, newPasswd);
686                if (count > 0) {
687                    showResetWarning(count);
688                    return false;
689                } else {
690                    // done or reset
691                    return true;
692                }
693            } else {
694                initCstor(newPasswd);
695                return true;
696            }
697        }
698
699        private TextView showError(int messageId) {
700            TextView v = (TextView) mView.findViewById(R.id.cstor_error);
701            v.setText(messageId);
702            if (v != null) v.setVisibility(View.VISIBLE);
703            return v;
704        }
705
706        private void hideError() {
707            View v = mView.findViewById(R.id.cstor_error);
708            if (v != null) v.setVisibility(View.GONE);
709        }
710
711        private String getText(int viewId) {
712            return ((TextView) mView.findViewById(viewId)).getText().toString();
713        }
714
715        private void setText(int viewId, String text) {
716            TextView v = (TextView) mView.findViewById(viewId);
717            if (v != null) v.setText(text);
718        }
719
720        private void enablePreferences(boolean enabled) {
721            mAccessCheckBox.setEnabled(enabled);
722            mResetButton.setEnabled(enabled);
723        }
724
725        private Preference createAccessCheckBox() {
726            CheckBoxPreference pref = new CheckBoxPreference(
727                    SecuritySettings.this);
728            pref.setTitle(R.string.cstor_access_title);
729            pref.setSummary(R.string.cstor_access_summary);
730            pref.setChecked(isCstorUnlocked());
731            pref.setOnPreferenceChangeListener(
732                    new Preference.OnPreferenceChangeListener() {
733                        public boolean onPreferenceChange(
734                                Preference pref, Object value) {
735                            if (((Boolean) value)) {
736                                showDialog(isCstorInitialized()
737                                        ? CSTOR_UNLOCK_DIALOG
738                                        : CSTOR_INIT_DIALOG);
739                            } else {
740                                lockCstor();
741                            }
742                            return true;
743                        }
744                    });
745            pref.setEnabled(isCstorInitialized());
746            mAccessCheckBox = pref;
747            return pref;
748        }
749
750        private Preference createSetPasswordPreference() {
751            Preference pref = new Preference(SecuritySettings.this);
752            pref.setTitle(R.string.cstor_set_passwd_title);
753            pref.setSummary(R.string.cstor_set_passwd_summary);
754            pref.setOnPreferenceClickListener(
755                    new Preference.OnPreferenceClickListener() {
756                        public boolean onPreferenceClick(Preference pref) {
757                            showDialog(isCstorInitialized()
758                                    ? CSTOR_CHANGE_PASSWORD_DIALOG
759                                    : CSTOR_INIT_DIALOG);
760                            return true;
761                        }
762                    });
763            return pref;
764        }
765
766        private Preference createResetPreference() {
767            Preference pref = new Preference(SecuritySettings.this);
768            pref.setTitle(R.string.cstor_reset_title);
769            pref.setSummary(R.string.cstor_reset_summary);
770            pref.setOnPreferenceClickListener(
771                    new Preference.OnPreferenceClickListener() {
772                        public boolean onPreferenceClick(Preference pref) {
773                            showDialog(CSTOR_RESET_DIALOG);
774                            return true;
775                        }
776                    });
777            pref.setEnabled(isCstorInitialized());
778            mResetButton = pref;
779            return pref;
780        }
781
782        private Dialog createUnlockDialog() {
783            mDialogId = CSTOR_UNLOCK_DIALOG;
784            mView = View.inflate(SecuritySettings.this,
785                    R.layout.cstor_unlock_dialog_view, null);
786            hideError();
787
788            Dialog d = new AlertDialog.Builder(SecuritySettings.this)
789                    .setView(mView)
790                    .setTitle(R.string.cstor_access_dialog_title)
791                    .setPositiveButton(android.R.string.ok, this)
792                    .setNegativeButton(android.R.string.cancel, this)
793                    .setCancelable(false)
794                    .create();
795            d.setOnDismissListener(this);
796            return d;
797        }
798
799        private Dialog createSetPasswordDialog(int id) {
800            mDialogId = id;
801            mView = View.inflate(SecuritySettings.this,
802                    R.layout.cstor_set_password_dialog_view, null);
803            hideError();
804
805            switch (id) {
806                case CSTOR_INIT_DIALOG:
807                    mView.findViewById(R.id.cstor_old_password_block)
808                            .setVisibility(View.GONE);
809                    break;
810
811                case CSTOR_CHANGE_PASSWORD_DIALOG:
812                    mView.findViewById(R.id.cstor_first_time_hint)
813                            .setVisibility(View.GONE);
814                    break;
815
816                default:
817                    throw new RuntimeException(
818                            "Unknown dialog id: " + mDialogId);
819            }
820
821            Dialog d = new AlertDialog.Builder(SecuritySettings.this)
822                    .setView(mView)
823                    .setTitle(R.string.cstor_set_passwd_dialog_title)
824                    .setPositiveButton(android.R.string.ok, this)
825                    .setNegativeButton(android.R.string.cancel, this)
826                    .setCancelable(false)
827                    .create();
828            d.setOnDismissListener(this);
829            return d;
830        }
831
832        private Dialog createResetDialog() {
833            mDialogId = CSTOR_RESET_DIALOG;
834            return new AlertDialog.Builder(SecuritySettings.this)
835                    .setTitle(android.R.string.dialog_alert_title)
836                    .setIcon(android.R.drawable.ic_dialog_alert)
837                    .setMessage(R.string.cstor_reset_hint)
838                    .setPositiveButton(getString(android.R.string.ok), this)
839                    .setNegativeButton(getString(android.R.string.cancel), this)
840                    .create();
841        }
842
843        private Dialog createNameCredentialDialog() {
844            mDialogId = CSTOR_NAME_CREDENTIAL_DIALOG;
845            mView = View.inflate(SecuritySettings.this,
846                    R.layout.cstor_name_credential_dialog_view, null);
847            hideError();
848
849            setText(R.id.cstor_credential_name_title,
850                    getString(R.string.cstor_credential_name));
851            setText(R.id.cstor_credential_info_title,
852                    getString(R.string.cstor_credential_info));
853            setText(R.id.cstor_credential_info,
854                    mCstorAddCredentialHelper.getDescription().toString());
855
856            Dialog d = new AlertDialog.Builder(SecuritySettings.this)
857                    .setView(mView)
858                    .setTitle(R.string.cstor_name_credential_dialog_title)
859                    .setPositiveButton(android.R.string.ok, this)
860                    .setNegativeButton(android.R.string.cancel, this)
861                    .setCancelable(false)
862                    .create();
863            d.setOnDismissListener(this);
864            return d;
865        }
866    }
867
868    private class CstorAddCredentialHelper {
869        private String mTypeName;
870        private List<byte[]> mItemList;
871        private List<String> mNamespaceList;
872        private String mDescription;
873        private String mName;
874
875        CstorAddCredentialHelper(Intent intent) {
876            parse(intent);
877        }
878
879        String getTypeName() {
880            return mTypeName;
881        }
882
883        CharSequence getDescription() {
884            return Html.fromHtml(mDescription);
885        }
886
887        void setName(String name) {
888            mName = name;
889        }
890
891        String getName() {
892            return mName;
893        }
894
895        int saveToStorage() {
896            Keystore ks = Keystore.getInstance();
897            for (int i = 0, count = mItemList.size(); i < count; i++) {
898                byte[] blob = mItemList.get(i);
899                int ret = ks.put(mNamespaceList.get(i), mName, new String(blob));
900                if (ret < 0) return ret;
901            }
902            return 0;
903        }
904
905        private void parse(Intent intent) {
906            mTypeName = intent.getStringExtra(KEY_CSTOR_TYPE_NAME);
907            mItemList = new ArrayList<byte[]>();
908            mNamespaceList = new ArrayList<String>();
909            for (int i = 0; ; i++) {
910                byte[] blob = intent.getByteArrayExtra(KEY_CSTOR_ITEM + i);
911                if (blob == null) break;
912                mItemList.add(blob);
913                mNamespaceList.add(intent.getStringExtra(
914                        KEY_CSTOR_NAMESPACE + i));
915            }
916
917            // build description string
918            StringBuilder sb = new StringBuilder();
919            for (int i = 0; ; i++) {
920                String s = intent.getStringExtra(KEY_CSTOR_DESCRIPTION + i);
921                if (s == null) break;
922                sb.append(s).append("<br>");
923            }
924            mDescription = sb.toString();
925        }
926    }
927}
928