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