• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.settings;
18 
19 
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.Dialog;
23 import android.content.ContentQueryMap;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.Intent;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.database.Cursor;
30 import android.location.LocationManager;
31 import android.os.Bundle;
32 import android.preference.CheckBoxPreference;
33 import android.preference.EditTextPreference;
34 import android.preference.Preference;
35 import android.preference.PreferenceActivity;
36 import android.preference.PreferenceCategory;
37 import android.preference.PreferenceGroup;
38 import android.preference.PreferenceScreen;
39 import android.provider.Settings;
40 import android.security.CertTool;
41 import android.security.Keystore;
42 import android.text.Html;
43 import android.text.TextUtils;
44 import android.text.method.LinkMovementMethod;
45 import android.util.Log;
46 import android.view.View;
47 import android.widget.TextView;
48 import android.widget.Toast;
49 
50 import com.android.internal.widget.LockPatternUtils;
51 import android.telephony.TelephonyManager;
52 
53 import java.util.ArrayList;
54 import java.util.List;
55 import java.util.Observable;
56 import java.util.Observer;
57 
58 /**
59  * Gesture lock pattern settings.
60  */
61 public class SecuritySettings extends PreferenceActivity implements
62         DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
63 
64     // Lock Settings
65 
66     private static final String KEY_LOCK_ENABLED = "lockenabled";
67     private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
68     private static final String KEY_TACTILE_FEEDBACK_ENABLED = "tactilefeedback";
69     private static final int CONFIRM_PATTERN_THEN_DISABLE_AND_CLEAR_REQUEST_CODE = 55;
70 
71     private LockPatternUtils mLockPatternUtils;
72     private CheckBoxPreference mLockEnabled;
73     private CheckBoxPreference mVisiblePattern;
74     private CheckBoxPreference mTactileFeedback;
75     private Preference mChoosePattern;
76 
77     private CheckBoxPreference mShowPassword;
78 
79     // Location Settings
80     private static final String LOCATION_CATEGORY = "location_category";
81     private static final String LOCATION_NETWORK = "location_network";
82     private static final String LOCATION_GPS = "location_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 
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 {
update(Observable o, Object arg)119         public void update(Observable o, Object arg) {
120             updateToggles();
121         }
122     }
123 
124     @Override
onCreate(Bundle savedInstanceState)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         mUseLocation = (CheckBoxPreference) getPreferenceScreen().findPreference(USE_LOCATION);
136 
137         // Vendor specific
138         try {
139             if (mUseLocation != null
140                     && getPackageManager().getPackageInfo(GSETTINGS_PROVIDER, 0) == null) {
141                 ((PreferenceGroup)findPreference(LOCATION_CATEGORY))
142                         .removePreference(mUseLocation);
143             }
144         } catch (NameNotFoundException nnfe) {
145         }
146         updateToggles();
147 
148         // listen for Location Manager settings changes
149         Cursor settingsCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, null,
150                 "(" + Settings.System.NAME + "=?)",
151                 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
152                 null);
153         mContentQueryMap = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, null);
154         mContentQueryMap.addObserver(new SettingsObserver());
155         boolean doneUseLocation = savedInstanceState == null
156                 ? false : savedInstanceState.getBoolean(KEY_DONE_USE_LOCATION, true);
157         if (!doneUseLocation && (getIntent().getBooleanExtra("SHOW_USE_LOCATION", false)
158                 || savedInstanceState != null)) {
159             showUseLocationDialog(true);
160         }
161 
162         mCstorHelper.handleCstorIntents(getIntent());
163     }
164 
createPreferenceHierarchy()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         int s = mCstorHelper.getCstorState();
237         credStoreCat.addPreference(mCstorHelper.createAccessCheckBox(s));
238         credStoreCat.addPreference(mCstorHelper.createSetPasswordPreference());
239         credStoreCat.addPreference(mCstorHelper.createResetPreference(s));
240 
241         return root;
242     }
243 
244     @Override
onResume()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
onStop()268     public void onStop() {
269         if (mUseLocationDialog != null && mUseLocationDialog.isShowing()) {
270             mUseLocationDialog.dismiss();
271         }
272         mUseLocationDialog = null;
273         super.onStop();
274     }
275 
276     @Override
onSaveInstanceState(Bundle icicle)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
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)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             Settings.Secure.setLocationProviderEnabled(getContentResolver(),
303                     LocationManager.GPS_PROVIDER, mGps.isChecked());
304         } else if (preference == mUseLocation) {
305             //normally called on the toggle click
306             if (mUseLocation.isChecked()) {
307                 showUseLocationDialog(false);
308             } else {
309                 updateUseLocation();
310             }
311         }
312 
313         return false;
314     }
315 
showPrivacyPolicy()316     private void showPrivacyPolicy() {
317         Intent intent = new Intent("android.settings.TERMS");
318         startActivity(intent);
319     }
320 
showUseLocationDialog(boolean force)321     private void showUseLocationDialog(boolean force) {
322         // Show a warning to the user that location data will be shared
323         mOkClicked = false;
324         if (force) {
325             mUseLocation.setChecked(true);
326         }
327 
328         CharSequence msg = getResources().getText(R.string.use_location_warning_message);
329         mUseLocationDialog = new AlertDialog.Builder(this).setMessage(msg)
330                 .setTitle(R.string.use_location_title)
331                 .setIcon(android.R.drawable.ic_dialog_alert)
332                 .setPositiveButton(R.string.agree, this)
333                 .setNegativeButton(R.string.disagree, this)
334                 .show();
335         ((TextView)mUseLocationDialog.findViewById(android.R.id.message))
336                 .setMovementMethod(LinkMovementMethod.getInstance());
337         mUseLocationDialog.setOnDismissListener(this);
338     }
339 
340     /*
341      * Creates toggles for each available location provider
342      */
updateToggles()343     private void updateToggles() {
344         ContentResolver res = getContentResolver();
345         mNetwork.setChecked(Settings.Secure.isLocationProviderEnabled(
346                 res, LocationManager.NETWORK_PROVIDER));
347         mGps.setChecked(Settings.Secure.isLocationProviderEnabled(
348                 res, LocationManager.GPS_PROVIDER));
349         mUseLocation.setChecked(Settings.Secure.getInt(res,
350                 Settings.Secure.USE_LOCATION_FOR_SERVICES, 2) == 1);
351     }
352 
isToggled(Preference pref)353     private boolean isToggled(Preference pref) {
354         return ((CheckBoxPreference) pref).isChecked();
355     }
356 
updateUseLocation()357     private void updateUseLocation() {
358         boolean use = mUseLocation.isChecked();
359         Settings.Secure.putInt(getContentResolver(),
360                 Settings.Secure.USE_LOCATION_FOR_SERVICES, use ? 1 : 0);
361     }
362 
363 
364     /**
365      * For the user to disable keyguard, we first make them verify their
366      * existing pattern.
367      */
368     private class LockEnabledPref extends CheckBoxPreference {
369 
LockEnabledPref(Context context)370         public LockEnabledPref(Context context) {
371             super(context);
372         }
373 
374         @Override
onClick()375         protected void onClick() {
376             if (mLockPatternUtils.savedPatternExists() && isChecked()) {
377                 confirmPatternThenDisableAndClear();
378             } else {
379                 super.onClick();
380             }
381         }
382     }
383 
384     /**
385      * Launch screen to confirm the existing lock pattern.
386      * @see #onActivityResult(int, int, android.content.Intent)
387      */
confirmPatternThenDisableAndClear()388     private void confirmPatternThenDisableAndClear() {
389         final Intent intent = new Intent();
390         intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
391         startActivityForResult(intent, CONFIRM_PATTERN_THEN_DISABLE_AND_CLEAR_REQUEST_CODE);
392     }
393 
394     /**
395      * @see #confirmPatternThenDisableAndClear
396      */
397     @Override
onActivityResult(int requestCode, int resultCode, Intent data)398     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
399         super.onActivityResult(requestCode, resultCode, data);
400 
401         final boolean resultOk = resultCode == Activity.RESULT_OK;
402 
403         if ((requestCode == CONFIRM_PATTERN_THEN_DISABLE_AND_CLEAR_REQUEST_CODE)
404                 && resultOk) {
405             mLockPatternUtils.setLockPatternEnabled(false);
406             mLockPatternUtils.saveLockPattern(null);
407         }
408     }
409 
onClick(DialogInterface dialog, int which)410     public void onClick(DialogInterface dialog, int which) {
411         if (which == DialogInterface.BUTTON_POSITIVE) {
412             //updateProviders();
413             mOkClicked = true;
414         } else {
415             // Reset the toggle
416             mUseLocation.setChecked(false);
417         }
418         updateUseLocation();
419     }
420 
onDismiss(DialogInterface dialog)421     public void onDismiss(DialogInterface dialog) {
422         // Assuming that onClick gets called first
423         if (!mOkClicked) {
424             mUseLocation.setChecked(false);
425         }
426     }
427 
428     @Override
onCreateDialog(int id)429     protected Dialog onCreateDialog (int id) {
430         switch (id) {
431             case CSTOR_INIT_DIALOG:
432             case CSTOR_CHANGE_PASSWORD_DIALOG:
433                 return mCstorHelper.createSetPasswordDialog(id);
434 
435             case CSTOR_UNLOCK_DIALOG:
436                 return mCstorHelper.createUnlockDialog();
437 
438             case CSTOR_RESET_DIALOG:
439                 return mCstorHelper.createResetDialog();
440 
441             case CSTOR_NAME_CREDENTIAL_DIALOG:
442                 return mCstorHelper.createNameCredentialDialog();
443 
444             default:
445                 return null;
446         }
447     }
448 
449     private class CstorHelper implements DialogInterface.OnClickListener,
450             DialogInterface.OnDismissListener,
451             DialogInterface.OnCancelListener {
452         private Keystore mKeystore = Keystore.getInstance();
453         private View mView;
454         private int mDialogId;
455         private boolean mConfirm = true;
456 
457         private CheckBoxPreference mAccessCheckBox;
458         private Preference mResetButton;
459 
460         private Intent mSpecialIntent;
461         private CstorAddCredentialHelper mCstorAddCredentialHelper;
462 
handleCstorIntents(Intent intent)463         void handleCstorIntents(Intent intent) {
464             if (intent == null) return;
465             String action = intent.getAction();
466 
467             if (ACTION_ADD_CREDENTIAL.equals(action)) {
468                 mCstorAddCredentialHelper = new CstorAddCredentialHelper(intent);
469                 showCstorDialog(CSTOR_NAME_CREDENTIAL_DIALOG);
470             } else if (ACTION_UNLOCK_CREDENTIAL_STORAGE.equals(action)) {
471                 mSpecialIntent = intent;
472                 showCstorDialog(mCstorHelper.isCstorInitialized()
473                         ? CSTOR_UNLOCK_DIALOG
474                         : CSTOR_INIT_DIALOG);
475             }
476         }
477 
showCstorDialog(int dialogId)478         private void showCstorDialog(int dialogId) {
479             mDialogId = dialogId;
480             showDialog(dialogId);
481 
482             if (dialogId == CSTOR_NAME_CREDENTIAL_DIALOG) {
483                 // set mView back as mView may be replaced by CSTOR_INIT_DIALOG
484                 // or CSTOR_UNLOCK_DIALOG
485                 mView = mCstorAddCredentialHelper.mView;
486             }
487         }
488 
getCstorState()489         private int getCstorState() {
490             return mKeystore.getState();
491         }
492 
isCstorUnlocked()493         private boolean isCstorUnlocked() {
494             return (mKeystore.getState() == Keystore.UNLOCKED);
495         }
496 
isCstorInitialized()497         private boolean isCstorInitialized() {
498             return (mKeystore.getState() != Keystore.UNINITIALIZED);
499         }
500 
lockCstor()501         private void lockCstor() {
502             mKeystore.lock();
503             mAccessCheckBox.setChecked(false);
504         }
505 
unlockCstor(String passwd)506         private int unlockCstor(String passwd) {
507             int ret = mKeystore.unlock(passwd);
508             if (ret == -1) resetCstor();
509             if (ret == 0) {
510                 Toast.makeText(SecuritySettings.this, R.string.cstor_is_enabled,
511                         Toast.LENGTH_SHORT).show();
512             }
513             return ret;
514         }
515 
changeCstorPassword(String oldPasswd, String newPasswd)516         private int changeCstorPassword(String oldPasswd, String newPasswd) {
517             int ret = mKeystore.changePassword(oldPasswd, newPasswd);
518             if (ret == -1) resetCstor();
519             return ret;
520         }
521 
initCstor(String passwd)522         private void initCstor(String passwd) {
523             mKeystore.setPassword(passwd);
524             enablePreferences(true);
525             mAccessCheckBox.setChecked(true);
526             Toast.makeText(SecuritySettings.this, R.string.cstor_is_enabled,
527                     Toast.LENGTH_SHORT).show();
528         }
529 
resetCstor()530         private void resetCstor() {
531             mKeystore.reset();
532             enablePreferences(false);
533             mAccessCheckBox.setChecked(false);
534             Toast.makeText(SecuritySettings.this, R.string.cstor_is_reset,
535                     Toast.LENGTH_LONG).show();
536         }
537 
addCredential()538         private boolean addCredential() {
539             if (mCstorAddCredentialHelper.saveToStorage() != 0) {
540                 // set mView back as mView may be replaced by CSTOR_INIT_DIALOG
541                 // or CSTOR_UNLOCK_DIALOG
542                 mView = mCstorAddCredentialHelper.mView;
543                 if (mCstorAddCredentialHelper.isPkcs12Keystore()) {
544                     showError(R.string.cstor_password_error);
545                 } else {
546                     showError(R.string.cstor_storage_error);
547                 }
548                 Log.d("CSTOR", "failed to add credential");
549                 return false;
550             }
551             Log.d("CSTOR", "credential is added: "
552                     + mCstorAddCredentialHelper.getName());
553             String formatString =
554                     getString(R.string.cstor_is_added);
555             String message = String.format(formatString,
556                     mCstorAddCredentialHelper.getName());
557             Toast.makeText(SecuritySettings.this, message,
558                     Toast.LENGTH_LONG).show();
559             return true;
560         }
561 
onCancel(DialogInterface dialog)562         public void onCancel(DialogInterface dialog) {
563             if (mCstorAddCredentialHelper == null) return;
564 
565             switch (mDialogId) {
566                 case CSTOR_INIT_DIALOG:
567                 case CSTOR_UNLOCK_DIALOG:
568                     Toast.makeText(SecuritySettings.this,
569                             R.string.cstor_unable_to_save_cert,
570                             Toast.LENGTH_LONG).show();
571                     break;
572 
573                 case CSTOR_NAME_CREDENTIAL_DIALOG:
574                     Toast.makeText(SecuritySettings.this,
575                             R.string.cstor_cert_not_saved,
576                             Toast.LENGTH_LONG).show();
577                     break;
578             }
579             mCstorAddCredentialHelper = null;
580             finish();
581         }
582 
onClick(DialogInterface dialog, int which)583         public void onClick(DialogInterface dialog, int which) {
584             if (which == DialogInterface.BUTTON_NEGATIVE) {
585                 onCancel(dialog);
586                 return;
587             }
588 
589             switch (mDialogId) {
590                 case CSTOR_INIT_DIALOG:
591                 case CSTOR_CHANGE_PASSWORD_DIALOG:
592                     mConfirm = checkPasswords((Dialog) dialog);
593                     break;
594 
595                 case CSTOR_UNLOCK_DIALOG:
596                     mConfirm = checkUnlockPassword((Dialog) dialog);
597                     break;
598 
599                 case CSTOR_RESET_DIALOG:
600                     resetCstor();
601                     break;
602 
603                 case CSTOR_NAME_CREDENTIAL_DIALOG:
604                     mConfirm = checkAddCredential();
605                     break;
606             }
607         }
608 
onDismiss(DialogInterface dialog)609         public void onDismiss(DialogInterface dialog) {
610             if (!mConfirm) {
611                 mConfirm = true;
612                 showCstorDialog(mDialogId);
613             } else {
614                 if (mDialogId == CSTOR_UNLOCK_DIALOG) {
615                     mAccessCheckBox.setChecked(isCstorUnlocked());
616                 }
617 
618                 if (mCstorAddCredentialHelper != null) {
619                     if (!isCstorInitialized()) {
620                         showCstorDialog(CSTOR_INIT_DIALOG);
621                     } else if (!isCstorUnlocked()) {
622                         showCstorDialog(CSTOR_UNLOCK_DIALOG);
623                     } else {
624                         if (addCredential()) {
625                             // succeeded
626                             finish();
627                         } else {
628                             // failed
629                             if (mDialogId != CSTOR_NAME_CREDENTIAL_DIALOG) {
630                                 removeDialog(mDialogId);
631                             }
632                             showCstorDialog(CSTOR_NAME_CREDENTIAL_DIALOG);
633                         }
634                     }
635                     return;
636                 } else if (mSpecialIntent != null) {
637                     finish();
638                 }
639                 removeDialog(mDialogId);
640             }
641         }
642 
showResetWarning(int count)643         private void showResetWarning(int count) {
644             TextView v = showError(count <= 3
645                     ? R.string.cstor_password_error_reset_warning
646                     : R.string.cstor_password_error);
647             if (count <= 3) {
648                 if (count == 1) {
649                     v.setText(R.string.cstor_password_error_reset_warning);
650                 } else {
651                     String format = getString(
652                             R.string.cstor_password_error_reset_warning_plural);
653                     v.setText(String.format(format, count));
654                 }
655             }
656         }
657 
checkAddCredential()658         private boolean checkAddCredential() {
659             hideError();
660 
661             String name = getText(R.id.cstor_credential_name);
662             if (TextUtils.isEmpty(name)) {
663                 showError(R.string.cstor_name_empty_error);
664                 return false;
665             }
666 
667             for (int i = 0, len = name.length(); i < len; i++) {
668                 if (!Character.isLetterOrDigit(name.charAt(i))) {
669                     showError(R.string.cstor_name_char_error);
670                     return false;
671                 }
672             }
673 
674             mCstorAddCredentialHelper.setName(name);
675 
676             if (mCstorAddCredentialHelper.isPkcs12Keystore()) {
677                 String password = getText(R.id.cstor_credential_password);
678                 if (TextUtils.isEmpty(password)) {
679                     showError(R.string.cstor_password_empty_error);
680                     return false;
681                 }
682 
683                 mCstorAddCredentialHelper.setPassword(password);
684             }
685 
686             return true;
687         }
688 
689         // returns true if the password is long enough and does not contain
690         // characters that we don't like
verifyPassword(String passwd)691         private boolean verifyPassword(String passwd) {
692             if (passwd == null) {
693                 showError(R.string.cstor_passwords_empty_error);
694                 return false;
695             } else if ((passwd.length() < CSTOR_MIN_PASSWORD_LENGTH)
696                     || passwd.contains(" ")) {
697                 showError(R.string.cstor_password_verification_error);
698                 return false;
699             } else {
700                 return true;
701             }
702         }
703 
704         // returns true if the password is ok
checkUnlockPassword(Dialog d)705         private boolean checkUnlockPassword(Dialog d) {
706             hideError();
707 
708             String passwd = getText(R.id.cstor_password);
709             if (TextUtils.isEmpty(passwd)) {
710                 showError(R.string.cstor_password_empty_error);
711                 return false;
712             }
713 
714             int count = unlockCstor(passwd);
715             if (count > 0) {
716                 showResetWarning(count);
717                 return false;
718             } else {
719                 // done or reset
720                 return true;
721             }
722         }
723 
724         // returns true if the passwords are ok
checkPasswords(Dialog d)725         private boolean checkPasswords(Dialog d) {
726             hideError();
727 
728             String oldPasswd = getText(R.id.cstor_old_password);
729             String newPasswd = getText(R.id.cstor_new_password);
730             String confirmPasswd = getText(R.id.cstor_confirm_password);
731 
732             if ((mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG)
733                     && TextUtils.isEmpty(oldPasswd)) {
734                 showError(R.string.cstor_password_empty_error);
735                 return false;
736             }
737 
738             if (TextUtils.isEmpty(newPasswd)
739                     && TextUtils.isEmpty(confirmPasswd)) {
740                 showError(R.string.cstor_passwords_empty_error);
741                 return false;
742             }
743 
744             if (!verifyPassword(newPasswd)) {
745                 return false;
746             } else if (!newPasswd.equals(confirmPasswd)) {
747                 showError(R.string.cstor_passwords_error);
748                 return false;
749             }
750 
751             if (mDialogId == CSTOR_CHANGE_PASSWORD_DIALOG) {
752                 int count = changeCstorPassword(oldPasswd, newPasswd);
753                 if (count > 0) {
754                     showResetWarning(count);
755                     return false;
756                 } else {
757                     // done or reset
758                     return true;
759                 }
760             } else {
761                 initCstor(newPasswd);
762                 return true;
763             }
764         }
765 
showError(int messageId)766         private TextView showError(int messageId) {
767             TextView v = (TextView) mView.findViewById(R.id.cstor_error);
768             v.setText(messageId);
769             if (v != null) v.setVisibility(View.VISIBLE);
770             return v;
771         }
772 
hide(int viewId)773         private void hide(int viewId) {
774             View v = mView.findViewById(viewId);
775             if (v != null) v.setVisibility(View.GONE);
776         }
777 
hideError()778         private void hideError() {
779             hide(R.id.cstor_error);
780         }
781 
getText(int viewId)782         private String getText(int viewId) {
783             return ((TextView) mView.findViewById(viewId)).getText().toString();
784         }
785 
setText(int viewId, String text)786         private void setText(int viewId, String text) {
787             TextView v = (TextView) mView.findViewById(viewId);
788             if (v != null) v.setText(text);
789         }
790 
setText(int viewId, int textId)791         private void setText(int viewId, int textId) {
792             TextView v = (TextView) mView.findViewById(viewId);
793             if (v != null) v.setText(textId);
794         }
795 
enablePreferences(boolean enabled)796         private void enablePreferences(boolean enabled) {
797             mAccessCheckBox.setEnabled(enabled);
798             mResetButton.setEnabled(enabled);
799         }
800 
createAccessCheckBox(int state)801         private Preference createAccessCheckBox(int state) {
802             CheckBoxPreference pref = new CheckBoxPreference(
803                     SecuritySettings.this);
804             pref.setTitle(R.string.cstor_access_title);
805             pref.setSummary(R.string.cstor_access_summary);
806             pref.setEnabled(state != Keystore.UNINITIALIZED);
807             pref.setChecked(state == Keystore.UNLOCKED);
808             pref.setOnPreferenceChangeListener(
809                     new Preference.OnPreferenceChangeListener() {
810                         public boolean onPreferenceChange(
811                                 Preference pref, Object value) {
812                             if (((Boolean) value)) {
813                                 showCstorDialog(CSTOR_UNLOCK_DIALOG);
814                             } else {
815                                 lockCstor();
816                             }
817                             return true;
818                         }
819                     });
820             mAccessCheckBox = pref;
821             return pref;
822         }
823 
createSetPasswordPreference()824         private Preference createSetPasswordPreference() {
825             Preference pref = new Preference(SecuritySettings.this);
826             pref.setTitle(R.string.cstor_set_passwd_title);
827             pref.setSummary(R.string.cstor_set_passwd_summary);
828             pref.setOnPreferenceClickListener(
829                     new Preference.OnPreferenceClickListener() {
830                         public boolean onPreferenceClick(Preference pref) {
831                             showCstorDialog(isCstorInitialized()
832                                     ? CSTOR_CHANGE_PASSWORD_DIALOG
833                                     : CSTOR_INIT_DIALOG);
834                             return true;
835                         }
836                     });
837             return pref;
838         }
839 
createResetPreference(int state)840         private Preference createResetPreference(int state) {
841             Preference pref = new Preference(SecuritySettings.this);
842             pref.setTitle(R.string.cstor_reset_title);
843             pref.setSummary(R.string.cstor_reset_summary);
844             pref.setOnPreferenceClickListener(
845                     new Preference.OnPreferenceClickListener() {
846                         public boolean onPreferenceClick(Preference pref) {
847                             showCstorDialog(CSTOR_RESET_DIALOG);
848                             return true;
849                         }
850                     });
851             pref.setEnabled(state != Keystore.UNINITIALIZED);
852             mResetButton = pref;
853             return pref;
854         }
855 
createUnlockDialog()856         private Dialog createUnlockDialog() {
857             mView = View.inflate(SecuritySettings.this,
858                     R.layout.cstor_unlock_dialog_view, null);
859             hideError();
860 
861             // show extra hint only when the action comes from outside
862             if ((mSpecialIntent == null)
863                     && (mCstorAddCredentialHelper == null)) {
864                 hide(R.id.cstor_access_dialog_hint_from_action);
865             }
866 
867             Dialog d = new AlertDialog.Builder(SecuritySettings.this)
868                     .setView(mView)
869                     .setTitle(R.string.cstor_access_dialog_title)
870                     .setPositiveButton(android.R.string.ok, this)
871                     .setNegativeButton(android.R.string.cancel, this)
872                     .setOnCancelListener(this)
873                     .create();
874             d.setOnDismissListener(this);
875             return d;
876         }
877 
createSetPasswordDialog(int id)878         private Dialog createSetPasswordDialog(int id) {
879             mView = View.inflate(SecuritySettings.this,
880                     R.layout.cstor_set_password_dialog_view, null);
881             hideError();
882 
883             // show extra hint only when the action comes from outside
884             if ((mSpecialIntent != null)
885                     || (mCstorAddCredentialHelper != null)) {
886                 setText(R.id.cstor_first_time_hint,
887                         R.string.cstor_first_time_hint_from_action);
888             }
889 
890             switch (id) {
891                 case CSTOR_INIT_DIALOG:
892                     mView.findViewById(R.id.cstor_old_password_block)
893                             .setVisibility(View.GONE);
894                     break;
895 
896                 case CSTOR_CHANGE_PASSWORD_DIALOG:
897                     mView.findViewById(R.id.cstor_first_time_hint)
898                             .setVisibility(View.GONE);
899                     break;
900 
901                 default:
902                     throw new RuntimeException(
903                             "Unknown dialog id: " + mDialogId);
904             }
905 
906             Dialog d = new AlertDialog.Builder(SecuritySettings.this)
907                     .setView(mView)
908                     .setTitle(R.string.cstor_set_passwd_dialog_title)
909                     .setPositiveButton(android.R.string.ok, this)
910                     .setNegativeButton(android.R.string.cancel, this)
911                     .setOnCancelListener(this)
912                     .create();
913             d.setOnDismissListener(this);
914             return d;
915         }
916 
createResetDialog()917         private Dialog createResetDialog() {
918             return new AlertDialog.Builder(SecuritySettings.this)
919                     .setTitle(android.R.string.dialog_alert_title)
920                     .setIcon(android.R.drawable.ic_dialog_alert)
921                     .setMessage(R.string.cstor_reset_hint)
922                     .setPositiveButton(getString(android.R.string.ok), this)
923                     .setNegativeButton(getString(android.R.string.cancel), this)
924                     .create();
925         }
926 
createNameCredentialDialog()927         private Dialog createNameCredentialDialog() {
928             mView = View.inflate(SecuritySettings.this,
929                     R.layout.cstor_name_credential_dialog_view, null);
930             if (mCstorAddCredentialHelper != null) {
931                 mCstorAddCredentialHelper.mView = mView;
932             }
933 
934             hideError();
935             if (!mCstorAddCredentialHelper.isPkcs12Keystore()) {
936                 hide(R.id.cstor_credential_password_container);
937             }
938 
939             setText(R.id.cstor_credential_name_title,
940                     R.string.cstor_credential_name);
941             setText(R.id.cstor_credential_info_title,
942                     R.string.cstor_credential_info);
943             setText(R.id.cstor_credential_info,
944                     mCstorAddCredentialHelper.getDescription().toString());
945 
946             Dialog d = new AlertDialog.Builder(SecuritySettings.this)
947                     .setView(mView)
948                     .setTitle(R.string.cstor_name_credential_dialog_title)
949                     .setPositiveButton(android.R.string.ok, this)
950                     .setNegativeButton(android.R.string.cancel, this)
951                     .setOnCancelListener(this)
952                     .create();
953             d.setOnDismissListener(this);
954             return d;
955         }
956     }
957 
958     private class CstorAddCredentialHelper {
959         private String mTypeName;
960         private List<byte[]> mItemList;
961         private List<String> mNamespaceList;
962         private String mDescription;
963         private String mName;
964         private String mPassword;
965         private View mView;
966 
CstorAddCredentialHelper(Intent intent)967         CstorAddCredentialHelper(Intent intent) {
968             parse(intent);
969         }
970 
getTypeName()971         String getTypeName() {
972             return mTypeName;
973         }
974 
isPkcs12Keystore()975         boolean isPkcs12Keystore() {
976             return CertTool.TITLE_PKCS12_KEYSTORE.equals(mTypeName);
977         }
978 
getDescription()979         CharSequence getDescription() {
980             return Html.fromHtml(mDescription);
981         }
982 
setName(String name)983         void setName(String name) {
984             mName = name;
985         }
986 
getName()987         String getName() {
988             return mName;
989         }
990 
setPassword(String password)991         void setPassword(String password) {
992             mPassword = password;
993         }
994 
getPassword()995         String getPassword() {
996             return mPassword;
997         }
998 
saveToStorage()999         int saveToStorage() {
1000             if (isPkcs12Keystore()) {
1001                 return CertTool.getInstance().addPkcs12Keystore(
1002                         mItemList.get(0), mPassword, mName);
1003             } else {
1004                 Keystore ks = Keystore.getInstance();
1005                 for (int i = 0, count = mItemList.size(); i < count; i++) {
1006                     byte[] blob = mItemList.get(i);
1007                     int ret = ks.put(mNamespaceList.get(i), mName,
1008                             new String(blob));
1009                     if (ret != 0) return ret;
1010                 }
1011             }
1012             return 0;
1013         }
1014 
parse(Intent intent)1015         private void parse(Intent intent) {
1016             mTypeName = intent.getStringExtra(KEY_CSTOR_TYPE_NAME);
1017             mItemList = new ArrayList<byte[]>();
1018             mNamespaceList = new ArrayList<String>();
1019             for (int i = 0; ; i++) {
1020                 byte[] blob = intent.getByteArrayExtra(KEY_CSTOR_ITEM + i);
1021                 if (blob == null) break;
1022                 mItemList.add(blob);
1023                 mNamespaceList.add(intent.getStringExtra(
1024                         KEY_CSTOR_NAMESPACE + i));
1025             }
1026 
1027             // build description string
1028             StringBuilder sb = new StringBuilder();
1029             for (int i = 0; ; i++) {
1030                 String s = intent.getStringExtra(KEY_CSTOR_DESCRIPTION + i);
1031                 if (s == null) break;
1032                 sb.append(s).append("<br>");
1033             }
1034             mDescription = sb.toString();
1035         }
1036     }
1037 }
1038