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