ChooseLockGeneric.java revision 23c2acfd4c01b270c998a4a497c658cc3d842473
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
17package com.android.settings;
18
19import android.accessibilityservice.AccessibilityServiceInfo;
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.app.Dialog;
23import android.app.DialogFragment;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.admin.DevicePolicyManager;
27import android.content.Context;
28import android.content.DialogInterface;
29import android.content.Intent;
30import android.hardware.fingerprint.Fingerprint;
31import android.hardware.fingerprint.FingerprintManager;
32import android.hardware.fingerprint.FingerprintManager.RemovalCallback;
33import android.os.Bundle;
34import android.os.Process;
35import android.os.UserHandle;
36import android.os.UserManager;
37import android.os.storage.StorageManager;
38import android.security.KeyStore;
39import android.support.v7.preference.Preference;
40import android.support.v7.preference.PreferenceScreen;
41import android.text.TextUtils;
42import android.util.EventLog;
43import android.util.Log;
44import android.view.View;
45import android.view.accessibility.AccessibilityManager;
46import android.widget.Toast;
47
48import com.android.internal.logging.MetricsProto.MetricsEvent;
49import com.android.internal.widget.LockPatternUtils;
50import com.android.settingslib.RestrictedLockUtils;
51import com.android.settingslib.RestrictedPreference;
52
53import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
54
55public class ChooseLockGeneric extends SettingsActivity {
56    public static final String CONFIRM_CREDENTIALS = "confirm_credentials";
57
58    @Override
59    public Intent getIntent() {
60        Intent modIntent = new Intent(super.getIntent());
61        modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
62        return modIntent;
63    }
64
65    @Override
66    protected boolean isValidFragment(String fragmentName) {
67        if (ChooseLockGenericFragment.class.getName().equals(fragmentName)) return true;
68        return false;
69    }
70
71    /* package */ Class<? extends Fragment> getFragmentClass() {
72        return ChooseLockGenericFragment.class;
73    }
74
75    public static class InternalActivity extends ChooseLockGeneric {
76    }
77
78    public static class ChooseLockGenericFragment extends SettingsPreferenceFragment {
79        private static final String TAG = "ChooseLockGenericFragment";
80        private static final int MIN_PASSWORD_LENGTH = 4;
81        private static final String KEY_UNLOCK_SET_OFF = "unlock_set_off";
82        private static final String KEY_UNLOCK_SET_NONE = "unlock_set_none";
83        private static final String KEY_UNLOCK_SET_PIN = "unlock_set_pin";
84        private static final String KEY_UNLOCK_SET_PASSWORD = "unlock_set_password";
85        private static final String KEY_UNLOCK_SET_PATTERN = "unlock_set_pattern";
86        private static final String PASSWORD_CONFIRMED = "password_confirmed";
87        private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation";
88        public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
89        public static final String HIDE_DISABLED_PREFS = "hide_disabled_prefs";
90        public static final String ENCRYPT_REQUESTED_QUALITY = "encrypt_requested_quality";
91        public static final String ENCRYPT_REQUESTED_DISABLED = "encrypt_requested_disabled";
92        public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog";
93
94        private static final int CONFIRM_EXISTING_REQUEST = 100;
95        private static final int ENABLE_ENCRYPTION_REQUEST = 101;
96        private static final int CHOOSE_LOCK_REQUEST = 102;
97
98        private ChooseLockSettingsHelper mChooseLockSettingsHelper;
99        private DevicePolicyManager mDPM;
100        private KeyStore mKeyStore;
101        private boolean mHasChallenge = false;
102        private long mChallenge;
103        private boolean mPasswordConfirmed = false;
104        private boolean mWaitingForConfirmation = false;
105        private int mEncryptionRequestQuality;
106        private boolean mEncryptionRequestDisabled;
107        private boolean mRequirePassword;
108        private boolean mForChangeCredRequiredForBoot = false;
109        private String mUserPassword;
110        private LockPatternUtils mLockPatternUtils;
111        private FingerprintManager mFingerprintManager;
112        private int mUserId;
113        private RemovalCallback mRemovalCallback = new RemovalCallback() {
114
115            @Override
116            public void onRemovalSucceeded(Fingerprint fingerprint) {
117                Log.v(TAG, "Fingerprint removed: " + fingerprint.getFingerId());
118                if (mFingerprintManager.getEnrolledFingerprints().size() == 0) {
119                    finish();
120                }
121            }
122
123            @Override
124            public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {
125                Activity activity = getActivity();
126                if (activity != null) {
127                    Toast.makeText(getActivity(), errString, Toast.LENGTH_SHORT);
128                }
129                finish();
130            }
131        };
132
133        protected boolean mForFingerprint = false;
134
135        @Override
136        protected int getMetricsCategory() {
137            return MetricsEvent.CHOOSE_LOCK_GENERIC;
138        }
139
140        @Override
141        public void onCreate(Bundle savedInstanceState) {
142            super.onCreate(savedInstanceState);
143
144            mFingerprintManager =
145                (FingerprintManager) getActivity().getSystemService(Context.FINGERPRINT_SERVICE);
146            mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
147            mKeyStore = KeyStore.getInstance();
148            mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
149            mLockPatternUtils = new LockPatternUtils(getActivity());
150
151            // Defaults to needing to confirm credentials
152            final boolean confirmCredentials = getActivity().getIntent()
153                .getBooleanExtra(CONFIRM_CREDENTIALS, true);
154            if (getActivity() instanceof ChooseLockGeneric.InternalActivity) {
155                mPasswordConfirmed = !confirmCredentials;
156            }
157
158            mHasChallenge = getActivity().getIntent().getBooleanExtra(
159                    ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
160            mChallenge = getActivity().getIntent().getLongExtra(
161                    ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
162            mForFingerprint = getActivity().getIntent().getBooleanExtra(
163                    ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
164            mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
165                    ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
166
167            if (savedInstanceState != null) {
168                mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
169                mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
170                mEncryptionRequestQuality = savedInstanceState.getInt(ENCRYPT_REQUESTED_QUALITY);
171                mEncryptionRequestDisabled = savedInstanceState.getBoolean(
172                        ENCRYPT_REQUESTED_DISABLED);
173            }
174
175            int targetUser = Utils.getSecureTargetUser(
176                    getActivity().getActivityToken(),
177                    UserManager.get(getActivity()),
178                    null,
179                    getActivity().getIntent().getExtras()).getIdentifier();
180            if (DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(
181                    getActivity().getIntent().getAction()) ||
182                    !mLockPatternUtils.isSeparateProfileChallengeAllowed(targetUser)) {
183                // Always use parent if explicitely requested or if profile challenge is not
184                // supported
185                Bundle arguments = getArguments();
186                mUserId = Utils.getUserIdFromBundle(getContext(), arguments != null ? arguments
187                        : getActivity().getIntent().getExtras());
188            } else {
189                mUserId = targetUser;
190            }
191
192            if (mPasswordConfirmed) {
193                updatePreferencesOrFinish();
194                if (mForChangeCredRequiredForBoot) {
195                    maybeEnableEncryption(mLockPatternUtils.getKeyguardStoredPasswordQuality(
196                            mUserId), false);
197                }
198            } else if (!mWaitingForConfirmation) {
199                ChooseLockSettingsHelper helper =
200                        new ChooseLockSettingsHelper(this.getActivity(), this);
201                if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
202                        getString(R.string.unlock_set_unlock_launch_picker_title), true, mUserId)) {
203                    mPasswordConfirmed = true; // no password set, so no need to confirm
204                    updatePreferencesOrFinish();
205                } else {
206                    mWaitingForConfirmation = true;
207                }
208            }
209            addHeaderView();
210        }
211
212        protected void addHeaderView() {
213            if (mForFingerprint) {
214                setHeaderView(R.layout.choose_lock_generic_fingerprint_header);
215            }
216        }
217
218        @Override
219        public boolean onPreferenceTreeClick(Preference preference) {
220            final String key = preference.getKey();
221
222            if (!isUnlockMethodSecure(key) && mLockPatternUtils.isSecure(mUserId)) {
223                // Show the disabling FRP warning only when the user is switching from a secure
224                // unlock method to an insecure one
225                showFactoryResetProtectionWarningDialog(key);
226                return true;
227            } else {
228                return setUnlockMethod(key);
229            }
230        }
231
232        /**
233         * If the device has encryption already enabled, then ask the user if they
234         * also want to encrypt the phone with this password.
235         *
236         * @param quality
237         * @param disabled
238         */
239        // TODO: why does this take disabled, its always called with a quality higher than
240        // what makes sense with disabled == true
241        private void maybeEnableEncryption(int quality, boolean disabled) {
242            DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
243            if (UserManager.get(getActivity()).isAdminUser()
244                    && mUserId == UserHandle.myUserId()
245                    && LockPatternUtils.isDeviceEncryptionEnabled()
246                    && !LockPatternUtils.isFileEncryptionEnabled()
247                    && !dpm.getDoNotAskCredentialsOnBoot()) {
248                mEncryptionRequestQuality = quality;
249                mEncryptionRequestDisabled = disabled;
250                // Get the intent that the encryption interstitial should start for creating
251                // the new unlock method.
252                Intent unlockMethodIntent = getIntentForUnlockMethod(quality, disabled);
253                unlockMethodIntent.putExtra(
254                        ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT,
255                        mForChangeCredRequiredForBoot);
256                final Context context = getActivity();
257                // If accessibility is enabled and the user hasn't seen this dialog before, set the
258                // default state to agree with that which is compatible with accessibility
259                // (password not required).
260                final boolean accEn = AccessibilityManager.getInstance(context).isEnabled();
261                final boolean required = mLockPatternUtils.isCredentialRequiredToDecrypt(!accEn);
262                Intent intent = getEncryptionInterstitialIntent(context, quality, required,
263                        unlockMethodIntent);
264                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
265                        mForFingerprint);
266                startActivityForResult(intent, ENABLE_ENCRYPTION_REQUEST);
267            } else {
268                if (mForChangeCredRequiredForBoot) {
269                    // Welp, couldn't change it. Oh well.
270                    finish();
271                    return;
272                }
273                mRequirePassword = false; // device encryption not enabled or not device owner.
274                updateUnlockMethodAndFinish(quality, disabled);
275            }
276        }
277
278        @Override
279        public void onActivityResult(int requestCode, int resultCode, Intent data) {
280            super.onActivityResult(requestCode, resultCode, data);
281            mWaitingForConfirmation = false;
282            if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
283                mPasswordConfirmed = true;
284                mUserPassword = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
285                updatePreferencesOrFinish();
286                if (mForChangeCredRequiredForBoot) {
287                    if (!TextUtils.isEmpty(mUserPassword)) {
288                        maybeEnableEncryption(
289                                mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false);
290                    } else {
291                        finish();
292                    }
293                }
294            } else if (requestCode == CHOOSE_LOCK_REQUEST
295                    || requestCode == ENABLE_ENCRYPTION_REQUEST) {
296                if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) {
297                    getActivity().setResult(resultCode, data);
298                    finish();
299                }
300            } else {
301                getActivity().setResult(Activity.RESULT_CANCELED);
302                finish();
303            }
304            if (requestCode == Activity.RESULT_CANCELED && mForChangeCredRequiredForBoot) {
305                finish();
306            }
307        }
308
309        @Override
310        public void onSaveInstanceState(Bundle outState) {
311            super.onSaveInstanceState(outState);
312            // Saved so we don't force user to re-enter their password if configuration changes
313            outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
314            outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation);
315            outState.putInt(ENCRYPT_REQUESTED_QUALITY, mEncryptionRequestQuality);
316            outState.putBoolean(ENCRYPT_REQUESTED_DISABLED, mEncryptionRequestDisabled);
317        }
318
319        private void updatePreferencesOrFinish() {
320            Intent intent = getActivity().getIntent();
321            int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
322            if (quality == -1) {
323                // If caller didn't specify password quality, show UI and allow the user to choose.
324                quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
325                quality = upgradeQuality(quality);
326                final boolean hideDisabledPrefs = intent.getBooleanExtra(
327                        HIDE_DISABLED_PREFS, false);
328                final PreferenceScreen prefScreen = getPreferenceScreen();
329                if (prefScreen != null) {
330                    prefScreen.removeAll();
331                }
332                addPreferences();
333                disableUnusablePreferences(quality, hideDisabledPrefs);
334                updatePreferenceText();
335                updateCurrentPreference();
336                updatePreferenceSummaryIfNeeded();
337            } else {
338                updateUnlockMethodAndFinish(quality, false);
339            }
340        }
341
342        protected void addPreferences() {
343            addPreferencesFromResource(R.xml.security_settings_picker);
344        }
345
346        private void updatePreferenceText() {
347            if (mForFingerprint) {
348                Preference pattern = findPreference(KEY_UNLOCK_SET_PATTERN);
349                pattern.setTitle(R.string.fingerprint_unlock_set_unlock_pattern);
350
351                Preference pin = findPreference(KEY_UNLOCK_SET_PIN);
352                pin.setTitle(R.string.fingerprint_unlock_set_unlock_pin);
353
354                Preference password = findPreference(KEY_UNLOCK_SET_PASSWORD);
355                password.setTitle(R.string.fingerprint_unlock_set_unlock_password);
356            }
357        }
358
359        private void updateCurrentPreference() {
360            String currentKey = getKeyForCurrent();
361            Preference preference = findPreference(currentKey);
362            if (preference != null) {
363                preference.setSummary(R.string.current_screen_lock);
364            }
365        }
366
367        private String getKeyForCurrent() {
368            final int credentialOwner = UserManager.get(getContext())
369                    .getCredentialOwnerProfile(mUserId);
370            if (mLockPatternUtils.isLockScreenDisabled(credentialOwner)) {
371                return KEY_UNLOCK_SET_OFF;
372            }
373            switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(credentialOwner)) {
374                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
375                    return KEY_UNLOCK_SET_PATTERN;
376                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
377                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
378                    return KEY_UNLOCK_SET_PIN;
379                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
380                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
381                    return KEY_UNLOCK_SET_PASSWORD;
382                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
383                    return KEY_UNLOCK_SET_NONE;
384            }
385            return null;
386        }
387
388        /** increases the quality if necessary */
389        private int upgradeQuality(int quality) {
390            quality = upgradeQualityForDPM(quality);
391            return quality;
392        }
393
394        private int upgradeQualityForDPM(int quality) {
395            // Compare min allowed password quality
396            int minQuality = mDPM.getPasswordQuality(null);
397            if (quality < minQuality) {
398                quality = minQuality;
399            }
400            return quality;
401        }
402
403        /***
404         * Disables preferences that are less secure than required quality. The actual
405         * implementation is in disableUnusablePreferenceImpl.
406         *
407         * @param quality the requested quality.
408         * @param hideDisabledPrefs if false preferences show why they were disabled; otherwise
409         * they're not shown at all.
410         */
411        protected void disableUnusablePreferences(final int quality, boolean hideDisabledPrefs) {
412            disableUnusablePreferencesImpl(quality, hideDisabledPrefs);
413        }
414
415        /***
416         * Disables preferences that are less secure than required quality.
417         *
418         * @param quality the requested quality.
419         * @param hideDisabled whether to hide disable screen lock options.
420         */
421        protected void disableUnusablePreferencesImpl(final int quality,
422                boolean hideDisabled) {
423            final PreferenceScreen entries = getPreferenceScreen();
424
425            int adminEnforcedQuality = mDPM.getPasswordQuality(null);
426            EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfPasswordQualityIsSet(
427                    getActivity(), mUserId);
428            for (int i = entries.getPreferenceCount() - 1; i >= 0; --i) {
429                Preference pref = entries.getPreference(i);
430                if (pref instanceof RestrictedPreference) {
431                    final String key = pref.getKey();
432                    boolean enabled = true;
433                    boolean visible = true;
434                    boolean disabledByAdmin = false;
435                    if (KEY_UNLOCK_SET_OFF.equals(key)) {
436                        enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
437                        if (getResources().getBoolean(R.bool.config_hide_none_security_option)) {
438                            enabled = false;
439                            visible = false;
440                        }
441                        disabledByAdmin = adminEnforcedQuality
442                                > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
443                    } else if (KEY_UNLOCK_SET_NONE.equals(key)) {
444                        if (mUserId != UserHandle.myUserId()) {
445                            // Swipe doesn't make sense for profiles.
446                            visible = false;
447                        }
448                        enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
449                        disabledByAdmin = adminEnforcedQuality
450                                > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
451                    } else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
452                        enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
453                        disabledByAdmin = adminEnforcedQuality
454                                > DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
455                    } else if (KEY_UNLOCK_SET_PIN.equals(key)) {
456                        enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
457                        disabledByAdmin = adminEnforcedQuality
458                                > DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
459                    } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
460                        enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
461                        disabledByAdmin = adminEnforcedQuality
462                                > DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
463                    }
464                    if (hideDisabled) {
465                        visible = enabled;
466                    }
467                    if (!visible) {
468                        entries.removePreference(pref);
469                    } else if (disabledByAdmin && enforcedAdmin != null) {
470                        ((RestrictedPreference) pref).setDisabledByAdmin(enforcedAdmin);
471                    } else if (!enabled) {
472                        // we need to setDisabledByAdmin to null first to disable the padlock
473                        // in case it was set earlier.
474                        ((RestrictedPreference) pref).setDisabledByAdmin(null);
475                        pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
476                        pref.setEnabled(false);
477                    } else {
478                        ((RestrictedPreference) pref).setDisabledByAdmin(null);
479                    }
480                }
481            }
482        }
483
484        private void updatePreferenceSummaryIfNeeded() {
485            // On a default block encrypted device with accessibility, add a warning
486            // that your data is not credential encrypted
487            if (!StorageManager.isBlockEncrypted()) {
488                return;
489            }
490
491            if (StorageManager.isNonDefaultBlockEncrypted()) {
492                return;
493            }
494
495            if (AccessibilityManager.getInstance(getActivity()).getEnabledAccessibilityServiceList(
496                    AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
497                return;
498            }
499
500            CharSequence summary = getString(R.string.secure_lock_encryption_warning);
501
502            PreferenceScreen screen = getPreferenceScreen();
503            final int preferenceCount = screen.getPreferenceCount();
504            for (int i = 0; i < preferenceCount; i++) {
505                Preference preference = screen.getPreference(i);
506                switch (preference.getKey()) {
507                    case KEY_UNLOCK_SET_PATTERN:
508                    case KEY_UNLOCK_SET_PIN:
509                    case KEY_UNLOCK_SET_PASSWORD: {
510                        preference.setSummary(summary);
511                    } break;
512                }
513            }
514        }
515
516        protected Intent getLockPasswordIntent(Context context, int quality,
517                int minLength, final int maxLength,
518                boolean requirePasswordToDecrypt, boolean confirmCredentials, int userId) {
519            return ChooseLockPassword.createIntent(context, quality, minLength,
520                    maxLength, requirePasswordToDecrypt, confirmCredentials, userId);
521        }
522
523        protected Intent getLockPasswordIntent(Context context, int quality,
524                int minLength, final int maxLength,
525                boolean requirePasswordToDecrypt, long challenge, int userId) {
526            return ChooseLockPassword.createIntent(context, quality, minLength,
527                    maxLength, requirePasswordToDecrypt, challenge, userId);
528        }
529
530        protected Intent getLockPasswordIntent(Context context, int quality, int minLength,
531                int maxLength, boolean requirePasswordToDecrypt, String password, int userId) {
532            return ChooseLockPassword.createIntent(context, quality, minLength, maxLength,
533                    requirePasswordToDecrypt, password, userId);
534        }
535
536        protected Intent getLockPatternIntent(Context context, final boolean requirePassword,
537                final boolean confirmCredentials, int userId) {
538            return ChooseLockPattern.createIntent(context, requirePassword,
539                    confirmCredentials, userId);
540        }
541
542        protected Intent getLockPatternIntent(Context context, final boolean requirePassword,
543               long challenge, int userId) {
544            return ChooseLockPattern.createIntent(context, requirePassword, challenge, userId);
545        }
546
547        protected Intent getLockPatternIntent(Context context, final boolean requirePassword,
548                final String pattern, int userId) {
549            return ChooseLockPattern.createIntent(context, requirePassword, pattern, userId);
550        }
551
552        protected Intent getEncryptionInterstitialIntent(Context context, int quality,
553                boolean required, Intent unlockMethodIntent) {
554            return EncryptionInterstitial.createStartIntent(context, quality, required,
555                    unlockMethodIntent);
556        }
557
558        /**
559         * Invokes an activity to change the user's pattern, password or PIN based on given quality
560         * and minimum quality specified by DevicePolicyManager. If quality is
561         * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared.
562         *
563         * @param quality the desired quality. Ignored if DevicePolicyManager requires more security
564         * @param disabled whether or not to show LockScreen at all. Only meaningful when quality is
565         * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}
566         */
567        void updateUnlockMethodAndFinish(int quality, boolean disabled) {
568            // Sanity check. We should never get here without confirming user's existing password.
569            if (!mPasswordConfirmed) {
570                throw new IllegalStateException("Tried to update password without confirming it");
571            }
572
573            quality = upgradeQuality(quality);
574            Intent intent = getIntentForUnlockMethod(quality, disabled);
575            if (intent != null) {
576                startActivityForResult(intent, CHOOSE_LOCK_REQUEST);
577                return;
578            }
579
580            if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
581                mLockPatternUtils.setSeparateProfileChallengeEnabled(mUserId, true);
582                mChooseLockSettingsHelper.utils().clearLock(mUserId);
583                mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId);
584                removeAllFingerprintTemplatesAndFinish();
585                getActivity().setResult(Activity.RESULT_OK);
586            } else {
587                removeAllFingerprintTemplatesAndFinish();
588            }
589        }
590
591        private Intent getIntentForUnlockMethod(int quality, boolean disabled) {
592            Intent intent = null;
593            final Context context = getActivity();
594            if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
595                int minLength = mDPM.getPasswordMinimumLength(null);
596                if (minLength < MIN_PASSWORD_LENGTH) {
597                    minLength = MIN_PASSWORD_LENGTH;
598                }
599                final int maxLength = mDPM.getPasswordMaximumLength(quality);
600                if (mHasChallenge) {
601                    intent = getLockPasswordIntent(context, quality, minLength,
602                            maxLength, mRequirePassword, mChallenge, mUserId);
603                } else {
604                    intent = getLockPasswordIntent(context, quality, minLength,
605                            maxLength, mRequirePassword, mUserPassword, mUserId);
606                }
607            } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
608                if (mHasChallenge) {
609                    intent = getLockPatternIntent(context, mRequirePassword,
610                            mChallenge, mUserId);
611                } else {
612                    intent = getLockPatternIntent(context, mRequirePassword,
613                            mUserPassword, mUserId);
614                }
615            }
616            return intent;
617        }
618
619        private void removeAllFingerprintTemplatesAndFinish() {
620            if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()
621                    && mFingerprintManager.hasEnrolledFingerprints(mUserId)) {
622                mFingerprintManager.setActiveUser(mUserId);
623                mFingerprintManager.remove(
624                        new Fingerprint(null, mUserId, 0, 0), mUserId,
625                        new RemovalCallback() {
626                            @Override
627                            public void onRemovalError(Fingerprint fp, int errMsgId,
628                                    CharSequence errString) {
629                                mRemovalCallback.onRemovalError(fp, errMsgId, errString);
630                                mFingerprintManager.setActiveUser(UserHandle.myUserId());
631                            }
632
633                            @Override
634                            public void onRemovalSucceeded(Fingerprint fingerprint) {
635                                mRemovalCallback.onRemovalSucceeded(fingerprint);
636                                mFingerprintManager.setActiveUser(UserHandle.myUserId());
637                            }
638                        });
639            }
640            finish();
641        }
642
643        @Override
644        public void onDestroy() {
645            super.onDestroy();
646        }
647
648        @Override
649        protected int getHelpResource() {
650            return R.string.help_url_choose_lockscreen;
651        }
652
653        private int getResIdForFactoryResetProtectionWarningTitle() {
654            boolean isProfile = Utils.isManagedProfile(UserManager.get(getActivity()), mUserId);
655            return isProfile ? R.string.unlock_disable_frp_warning_title_profile
656                    : R.string.unlock_disable_frp_warning_title;
657        }
658
659        private int getResIdForFactoryResetProtectionWarningMessage() {
660            boolean hasFingerprints = mFingerprintManager.hasEnrolledFingerprints(mUserId);
661            boolean isProfile = Utils.isManagedProfile(UserManager.get(getActivity()), mUserId);
662            switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) {
663                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
664                    if (hasFingerprints && isProfile) {
665                        return R.string
666                                .unlock_disable_frp_warning_content_pattern_fingerprint_profile;
667                    } else if (hasFingerprints && !isProfile) {
668                        return R.string.unlock_disable_frp_warning_content_pattern_fingerprint;
669                    } else if (isProfile) {
670                        return R.string.unlock_disable_frp_warning_content_pattern_profile;
671                    } else {
672                        return R.string.unlock_disable_frp_warning_content_pattern;
673                    }
674                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
675                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
676                    if (hasFingerprints && isProfile) {
677                        return R.string.unlock_disable_frp_warning_content_pin_fingerprint_profile;
678                    } else if (hasFingerprints && !isProfile) {
679                        return R.string.unlock_disable_frp_warning_content_pin_fingerprint;
680                    } else if (isProfile) {
681                        return R.string.unlock_disable_frp_warning_content_pin_profile;
682                    } else {
683                        return R.string.unlock_disable_frp_warning_content_pin;
684                    }
685                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
686                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
687                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
688                    if (hasFingerprints && isProfile) {
689                        return R.string
690                                .unlock_disable_frp_warning_content_password_fingerprint_profile;
691                    } else if (hasFingerprints && !isProfile) {
692                        return R.string.unlock_disable_frp_warning_content_password_fingerprint;
693                    } else if (isProfile) {
694                        return R.string.unlock_disable_frp_warning_content_password_profile;
695                    } else {
696                        return R.string.unlock_disable_frp_warning_content_password;
697                    }
698                default:
699                    if (hasFingerprints && isProfile) {
700                        return R.string
701                                .unlock_disable_frp_warning_content_unknown_fingerprint_profile;
702                    } else if (hasFingerprints && !isProfile) {
703                        return R.string.unlock_disable_frp_warning_content_unknown_fingerprint;
704                    } else if (isProfile) {
705                        return R.string.unlock_disable_frp_warning_content_unknown_profile;
706                    } else {
707                        return R.string.unlock_disable_frp_warning_content_unknown;
708                    }
709            }
710        }
711
712        private boolean isUnlockMethodSecure(String unlockMethod) {
713            return !(KEY_UNLOCK_SET_OFF.equals(unlockMethod) ||
714                    KEY_UNLOCK_SET_NONE.equals(unlockMethod));
715        }
716
717        private boolean setUnlockMethod(String unlockMethod) {
718            EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
719
720            if (KEY_UNLOCK_SET_OFF.equals(unlockMethod)) {
721                updateUnlockMethodAndFinish(
722                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true /* disabled */ );
723            } else if (KEY_UNLOCK_SET_NONE.equals(unlockMethod)) {
724                updateUnlockMethodAndFinish(
725                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false /* disabled */ );
726            } else if (KEY_UNLOCK_SET_PATTERN.equals(unlockMethod)) {
727                maybeEnableEncryption(
728                        DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
729            } else if (KEY_UNLOCK_SET_PIN.equals(unlockMethod)) {
730                maybeEnableEncryption(
731                        DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
732            } else if (KEY_UNLOCK_SET_PASSWORD.equals(unlockMethod)) {
733                maybeEnableEncryption(
734                        DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
735            } else {
736                Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
737                return false;
738            }
739            return true;
740        }
741
742        private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) {
743            int title = getResIdForFactoryResetProtectionWarningTitle();
744            int message = getResIdForFactoryResetProtectionWarningMessage();
745            FactoryResetProtectionWarningDialog dialog =
746                    FactoryResetProtectionWarningDialog.newInstance(
747                            title, message, unlockMethodToSet);
748            dialog.show(getChildFragmentManager(), TAG_FRP_WARNING_DIALOG);
749        }
750
751        public static class FactoryResetProtectionWarningDialog extends DialogFragment {
752
753            private static final String ARG_TITLE_RES = "titleRes";
754            private static final String ARG_MESSAGE_RES = "messageRes";
755            private static final String ARG_UNLOCK_METHOD_TO_SET = "unlockMethodToSet";
756
757            public static FactoryResetProtectionWarningDialog newInstance(
758                    int titleRes, int messageRes, String unlockMethodToSet) {
759                FactoryResetProtectionWarningDialog frag =
760                        new FactoryResetProtectionWarningDialog();
761                Bundle args = new Bundle();
762                args.putInt(ARG_TITLE_RES, titleRes);
763                args.putInt(ARG_MESSAGE_RES, messageRes);
764                args.putString(ARG_UNLOCK_METHOD_TO_SET, unlockMethodToSet);
765                frag.setArguments(args);
766                return frag;
767            }
768
769            @Override
770            public void show(FragmentManager manager, String tag) {
771                if (manager.findFragmentByTag(tag) == null) {
772                    // Prevent opening multiple dialogs if tapped on button quickly
773                    super.show(manager, tag);
774                }
775            }
776
777            @Override
778            public Dialog onCreateDialog(Bundle savedInstanceState) {
779                final Bundle args = getArguments();
780
781                return new AlertDialog.Builder(getActivity())
782                        .setTitle(args.getInt(ARG_TITLE_RES))
783                        .setMessage(args.getInt(ARG_MESSAGE_RES))
784                        .setPositiveButton(R.string.unlock_disable_frp_warning_ok,
785                                new DialogInterface.OnClickListener() {
786                                    @Override
787                                    public void onClick(DialogInterface dialog, int whichButton) {
788                                        ((ChooseLockGenericFragment) getParentFragment())
789                                                .setUnlockMethod(
790                                                        args.getString(ARG_UNLOCK_METHOD_TO_SET));
791                                    }
792                                }
793                        )
794                        .setNegativeButton(R.string.cancel,
795                                new DialogInterface.OnClickListener() {
796                                    @Override
797                                    public void onClick(DialogInterface dialog, int whichButton) {
798                                        dismiss();
799                                    }
800                                }
801                        )
802                        .create();
803            }
804        }
805    }
806}
807