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