| /* |
| * Copyright (C) 2010 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.password; |
| |
| import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD; |
| import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD; |
| |
| import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED; |
| |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.app.Activity; |
| import android.app.AlertDialog; |
| import android.app.Dialog; |
| import android.app.Fragment; |
| import android.app.FragmentManager; |
| import android.app.admin.DevicePolicyManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.UserInfo; |
| import android.hardware.fingerprint.Fingerprint; |
| import android.hardware.fingerprint.FingerprintManager; |
| import android.hardware.fingerprint.FingerprintManager.RemovalCallback; |
| import android.os.Bundle; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.os.storage.StorageManager; |
| import android.security.KeyStore; |
| import android.support.annotation.StringRes; |
| import android.support.v7.preference.Preference; |
| import android.support.v7.preference.PreferenceScreen; |
| import android.text.TextUtils; |
| import android.util.EventLog; |
| import android.util.Log; |
| import android.view.accessibility.AccessibilityManager; |
| import android.widget.TextView; |
| |
| import com.android.internal.logging.nano.MetricsProto.MetricsEvent; |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.settings.EncryptionInterstitial; |
| import com.android.settings.EventLogTags; |
| import com.android.settings.R; |
| import com.android.settings.SettingsActivity; |
| import com.android.settings.SettingsPreferenceFragment; |
| import com.android.settings.Utils; |
| import com.android.settings.core.instrumentation.InstrumentedDialogFragment; |
| import com.android.settings.fingerprint.FingerprintEnrollBase; |
| import com.android.settings.fingerprint.FingerprintEnrollFindSensor; |
| import com.android.settingslib.RestrictedLockUtils; |
| import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; |
| import com.android.settingslib.RestrictedPreference; |
| |
| import java.util.List; |
| |
| public class ChooseLockGeneric extends SettingsActivity { |
| public static final String CONFIRM_CREDENTIALS = "confirm_credentials"; |
| |
| @Override |
| public Intent getIntent() { |
| Intent modIntent = new Intent(super.getIntent()); |
| modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName()); |
| |
| String action = modIntent.getAction(); |
| if (ACTION_SET_NEW_PASSWORD.equals(action) |
| || ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(action)) { |
| modIntent.putExtra(EXTRA_HIDE_DRAWER, true); |
| } |
| return modIntent; |
| } |
| |
| @Override |
| protected boolean isValidFragment(String fragmentName) { |
| if (ChooseLockGenericFragment.class.getName().equals(fragmentName)) return true; |
| return false; |
| } |
| |
| /* package */ Class<? extends Fragment> getFragmentClass() { |
| return ChooseLockGenericFragment.class; |
| } |
| |
| public static class InternalActivity extends ChooseLockGeneric { |
| } |
| |
| public static class ChooseLockGenericFragment extends SettingsPreferenceFragment { |
| |
| private static final String TAG = "ChooseLockGenericFragment"; |
| private static final int MIN_PASSWORD_LENGTH = 4; |
| private static final String KEY_SKIP_FINGERPRINT = "unlock_skip_fingerprint"; |
| private static final String PASSWORD_CONFIRMED = "password_confirmed"; |
| private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation"; |
| public static final String MINIMUM_QUALITY_KEY = "minimum_quality"; |
| public static final String HIDE_DISABLED_PREFS = "hide_disabled_prefs"; |
| public static final String ENCRYPT_REQUESTED_QUALITY = "encrypt_requested_quality"; |
| public static final String ENCRYPT_REQUESTED_DISABLED = "encrypt_requested_disabled"; |
| public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog"; |
| |
| /** |
| * Boolean extra determining whether a "screen lock options" button should be shown. This |
| * extra is both sent and received by ChooseLockGeneric. |
| * |
| * When this extra is false, nothing will be done. |
| * When ChooseLockGeneric receives this extra set as true, and if ChooseLockGeneric is |
| * starting ChooseLockPassword or ChooseLockPattern automatically without user interaction, |
| * ChooseLockGeneric will set this extra to true when starting ChooseLockPassword/Pattern. |
| * |
| * This gives the user the choice to select a different screen lock type, even if |
| * ChooseLockGeneric selected a default. |
| */ |
| public static final String EXTRA_SHOW_OPTIONS_BUTTON = "show_options_button"; |
| |
| /** |
| * Original intent extras used to start this activity. This is passed to ChooseLockPassword |
| * when the "screen lock options" button is shown, so that when that button is clicked, |
| * ChooseLockGeneric can be relaunched with the same extras. |
| */ |
| public static final String EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS = "choose_lock_generic_extras"; |
| |
| private static final int CONFIRM_EXISTING_REQUEST = 100; |
| private static final int ENABLE_ENCRYPTION_REQUEST = 101; |
| private static final int CHOOSE_LOCK_REQUEST = 102; |
| private static final int CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST = 103; |
| private static final int SKIP_FINGERPRINT_REQUEST = 104; |
| |
| private ChooseLockSettingsHelper mChooseLockSettingsHelper; |
| private DevicePolicyManager mDPM; |
| private KeyStore mKeyStore; |
| private boolean mHasChallenge = false; |
| private long mChallenge; |
| private boolean mPasswordConfirmed = false; |
| private boolean mWaitingForConfirmation = false; |
| private int mEncryptionRequestQuality; |
| private boolean mEncryptionRequestDisabled; |
| private boolean mForChangeCredRequiredForBoot = false; |
| private String mUserPassword; |
| private LockPatternUtils mLockPatternUtils; |
| private FingerprintManager mFingerprintManager; |
| private int mUserId; |
| private boolean mHideDrawer = false; |
| private ManagedLockPasswordProvider mManagedPasswordProvider; |
| private boolean mIsSetNewPassword = false; |
| private UserManager mUserManager; |
| private ChooseLockGenericController mController; |
| |
| protected boolean mForFingerprint = false; |
| |
| @Override |
| public int getMetricsCategory() { |
| return MetricsEvent.CHOOSE_LOCK_GENERIC; |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| String chooseLockAction = getActivity().getIntent().getAction(); |
| mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity()); |
| mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); |
| mKeyStore = KeyStore.getInstance(); |
| mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity()); |
| mLockPatternUtils = new LockPatternUtils(getActivity()); |
| mIsSetNewPassword = ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction) |
| || ACTION_SET_NEW_PASSWORD.equals(chooseLockAction); |
| |
| // Defaults to needing to confirm credentials |
| final boolean confirmCredentials = getActivity().getIntent() |
| .getBooleanExtra(CONFIRM_CREDENTIALS, true); |
| if (getActivity() instanceof ChooseLockGeneric.InternalActivity) { |
| mPasswordConfirmed = !confirmCredentials; |
| mUserPassword = getActivity().getIntent().getStringExtra( |
| ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); |
| } |
| mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false); |
| |
| mHasChallenge = getActivity().getIntent().getBooleanExtra( |
| ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false); |
| mChallenge = getActivity().getIntent().getLongExtra( |
| ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0); |
| mForFingerprint = getActivity().getIntent().getBooleanExtra( |
| ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false); |
| mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean( |
| ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT); |
| mUserManager = UserManager.get(getActivity()); |
| |
| if (savedInstanceState != null) { |
| mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED); |
| mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION); |
| mEncryptionRequestQuality = savedInstanceState.getInt(ENCRYPT_REQUESTED_QUALITY); |
| mEncryptionRequestDisabled = savedInstanceState.getBoolean( |
| ENCRYPT_REQUESTED_DISABLED); |
| if (mUserPassword == null) { |
| mUserPassword = savedInstanceState.getString( |
| ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); |
| } |
| } |
| |
| // a) If this is started from other user, use that user id. |
| // b) If this is started from the same user, read the extra if this is launched |
| // from Settings app itself. |
| // c) Otherwise, use UserHandle.myUserId(). |
| mUserId = Utils.getSecureTargetUser( |
| getActivity().getActivityToken(), |
| UserManager.get(getActivity()), |
| getArguments(), |
| getActivity().getIntent().getExtras()).getIdentifier(); |
| mController = new ChooseLockGenericController(getContext(), mUserId); |
| if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction) |
| && UserManager.get(getActivity()).isManagedProfile(mUserId) |
| && mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) { |
| getActivity().setTitle(R.string.lock_settings_picker_title_profile); |
| } |
| |
| mManagedPasswordProvider = ManagedLockPasswordProvider.get(getActivity(), mUserId); |
| |
| if (mPasswordConfirmed) { |
| updatePreferencesOrFinish(savedInstanceState != null); |
| if (mForChangeCredRequiredForBoot) { |
| maybeEnableEncryption(mLockPatternUtils.getKeyguardStoredPasswordQuality( |
| mUserId), false); |
| } |
| } else if (!mWaitingForConfirmation) { |
| ChooseLockSettingsHelper helper = |
| new ChooseLockSettingsHelper(this.getActivity(), this); |
| boolean managedProfileWithUnifiedLock = |
| UserManager.get(getActivity()).isManagedProfile(mUserId) |
| && !mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId); |
| if (managedProfileWithUnifiedLock |
| || !helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, |
| getString(R.string.unlock_set_unlock_launch_picker_title), true, mUserId)) { |
| mPasswordConfirmed = true; // no password set, so no need to confirm |
| updatePreferencesOrFinish(savedInstanceState != null); |
| } else { |
| mWaitingForConfirmation = true; |
| } |
| } |
| addHeaderView(); |
| } |
| |
| protected void addHeaderView() { |
| if (mForFingerprint) { |
| setHeaderView(R.layout.choose_lock_generic_fingerprint_header); |
| if (mIsSetNewPassword) { |
| ((TextView) getHeaderView().findViewById(R.id.fingerprint_header_description)) |
| .setText(R.string.fingerprint_unlock_title); |
| } |
| } |
| } |
| |
| @Override |
| public boolean onPreferenceTreeClick(Preference preference) { |
| final String key = preference.getKey(); |
| |
| if (!isUnlockMethodSecure(key) && mLockPatternUtils.isSecure(mUserId)) { |
| // Show the disabling FRP warning only when the user is switching from a secure |
| // unlock method to an insecure one |
| showFactoryResetProtectionWarningDialog(key); |
| return true; |
| } else if (KEY_SKIP_FINGERPRINT.equals(key)) { |
| Intent chooseLockGenericIntent = new Intent(getActivity(), |
| ChooseLockGeneric.InternalActivity.class); |
| chooseLockGenericIntent.setAction(getIntent().getAction()); |
| // Forward the target user id to ChooseLockGeneric. |
| chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId); |
| chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed); |
| if (mUserPassword != null) { |
| chooseLockGenericIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, |
| mUserPassword); |
| } |
| startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST); |
| return true; |
| } else { |
| return setUnlockMethod(key); |
| } |
| } |
| |
| /** |
| * If the device has encryption already enabled, then ask the user if they |
| * also want to encrypt the phone with this password. |
| * |
| * @param quality |
| * @param disabled |
| */ |
| // TODO: why does this take disabled, its always called with a quality higher than |
| // what makes sense with disabled == true |
| private void maybeEnableEncryption(int quality, boolean disabled) { |
| DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE); |
| if (UserManager.get(getActivity()).isAdminUser() |
| && mUserId == UserHandle.myUserId() |
| && LockPatternUtils.isDeviceEncryptionEnabled() |
| && !LockPatternUtils.isFileEncryptionEnabled() |
| && !dpm.getDoNotAskCredentialsOnBoot()) { |
| mEncryptionRequestQuality = quality; |
| mEncryptionRequestDisabled = disabled; |
| // Get the intent that the encryption interstitial should start for creating |
| // the new unlock method. |
| Intent unlockMethodIntent = getIntentForUnlockMethod(quality); |
| unlockMethodIntent.putExtra( |
| ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, |
| mForChangeCredRequiredForBoot); |
| final Context context = getActivity(); |
| // If accessibility is enabled and the user hasn't seen this dialog before, set the |
| // default state to agree with that which is compatible with accessibility |
| // (password not required). |
| final boolean accEn = AccessibilityManager.getInstance(context).isEnabled(); |
| final boolean required = mLockPatternUtils.isCredentialRequiredToDecrypt(!accEn); |
| Intent intent = getEncryptionInterstitialIntent(context, quality, required, |
| unlockMethodIntent); |
| intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, |
| mForFingerprint); |
| intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer); |
| startActivityForResult( |
| intent, |
| mIsSetNewPassword && mHasChallenge |
| ? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST |
| : ENABLE_ENCRYPTION_REQUEST); |
| } else { |
| if (mForChangeCredRequiredForBoot) { |
| // Welp, couldn't change it. Oh well. |
| finish(); |
| return; |
| } |
| updateUnlockMethodAndFinish(quality, disabled, false /* chooseLockSkipped */); |
| } |
| } |
| |
| @Override |
| public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| super.onActivityResult(requestCode, resultCode, data); |
| mWaitingForConfirmation = false; |
| if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) { |
| mPasswordConfirmed = true; |
| mUserPassword = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); |
| updatePreferencesOrFinish(false /* isRecreatingActivity */); |
| if (mForChangeCredRequiredForBoot) { |
| if (!TextUtils.isEmpty(mUserPassword)) { |
| maybeEnableEncryption( |
| mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false); |
| } else { |
| finish(); |
| } |
| } |
| } else if (requestCode == CHOOSE_LOCK_REQUEST |
| || requestCode == ENABLE_ENCRYPTION_REQUEST) { |
| if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) { |
| getActivity().setResult(resultCode, data); |
| finish(); |
| } else { |
| // If PASSWORD_TYPE_KEY is set, this activity is used as a trampoline to start |
| // the actual password enrollment. If the result is canceled, which means the |
| // user pressed back, finish the activity with result canceled. |
| int quality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1); |
| if (quality != -1) { |
| getActivity().setResult(RESULT_CANCELED, data); |
| finish(); |
| } |
| } |
| } else if (requestCode == CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST |
| && resultCode == FingerprintEnrollBase.RESULT_FINISHED) { |
| Intent intent = getFindSensorIntent(getActivity()); |
| if (data != null) { |
| intent.putExtras(data.getExtras()); |
| } |
| // Forward the target user id to fingerprint setup page. |
| intent.putExtra(Intent.EXTRA_USER_ID, mUserId); |
| startActivity(intent); |
| finish(); |
| } else if (requestCode == SKIP_FINGERPRINT_REQUEST) { |
| if (resultCode != RESULT_CANCELED) { |
| getActivity().setResult( |
| resultCode == RESULT_FINISHED ? RESULT_OK : resultCode, data); |
| finish(); |
| } |
| } else { |
| getActivity().setResult(Activity.RESULT_CANCELED); |
| finish(); |
| } |
| if (requestCode == Activity.RESULT_CANCELED && mForChangeCredRequiredForBoot) { |
| finish(); |
| } |
| } |
| |
| protected Intent getFindSensorIntent(Context context) { |
| return new Intent(context, FingerprintEnrollFindSensor.class); |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| // Saved so we don't force user to re-enter their password if configuration changes |
| outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed); |
| outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation); |
| outState.putInt(ENCRYPT_REQUESTED_QUALITY, mEncryptionRequestQuality); |
| outState.putBoolean(ENCRYPT_REQUESTED_DISABLED, mEncryptionRequestDisabled); |
| if (mUserPassword != null) { |
| outState.putString(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword); |
| } |
| } |
| |
| private void updatePreferencesOrFinish(boolean isRecreatingActivity) { |
| Intent intent = getActivity().getIntent(); |
| int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1); |
| if (quality == -1) { |
| // If caller didn't specify password quality, show UI and allow the user to choose. |
| quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1); |
| quality = mController.upgradeQuality(quality); |
| final boolean hideDisabledPrefs = intent.getBooleanExtra( |
| HIDE_DISABLED_PREFS, false); |
| final PreferenceScreen prefScreen = getPreferenceScreen(); |
| if (prefScreen != null) { |
| prefScreen.removeAll(); |
| } |
| addPreferences(); |
| disableUnusablePreferences(quality, hideDisabledPrefs); |
| updatePreferenceText(); |
| updateCurrentPreference(); |
| updatePreferenceSummaryIfNeeded(); |
| } else if (!isRecreatingActivity) { |
| // Don't start the activity again if we are recreated for configuration change |
| updateUnlockMethodAndFinish(quality, false, true /* chooseLockSkipped */); |
| } |
| } |
| |
| protected void addPreferences() { |
| addPreferencesFromResource(R.xml.security_settings_picker); |
| |
| // Used for testing purposes |
| findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none); |
| findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none); |
| findPreference(ScreenLockType.PIN.preferenceKey).setViewId(R.id.lock_pin); |
| findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password); |
| } |
| |
| private void updatePreferenceText() { |
| if (mForFingerprint) { |
| setPreferenceTitle(ScreenLockType.PATTERN, |
| R.string.fingerprint_unlock_set_unlock_pattern); |
| setPreferenceTitle(ScreenLockType.PIN, R.string.fingerprint_unlock_set_unlock_pin); |
| setPreferenceTitle(ScreenLockType.PASSWORD, |
| R.string.fingerprint_unlock_set_unlock_password); |
| } |
| |
| if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) { |
| setPreferenceTitle(ScreenLockType.MANAGED, |
| mManagedPasswordProvider.getPickerOptionTitle(mForFingerprint)); |
| } else { |
| removePreference(ScreenLockType.MANAGED.preferenceKey); |
| } |
| |
| if (!(mForFingerprint && mIsSetNewPassword)) { |
| removePreference(KEY_SKIP_FINGERPRINT); |
| } |
| } |
| |
| private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) { |
| Preference preference = findPreference(lock.preferenceKey); |
| if (preference != null) { |
| preference.setTitle(title); |
| } |
| } |
| |
| private void setPreferenceTitle(ScreenLockType lock, CharSequence title) { |
| Preference preference = findPreference(lock.preferenceKey); |
| if (preference != null) { |
| preference.setTitle(title); |
| } |
| } |
| |
| private void setPreferenceSummary(ScreenLockType lock, @StringRes int summary) { |
| Preference preference = findPreference(lock.preferenceKey); |
| if (preference != null) { |
| preference.setSummary(summary); |
| } |
| } |
| |
| private void updateCurrentPreference() { |
| String currentKey = getKeyForCurrent(); |
| Preference preference = findPreference(currentKey); |
| if (preference != null) { |
| preference.setSummary(R.string.current_screen_lock); |
| } |
| } |
| |
| private String getKeyForCurrent() { |
| final int credentialOwner = UserManager.get(getContext()) |
| .getCredentialOwnerProfile(mUserId); |
| if (mLockPatternUtils.isLockScreenDisabled(credentialOwner)) { |
| return ScreenLockType.NONE.preferenceKey; |
| } |
| ScreenLockType lock = |
| ScreenLockType.fromQuality( |
| mLockPatternUtils.getKeyguardStoredPasswordQuality(credentialOwner)); |
| return lock != null ? lock.preferenceKey : null; |
| } |
| |
| /*** |
| * Disables preferences that are less secure than required quality. The actual |
| * implementation is in disableUnusablePreferenceImpl. |
| * |
| * @param quality the requested quality. |
| * @param hideDisabledPrefs if false preferences show why they were disabled; otherwise |
| * they're not shown at all. |
| */ |
| protected void disableUnusablePreferences(final int quality, boolean hideDisabledPrefs) { |
| disableUnusablePreferencesImpl(quality, hideDisabledPrefs); |
| } |
| |
| /*** |
| * Disables preferences that are less secure than required quality. |
| * |
| * @param quality the requested quality. |
| * @param hideDisabled whether to hide disable screen lock options. |
| */ |
| protected void disableUnusablePreferencesImpl(final int quality, |
| boolean hideDisabled) { |
| final PreferenceScreen entries = getPreferenceScreen(); |
| |
| int adminEnforcedQuality = mDPM.getPasswordQuality(null, mUserId); |
| EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfPasswordQualityIsSet( |
| getActivity(), mUserId); |
| |
| for (ScreenLockType lock : ScreenLockType.values()) { |
| String key = lock.preferenceKey; |
| Preference pref = findPreference(key); |
| if (pref instanceof RestrictedPreference) { |
| boolean visible = mController.isScreenLockVisible(lock); |
| boolean enabled = mController.isScreenLockEnabled(lock, quality); |
| boolean disabledByAdmin = |
| mController.isScreenLockDisabledByAdmin(lock, adminEnforcedQuality); |
| if (hideDisabled) { |
| visible = visible && enabled; |
| } |
| if (!visible) { |
| entries.removePreference(pref); |
| } else if (disabledByAdmin && enforcedAdmin != null) { |
| ((RestrictedPreference) pref).setDisabledByAdmin(enforcedAdmin); |
| } else if (!enabled) { |
| // we need to setDisabledByAdmin to null first to disable the padlock |
| // in case it was set earlier. |
| ((RestrictedPreference) pref).setDisabledByAdmin(null); |
| pref.setSummary(R.string.unlock_set_unlock_disabled_summary); |
| pref.setEnabled(false); |
| } else { |
| ((RestrictedPreference) pref).setDisabledByAdmin(null); |
| } |
| } |
| } |
| } |
| |
| private void updatePreferenceSummaryIfNeeded() { |
| // On a default block encrypted device with accessibility, add a warning |
| // that your data is not credential encrypted |
| if (!StorageManager.isBlockEncrypted()) { |
| return; |
| } |
| |
| if (StorageManager.isNonDefaultBlockEncrypted()) { |
| return; |
| } |
| |
| if (AccessibilityManager.getInstance(getActivity()).getEnabledAccessibilityServiceList( |
| AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) { |
| return; |
| } |
| |
| setPreferenceSummary(ScreenLockType.PATTERN, R.string.secure_lock_encryption_warning); |
| setPreferenceSummary(ScreenLockType.PIN, R.string.secure_lock_encryption_warning); |
| setPreferenceSummary(ScreenLockType.PASSWORD, R.string.secure_lock_encryption_warning); |
| setPreferenceSummary(ScreenLockType.MANAGED, R.string.secure_lock_encryption_warning); |
| } |
| |
| protected Intent getLockManagedPasswordIntent(String password) { |
| return mManagedPasswordProvider.createIntent(false, password); |
| } |
| |
| protected Intent getLockPasswordIntent(int quality, int minLength, int maxLength) { |
| ChooseLockPassword.IntentBuilder builder = |
| new ChooseLockPassword.IntentBuilder(getContext()) |
| .setPasswordQuality(quality) |
| .setPasswordLengthRange(minLength, maxLength) |
| .setForFingerprint(mForFingerprint) |
| .setUserId(mUserId); |
| if (mHasChallenge) { |
| builder.setChallenge(mChallenge); |
| } |
| if (mUserPassword != null) { |
| builder.setPassword(mUserPassword); |
| } |
| return builder.build(); |
| } |
| |
| protected Intent getLockPatternIntent() { |
| ChooseLockPattern.IntentBuilder builder = |
| new ChooseLockPattern.IntentBuilder(getContext()) |
| .setForFingerprint(mForFingerprint) |
| .setUserId(mUserId); |
| if (mHasChallenge) { |
| builder.setChallenge(mChallenge); |
| } |
| if (mUserPassword != null) { |
| builder.setPattern(mUserPassword); |
| } |
| return builder.build(); |
| } |
| |
| protected Intent getEncryptionInterstitialIntent(Context context, int quality, |
| boolean required, Intent unlockMethodIntent) { |
| return EncryptionInterstitial.createStartIntent(context, quality, required, |
| unlockMethodIntent); |
| } |
| |
| /** |
| * Invokes an activity to change the user's pattern, password or PIN based on given quality |
| * and minimum quality specified by DevicePolicyManager. If quality is |
| * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared. |
| * |
| * @param quality the desired quality. Ignored if DevicePolicyManager requires more security |
| * @param disabled whether or not to show LockScreen at all. Only meaningful when quality is |
| * @param chooseLockSkipped whether or not this activity is skipped. This is true when this |
| * activity was not shown to the user at all, instead automatically proceeding based on |
| * the given intent extras, typically {@link LockPatternUtils#PASSWORD_TYPE_KEY}. |
| * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED} |
| */ |
| void updateUnlockMethodAndFinish(int quality, boolean disabled, boolean chooseLockSkipped) { |
| // Sanity check. We should never get here without confirming user's existing password. |
| if (!mPasswordConfirmed) { |
| throw new IllegalStateException("Tried to update password without confirming it"); |
| } |
| |
| quality = mController.upgradeQuality(quality); |
| Intent intent = getIntentForUnlockMethod(quality); |
| if (intent != null) { |
| if (getIntent().getBooleanExtra(EXTRA_SHOW_OPTIONS_BUTTON, false)) { |
| intent.putExtra(EXTRA_SHOW_OPTIONS_BUTTON, chooseLockSkipped); |
| } |
| intent.putExtra(EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, getIntent().getExtras()); |
| startActivityForResult(intent, |
| mIsSetNewPassword && mHasChallenge |
| ? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST |
| : CHOOSE_LOCK_REQUEST); |
| return; |
| } |
| |
| if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { |
| mLockPatternUtils.setSeparateProfileChallengeEnabled(mUserId, true, mUserPassword); |
| mChooseLockSettingsHelper.utils().clearLock(mUserPassword, mUserId); |
| mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId); |
| getActivity().setResult(Activity.RESULT_OK); |
| removeAllFingerprintForUserAndFinish(mUserId); |
| } else { |
| removeAllFingerprintForUserAndFinish(mUserId); |
| } |
| } |
| |
| private Intent getIntentForUnlockMethod(int quality) { |
| Intent intent = null; |
| if (quality >= DevicePolicyManager.PASSWORD_QUALITY_MANAGED) { |
| intent = getLockManagedPasswordIntent(mUserPassword); |
| } else if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { |
| int minLength = mDPM.getPasswordMinimumLength(null, mUserId); |
| if (minLength < MIN_PASSWORD_LENGTH) { |
| minLength = MIN_PASSWORD_LENGTH; |
| } |
| final int maxLength = mDPM.getPasswordMaximumLength(quality); |
| intent = getLockPasswordIntent(quality, minLength, maxLength); |
| } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { |
| intent = getLockPatternIntent(); |
| } |
| if (intent != null) { |
| intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer); |
| } |
| return intent; |
| } |
| |
| private void removeAllFingerprintForUserAndFinish(final int userId) { |
| if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) { |
| if (mFingerprintManager.hasEnrolledFingerprints(userId)) { |
| mFingerprintManager.setActiveUser(userId); |
| // For the purposes of M and N, groupId is the same as userId. |
| final int groupId = userId; |
| Fingerprint finger = new Fingerprint(null, groupId, 0, 0); |
| mFingerprintManager.remove(finger, userId, |
| new RemovalCallback() { |
| @Override |
| public void onRemovalError(Fingerprint fp, int errMsgId, |
| CharSequence errString) { |
| Log.e(TAG, String.format( |
| "Can't remove fingerprint %d in group %d. Reason: %s", |
| fp.getFingerId(), fp.getGroupId(), errString)); |
| // TODO: need to proceed with the removal of managed profile |
| // fingerprints and finish() gracefully. |
| } |
| |
| @Override |
| public void onRemovalSucceeded(Fingerprint fp, int remaining) { |
| if (remaining == 0) { |
| removeManagedProfileFingerprintsAndFinishIfNecessary(userId); |
| } |
| } |
| }); |
| } else { |
| // No fingerprints in this user, we may also want to delete managed profile |
| // fingerprints |
| removeManagedProfileFingerprintsAndFinishIfNecessary(userId); |
| } |
| } else { |
| // The removal callback will call finish, once all fingerprints are removed. |
| // We need to wait for that to occur, otherwise, the UI will still show that |
| // fingerprints exist even though they are (about to) be removed depending on |
| // the race condition. |
| finish(); |
| } |
| } |
| |
| private void removeManagedProfileFingerprintsAndFinishIfNecessary(final int parentUserId) { |
| if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) { |
| mFingerprintManager.setActiveUser(UserHandle.myUserId()); |
| } |
| boolean hasChildProfile = false; |
| if (!mUserManager.getUserInfo(parentUserId).isManagedProfile()) { |
| // Current user is primary profile, remove work profile fingerprints if necessary |
| final List<UserInfo> profiles = mUserManager.getProfiles(parentUserId); |
| final int profilesSize = profiles.size(); |
| for (int i = 0; i < profilesSize; i++) { |
| final UserInfo userInfo = profiles.get(i); |
| if (userInfo.isManagedProfile() && !mLockPatternUtils |
| .isSeparateProfileChallengeEnabled(userInfo.id)) { |
| removeAllFingerprintForUserAndFinish(userInfo.id); |
| hasChildProfile = true; |
| break; |
| } |
| } |
| } |
| if (!hasChildProfile) { |
| finish(); |
| } |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| } |
| |
| @Override |
| protected int getHelpResource() { |
| return R.string.help_url_choose_lockscreen; |
| } |
| |
| private int getResIdForFactoryResetProtectionWarningTitle() { |
| boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mUserId); |
| return isProfile ? R.string.unlock_disable_frp_warning_title_profile |
| : R.string.unlock_disable_frp_warning_title; |
| } |
| |
| private int getResIdForFactoryResetProtectionWarningMessage() { |
| final boolean hasFingerprints; |
| if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) { |
| hasFingerprints = mFingerprintManager.hasEnrolledFingerprints(mUserId); |
| } else { |
| hasFingerprints = false; |
| } |
| boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mUserId); |
| switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) { |
| case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: |
| if (hasFingerprints && isProfile) { |
| return R.string |
| .unlock_disable_frp_warning_content_pattern_fingerprint_profile; |
| } else if (hasFingerprints && !isProfile) { |
| return R.string.unlock_disable_frp_warning_content_pattern_fingerprint; |
| } else if (isProfile) { |
| return R.string.unlock_disable_frp_warning_content_pattern_profile; |
| } else { |
| return R.string.unlock_disable_frp_warning_content_pattern; |
| } |
| case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: |
| case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: |
| if (hasFingerprints && isProfile) { |
| return R.string.unlock_disable_frp_warning_content_pin_fingerprint_profile; |
| } else if (hasFingerprints && !isProfile) { |
| return R.string.unlock_disable_frp_warning_content_pin_fingerprint; |
| } else if (isProfile) { |
| return R.string.unlock_disable_frp_warning_content_pin_profile; |
| } else { |
| return R.string.unlock_disable_frp_warning_content_pin; |
| } |
| case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: |
| case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: |
| case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: |
| case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: |
| if (hasFingerprints && isProfile) { |
| return R.string |
| .unlock_disable_frp_warning_content_password_fingerprint_profile; |
| } else if (hasFingerprints && !isProfile) { |
| return R.string.unlock_disable_frp_warning_content_password_fingerprint; |
| } else if (isProfile) { |
| return R.string.unlock_disable_frp_warning_content_password_profile; |
| } else { |
| return R.string.unlock_disable_frp_warning_content_password; |
| } |
| default: |
| if (hasFingerprints && isProfile) { |
| return R.string |
| .unlock_disable_frp_warning_content_unknown_fingerprint_profile; |
| } else if (hasFingerprints && !isProfile) { |
| return R.string.unlock_disable_frp_warning_content_unknown_fingerprint; |
| } else if (isProfile) { |
| return R.string.unlock_disable_frp_warning_content_unknown_profile; |
| } else { |
| return R.string.unlock_disable_frp_warning_content_unknown; |
| } |
| } |
| } |
| |
| private boolean isUnlockMethodSecure(String unlockMethod) { |
| return !(ScreenLockType.SWIPE.preferenceKey.equals(unlockMethod) || |
| ScreenLockType.NONE.preferenceKey.equals(unlockMethod)); |
| } |
| |
| private boolean setUnlockMethod(String unlockMethod) { |
| EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod); |
| |
| ScreenLockType lock = ScreenLockType.fromKey(unlockMethod); |
| if (lock != null) { |
| switch (lock) { |
| case NONE: |
| case SWIPE: |
| updateUnlockMethodAndFinish( |
| lock.defaultQuality, |
| lock == ScreenLockType.NONE, |
| false /* chooseLockSkipped */); |
| return true; |
| case PATTERN: |
| case PIN: |
| case PASSWORD: |
| case MANAGED: |
| maybeEnableEncryption(lock.defaultQuality, false); |
| return true; |
| } |
| } |
| Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod); |
| return false; |
| } |
| |
| private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) { |
| int title = getResIdForFactoryResetProtectionWarningTitle(); |
| int message = getResIdForFactoryResetProtectionWarningMessage(); |
| FactoryResetProtectionWarningDialog dialog = |
| FactoryResetProtectionWarningDialog.newInstance( |
| title, message, unlockMethodToSet); |
| dialog.show(getChildFragmentManager(), TAG_FRP_WARNING_DIALOG); |
| } |
| |
| public static class FactoryResetProtectionWarningDialog extends InstrumentedDialogFragment { |
| |
| private static final String ARG_TITLE_RES = "titleRes"; |
| private static final String ARG_MESSAGE_RES = "messageRes"; |
| private static final String ARG_UNLOCK_METHOD_TO_SET = "unlockMethodToSet"; |
| |
| public static FactoryResetProtectionWarningDialog newInstance( |
| int titleRes, int messageRes, String unlockMethodToSet) { |
| FactoryResetProtectionWarningDialog frag = |
| new FactoryResetProtectionWarningDialog(); |
| Bundle args = new Bundle(); |
| args.putInt(ARG_TITLE_RES, titleRes); |
| args.putInt(ARG_MESSAGE_RES, messageRes); |
| args.putString(ARG_UNLOCK_METHOD_TO_SET, unlockMethodToSet); |
| frag.setArguments(args); |
| return frag; |
| } |
| |
| @Override |
| public void show(FragmentManager manager, String tag) { |
| if (manager.findFragmentByTag(tag) == null) { |
| // Prevent opening multiple dialogs if tapped on button quickly |
| super.show(manager, tag); |
| } |
| } |
| |
| @Override |
| public Dialog onCreateDialog(Bundle savedInstanceState) { |
| final Bundle args = getArguments(); |
| |
| return new AlertDialog.Builder(getActivity()) |
| .setTitle(args.getInt(ARG_TITLE_RES)) |
| .setMessage(args.getInt(ARG_MESSAGE_RES)) |
| .setPositiveButton(R.string.unlock_disable_frp_warning_ok, |
| (dialog, whichButton) -> { |
| String unlockMethod = args.getString(ARG_UNLOCK_METHOD_TO_SET); |
| ((ChooseLockGenericFragment) getParentFragment()) |
| .setUnlockMethod(unlockMethod); |
| }) |
| .setNegativeButton(R.string.cancel, (dialog, whichButton) -> dismiss()) |
| .create(); |
| } |
| |
| @Override |
| public int getMetricsCategory() { |
| return MetricsEvent.DIALOG_FRP; |
| } |
| } |
| } |
| } |