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