2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
20 import java.util.Observable;
21 import java.util.Observer;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.app.admin.DevicePolicyManager;
26 import android.content.ContentQueryMap;
27 import android.content.ContentResolver;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.database.Cursor;
32 import android.location.LocationManager;
33 import android.os.Bundle;
34 import android.os.SystemProperties;
35 import android.preference.CheckBoxPreference;
36 import android.preference.Preference;
37 import android.preference.PreferenceActivity;
38 import android.preference.PreferenceCategory;
39 import android.preference.PreferenceManager;
40 import android.preference.PreferenceScreen;
41 import android.provider.Settings;
42 import android.security.Credentials;
43 import android.security.KeyStore;
44 import android.telephony.TelephonyManager;
45 import android.view.View;
46 import android.widget.TextView;
47 import android.widget.Toast;
49 import com.android.internal.widget.LockPatternUtils;
52 * Gesture lock pattern settings.
54 public class SecuritySettings extends PreferenceActivity {
55 private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change";
58 private static final String PACKAGE = "com.android.settings";
59 private static final String ICC_LOCK_SETTINGS = PACKAGE + ".IccLockSettings";
61 private static final String KEY_LOCK_ENABLED = "lockenabled";
62 private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
63 private static final String KEY_TACTILE_FEEDBACK_ENABLED = "unlock_tactile_feedback";
65 // Encrypted File Systems constants
66 private static final String PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
67 private static final String PROPERTY_EFS_TRANSITION = "persist.security.efs.trans";
69 private CheckBoxPreference mVisiblePattern;
70 private CheckBoxPreference mTactileFeedback;
72 private CheckBoxPreference mShowPassword;
75 private static final String LOCATION_NETWORK = "location_network";
76 private static final String LOCATION_GPS = "location_gps";
77 private static final String ASSISTED_GPS = "assisted_gps";
78 private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
81 private CredentialStorage mCredentialStorage = new CredentialStorage();
83 // Encrypted file system
84 private CheckBoxPreference mEncryptedFSEnabled;
86 private CheckBoxPreference mNetwork;
87 private CheckBoxPreference mGps;
88 private CheckBoxPreference mAssistedGps;
90 DevicePolicyManager mDPM;
92 // These provide support for receiving notification when Location Manager settings change.
93 // This is necessary because the Network Location Provider can change settings
94 // if the user does not confirm enabling the provider.
95 private ContentQueryMap mContentQueryMap;
96 private ChooseLockSettingsHelper mChooseLockSettingsHelper;
97 private LockPatternUtils mLockPatternUtils;
98 private final class SettingsObserver implements Observer {
99 public void update(Observable o, Object arg) {
105 protected void onCreate(Bundle savedInstanceState) {
106 super.onCreate(savedInstanceState);
108 mLockPatternUtils = new LockPatternUtils(this);
110 mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);
112 mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
114 createPreferenceHierarchy();
118 // listen for Location Manager settings changes
119 Cursor settingsCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, null,
120 "(" + Settings.System.NAME + "=?)",
121 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
123 mContentQueryMap = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, null);
124 mContentQueryMap.addObserver(new SettingsObserver());
127 private PreferenceScreen createPreferenceHierarchy() {
128 PreferenceScreen root = this.getPreferenceScreen();
132 addPreferencesFromResource(R.xml.security_settings);
133 root = this.getPreferenceScreen();
135 mNetwork = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_NETWORK);
136 mGps = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_GPS);
137 mAssistedGps = (CheckBoxPreference) getPreferenceScreen().findPreference(ASSISTED_GPS);
139 PreferenceManager pm = getPreferenceManager();
142 if (!mLockPatternUtils.isSecure()) {
143 addPreferencesFromResource(R.xml.security_settings_chooser);
145 switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) {
146 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
147 addPreferencesFromResource(R.xml.security_settings_pattern);
149 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
150 addPreferencesFromResource(R.xml.security_settings_pin);
152 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
153 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
154 addPreferencesFromResource(R.xml.security_settings_password);
159 // set or change current. Should be common to all unlock preference screens
160 // mSetOrChange = (PreferenceScreen) pm.findPreference(KEY_UNLOCK_SET_OR_CHANGE);
163 mVisiblePattern = (CheckBoxPreference) pm.findPreference(KEY_VISIBLE_PATTERN);
165 // tactile feedback. Should be common to all unlock preference screens.
166 mTactileFeedback = (CheckBoxPreference) pm.findPreference(KEY_TACTILE_FEEDBACK_ENABLED);
168 int activePhoneType = TelephonyManager.getDefault().getPhoneType();
170 // do not display SIM lock for CDMA phone
171 if (TelephonyManager.PHONE_TYPE_CDMA != activePhoneType)
173 PreferenceScreen simLockPreferences = getPreferenceManager()
174 .createPreferenceScreen(this);
175 simLockPreferences.setTitle(R.string.sim_lock_settings_category);
176 // Intent to launch SIM lock settings
177 simLockPreferences.setIntent(new Intent().setClassName(PACKAGE, ICC_LOCK_SETTINGS));
178 PreferenceCategory simLockCat = new PreferenceCategory(this);
179 simLockCat.setTitle(R.string.sim_lock_settings_title);
180 root.addPreference(simLockCat);
181 simLockCat.addPreference(simLockPreferences);
185 PreferenceCategory passwordsCat = new PreferenceCategory(this);
186 passwordsCat.setTitle(R.string.security_passwords_title);
187 root.addPreference(passwordsCat);
189 CheckBoxPreference showPassword = mShowPassword = new CheckBoxPreference(this);
190 showPassword.setKey("show_password");
191 showPassword.setTitle(R.string.show_password);
192 showPassword.setSummary(R.string.show_password_summary);
193 showPassword.setPersistent(false);
194 passwordsCat.addPreference(showPassword);
197 PreferenceCategory devicePoliciesCat = new PreferenceCategory(this);
198 devicePoliciesCat.setTitle(R.string.device_admin_title);
199 root.addPreference(devicePoliciesCat);
201 Preference deviceAdminButton = new Preference(this);
202 deviceAdminButton.setTitle(R.string.manage_device_admin);
203 deviceAdminButton.setSummary(R.string.manage_device_admin_summary);
204 Intent deviceAdminIntent = new Intent();
205 deviceAdminIntent.setClass(this, DeviceAdminSettings.class);
206 deviceAdminButton.setIntent(deviceAdminIntent);
207 devicePoliciesCat.addPreference(deviceAdminButton);
209 // Credential storage
210 PreferenceCategory credentialsCat = new PreferenceCategory(this);
211 credentialsCat.setTitle(R.string.credentials_category);
212 root.addPreference(credentialsCat);
213 mCredentialStorage.createPreferences(credentialsCat, CredentialStorage.TYPE_KEYSTORE);
215 // File System Encryption
216 PreferenceCategory encryptedfsCat = new PreferenceCategory(this);
217 encryptedfsCat.setTitle(R.string.encrypted_fs_category);
218 //root.addPreference(encryptedfsCat);
219 mCredentialStorage.createPreferences(encryptedfsCat, CredentialStorage.TYPE_ENCRYPTEDFS);
224 protected void onResume() {
227 final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
228 if (mVisiblePattern != null) {
229 mVisiblePattern.setChecked(lockPatternUtils.isVisiblePatternEnabled());
231 if (mTactileFeedback != null) {
232 mTactileFeedback.setChecked(lockPatternUtils.isTactileFeedbackEnabled());
235 mShowPassword.setChecked(Settings.System.getInt(getContentResolver(),
236 Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
238 mCredentialStorage.resume();
242 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
243 Preference preference) {
244 final String key = preference.getKey();
246 final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
247 if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
248 Intent intent = new Intent(this, ChooseLockGeneric.class);
249 startActivityForResult(intent, SET_OR_CHANGE_LOCK_METHOD_REQUEST);
250 } else if (KEY_LOCK_ENABLED.equals(key)) {
251 lockPatternUtils.setLockPatternEnabled(isToggled(preference));
252 } else if (KEY_VISIBLE_PATTERN.equals(key)) {
253 lockPatternUtils.setVisiblePatternEnabled(isToggled(preference));
254 } else if (KEY_TACTILE_FEEDBACK_ENABLED.equals(key)) {
255 lockPatternUtils.setTactileFeedbackEnabled(isToggled(preference));
256 } else if (preference == mShowPassword) {
257 Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD,
258 mShowPassword.isChecked() ? 1 : 0);
259 } else if (preference == mNetwork) {
260 Settings.Secure.setLocationProviderEnabled(getContentResolver(),
261 LocationManager.NETWORK_PROVIDER, mNetwork.isChecked());
262 } else if (preference == mGps) {
263 boolean enabled = mGps.isChecked();
264 Settings.Secure.setLocationProviderEnabled(getContentResolver(),
265 LocationManager.GPS_PROVIDER, enabled);
266 if (mAssistedGps != null) {
267 mAssistedGps.setEnabled(enabled);
269 } else if (preference == mAssistedGps) {
270 Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED,
271 mAssistedGps.isChecked() ? 1 : 0);
278 * Creates toggles for each available location provider
280 private void updateToggles() {
281 ContentResolver res = getContentResolver();
282 boolean gpsEnabled = Settings.Secure.isLocationProviderEnabled(
283 res, LocationManager.GPS_PROVIDER);
284 mNetwork.setChecked(Settings.Secure.isLocationProviderEnabled(
285 res, LocationManager.NETWORK_PROVIDER));
286 mGps.setChecked(gpsEnabled);
287 if (mAssistedGps != null) {
288 mAssistedGps.setChecked(Settings.Secure.getInt(res,
289 Settings.Secure.ASSISTED_GPS_ENABLED, 2) == 1);
290 mAssistedGps.setEnabled(gpsEnabled);
294 private boolean isToggled(Preference pref) {
295 return ((CheckBoxPreference) pref).isChecked();
299 * @see #confirmPatternThenDisableAndClear
302 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
303 super.onActivityResult(requestCode, resultCode, data);
304 createPreferenceHierarchy();
307 private class CredentialStorage implements DialogInterface.OnClickListener,
308 DialogInterface.OnDismissListener, Preference.OnPreferenceChangeListener,
309 Preference.OnPreferenceClickListener {
310 private static final int MINIMUM_PASSWORD_LENGTH = 8;
312 private static final int TYPE_KEYSTORE = 0;
313 private static final int TYPE_ENCRYPTEDFS = 1;
315 // Dialog identifiers
316 private static final int DLG_BASE = 0;
317 private static final int DLG_UNLOCK = DLG_BASE + 1;
318 private static final int DLG_PASSWORD = DLG_UNLOCK + 1;
319 private static final int DLG_RESET = DLG_PASSWORD + 1;
320 private static final int DLG_ENABLE_EFS = DLG_RESET + 1;
322 private KeyStore mKeyStore = KeyStore.getInstance();
324 private boolean mSubmit = false;
325 private boolean mExternal = false;
327 private boolean mWillEnableEncryptedFS;
328 private int mShowingDialog = 0;
330 // Key Store controls
331 private CheckBoxPreference mAccessCheckBox;
332 private Preference mInstallButton;
333 private Preference mPasswordButton;
334 private Preference mResetButton;
337 // Encrypted file system controls
338 private CheckBoxPreference mEncryptedFSEnabled;
341 mState = mKeyStore.test();
342 updatePreferences(mState);
344 Intent intent = getIntent();
345 if (!mExternal && intent != null &&
346 Credentials.UNLOCK_ACTION.equals(intent.getAction())) {
348 if (mState == KeyStore.UNINITIALIZED) {
349 showPasswordDialog();
350 } else if (mState == KeyStore.LOCKED) {
358 private void initialize(String password) {
359 mKeyStore.password(password);
360 updatePreferences(KeyStore.NO_ERROR);
363 private void reset() {
365 updatePreferences(KeyStore.UNINITIALIZED);
368 private void lock() {
370 updatePreferences(KeyStore.LOCKED);
373 private int unlock(String password) {
374 mKeyStore.unlock(password);
375 return mKeyStore.getLastError();
378 private int changePassword(String oldPassword, String newPassword) {
379 mKeyStore.password(oldPassword, newPassword);
380 return mKeyStore.getLastError();
383 public boolean onPreferenceChange(Preference preference, Object value) {
384 if (preference == mAccessCheckBox) {
385 if ((Boolean) value) {
391 } else if (preference == mEncryptedFSEnabled) {
392 Boolean bval = (Boolean)value;
393 mWillEnableEncryptedFS = bval.booleanValue();
394 showSwitchEncryptedFSDialog();
399 public boolean onPreferenceClick(Preference preference) {
400 if (preference == mInstallButton) {
401 Credentials.getInstance().installFromSdCard(SecuritySettings.this);
402 } else if (preference == mPasswordButton) {
403 showPasswordDialog();
404 } else if (preference == mResetButton) {
412 public void onClick(DialogInterface dialog, int button) {
413 if (mShowingDialog != DLG_ENABLE_EFS) {
414 mSubmit = (button == DialogInterface.BUTTON_POSITIVE);
415 if (button == DialogInterface.BUTTON_NEUTRAL) {
419 if (button == DialogInterface.BUTTON_POSITIVE) {
420 Intent intent = new Intent("android.intent.action.MASTER_CLEAR");
421 intent.putExtra("enableEFS", mWillEnableEncryptedFS);
422 sendBroadcast(intent);
423 updatePreferences(mState);
424 } else if (button == DialogInterface.BUTTON_NEGATIVE) {
426 Toast.makeText(SecuritySettings.this, R.string.encrypted_fs_cancel_confirm,
427 Toast.LENGTH_SHORT).show();
428 updatePreferences(mState);
430 // Unknown - should not happen
436 public void onDismiss(DialogInterface dialog) {
437 if (mSubmit && !isFinishing()) {
439 if (!checkPassword((Dialog) dialog)) {
440 ((Dialog) dialog).show();
444 updatePreferences(mState);
450 // Return true if there is no error.
451 private boolean checkPassword(Dialog dialog) {
452 String oldPassword = getText(dialog, R.id.old_password);
453 String newPassword = getText(dialog, R.id.new_password);
454 String confirmPassword = getText(dialog, R.id.confirm_password);
456 if (oldPassword != null && oldPassword.length() == 0) {
457 showError(dialog, R.string.credentials_password_empty);
459 } else if (newPassword == null) {
460 return !checkError(dialog, unlock(oldPassword));
461 } else if (newPassword.length() == 0 || confirmPassword.length() == 0) {
462 showError(dialog, R.string.credentials_passwords_empty);
463 } else if (newPassword.length() < MINIMUM_PASSWORD_LENGTH) {
464 showError(dialog, R.string.credentials_password_too_short);
465 } else if (!newPassword.equals(confirmPassword)) {
466 showError(dialog, R.string.credentials_passwords_mismatch);
467 } else if (oldPassword == null) {
468 initialize(newPassword);
471 return !checkError(dialog, changePassword(oldPassword, newPassword));
476 // Return false if there is no error.
477 private boolean checkError(Dialog dialog, int error) {
478 if (error == KeyStore.NO_ERROR) {
479 updatePreferences(KeyStore.NO_ERROR);
482 if (error == KeyStore.UNINITIALIZED) {
483 updatePreferences(KeyStore.UNINITIALIZED);
486 if (error < KeyStore.WRONG_PASSWORD) {
489 int count = error - KeyStore.WRONG_PASSWORD + 1;
491 showError(dialog, R.string.credentials_wrong_password);
492 } else if (count == 1) {
493 showError(dialog, R.string.credentials_reset_warning);
495 showError(dialog, R.string.credentials_reset_warning_plural, count);
500 private String getText(Dialog dialog, int viewId) {
501 TextView view = (TextView) dialog.findViewById(viewId);
502 return (view == null || view.getVisibility() == View.GONE) ? null :
503 view.getText().toString();
506 private void showError(Dialog dialog, int stringId, Object... formatArgs) {
507 TextView view = (TextView) dialog.findViewById(R.id.error);
509 if (formatArgs == null || formatArgs.length == 0) {
510 view.setText(stringId);
512 view.setText(dialog.getContext().getString(stringId, formatArgs));
514 view.setVisibility(View.VISIBLE);
518 private void createPreferences(PreferenceCategory category, int type) {
521 mAccessCheckBox = new CheckBoxPreference(SecuritySettings.this);
522 mAccessCheckBox.setTitle(R.string.credentials_access);
523 mAccessCheckBox.setSummary(R.string.credentials_access_summary);
524 mAccessCheckBox.setOnPreferenceChangeListener(this);
525 category.addPreference(mAccessCheckBox);
527 mInstallButton = new Preference(SecuritySettings.this);
528 mInstallButton.setTitle(R.string.credentials_install_certificates);
529 mInstallButton.setSummary(R.string.credentials_install_certificates_summary);
530 mInstallButton.setOnPreferenceClickListener(this);
531 category.addPreference(mInstallButton);
533 mPasswordButton = new Preference(SecuritySettings.this);
534 mPasswordButton.setTitle(R.string.credentials_set_password);
535 mPasswordButton.setSummary(R.string.credentials_set_password_summary);
536 mPasswordButton.setOnPreferenceClickListener(this);
537 category.addPreference(mPasswordButton);
539 mResetButton = new Preference(SecuritySettings.this);
540 mResetButton.setTitle(R.string.credentials_reset);
541 mResetButton.setSummary(R.string.credentials_reset_summary);
542 mResetButton.setOnPreferenceClickListener(this);
543 category.addPreference(mResetButton);
546 case TYPE_ENCRYPTEDFS:
547 mEncryptedFSEnabled = new CheckBoxPreference(SecuritySettings.this);
548 mEncryptedFSEnabled.setTitle(R.string.encrypted_fs_enable);
549 mEncryptedFSEnabled.setSummary(R.string.encrypted_fs_enable_summary);
550 mEncryptedFSEnabled.setOnPreferenceChangeListener(this);
551 // category.addPreference(mEncryptedFSEnabled);
556 private void updatePreferences(int state) {
557 mAccessCheckBox.setChecked(state == KeyStore.NO_ERROR);
558 boolean encFSEnabled = SystemProperties.getBoolean(PROPERTY_EFS_ENABLED,
560 mResetButton.setEnabled((!encFSEnabled) && (state != KeyStore.UNINITIALIZED));
561 mAccessCheckBox.setEnabled((state != KeyStore.UNINITIALIZED) && (!encFSEnabled));
563 // Encrypted File system preferences
564 mEncryptedFSEnabled.setChecked(encFSEnabled);
566 // Show a toast message if the state is changed.
567 if (mState == state) {
569 } else if (state == KeyStore.NO_ERROR) {
570 Toast.makeText(SecuritySettings.this, R.string.credentials_enabled,
571 Toast.LENGTH_SHORT).show();
572 } else if (state == KeyStore.UNINITIALIZED) {
573 Toast.makeText(SecuritySettings.this, R.string.credentials_erased,
574 Toast.LENGTH_SHORT).show();
575 } else if (state == KeyStore.LOCKED) {
576 Toast.makeText(SecuritySettings.this, R.string.credentials_disabled,
577 Toast.LENGTH_SHORT).show();
582 private void showUnlockDialog() {
583 View view = View.inflate(SecuritySettings.this,
584 R.layout.credentials_unlock_dialog, null);
586 // Show extra hint only when the action comes from outside.
588 view.findViewById(R.id.hint).setVisibility(View.VISIBLE);
591 Dialog dialog = new AlertDialog.Builder(SecuritySettings.this)
593 .setTitle(R.string.credentials_unlock)
594 .setPositiveButton(android.R.string.ok, this)
595 .setNegativeButton(android.R.string.cancel, this)
597 dialog.setOnDismissListener(this);
598 mShowingDialog = DLG_UNLOCK;
602 private void showPasswordDialog() {
603 View view = View.inflate(SecuritySettings.this,
604 R.layout.credentials_password_dialog, null);
606 if (mState == KeyStore.UNINITIALIZED) {
607 view.findViewById(R.id.hint).setVisibility(View.VISIBLE);
609 view.findViewById(R.id.old_password_prompt).setVisibility(View.VISIBLE);
610 view.findViewById(R.id.old_password).setVisibility(View.VISIBLE);
613 Dialog dialog = new AlertDialog.Builder(SecuritySettings.this)
615 .setTitle(R.string.credentials_set_password)
616 .setPositiveButton(android.R.string.ok, this)
617 .setNegativeButton(android.R.string.cancel, this)
619 dialog.setOnDismissListener(this);
620 mShowingDialog = DLG_PASSWORD;
624 private void showResetDialog() {
625 mShowingDialog = DLG_RESET;
626 new AlertDialog.Builder(SecuritySettings.this)
627 .setTitle(android.R.string.dialog_alert_title)
628 .setIcon(android.R.drawable.ic_dialog_alert)
629 .setMessage(R.string.credentials_reset_hint)
630 .setNeutralButton(getString(android.R.string.ok), this)
631 .setNegativeButton(getString(android.R.string.cancel), this)
635 private void showSwitchEncryptedFSDialog() {
636 AlertDialog.Builder builder = new AlertDialog.Builder(SecuritySettings.this)
637 .setCancelable(false)
638 .setTitle(R.string.encrypted_fs_alert_dialog_title);
640 mShowingDialog = DLG_ENABLE_EFS;
641 if (mWillEnableEncryptedFS) {
642 builder.setMessage(R.string.encrypted_fs_enable_dialog)
643 .setPositiveButton(R.string.encrypted_fs_enable_button, this)
644 .setNegativeButton(R.string.encrypted_fs_cancel_button, this)
647 builder.setMessage(R.string.encrypted_fs_disable_dialog)
648 .setPositiveButton(R.string.encrypted_fs_disable_button, this)
649 .setNegativeButton(R.string.encrypted_fs_cancel_button, this)