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