Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2010 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 import android.app.Activity;
     20 import android.app.PendingIntent;
     21 import android.app.admin.DevicePolicyManager;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.os.Bundle;
     25 import android.preference.Preference;
     26 import android.preference.PreferenceActivity;
     27 import android.preference.PreferenceScreen;
     28 import android.security.KeyStore;
     29 import android.view.LayoutInflater;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.widget.ListView;
     33 
     34 import com.android.internal.widget.LockPatternUtils;
     35 
     36 public class ChooseLockGeneric extends PreferenceActivity {
     37 
     38     @Override
     39     public Intent getIntent() {
     40         Intent modIntent = new Intent(super.getIntent());
     41         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockGenericFragment.class.getName());
     42         modIntent.putExtra(EXTRA_NO_HEADERS, true);
     43         return modIntent;
     44     }
     45 
     46     public static class ChooseLockGenericFragment extends SettingsPreferenceFragment {
     47         private static final int MIN_PASSWORD_LENGTH = 4;
     48         private static final String KEY_UNLOCK_BACKUP_INFO = "unlock_backup_info";
     49         private static final String KEY_UNLOCK_SET_OFF = "unlock_set_off";
     50         private static final String KEY_UNLOCK_SET_NONE = "unlock_set_none";
     51         private static final String KEY_UNLOCK_SET_BIOMETRIC_WEAK = "unlock_set_biometric_weak";
     52         private static final String KEY_UNLOCK_SET_PIN = "unlock_set_pin";
     53         private static final String KEY_UNLOCK_SET_PASSWORD = "unlock_set_password";
     54         private static final String KEY_UNLOCK_SET_PATTERN = "unlock_set_pattern";
     55         private static final int CONFIRM_EXISTING_REQUEST = 100;
     56         private static final int FALLBACK_REQUEST = 101;
     57         private static final String PASSWORD_CONFIRMED = "password_confirmed";
     58         private static final String CONFIRM_CREDENTIALS = "confirm_credentials";
     59         public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
     60 
     61         private static final boolean ALWAY_SHOW_TUTORIAL = true;
     62 
     63         private ChooseLockSettingsHelper mChooseLockSettingsHelper;
     64         private DevicePolicyManager mDPM;
     65         private KeyStore mKeyStore;
     66         private boolean mPasswordConfirmed = false;
     67 
     68         @Override
     69         public void onCreate(Bundle savedInstanceState) {
     70             super.onCreate(savedInstanceState);
     71 
     72             mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
     73             mKeyStore = KeyStore.getInstance();
     74             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
     75 
     76             // Defaults to needing to confirm credentials
     77             final boolean confirmCredentials = getActivity().getIntent()
     78                 .getBooleanExtra(CONFIRM_CREDENTIALS, true);
     79             mPasswordConfirmed = !confirmCredentials;
     80 
     81             if (savedInstanceState != null) {
     82                 mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
     83             }
     84 
     85             if (!mPasswordConfirmed) {
     86                 ChooseLockSettingsHelper helper =
     87                         new ChooseLockSettingsHelper(this.getActivity(), this);
     88                 if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) {
     89                     mPasswordConfirmed = true; // no password set, so no need to confirm
     90                     updatePreferencesOrFinish();
     91                 }
     92             } else {
     93                 updatePreferencesOrFinish();
     94             }
     95         }
     96 
     97         @Override
     98         public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
     99                 Preference preference) {
    100             final String key = preference.getKey();
    101             boolean handled = true;
    102             if (KEY_UNLOCK_SET_OFF.equals(key)) {
    103                 updateUnlockMethodAndFinish(
    104                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true);
    105             } else if (KEY_UNLOCK_SET_NONE.equals(key)) {
    106                 updateUnlockMethodAndFinish(
    107                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false);
    108             } else if (KEY_UNLOCK_SET_BIOMETRIC_WEAK.equals(key)) {
    109                 updateUnlockMethodAndFinish(
    110                         DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, false);
    111             }else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
    112                 updateUnlockMethodAndFinish(
    113                         DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
    114             } else if (KEY_UNLOCK_SET_PIN.equals(key)) {
    115                 updateUnlockMethodAndFinish(
    116                         DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
    117             } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
    118                 updateUnlockMethodAndFinish(
    119                         DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
    120             } else {
    121                 handled = false;
    122             }
    123             return handled;
    124         }
    125 
    126         @Override
    127         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    128                 Bundle savedInstanceState) {
    129             View v = super.onCreateView(inflater, container, savedInstanceState);
    130             final boolean onlyShowFallback = getActivity().getIntent()
    131                     .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
    132             if (onlyShowFallback) {
    133                 View header = v.inflate(getActivity(),
    134                         R.layout.weak_biometric_fallback_header, null);
    135                 ((ListView) v.findViewById(android.R.id.list)).addHeaderView(header, null, false);
    136             }
    137 
    138             return v;
    139         }
    140 
    141         @Override
    142         public void onActivityResult(int requestCode, int resultCode, Intent data) {
    143             super.onActivityResult(requestCode, resultCode, data);
    144             if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
    145                 mPasswordConfirmed = true;
    146                 updatePreferencesOrFinish();
    147             } else if(requestCode == FALLBACK_REQUEST) {
    148                 mChooseLockSettingsHelper.utils().deleteTempGallery();
    149                 getActivity().setResult(resultCode);
    150                 finish();
    151             } else {
    152                 getActivity().setResult(Activity.RESULT_CANCELED);
    153                 finish();
    154             }
    155         }
    156 
    157         @Override
    158         public void onSaveInstanceState(Bundle outState) {
    159             super.onSaveInstanceState(outState);
    160             // Saved so we don't force user to re-enter their password if configuration changes
    161             outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
    162         }
    163 
    164         private void updatePreferencesOrFinish() {
    165             Intent intent = getActivity().getIntent();
    166             int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
    167             if (quality == -1) {
    168                 // If caller didn't specify password quality, show UI and allow the user to choose.
    169                 quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
    170                 quality = upgradeQuality(quality);
    171                 final PreferenceScreen prefScreen = getPreferenceScreen();
    172                 if (prefScreen != null) {
    173                     prefScreen.removeAll();
    174                 }
    175                 addPreferencesFromResource(R.xml.security_settings_picker);
    176                 disableUnusablePreferences(quality);
    177             } else {
    178                 updateUnlockMethodAndFinish(quality, false);
    179             }
    180         }
    181 
    182         private int upgradeQuality(int quality) {
    183             quality = upgradeQualityForDPM(quality);
    184             quality = upgradeQualityForEncryption(quality);
    185             quality = upgradeQualityForKeyStore(quality);
    186             return quality;
    187         }
    188 
    189         private int upgradeQualityForDPM(int quality) {
    190             // Compare min allowed password quality
    191             int minQuality = mDPM.getPasswordQuality(null);
    192             if (quality < minQuality) {
    193                 quality = minQuality;
    194             }
    195             return quality;
    196         }
    197 
    198         /**
    199          * Mix in "encryption minimums" to any given quality value.  This prevents users
    200          * from downgrading the pattern/pin/password to a level below the minimums.
    201          *
    202          * ASSUMPTION:  Setting quality is sufficient (e.g. minimum lengths will be set
    203          * appropriately.)
    204          */
    205         private int upgradeQualityForEncryption(int quality) {
    206             int encryptionStatus = mDPM.getStorageEncryptionStatus();
    207             boolean encrypted = (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE)
    208                     || (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING);
    209             if (encrypted) {
    210                 if (quality < CryptKeeperSettings.MIN_PASSWORD_QUALITY) {
    211                     quality = CryptKeeperSettings.MIN_PASSWORD_QUALITY;
    212                 }
    213             }
    214             return quality;
    215         }
    216 
    217         private int upgradeQualityForKeyStore(int quality) {
    218             if (!mKeyStore.isEmpty()) {
    219                 if (quality < CredentialStorage.MIN_PASSWORD_QUALITY) {
    220                     quality = CredentialStorage.MIN_PASSWORD_QUALITY;
    221                 }
    222             }
    223             return quality;
    224         }
    225 
    226         /***
    227          * Disables preferences that are less secure than required quality.
    228          *
    229          * @param quality the requested quality.
    230          */
    231         private void disableUnusablePreferences(final int quality) {
    232             final PreferenceScreen entries = getPreferenceScreen();
    233             final boolean onlyShowFallback = getActivity().getIntent()
    234                     .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
    235             final boolean weakBiometricAvailable =
    236                     mChooseLockSettingsHelper.utils().isBiometricWeakInstalled();
    237             for (int i = entries.getPreferenceCount() - 1; i >= 0; --i) {
    238                 Preference pref = entries.getPreference(i);
    239                 if (pref instanceof PreferenceScreen) {
    240                     final String key = ((PreferenceScreen) pref).getKey();
    241                     boolean enabled = true;
    242                     boolean visible = true;
    243                     if (KEY_UNLOCK_SET_OFF.equals(key)) {
    244                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
    245                     } else if (KEY_UNLOCK_SET_NONE.equals(key)) {
    246                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
    247                     } else if (KEY_UNLOCK_SET_BIOMETRIC_WEAK.equals(key)) {
    248                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
    249                         visible = weakBiometricAvailable; // If not available, then don't show it.
    250                     } else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
    251                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
    252                     } else if (KEY_UNLOCK_SET_PIN.equals(key)) {
    253                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
    254                     } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
    255                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
    256                     }
    257                     if (!visible || (onlyShowFallback && !allowedForFallback(key))) {
    258                         entries.removePreference(pref);
    259                     } else if (!enabled) {
    260                         pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
    261                         pref.setEnabled(false);
    262                     }
    263                 }
    264             }
    265         }
    266 
    267         /**
    268          * Check whether the key is allowed for fallback (e.g. bio sensor). Returns true if it's
    269          * supported as a backup.
    270          *
    271          * @param key
    272          * @return true if allowed
    273          */
    274         private boolean allowedForFallback(String key) {
    275             return KEY_UNLOCK_BACKUP_INFO.equals(key)  ||
    276                     KEY_UNLOCK_SET_PATTERN.equals(key) || KEY_UNLOCK_SET_PIN.equals(key);
    277         }
    278 
    279         private Intent getBiometricSensorIntent() {
    280             Intent fallBackIntent = new Intent().setClass(getActivity(), ChooseLockGeneric.class);
    281             fallBackIntent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, true);
    282             fallBackIntent.putExtra(CONFIRM_CREDENTIALS, false);
    283             fallBackIntent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE,
    284                     R.string.backup_lock_settings_picker_title);
    285 
    286             boolean showTutorial = ALWAY_SHOW_TUTORIAL ||
    287                     !mChooseLockSettingsHelper.utils().isBiometricWeakEverChosen();
    288             Intent intent = new Intent();
    289             intent.setClassName("com.android.facelock", "com.android.facelock.SetupIntro");
    290             intent.putExtra("showTutorial", showTutorial);
    291             PendingIntent pending = PendingIntent.getActivity(getActivity(), 0, fallBackIntent, 0);
    292             intent.putExtra("PendingIntent", pending);
    293             return intent;
    294         }
    295 
    296         /**
    297          * Invokes an activity to change the user's pattern, password or PIN based on given quality
    298          * and minimum quality specified by DevicePolicyManager. If quality is
    299          * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared.
    300          *
    301          * @param quality the desired quality. Ignored if DevicePolicyManager requires more security
    302          * @param disabled whether or not to show LockScreen at all. Only meaningful when quality is
    303          * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}
    304          */
    305         void updateUnlockMethodAndFinish(int quality, boolean disabled) {
    306             // Sanity check. We should never get here without confirming user's existing password.
    307             if (!mPasswordConfirmed) {
    308                 throw new IllegalStateException("Tried to update password without confirming it");
    309             }
    310 
    311             final boolean isFallback = getActivity().getIntent()
    312                 .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
    313 
    314             quality = upgradeQuality(quality);
    315 
    316             if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
    317                 int minLength = mDPM.getPasswordMinimumLength(null);
    318                 if (minLength < MIN_PASSWORD_LENGTH) {
    319                     minLength = MIN_PASSWORD_LENGTH;
    320                 }
    321                 final int maxLength = mDPM.getPasswordMaximumLength(quality);
    322                 Intent intent = new Intent().setClass(getActivity(), ChooseLockPassword.class);
    323                 intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
    324                 intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength);
    325                 intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength);
    326                 intent.putExtra(CONFIRM_CREDENTIALS, false);
    327                 intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
    328                         isFallback);
    329                 if(isFallback) {
    330                     startActivityForResult(intent, FALLBACK_REQUEST);
    331                     return;
    332                 } else {
    333                     intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    334                     startActivity(intent);
    335                 }
    336             } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
    337                 boolean showTutorial = !mChooseLockSettingsHelper.utils().isPatternEverChosen();
    338                 Intent intent = new Intent();
    339                 intent.setClass(getActivity(), showTutorial
    340                         ? ChooseLockPatternTutorial.class
    341                         : ChooseLockPattern.class);
    342                 intent.putExtra("key_lock_method", "pattern");
    343                 intent.putExtra(CONFIRM_CREDENTIALS, false);
    344                 intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
    345                         isFallback);
    346                 if(isFallback) {
    347                     startActivityForResult(intent, FALLBACK_REQUEST);
    348                     return;
    349                 } else {
    350                     intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    351                     startActivity(intent);
    352                 }
    353             } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
    354                 Intent intent = getBiometricSensorIntent();
    355                 startActivity(intent);
    356             } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
    357                 mChooseLockSettingsHelper.utils().clearLock(false);
    358                 mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);
    359                 getActivity().setResult(Activity.RESULT_OK);
    360             }
    361             finish();
    362         }
    363     }
    364 }
    365