/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; import com.android.internal.widget.LockPatternUtils; import android.app.admin.DevicePolicyManager; import android.content.ContentQueryMap; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.location.LocationManager; import android.os.Bundle; import android.os.SystemProperties; import android.os.Vibrator; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings; import android.security.KeyStore; import android.telephony.TelephonyManager; import android.util.Log; import java.util.ArrayList; import java.util.Observable; import java.util.Observer; /** * Gesture lock pattern settings. */ public class SecuritySettings extends SettingsPreferenceFragment implements OnPreferenceChangeListener { // Lock Settings private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change"; private static final String KEY_LOCK_ENABLED = "lockenabled"; private static final String KEY_VISIBLE_PATTERN = "visiblepattern"; private static final String KEY_TACTILE_FEEDBACK_ENABLED = "unlock_tactile_feedback"; private static final String KEY_SECURITY_CATEGORY = "security_category"; private static final String KEY_LOCK_AFTER_TIMEOUT = "lock_after_timeout"; private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123; // Location Settings private static final String KEY_LOCATION_CATEGORY = "location_category"; private static final String KEY_LOCATION_NETWORK = "location_network"; private static final String KEY_LOCATION_GPS = "location_gps"; private static final String KEY_ASSISTED_GPS = "assisted_gps"; private static final String KEY_USE_LOCATION = "location_use_for_services"; // Misc Settings private static final String KEY_SIM_LOCK = "sim_lock"; private static final String KEY_SHOW_PASSWORD = "show_password"; private static final String KEY_ENABLE_CREDENTIALS = "enable_credentials"; private static final String KEY_RESET_CREDENTIALS = "reset_credentials"; private static final String TAG = "SecuritySettings"; private CheckBoxPreference mNetwork; private CheckBoxPreference mGps; private CheckBoxPreference mAssistedGps; private CheckBoxPreference mUseLocation; DevicePolicyManager mDPM; // These provide support for receiving notification when Location Manager settings change. // This is necessary because the Network Location Provider can change settings // if the user does not confirm enabling the provider. private ContentQueryMap mContentQueryMap; private ChooseLockSettingsHelper mChooseLockSettingsHelper; private LockPatternUtils mLockPatternUtils; private ListPreference mLockAfter; private Observer mSettingsObserver; private CheckBoxPreference mVisiblePattern; private CheckBoxPreference mTactileFeedback; private CheckBoxPreference mShowPassword; private CheckBoxPreference mEnableCredentials; private Preference mResetCredentials; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLockPatternUtils = new LockPatternUtils(getActivity()); mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE); mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity()); } @Override public void onStart() { super.onStart(); // listen for Location Manager settings changes Cursor settingsCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, null, "(" + Settings.System.NAME + "=?)", new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null); mContentQueryMap = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, null); } @Override public void onStop() { super.onStop(); if (mSettingsObserver != null) { mContentQueryMap.deleteObserver(mSettingsObserver); } } private PreferenceScreen createPreferenceHierarchy() { PreferenceScreen root = getPreferenceScreen(); if (root != null) { root.removeAll(); } addPreferencesFromResource(R.xml.security_settings); root = getPreferenceScreen(); mNetwork = (CheckBoxPreference) root.findPreference(KEY_LOCATION_NETWORK); mGps = (CheckBoxPreference) root.findPreference(KEY_LOCATION_GPS); mAssistedGps = (CheckBoxPreference) root.findPreference(KEY_ASSISTED_GPS); if (GoogleLocationSettingHelper.isAvailable(getActivity())) { // GSF present, Add setting for 'Use My Location' PreferenceGroup locationCat = (PreferenceGroup) root.findPreference(KEY_LOCATION_CATEGORY); CheckBoxPreference useLocation = new CheckBoxPreference(getActivity()); useLocation.setKey(KEY_USE_LOCATION); useLocation.setTitle(R.string.use_location_title); useLocation.setSummaryOn(R.string.use_location_summary_enabled); useLocation.setSummaryOff(R.string.use_location_summary_disabled); useLocation.setChecked( GoogleLocationSettingHelper.getUseLocationForServices(getActivity()) == GoogleLocationSettingHelper.USE_LOCATION_FOR_SERVICES_ON); useLocation.setPersistent(false); useLocation.setOnPreferenceChangeListener(this); locationCat.addPreference(useLocation); mUseLocation = useLocation; } // Add options for lock/unlock screen int resid = 0; if (!mLockPatternUtils.isSecure()) { if (mLockPatternUtils.isLockScreenDisabled()) { resid = R.xml.security_settings_lockscreen; } else { resid = R.xml.security_settings_chooser; } } else { switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) { case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: resid = R.xml.security_settings_pattern; break; case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: resid = R.xml.security_settings_pin; break; case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: resid = R.xml.security_settings_password; break; } } addPreferencesFromResource(resid); // Add options for device encryption String status = SystemProperties.get("ro.crypto.state", "unsupported"); if ("encrypted".equalsIgnoreCase(status)) { // The device is currently encrypted addPreferencesFromResource(R.xml.security_settings_encrypted); } else if ("unencrypted".equalsIgnoreCase(status)) { // This device support encryption but isn't encrypted addPreferencesFromResource(R.xml.security_settings_unencrypted); } // lock after preference mLockAfter = (ListPreference) root.findPreference(KEY_LOCK_AFTER_TIMEOUT); if (mLockAfter != null) { setupLockAfterPreference(); updateLockAfterPreferenceSummary(); } // visible pattern mVisiblePattern = (CheckBoxPreference) root.findPreference(KEY_VISIBLE_PATTERN); // tactile feedback. Should be common to all unlock preference screens. mTactileFeedback = (CheckBoxPreference) root.findPreference(KEY_TACTILE_FEEDBACK_ENABLED); if (!((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator()) { PreferenceGroup securityCategory = (PreferenceGroup) root.findPreference(KEY_SECURITY_CATEGORY); if (securityCategory != null && mTactileFeedback != null) { securityCategory.removePreference(mTactileFeedback); } } // Append the rest of the settings addPreferencesFromResource(R.xml.security_settings_misc); // Do not display SIM lock for CDMA phone if (TelephonyManager.PHONE_TYPE_CDMA == TelephonyManager.getDefault().getPhoneType()) { root.removePreference(root.findPreference(KEY_SIM_LOCK)); } // Show password mShowPassword = (CheckBoxPreference) root.findPreference(KEY_SHOW_PASSWORD); // Credential storage mEnableCredentials = (CheckBoxPreference) root.findPreference(KEY_ENABLE_CREDENTIALS); mEnableCredentials.setOnPreferenceChangeListener(this); mResetCredentials = root.findPreference(KEY_RESET_CREDENTIALS); return root; } private void setupLockAfterPreference() { // Compatible with pre-Froyo long currentTimeout = Settings.Secure.getLong(getContentResolver(), Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 5000); mLockAfter.setValue(String.valueOf(currentTimeout)); mLockAfter.setOnPreferenceChangeListener(this); final long adminTimeout = (mDPM != null ? mDPM.getMaximumTimeToLock(null) : 0); final long displayTimeout = Math.max(0, Settings.System.getInt(getContentResolver(), SCREEN_OFF_TIMEOUT, 0)); if (adminTimeout > 0) { // This setting is a slave to display timeout when a device policy is enforced. // As such, maxLockTimeout = adminTimeout - displayTimeout. // If there isn't enough time, shows "immediately" setting. disableUnusableTimeouts(Math.max(0, adminTimeout - displayTimeout)); } } private void updateLockAfterPreferenceSummary() { // Update summary message with current value long currentTimeout = Settings.Secure.getLong(getContentResolver(), Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 0); final CharSequence[] entries = mLockAfter.getEntries(); final CharSequence[] values = mLockAfter.getEntryValues(); int best = 0; for (int i = 0; i < values.length; i++) { long timeout = Long.valueOf(values[i].toString()); if (currentTimeout >= timeout) { best = i; } } mLockAfter.setSummary(getString(R.string.lock_after_timeout_summary, entries[best])); } private void disableUnusableTimeouts(long maxTimeout) { final CharSequence[] entries = mLockAfter.getEntries(); final CharSequence[] values = mLockAfter.getEntryValues(); ArrayList revisedEntries = new ArrayList(); ArrayList revisedValues = new ArrayList(); for (int i = 0; i < values.length; i++) { long timeout = Long.valueOf(values[i].toString()); if (timeout <= maxTimeout) { revisedEntries.add(entries[i]); revisedValues.add(values[i]); } } if (revisedEntries.size() != entries.length || revisedValues.size() != values.length) { mLockAfter.setEntries( revisedEntries.toArray(new CharSequence[revisedEntries.size()])); mLockAfter.setEntryValues( revisedValues.toArray(new CharSequence[revisedValues.size()])); final int userPreference = Integer.valueOf(mLockAfter.getValue()); if (userPreference <= maxTimeout) { mLockAfter.setValue(String.valueOf(userPreference)); } else { // There will be no highlighted selection since nothing in the list matches // maxTimeout. The user can still select anything less than maxTimeout. // TODO: maybe append maxTimeout to the list and mark selected. } } mLockAfter.setEnabled(revisedEntries.size() > 0); } @Override public void onResume() { super.onResume(); // Make sure we reload the preference hierarchy since some of these settings // depend on others... createPreferenceHierarchy(); updateLocationToggles(); if (mSettingsObserver == null) { mSettingsObserver = new Observer() { public void update(Observable o, Object arg) { updateLocationToggles(); } }; mContentQueryMap.addObserver(mSettingsObserver); } final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); if (mVisiblePattern != null) { mVisiblePattern.setChecked(lockPatternUtils.isVisiblePatternEnabled()); } if (mTactileFeedback != null) { mTactileFeedback.setChecked(lockPatternUtils.isTactileFeedbackEnabled()); } mShowPassword.setChecked(Settings.System.getInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, 1) != 0); int state = KeyStore.getInstance().test(); mEnableCredentials.setChecked(state == KeyStore.NO_ERROR); mEnableCredentials.setEnabled(state != KeyStore.UNINITIALIZED); mResetCredentials.setEnabled(state != KeyStore.UNINITIALIZED); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { final String key = preference.getKey(); final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment", SET_OR_CHANGE_LOCK_METHOD_REQUEST, null); } else if (KEY_LOCK_ENABLED.equals(key)) { lockPatternUtils.setLockPatternEnabled(isToggled(preference)); } else if (KEY_VISIBLE_PATTERN.equals(key)) { lockPatternUtils.setVisiblePatternEnabled(isToggled(preference)); } else if (KEY_TACTILE_FEEDBACK_ENABLED.equals(key)) { lockPatternUtils.setTactileFeedbackEnabled(isToggled(preference)); } else if (preference == mShowPassword) { Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, mShowPassword.isChecked() ? 1 : 0); } else if (preference == mNetwork) { Settings.Secure.setLocationProviderEnabled(getContentResolver(), LocationManager.NETWORK_PROVIDER, mNetwork.isChecked()); } else if (preference == mGps) { boolean enabled = mGps.isChecked(); Settings.Secure.setLocationProviderEnabled(getContentResolver(), LocationManager.GPS_PROVIDER, enabled); if (mAssistedGps != null) { mAssistedGps.setEnabled(enabled); } } else if (preference == mAssistedGps) { Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED, mAssistedGps.isChecked() ? 1 : 0); } else { // If we didn't handle it, let preferences handle it. return super.onPreferenceTreeClick(preferenceScreen, preference); } return true; } /* * Creates toggles for each available location provider */ private void updateLocationToggles() { ContentResolver res = getContentResolver(); boolean gpsEnabled = Settings.Secure.isLocationProviderEnabled( res, LocationManager.GPS_PROVIDER); mNetwork.setChecked(Settings.Secure.isLocationProviderEnabled( res, LocationManager.NETWORK_PROVIDER)); mGps.setChecked(gpsEnabled); if (mAssistedGps != null) { mAssistedGps.setChecked(Settings.Secure.getInt(res, Settings.Secure.ASSISTED_GPS_ENABLED, 2) == 1); mAssistedGps.setEnabled(gpsEnabled); } } private boolean isToggled(Preference pref) { return ((CheckBoxPreference) pref).isChecked(); } /** * @see #confirmPatternThenDisableAndClear */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); createPreferenceHierarchy(); } public boolean onPreferenceChange(Preference preference, Object value) { if (preference == mLockAfter) { int timeout = Integer.parseInt((String) value); try { Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, timeout); } catch (NumberFormatException e) { Log.e("SecuritySettings", "could not persist lockAfter timeout setting", e); } updateLockAfterPreferenceSummary(); } else if (preference == mUseLocation) { boolean newValue = (value == null ? false : (Boolean) value); GoogleLocationSettingHelper.setUseLocationForServices(getActivity(), newValue); // We don't want to change the value immediately here, since the user may click // disagree in the dialog that pops up. When the activity we just launched exits, this // activity will be restated and the new value re-read, so the checkbox will get its // new value then. return false; } else if (preference == mEnableCredentials) { if (value != null && (Boolean) value) { getActivity().startActivity(new Intent(CredentialStorage.ACTION_UNLOCK)); return false; } else { KeyStore.getInstance().lock(); } } return true; } }