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 static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; 21 22 import android.app.Activity; 23 import android.app.AlertDialog; 24 import android.app.admin.DevicePolicyManager; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.os.Bundle; 29 import android.os.UserId; 30 import android.os.Vibrator; 31 import android.preference.CheckBoxPreference; 32 import android.preference.ListPreference; 33 import android.preference.Preference; 34 import android.preference.Preference.OnPreferenceChangeListener; 35 import android.preference.PreferenceGroup; 36 import android.preference.PreferenceScreen; 37 import android.provider.Settings; 38 import android.security.KeyStore; 39 import android.telephony.TelephonyManager; 40 import android.util.Log; 41 42 import com.android.internal.telephony.Phone; 43 import com.android.internal.widget.LockPatternUtils; 44 45 import java.util.ArrayList; 46 47 /** 48 * Gesture lock pattern settings. 49 */ 50 public class SecuritySettings extends SettingsPreferenceFragment 51 implements OnPreferenceChangeListener, DialogInterface.OnClickListener { 52 53 // Lock Settings 54 private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change"; 55 private static final String KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING = 56 "biometric_weak_improve_matching"; 57 private static final String KEY_BIOMETRIC_WEAK_LIVELINESS = "biometric_weak_liveliness"; 58 private static final String KEY_LOCK_ENABLED = "lockenabled"; 59 private static final String KEY_VISIBLE_PATTERN = "visiblepattern"; 60 private static final String KEY_TACTILE_FEEDBACK_ENABLED = "unlock_tactile_feedback"; 61 private static final String KEY_SECURITY_CATEGORY = "security_category"; 62 private static final String KEY_LOCK_AFTER_TIMEOUT = "lock_after_timeout"; 63 private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123; 64 private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST = 124; 65 private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF = 125; 66 67 // Misc Settings 68 private static final String KEY_SIM_LOCK = "sim_lock"; 69 private static final String KEY_SHOW_PASSWORD = "show_password"; 70 private static final String KEY_RESET_CREDENTIALS = "reset_credentials"; 71 private static final String KEY_TOGGLE_INSTALL_APPLICATIONS = "toggle_install_applications"; 72 private static final String KEY_POWER_INSTANTLY_LOCKS = "power_button_instantly_locks"; 73 74 DevicePolicyManager mDPM; 75 76 private ChooseLockSettingsHelper mChooseLockSettingsHelper; 77 private LockPatternUtils mLockPatternUtils; 78 private ListPreference mLockAfter; 79 80 private CheckBoxPreference mBiometricWeakLiveliness; 81 private CheckBoxPreference mVisiblePattern; 82 private CheckBoxPreference mTactileFeedback; 83 84 private CheckBoxPreference mShowPassword; 85 86 private Preference mResetCredentials; 87 88 private CheckBoxPreference mToggleAppInstallation; 89 private DialogInterface mWarnInstallApps; 90 private CheckBoxPreference mPowerButtonInstantlyLocks; 91 92 @Override 93 public void onCreate(Bundle savedInstanceState) { 94 super.onCreate(savedInstanceState); 95 96 mLockPatternUtils = new LockPatternUtils(getActivity()); 97 98 mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE); 99 100 mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity()); 101 } 102 103 private PreferenceScreen createPreferenceHierarchy() { 104 PreferenceScreen root = getPreferenceScreen(); 105 if (root != null) { 106 root.removeAll(); 107 } 108 addPreferencesFromResource(R.xml.security_settings); 109 root = getPreferenceScreen(); 110 111 // Add options for lock/unlock screen 112 int resid = 0; 113 if (!mLockPatternUtils.isSecure()) { 114 if (mLockPatternUtils.isLockScreenDisabled()) { 115 resid = R.xml.security_settings_lockscreen; 116 } else { 117 resid = R.xml.security_settings_chooser; 118 } 119 } else if (mLockPatternUtils.usingBiometricWeak() && 120 mLockPatternUtils.isBiometricWeakInstalled()) { 121 resid = R.xml.security_settings_biometric_weak; 122 } else { 123 switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) { 124 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 125 resid = R.xml.security_settings_pattern; 126 break; 127 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 128 resid = R.xml.security_settings_pin; 129 break; 130 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: 131 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 132 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: 133 resid = R.xml.security_settings_password; 134 break; 135 } 136 } 137 addPreferencesFromResource(resid); 138 139 140 // Add options for device encryption 141 DevicePolicyManager dpm = 142 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 143 144 if (UserId.myUserId() == 0) { 145 switch (dpm.getStorageEncryptionStatus()) { 146 case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE: 147 // The device is currently encrypted. 148 addPreferencesFromResource(R.xml.security_settings_encrypted); 149 break; 150 case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE: 151 // This device supports encryption but isn't encrypted. 152 addPreferencesFromResource(R.xml.security_settings_unencrypted); 153 break; 154 } 155 } 156 157 // lock after preference 158 mLockAfter = (ListPreference) root.findPreference(KEY_LOCK_AFTER_TIMEOUT); 159 if (mLockAfter != null) { 160 setupLockAfterPreference(); 161 updateLockAfterPreferenceSummary(); 162 } 163 164 // biometric weak liveliness 165 mBiometricWeakLiveliness = 166 (CheckBoxPreference) root.findPreference(KEY_BIOMETRIC_WEAK_LIVELINESS); 167 168 // visible pattern 169 mVisiblePattern = (CheckBoxPreference) root.findPreference(KEY_VISIBLE_PATTERN); 170 171 // lock instantly on power key press 172 mPowerButtonInstantlyLocks = (CheckBoxPreference) root.findPreference( 173 KEY_POWER_INSTANTLY_LOCKS); 174 175 // don't display visible pattern if biometric and backup is not pattern 176 if (resid == R.xml.security_settings_biometric_weak && 177 mLockPatternUtils.getKeyguardStoredPasswordQuality() != 178 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { 179 PreferenceGroup securityCategory = (PreferenceGroup) 180 root.findPreference(KEY_SECURITY_CATEGORY); 181 if (securityCategory != null && mVisiblePattern != null) { 182 securityCategory.removePreference(root.findPreference(KEY_VISIBLE_PATTERN)); 183 } 184 } 185 186 // tactile feedback. Should be common to all unlock preference screens. 187 mTactileFeedback = (CheckBoxPreference) root.findPreference(KEY_TACTILE_FEEDBACK_ENABLED); 188 if (!((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator()) { 189 PreferenceGroup securityCategory = (PreferenceGroup) 190 root.findPreference(KEY_SECURITY_CATEGORY); 191 if (securityCategory != null && mTactileFeedback != null) { 192 securityCategory.removePreference(mTactileFeedback); 193 } 194 } 195 196 if (UserId.myUserId() > 0) { 197 return root; 198 } 199 // Rest are for primary user... 200 201 // Append the rest of the settings 202 addPreferencesFromResource(R.xml.security_settings_misc); 203 204 // Do not display SIM lock for devices without an Icc card 205 TelephonyManager tm = TelephonyManager.getDefault(); 206 if (!tm.hasIccCard()) { 207 root.removePreference(root.findPreference(KEY_SIM_LOCK)); 208 } else { 209 // Disable SIM lock if sim card is missing or unknown 210 if ((TelephonyManager.getDefault().getSimState() == 211 TelephonyManager.SIM_STATE_ABSENT) || 212 (TelephonyManager.getDefault().getSimState() == 213 TelephonyManager.SIM_STATE_UNKNOWN)) { 214 root.findPreference(KEY_SIM_LOCK).setEnabled(false); 215 } 216 } 217 218 // Show password 219 mShowPassword = (CheckBoxPreference) root.findPreference(KEY_SHOW_PASSWORD); 220 221 // Credential storage 222 mResetCredentials = root.findPreference(KEY_RESET_CREDENTIALS); 223 224 mToggleAppInstallation = (CheckBoxPreference) findPreference( 225 KEY_TOGGLE_INSTALL_APPLICATIONS); 226 mToggleAppInstallation.setChecked(isNonMarketAppsAllowed()); 227 228 return root; 229 } 230 231 private boolean isNonMarketAppsAllowed() { 232 return Settings.Secure.getInt(getContentResolver(), 233 Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0; 234 } 235 236 private void setNonMarketAppsAllowed(boolean enabled) { 237 // Change the system setting 238 Settings.Secure.putInt(getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 239 enabled ? 1 : 0); 240 } 241 242 private void warnAppInstallation() { 243 // TODO: DialogFragment? 244 mWarnInstallApps = new AlertDialog.Builder(getActivity()).setTitle( 245 getResources().getString(R.string.error_title)) 246 .setIcon(com.android.internal.R.drawable.ic_dialog_alert) 247 .setMessage(getResources().getString(R.string.install_all_warning)) 248 .setPositiveButton(android.R.string.yes, this) 249 .setNegativeButton(android.R.string.no, null) 250 .show(); 251 } 252 253 public void onClick(DialogInterface dialog, int which) { 254 if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON_POSITIVE) { 255 setNonMarketAppsAllowed(true); 256 if (mToggleAppInstallation != null) { 257 mToggleAppInstallation.setChecked(true); 258 } 259 } 260 } 261 262 @Override 263 public void onDestroy() { 264 super.onDestroy(); 265 if (mWarnInstallApps != null) { 266 mWarnInstallApps.dismiss(); 267 } 268 } 269 270 private void setupLockAfterPreference() { 271 // Compatible with pre-Froyo 272 long currentTimeout = Settings.Secure.getLong(getContentResolver(), 273 Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 5000); 274 mLockAfter.setValue(String.valueOf(currentTimeout)); 275 mLockAfter.setOnPreferenceChangeListener(this); 276 final long adminTimeout = (mDPM != null ? mDPM.getMaximumTimeToLock(null) : 0); 277 final long displayTimeout = Math.max(0, 278 Settings.System.getInt(getContentResolver(), SCREEN_OFF_TIMEOUT, 0)); 279 if (adminTimeout > 0) { 280 // This setting is a slave to display timeout when a device policy is enforced. 281 // As such, maxLockTimeout = adminTimeout - displayTimeout. 282 // If there isn't enough time, shows "immediately" setting. 283 disableUnusableTimeouts(Math.max(0, adminTimeout - displayTimeout)); 284 } 285 } 286 287 private void updateLockAfterPreferenceSummary() { 288 // Update summary message with current value 289 long currentTimeout = Settings.Secure.getLong(getContentResolver(), 290 Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 5000); 291 final CharSequence[] entries = mLockAfter.getEntries(); 292 final CharSequence[] values = mLockAfter.getEntryValues(); 293 int best = 0; 294 for (int i = 0; i < values.length; i++) { 295 long timeout = Long.valueOf(values[i].toString()); 296 if (currentTimeout >= timeout) { 297 best = i; 298 } 299 } 300 mLockAfter.setSummary(getString(R.string.lock_after_timeout_summary, entries[best])); 301 } 302 303 private void disableUnusableTimeouts(long maxTimeout) { 304 final CharSequence[] entries = mLockAfter.getEntries(); 305 final CharSequence[] values = mLockAfter.getEntryValues(); 306 ArrayList<CharSequence> revisedEntries = new ArrayList<CharSequence>(); 307 ArrayList<CharSequence> revisedValues = new ArrayList<CharSequence>(); 308 for (int i = 0; i < values.length; i++) { 309 long timeout = Long.valueOf(values[i].toString()); 310 if (timeout <= maxTimeout) { 311 revisedEntries.add(entries[i]); 312 revisedValues.add(values[i]); 313 } 314 } 315 if (revisedEntries.size() != entries.length || revisedValues.size() != values.length) { 316 mLockAfter.setEntries( 317 revisedEntries.toArray(new CharSequence[revisedEntries.size()])); 318 mLockAfter.setEntryValues( 319 revisedValues.toArray(new CharSequence[revisedValues.size()])); 320 final int userPreference = Integer.valueOf(mLockAfter.getValue()); 321 if (userPreference <= maxTimeout) { 322 mLockAfter.setValue(String.valueOf(userPreference)); 323 } else { 324 // There will be no highlighted selection since nothing in the list matches 325 // maxTimeout. The user can still select anything less than maxTimeout. 326 // TODO: maybe append maxTimeout to the list and mark selected. 327 } 328 } 329 mLockAfter.setEnabled(revisedEntries.size() > 0); 330 } 331 332 @Override 333 public void onResume() { 334 super.onResume(); 335 336 // Make sure we reload the preference hierarchy since some of these settings 337 // depend on others... 338 createPreferenceHierarchy(); 339 340 final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); 341 if (mBiometricWeakLiveliness != null) { 342 mBiometricWeakLiveliness.setChecked( 343 lockPatternUtils.isBiometricWeakLivelinessEnabled()); 344 } 345 if (mVisiblePattern != null) { 346 mVisiblePattern.setChecked(lockPatternUtils.isVisiblePatternEnabled()); 347 } 348 if (mTactileFeedback != null) { 349 mTactileFeedback.setChecked(lockPatternUtils.isTactileFeedbackEnabled()); 350 } 351 if (mPowerButtonInstantlyLocks != null) { 352 mPowerButtonInstantlyLocks.setChecked(lockPatternUtils.getPowerButtonInstantlyLocks()); 353 } 354 355 if (mShowPassword != null) { 356 mShowPassword.setChecked(Settings.System.getInt(getContentResolver(), 357 Settings.System.TEXT_SHOW_PASSWORD, 1) != 0); 358 } 359 360 KeyStore.State state = KeyStore.getInstance().state(); 361 if (mResetCredentials != null) { 362 mResetCredentials.setEnabled(state != KeyStore.State.UNINITIALIZED); 363 } 364 } 365 366 @Override 367 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 368 final String key = preference.getKey(); 369 370 final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); 371 if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { 372 startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment", 373 SET_OR_CHANGE_LOCK_METHOD_REQUEST, null); 374 } else if (KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING.equals(key)) { 375 ChooseLockSettingsHelper helper = 376 new ChooseLockSettingsHelper(this.getActivity(), this); 377 if (!helper.launchConfirmationActivity( 378 CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST, null, null)) { 379 // If this returns false, it means no password confirmation is required, so 380 // go ahead and start improve. 381 // Note: currently a backup is required for biometric_weak so this code path 382 // can't be reached, but is here in case things change in the future 383 startBiometricWeakImprove(); 384 } 385 } else if (KEY_BIOMETRIC_WEAK_LIVELINESS.equals(key)) { 386 if (isToggled(preference)) { 387 lockPatternUtils.setBiometricWeakLivelinessEnabled(true); 388 } else { 389 // In this case the user has just unchecked the checkbox, but this action requires 390 // them to confirm their password. We need to re-check the checkbox until 391 // they've confirmed their password 392 mBiometricWeakLiveliness.setChecked(true); 393 ChooseLockSettingsHelper helper = 394 new ChooseLockSettingsHelper(this.getActivity(), this); 395 if (!helper.launchConfirmationActivity( 396 CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF, null, null)) { 397 // If this returns false, it means no password confirmation is required, so 398 // go ahead and uncheck it here. 399 // Note: currently a backup is required for biometric_weak so this code path 400 // can't be reached, but is here in case things change in the future 401 lockPatternUtils.setBiometricWeakLivelinessEnabled(false); 402 mBiometricWeakLiveliness.setChecked(false); 403 } 404 } 405 } else if (KEY_LOCK_ENABLED.equals(key)) { 406 lockPatternUtils.setLockPatternEnabled(isToggled(preference)); 407 } else if (KEY_VISIBLE_PATTERN.equals(key)) { 408 lockPatternUtils.setVisiblePatternEnabled(isToggled(preference)); 409 } else if (KEY_TACTILE_FEEDBACK_ENABLED.equals(key)) { 410 lockPatternUtils.setTactileFeedbackEnabled(isToggled(preference)); 411 } else if (KEY_POWER_INSTANTLY_LOCKS.equals(key)) { 412 lockPatternUtils.setPowerButtonInstantlyLocks(isToggled(preference)); 413 } else if (preference == mShowPassword) { 414 Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, 415 mShowPassword.isChecked() ? 1 : 0); 416 } else if (preference == mToggleAppInstallation) { 417 if (mToggleAppInstallation.isChecked()) { 418 mToggleAppInstallation.setChecked(false); 419 warnAppInstallation(); 420 } else { 421 setNonMarketAppsAllowed(false); 422 } 423 } else { 424 // If we didn't handle it, let preferences handle it. 425 return super.onPreferenceTreeClick(preferenceScreen, preference); 426 } 427 428 return true; 429 } 430 431 private boolean isToggled(Preference pref) { 432 return ((CheckBoxPreference) pref).isChecked(); 433 } 434 435 /** 436 * see confirmPatternThenDisableAndClear 437 */ 438 @Override 439 public void onActivityResult(int requestCode, int resultCode, Intent data) { 440 super.onActivityResult(requestCode, resultCode, data); 441 if (requestCode == CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST && 442 resultCode == Activity.RESULT_OK) { 443 startBiometricWeakImprove(); 444 return; 445 } else if (requestCode == CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF && 446 resultCode == Activity.RESULT_OK) { 447 final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); 448 lockPatternUtils.setBiometricWeakLivelinessEnabled(false); 449 mBiometricWeakLiveliness.setChecked(false); 450 return; 451 } 452 createPreferenceHierarchy(); 453 } 454 455 public boolean onPreferenceChange(Preference preference, Object value) { 456 if (preference == mLockAfter) { 457 int timeout = Integer.parseInt((String) value); 458 try { 459 Settings.Secure.putInt(getContentResolver(), 460 Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, timeout); 461 } catch (NumberFormatException e) { 462 Log.e("SecuritySettings", "could not persist lockAfter timeout setting", e); 463 } 464 updateLockAfterPreferenceSummary(); 465 } 466 return true; 467 } 468 469 public void startBiometricWeakImprove(){ 470 Intent intent = new Intent(); 471 intent.setClassName("com.android.facelock", "com.android.facelock.AddToSetup"); 472 startActivity(intent); 473 } 474 } 475