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