SecuritySettings.java revision 666800a237066457c5703fc7c746a5d477249d59
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 com.android.internal.widget.LockPatternUtils;
23
24import android.app.admin.DevicePolicyManager;
25import android.content.ContentQueryMap;
26import android.content.ContentResolver;
27import android.content.Context;
28import android.content.Intent;
29import android.database.Cursor;
30import android.location.LocationManager;
31import android.os.Bundle;
32import android.os.Vibrator;
33import android.preference.CheckBoxPreference;
34import android.preference.ListPreference;
35import android.preference.Preference;
36import android.preference.Preference.OnPreferenceChangeListener;
37import android.preference.PreferenceGroup;
38import android.preference.PreferenceScreen;
39import android.provider.Settings;
40import android.security.KeyStore;
41import android.telephony.TelephonyManager;
42import android.util.Log;
43
44import java.util.ArrayList;
45import java.util.Observable;
46import java.util.Observer;
47
48/**
49 * Gesture lock pattern settings.
50 */
51public class SecuritySettings extends SettingsPreferenceFragment
52        implements OnPreferenceChangeListener {
53    private static final String KEY_ENCRYPTION = "encryption";
54
55    // Lock Settings
56    private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change";
57    private static final String KEY_LOCK_ENABLED = "lockenabled";
58    private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
59    private static final String KEY_TACTILE_FEEDBACK_ENABLED = "unlock_tactile_feedback";
60    private static final String KEY_SECURITY_CATEGORY = "security_category";
61    private static final String KEY_LOCK_AFTER_TIMEOUT = "lock_after_timeout";
62    private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
63
64    // Location Settings
65    private static final String KEY_LOCATION_CATEGORY = "location_category";
66    private static final String KEY_LOCATION_NETWORK = "location_network";
67    private static final String KEY_LOCATION_GPS = "location_gps";
68    private static final String KEY_ASSISTED_GPS = "assisted_gps";
69    private static final String KEY_USE_LOCATION = "location_use_for_services";
70
71    // Misc Settings
72    private static final String KEY_SIM_LOCK = "sim_lock";
73    private static final String KEY_SHOW_PASSWORD = "show_password";
74    private static final String KEY_ENABLE_CREDENTIALS = "enable_credentials";
75    private static final String KEY_RESET_CREDENTIALS = "reset_credentials";
76
77    private static final String TAG = "SecuritySettings";
78
79    private CheckBoxPreference mNetwork;
80    private CheckBoxPreference mGps;
81    private CheckBoxPreference mAssistedGps;
82    private CheckBoxPreference mUseLocation;
83
84    DevicePolicyManager mDPM;
85
86    // These provide support for receiving notification when Location Manager settings change.
87    // This is necessary because the Network Location Provider can change settings
88    // if the user does not confirm enabling the provider.
89    private ContentQueryMap mContentQueryMap;
90
91    private ChooseLockSettingsHelper mChooseLockSettingsHelper;
92    private LockPatternUtils mLockPatternUtils;
93    private ListPreference mLockAfter;
94
95    private Observer mSettingsObserver;
96
97    private CheckBoxPreference mVisiblePattern;
98    private CheckBoxPreference mTactileFeedback;
99
100    private CheckBoxPreference mShowPassword;
101
102    private CheckBoxPreference mEnableCredentials;
103    private Preference mResetCredentials;
104
105    @Override
106    public void onCreate(Bundle savedInstanceState) {
107        super.onCreate(savedInstanceState);
108
109        mLockPatternUtils = new LockPatternUtils(getActivity());
110
111        mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);
112
113        mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
114    }
115
116    @Override
117    public void onStart() {
118        super.onStart();
119        // listen for Location Manager settings changes
120        Cursor settingsCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, null,
121                "(" + Settings.System.NAME + "=?)",
122                new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
123                null);
124        mContentQueryMap = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, null);
125    }
126
127    @Override
128    public void onStop() {
129        super.onStop();
130        if (mSettingsObserver != null) {
131            mContentQueryMap.deleteObserver(mSettingsObserver);
132        }
133    }
134
135    private PreferenceScreen createPreferenceHierarchy() {
136        PreferenceScreen root = getPreferenceScreen();
137        if (root != null) {
138            root.removeAll();
139        }
140        addPreferencesFromResource(R.xml.security_settings);
141        root = getPreferenceScreen();
142
143        mNetwork = (CheckBoxPreference) root.findPreference(KEY_LOCATION_NETWORK);
144        mGps = (CheckBoxPreference) root.findPreference(KEY_LOCATION_GPS);
145        mAssistedGps = (CheckBoxPreference) root.findPreference(KEY_ASSISTED_GPS);
146        if (GoogleLocationSettingHelper.isAvailable(getActivity())) {
147            // GSF present, Add setting for 'Use My Location'
148            PreferenceGroup locationCat =
149                    (PreferenceGroup) root.findPreference(KEY_LOCATION_CATEGORY);
150            CheckBoxPreference useLocation = new CheckBoxPreference(getActivity());
151            useLocation.setKey(KEY_USE_LOCATION);
152            useLocation.setTitle(R.string.use_location_title);
153            useLocation.setSummaryOn(R.string.use_location_summary_enabled);
154            useLocation.setSummaryOff(R.string.use_location_summary_disabled);
155            useLocation.setChecked(
156                    GoogleLocationSettingHelper.getUseLocationForServices(getActivity())
157                    == GoogleLocationSettingHelper.USE_LOCATION_FOR_SERVICES_ON);
158            useLocation.setPersistent(false);
159            useLocation.setOnPreferenceChangeListener(this);
160            locationCat.addPreference(useLocation);
161            mUseLocation = useLocation;
162        }
163
164        // Add options for device encryption
165        // TODO: It still needs to be determined how a device specifies that it supports
166        // encryption. That mechanism needs to be checked before adding the following code
167
168        addPreferencesFromResource(R.xml.security_settings_encryption);
169
170        // Add options for lock/unlock screen
171        int resid = 0;
172        if (!mLockPatternUtils.isSecure()) {
173            if (mLockPatternUtils.isLockScreenDisabled()) {
174                resid = R.xml.security_settings_lockscreen;
175            } else {
176                resid = R.xml.security_settings_chooser;
177            }
178        } else {
179            switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) {
180                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
181                    resid = R.xml.security_settings_pattern;
182                    break;
183                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
184                    resid = R.xml.security_settings_pin;
185                    break;
186                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
187                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
188                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
189                    resid = R.xml.security_settings_password;
190                    break;
191            }
192        }
193        addPreferencesFromResource(resid);
194
195        // lock after preference
196        mLockAfter = (ListPreference) root.findPreference(KEY_LOCK_AFTER_TIMEOUT);
197        if (mLockAfter != null) {
198            setupLockAfterPreference();
199            updateLockAfterPreferenceSummary();
200        }
201
202        // visible pattern
203        mVisiblePattern = (CheckBoxPreference) root.findPreference(KEY_VISIBLE_PATTERN);
204
205        // tactile feedback. Should be common to all unlock preference screens.
206        mTactileFeedback = (CheckBoxPreference) root.findPreference(KEY_TACTILE_FEEDBACK_ENABLED);
207        if (!((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator()) {
208            PreferenceGroup securityCategory = (PreferenceGroup)
209                    root.findPreference(KEY_SECURITY_CATEGORY);
210            if (securityCategory != null && mTactileFeedback != null) {
211                securityCategory.removePreference(mTactileFeedback);
212            }
213        }
214
215        // Append the rest of the settings
216        addPreferencesFromResource(R.xml.security_settings_misc);
217
218        // Do not display SIM lock for CDMA phone
219        if (TelephonyManager.PHONE_TYPE_CDMA == TelephonyManager.getDefault().getPhoneType()) {
220            root.removePreference(root.findPreference(KEY_SIM_LOCK));
221        }
222
223        // Show password
224        mShowPassword = (CheckBoxPreference) root.findPreference(KEY_SHOW_PASSWORD);
225
226        // Credential storage
227        mEnableCredentials = (CheckBoxPreference) root.findPreference(KEY_ENABLE_CREDENTIALS);
228        mEnableCredentials.setOnPreferenceChangeListener(this);
229        mResetCredentials = root.findPreference(KEY_RESET_CREDENTIALS);
230
231        return root;
232    }
233
234    private void setupLockAfterPreference() {
235        // Compatible with pre-Froyo
236        long currentTimeout = Settings.Secure.getLong(getContentResolver(),
237                Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 5000);
238        mLockAfter.setValue(String.valueOf(currentTimeout));
239        mLockAfter.setOnPreferenceChangeListener(this);
240        final long adminTimeout = (mDPM != null ? mDPM.getMaximumTimeToLock(null) : 0);
241        final long displayTimeout = Math.max(0,
242                Settings.System.getInt(getContentResolver(), SCREEN_OFF_TIMEOUT, 0));
243        if (adminTimeout > 0) {
244            // This setting is a slave to display timeout when a device policy is enforced.
245            // As such, maxLockTimeout = adminTimeout - displayTimeout.
246            // If there isn't enough time, shows "immediately" setting.
247            disableUnusableTimeouts(Math.max(0, adminTimeout - displayTimeout));
248        }
249    }
250
251    private void updateLockAfterPreferenceSummary() {
252        // Update summary message with current value
253        long currentTimeout = Settings.Secure.getLong(getContentResolver(),
254                Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 0);
255        final CharSequence[] entries = mLockAfter.getEntries();
256        final CharSequence[] values = mLockAfter.getEntryValues();
257        int best = 0;
258        for (int i = 0; i < values.length; i++) {
259            long timeout = Long.valueOf(values[i].toString());
260            if (currentTimeout >= timeout) {
261                best = i;
262            }
263        }
264        mLockAfter.setSummary(getString(R.string.lock_after_timeout_summary, entries[best]));
265    }
266
267    private void disableUnusableTimeouts(long maxTimeout) {
268        final CharSequence[] entries = mLockAfter.getEntries();
269        final CharSequence[] values = mLockAfter.getEntryValues();
270        ArrayList<CharSequence> revisedEntries = new ArrayList<CharSequence>();
271        ArrayList<CharSequence> revisedValues = new ArrayList<CharSequence>();
272        for (int i = 0; i < values.length; i++) {
273            long timeout = Long.valueOf(values[i].toString());
274            if (timeout <= maxTimeout) {
275                revisedEntries.add(entries[i]);
276                revisedValues.add(values[i]);
277            }
278        }
279        if (revisedEntries.size() != entries.length || revisedValues.size() != values.length) {
280            mLockAfter.setEntries(
281                    revisedEntries.toArray(new CharSequence[revisedEntries.size()]));
282            mLockAfter.setEntryValues(
283                    revisedValues.toArray(new CharSequence[revisedValues.size()]));
284            final int userPreference = Integer.valueOf(mLockAfter.getValue());
285            if (userPreference <= maxTimeout) {
286                mLockAfter.setValue(String.valueOf(userPreference));
287            } else {
288                // There will be no highlighted selection since nothing in the list matches
289                // maxTimeout. The user can still select anything less than maxTimeout.
290                // TODO: maybe append maxTimeout to the list and mark selected.
291            }
292        }
293        mLockAfter.setEnabled(revisedEntries.size() > 0);
294    }
295
296    @Override
297    public void onResume() {
298        super.onResume();
299
300        // Make sure we reload the preference hierarchy since some of these settings
301        // depend on others...
302        createPreferenceHierarchy();
303        updateLocationToggles();
304
305        if (mSettingsObserver == null) {
306            mSettingsObserver = new Observer() {
307                public void update(Observable o, Object arg) {
308                    updateLocationToggles();
309                }
310            };
311            mContentQueryMap.addObserver(mSettingsObserver);
312        }
313
314        final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
315        if (mVisiblePattern != null) {
316            mVisiblePattern.setChecked(lockPatternUtils.isVisiblePatternEnabled());
317        }
318        if (mTactileFeedback != null) {
319            mTactileFeedback.setChecked(lockPatternUtils.isTactileFeedbackEnabled());
320        }
321
322        mShowPassword.setChecked(Settings.System.getInt(getContentResolver(),
323                Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
324
325        int state = KeyStore.getInstance().test();
326        mEnableCredentials.setChecked(state == KeyStore.NO_ERROR);
327        mEnableCredentials.setEnabled(state != KeyStore.UNINITIALIZED);
328        mResetCredentials.setEnabled(state != KeyStore.UNINITIALIZED);
329    }
330
331    @Override
332    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
333            Preference preference) {
334        final String key = preference.getKey();
335
336        final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
337        if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
338            startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment",
339                    SET_OR_CHANGE_LOCK_METHOD_REQUEST, null);
340        } else if (KEY_LOCK_ENABLED.equals(key)) {
341            lockPatternUtils.setLockPatternEnabled(isToggled(preference));
342        } else if (KEY_VISIBLE_PATTERN.equals(key)) {
343            lockPatternUtils.setVisiblePatternEnabled(isToggled(preference));
344        } else if (KEY_TACTILE_FEEDBACK_ENABLED.equals(key)) {
345            lockPatternUtils.setTactileFeedbackEnabled(isToggled(preference));
346        } else if (preference == mShowPassword) {
347            Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD,
348                    mShowPassword.isChecked() ? 1 : 0);
349        } else if (preference == mNetwork) {
350            Settings.Secure.setLocationProviderEnabled(getContentResolver(),
351                    LocationManager.NETWORK_PROVIDER, mNetwork.isChecked());
352        } else if (preference == mGps) {
353            boolean enabled = mGps.isChecked();
354            Settings.Secure.setLocationProviderEnabled(getContentResolver(),
355                    LocationManager.GPS_PROVIDER, enabled);
356            if (mAssistedGps != null) {
357                mAssistedGps.setEnabled(enabled);
358            }
359        } else if (preference == mAssistedGps) {
360            Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED,
361                    mAssistedGps.isChecked() ? 1 : 0);
362        } else {
363            // If we didn't handle it, let preferences handle it.
364            return super.onPreferenceTreeClick(preferenceScreen, preference);
365        }
366
367        return true;
368    }
369
370    /*
371     * Creates toggles for each available location provider
372     */
373    private void updateLocationToggles() {
374        ContentResolver res = getContentResolver();
375        boolean gpsEnabled = Settings.Secure.isLocationProviderEnabled(
376                res, LocationManager.GPS_PROVIDER);
377        mNetwork.setChecked(Settings.Secure.isLocationProviderEnabled(
378                res, LocationManager.NETWORK_PROVIDER));
379        mGps.setChecked(gpsEnabled);
380        if (mAssistedGps != null) {
381            mAssistedGps.setChecked(Settings.Secure.getInt(res,
382                    Settings.Secure.ASSISTED_GPS_ENABLED, 2) == 1);
383            mAssistedGps.setEnabled(gpsEnabled);
384        }
385    }
386
387    private boolean isToggled(Preference pref) {
388        return ((CheckBoxPreference) pref).isChecked();
389    }
390
391    /**
392     * @see #confirmPatternThenDisableAndClear
393     */
394    @Override
395    public void onActivityResult(int requestCode, int resultCode, Intent data) {
396        super.onActivityResult(requestCode, resultCode, data);
397        createPreferenceHierarchy();
398    }
399
400    public boolean onPreferenceChange(Preference preference, Object value) {
401        if (preference == mLockAfter) {
402            int timeout = Integer.parseInt((String) value);
403            try {
404                Settings.Secure.putInt(getContentResolver(),
405                        Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, timeout);
406            } catch (NumberFormatException e) {
407                Log.e("SecuritySettings", "could not persist lockAfter timeout setting", e);
408            }
409            updateLockAfterPreferenceSummary();
410        } else if (preference == mUseLocation) {
411            boolean newValue = (value == null ? false : (Boolean) value);
412            GoogleLocationSettingHelper.setUseLocationForServices(getActivity(), newValue);
413            // We don't want to change the value immediately here, since the user may click
414            // disagree in the dialog that pops up. When the activity we just launched exits, this
415            // activity will be restated and the new value re-read, so the checkbox will get its
416            // new value then.
417            return false;
418        } else if (preference == mEnableCredentials) {
419            if (value != null && (Boolean) value) {
420                getActivity().startActivity(new Intent(CredentialStorage.ACTION_UNLOCK));
421                return false;
422            } else {
423                KeyStore.getInstance().lock();
424            }
425        }
426        return true;
427    }
428}
429