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.password;
18
19import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
20import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
21
22import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED;
23
24import android.accessibilityservice.AccessibilityServiceInfo;
25import android.app.Activity;
26import android.app.AlertDialog;
27import android.app.Dialog;
28import android.app.Fragment;
29import android.app.FragmentManager;
30import android.app.admin.DevicePolicyManager;
31import android.content.Context;
32import android.content.Intent;
33import android.content.pm.UserInfo;
34import android.hardware.fingerprint.Fingerprint;
35import android.hardware.fingerprint.FingerprintManager;
36import android.hardware.fingerprint.FingerprintManager.RemovalCallback;
37import android.os.Bundle;
38import android.os.UserHandle;
39import android.os.UserManager;
40import android.os.storage.StorageManager;
41import android.security.KeyStore;
42import android.support.annotation.StringRes;
43import android.support.v7.preference.Preference;
44import android.support.v7.preference.PreferenceScreen;
45import android.text.TextUtils;
46import android.util.EventLog;
47import android.util.Log;
48import android.view.accessibility.AccessibilityManager;
49import android.widget.TextView;
50
51import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
52import com.android.internal.widget.LockPatternUtils;
53import com.android.settings.EncryptionInterstitial;
54import com.android.settings.EventLogTags;
55import com.android.settings.R;
56import com.android.settings.SettingsActivity;
57import com.android.settings.SettingsPreferenceFragment;
58import com.android.settings.Utils;
59import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
60import com.android.settings.fingerprint.FingerprintEnrollBase;
61import com.android.settings.fingerprint.FingerprintEnrollFindSensor;
62import com.android.settingslib.RestrictedLockUtils;
63import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
64import com.android.settingslib.RestrictedPreference;
65
66import java.util.List;
67
68public class ChooseLockGeneric extends SettingsActivity {
69    public static final String CONFIRM_CREDENTIALS = "confirm_credentials";
70
71    @Override
72    public Intent getIntent() {
73        Intent modIntent = new Intent(super.getIntent());
74        modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
75
76        String action = modIntent.getAction();
77        if (ACTION_SET_NEW_PASSWORD.equals(action)
78                || ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(action)) {
79            modIntent.putExtra(EXTRA_HIDE_DRAWER, true);
80        }
81        return modIntent;
82    }
83
84    @Override
85    protected boolean isValidFragment(String fragmentName) {
86        if (ChooseLockGenericFragment.class.getName().equals(fragmentName)) return true;
87        return false;
88    }
89
90    /* package */ Class<? extends Fragment> getFragmentClass() {
91        return ChooseLockGenericFragment.class;
92    }
93
94    public static class InternalActivity extends ChooseLockGeneric {
95    }
96
97    public static class ChooseLockGenericFragment extends SettingsPreferenceFragment {
98
99        private static final String TAG = "ChooseLockGenericFragment";
100        private static final int MIN_PASSWORD_LENGTH = 4;
101        private static final String KEY_SKIP_FINGERPRINT = "unlock_skip_fingerprint";
102        private static final String PASSWORD_CONFIRMED = "password_confirmed";
103        private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation";
104        public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
105        public static final String HIDE_DISABLED_PREFS = "hide_disabled_prefs";
106        public static final String ENCRYPT_REQUESTED_QUALITY = "encrypt_requested_quality";
107        public static final String ENCRYPT_REQUESTED_DISABLED = "encrypt_requested_disabled";
108        public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog";
109
110        /**
111         * Boolean extra determining whether a "screen lock options" button should be shown. This
112         * extra is both sent and received by ChooseLockGeneric.
113         *
114         * When this extra is false, nothing will be done.
115         * When ChooseLockGeneric receives this extra set as true, and if ChooseLockGeneric is
116         * starting ChooseLockPassword or ChooseLockPattern automatically without user interaction,
117         * ChooseLockGeneric will set this extra to true when starting ChooseLockPassword/Pattern.
118         *
119         * This gives the user the choice to select a different screen lock type, even if
120         * ChooseLockGeneric selected a default.
121         */
122        public static final String EXTRA_SHOW_OPTIONS_BUTTON = "show_options_button";
123
124        /**
125         * Original intent extras used to start this activity. This is passed to ChooseLockPassword
126         * when the "screen lock options" button is shown, so that when that button is clicked,
127         * ChooseLockGeneric can be relaunched with the same extras.
128         */
129        public static final String EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS = "choose_lock_generic_extras";
130
131        private static final int CONFIRM_EXISTING_REQUEST = 100;
132        private static final int ENABLE_ENCRYPTION_REQUEST = 101;
133        private static final int CHOOSE_LOCK_REQUEST = 102;
134        private static final int CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST = 103;
135        private static final int SKIP_FINGERPRINT_REQUEST = 104;
136
137        private ChooseLockSettingsHelper mChooseLockSettingsHelper;
138        private DevicePolicyManager mDPM;
139        private KeyStore mKeyStore;
140        private boolean mHasChallenge = false;
141        private long mChallenge;
142        private boolean mPasswordConfirmed = false;
143        private boolean mWaitingForConfirmation = false;
144        private int mEncryptionRequestQuality;
145        private boolean mEncryptionRequestDisabled;
146        private boolean mForChangeCredRequiredForBoot = false;
147        private String mUserPassword;
148        private LockPatternUtils mLockPatternUtils;
149        private FingerprintManager mFingerprintManager;
150        private int mUserId;
151        private boolean mHideDrawer = false;
152        private ManagedLockPasswordProvider mManagedPasswordProvider;
153        private boolean mIsSetNewPassword = false;
154        private UserManager mUserManager;
155        private ChooseLockGenericController mController;
156
157        protected boolean mForFingerprint = false;
158
159        @Override
160        public int getMetricsCategory() {
161            return MetricsEvent.CHOOSE_LOCK_GENERIC;
162        }
163
164        @Override
165        public void onCreate(Bundle savedInstanceState) {
166            super.onCreate(savedInstanceState);
167
168            String chooseLockAction = getActivity().getIntent().getAction();
169            mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
170            mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
171            mKeyStore = KeyStore.getInstance();
172            mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
173            mLockPatternUtils = new LockPatternUtils(getActivity());
174            mIsSetNewPassword = ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction)
175                    || ACTION_SET_NEW_PASSWORD.equals(chooseLockAction);
176
177            // Defaults to needing to confirm credentials
178            final boolean confirmCredentials = getActivity().getIntent()
179                .getBooleanExtra(CONFIRM_CREDENTIALS, true);
180            if (getActivity() instanceof ChooseLockGeneric.InternalActivity) {
181                mPasswordConfirmed = !confirmCredentials;
182                mUserPassword = getActivity().getIntent().getStringExtra(
183                        ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
184            }
185            mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
186
187            mHasChallenge = getActivity().getIntent().getBooleanExtra(
188                    ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
189            mChallenge = getActivity().getIntent().getLongExtra(
190                    ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
191            mForFingerprint = getActivity().getIntent().getBooleanExtra(
192                    ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
193            mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
194                    ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
195            mUserManager = UserManager.get(getActivity());
196
197            if (savedInstanceState != null) {
198                mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
199                mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
200                mEncryptionRequestQuality = savedInstanceState.getInt(ENCRYPT_REQUESTED_QUALITY);
201                mEncryptionRequestDisabled = savedInstanceState.getBoolean(
202                        ENCRYPT_REQUESTED_DISABLED);
203                if (mUserPassword == null) {
204                    mUserPassword = savedInstanceState.getString(
205                            ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
206                }
207            }
208
209            // a) If this is started from other user, use that user id.
210            // b) If this is started from the same user, read the extra if this is launched
211            //    from Settings app itself.
212            // c) Otherwise, use UserHandle.myUserId().
213            mUserId = Utils.getSecureTargetUser(
214                    getActivity().getActivityToken(),
215                    UserManager.get(getActivity()),
216                    getArguments(),
217                    getActivity().getIntent().getExtras()).getIdentifier();
218            mController = new ChooseLockGenericController(getContext(), mUserId);
219            if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
220                    && UserManager.get(getActivity()).isManagedProfile(mUserId)
221                    && mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
222                getActivity().setTitle(R.string.lock_settings_picker_title_profile);
223            }
224
225            mManagedPasswordProvider = ManagedLockPasswordProvider.get(getActivity(), mUserId);
226
227            if (mPasswordConfirmed) {
228                updatePreferencesOrFinish(savedInstanceState != null);
229                if (mForChangeCredRequiredForBoot) {
230                    maybeEnableEncryption(mLockPatternUtils.getKeyguardStoredPasswordQuality(
231                            mUserId), false);
232                }
233            } else if (!mWaitingForConfirmation) {
234                ChooseLockSettingsHelper helper =
235                        new ChooseLockSettingsHelper(this.getActivity(), this);
236                boolean managedProfileWithUnifiedLock =
237                        UserManager.get(getActivity()).isManagedProfile(mUserId)
238                        && !mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId);
239                boolean skipConfirmation = managedProfileWithUnifiedLock && !mIsSetNewPassword;
240                if (skipConfirmation
241                        || !helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
242                        getString(R.string.unlock_set_unlock_launch_picker_title), true, mUserId)) {
243                    mPasswordConfirmed = true; // no password set, so no need to confirm
244                    updatePreferencesOrFinish(savedInstanceState != null);
245                } else {
246                    mWaitingForConfirmation = true;
247                }
248            }
249            addHeaderView();
250        }
251
252        protected void addHeaderView() {
253            if (mForFingerprint) {
254                setHeaderView(R.layout.choose_lock_generic_fingerprint_header);
255                if (mIsSetNewPassword) {
256                    ((TextView) getHeaderView().findViewById(R.id.fingerprint_header_description))
257                            .setText(R.string.fingerprint_unlock_title);
258                }
259            }
260        }
261
262        @Override
263        public boolean onPreferenceTreeClick(Preference preference) {
264            final String key = preference.getKey();
265
266            if (!isUnlockMethodSecure(key) && mLockPatternUtils.isSecure(mUserId)) {
267                // Show the disabling FRP warning only when the user is switching from a secure
268                // unlock method to an insecure one
269                showFactoryResetProtectionWarningDialog(key);
270                return true;
271            } else if (KEY_SKIP_FINGERPRINT.equals(key)) {
272                Intent chooseLockGenericIntent = new Intent(getActivity(),
273                    ChooseLockGeneric.InternalActivity.class);
274                chooseLockGenericIntent.setAction(getIntent().getAction());
275                // Forward the target user id to  ChooseLockGeneric.
276                chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
277                chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
278                if (mUserPassword != null) {
279                    chooseLockGenericIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
280                            mUserPassword);
281                }
282                startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST);
283                return true;
284            } else {
285                return setUnlockMethod(key);
286            }
287        }
288
289        /**
290         * If the device has encryption already enabled, then ask the user if they
291         * also want to encrypt the phone with this password.
292         *
293         * @param quality
294         * @param disabled
295         */
296        // TODO: why does this take disabled, its always called with a quality higher than
297        // what makes sense with disabled == true
298        private void maybeEnableEncryption(int quality, boolean disabled) {
299            DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
300            if (UserManager.get(getActivity()).isAdminUser()
301                    && mUserId == UserHandle.myUserId()
302                    && LockPatternUtils.isDeviceEncryptionEnabled()
303                    && !LockPatternUtils.isFileEncryptionEnabled()
304                    && !dpm.getDoNotAskCredentialsOnBoot()) {
305                mEncryptionRequestQuality = quality;
306                mEncryptionRequestDisabled = disabled;
307                // Get the intent that the encryption interstitial should start for creating
308                // the new unlock method.
309                Intent unlockMethodIntent = getIntentForUnlockMethod(quality);
310                unlockMethodIntent.putExtra(
311                        ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT,
312                        mForChangeCredRequiredForBoot);
313                final Context context = getActivity();
314                // If accessibility is enabled and the user hasn't seen this dialog before, set the
315                // default state to agree with that which is compatible with accessibility
316                // (password not required).
317                final boolean accEn = AccessibilityManager.getInstance(context).isEnabled();
318                final boolean required = mLockPatternUtils.isCredentialRequiredToDecrypt(!accEn);
319                Intent intent = getEncryptionInterstitialIntent(context, quality, required,
320                        unlockMethodIntent);
321                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
322                        mForFingerprint);
323                intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
324                startActivityForResult(
325                        intent,
326                        mIsSetNewPassword && mHasChallenge
327                                ? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
328                                : ENABLE_ENCRYPTION_REQUEST);
329            } else {
330                if (mForChangeCredRequiredForBoot) {
331                    // Welp, couldn't change it. Oh well.
332                    finish();
333                    return;
334                }
335                updateUnlockMethodAndFinish(quality, disabled, false /* chooseLockSkipped */);
336            }
337        }
338
339        @Override
340        public void onActivityResult(int requestCode, int resultCode, Intent data) {
341            super.onActivityResult(requestCode, resultCode, data);
342            mWaitingForConfirmation = false;
343            if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
344                mPasswordConfirmed = true;
345                mUserPassword = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
346                updatePreferencesOrFinish(false /* isRecreatingActivity */);
347                if (mForChangeCredRequiredForBoot) {
348                    if (!TextUtils.isEmpty(mUserPassword)) {
349                        maybeEnableEncryption(
350                                mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false);
351                    } else {
352                        finish();
353                    }
354                }
355            } else if (requestCode == CHOOSE_LOCK_REQUEST
356                    || requestCode == ENABLE_ENCRYPTION_REQUEST) {
357                if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) {
358                    getActivity().setResult(resultCode, data);
359                    finish();
360                } else {
361                    // If PASSWORD_TYPE_KEY is set, this activity is used as a trampoline to start
362                    // the actual password enrollment. If the result is canceled, which means the
363                    // user pressed back, finish the activity with result canceled.
364                    int quality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
365                    if (quality != -1) {
366                        getActivity().setResult(RESULT_CANCELED, data);
367                        finish();
368                    }
369                }
370            } else if (requestCode == CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
371                    && resultCode == FingerprintEnrollBase.RESULT_FINISHED) {
372                Intent intent = getFindSensorIntent(getActivity());
373                if (data != null) {
374                    intent.putExtras(data.getExtras());
375                }
376                // Forward the target user id to fingerprint setup page.
377                intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
378                startActivity(intent);
379                finish();
380            } else if (requestCode == SKIP_FINGERPRINT_REQUEST) {
381                if (resultCode != RESULT_CANCELED) {
382                    getActivity().setResult(
383                            resultCode == RESULT_FINISHED ? RESULT_OK : resultCode, data);
384                    finish();
385                }
386            } else {
387                getActivity().setResult(Activity.RESULT_CANCELED);
388                finish();
389            }
390            if (requestCode == Activity.RESULT_CANCELED && mForChangeCredRequiredForBoot) {
391                finish();
392            }
393        }
394
395        protected Intent getFindSensorIntent(Context context) {
396            return new Intent(context, FingerprintEnrollFindSensor.class);
397        }
398
399        @Override
400        public void onSaveInstanceState(Bundle outState) {
401            super.onSaveInstanceState(outState);
402            // Saved so we don't force user to re-enter their password if configuration changes
403            outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
404            outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation);
405            outState.putInt(ENCRYPT_REQUESTED_QUALITY, mEncryptionRequestQuality);
406            outState.putBoolean(ENCRYPT_REQUESTED_DISABLED, mEncryptionRequestDisabled);
407            if (mUserPassword != null) {
408                outState.putString(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword);
409            }
410        }
411
412        private void updatePreferencesOrFinish(boolean isRecreatingActivity) {
413            Intent intent = getActivity().getIntent();
414            int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
415            if (quality == -1) {
416                // If caller didn't specify password quality, show UI and allow the user to choose.
417                quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
418                quality = mController.upgradeQuality(quality);
419                final boolean hideDisabledPrefs = intent.getBooleanExtra(
420                        HIDE_DISABLED_PREFS, false);
421                final PreferenceScreen prefScreen = getPreferenceScreen();
422                if (prefScreen != null) {
423                    prefScreen.removeAll();
424                }
425                addPreferences();
426                disableUnusablePreferences(quality, hideDisabledPrefs);
427                updatePreferenceText();
428                updateCurrentPreference();
429                updatePreferenceSummaryIfNeeded();
430            } else if (!isRecreatingActivity) {
431                // Don't start the activity again if we are recreated for configuration change
432                updateUnlockMethodAndFinish(quality, false, true /* chooseLockSkipped */);
433            }
434        }
435
436        protected void addPreferences() {
437            addPreferencesFromResource(R.xml.security_settings_picker);
438
439            // Used for testing purposes
440            findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
441            findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
442            findPreference(ScreenLockType.PIN.preferenceKey).setViewId(R.id.lock_pin);
443            findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
444        }
445
446        private void updatePreferenceText() {
447            if (mForFingerprint) {
448                setPreferenceTitle(ScreenLockType.PATTERN,
449                        R.string.fingerprint_unlock_set_unlock_pattern);
450                setPreferenceTitle(ScreenLockType.PIN, R.string.fingerprint_unlock_set_unlock_pin);
451                setPreferenceTitle(ScreenLockType.PASSWORD,
452                        R.string.fingerprint_unlock_set_unlock_password);
453            }
454
455            if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
456                setPreferenceTitle(ScreenLockType.MANAGED,
457                        mManagedPasswordProvider.getPickerOptionTitle(mForFingerprint));
458            } else {
459                removePreference(ScreenLockType.MANAGED.preferenceKey);
460            }
461
462            if (!(mForFingerprint && mIsSetNewPassword)) {
463                removePreference(KEY_SKIP_FINGERPRINT);
464            }
465        }
466
467        private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) {
468            Preference preference = findPreference(lock.preferenceKey);
469            if (preference != null) {
470                preference.setTitle(title);
471            }
472        }
473
474        private void setPreferenceTitle(ScreenLockType lock, CharSequence title) {
475            Preference preference = findPreference(lock.preferenceKey);
476            if (preference != null) {
477                preference.setTitle(title);
478            }
479        }
480
481        private void setPreferenceSummary(ScreenLockType lock, @StringRes int summary) {
482            Preference preference = findPreference(lock.preferenceKey);
483            if (preference != null) {
484                preference.setSummary(summary);
485            }
486        }
487
488        private void updateCurrentPreference() {
489            String currentKey = getKeyForCurrent();
490            Preference preference = findPreference(currentKey);
491            if (preference != null) {
492                preference.setSummary(R.string.current_screen_lock);
493            }
494        }
495
496        private String getKeyForCurrent() {
497            final int credentialOwner = UserManager.get(getContext())
498                    .getCredentialOwnerProfile(mUserId);
499            if (mLockPatternUtils.isLockScreenDisabled(credentialOwner)) {
500                return ScreenLockType.NONE.preferenceKey;
501            }
502            ScreenLockType lock =
503                    ScreenLockType.fromQuality(
504                            mLockPatternUtils.getKeyguardStoredPasswordQuality(credentialOwner));
505            return lock != null ? lock.preferenceKey : null;
506        }
507
508        /***
509         * Disables preferences that are less secure than required quality. The actual
510         * implementation is in disableUnusablePreferenceImpl.
511         *
512         * @param quality the requested quality.
513         * @param hideDisabledPrefs if false preferences show why they were disabled; otherwise
514         * they're not shown at all.
515         */
516        protected void disableUnusablePreferences(final int quality, boolean hideDisabledPrefs) {
517            disableUnusablePreferencesImpl(quality, hideDisabledPrefs);
518        }
519
520        /***
521         * Disables preferences that are less secure than required quality.
522         *
523         * @param quality the requested quality.
524         * @param hideDisabled whether to hide disable screen lock options.
525         */
526        protected void disableUnusablePreferencesImpl(final int quality,
527                boolean hideDisabled) {
528            final PreferenceScreen entries = getPreferenceScreen();
529
530            int adminEnforcedQuality = mDPM.getPasswordQuality(null, mUserId);
531            EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfPasswordQualityIsSet(
532                    getActivity(), mUserId);
533
534            for (ScreenLockType lock : ScreenLockType.values()) {
535                String key = lock.preferenceKey;
536                Preference pref = findPreference(key);
537                if (pref instanceof RestrictedPreference) {
538                    boolean visible = mController.isScreenLockVisible(lock);
539                    boolean enabled = mController.isScreenLockEnabled(lock, quality);
540                    boolean disabledByAdmin =
541                            mController.isScreenLockDisabledByAdmin(lock, adminEnforcedQuality);
542                    if (hideDisabled) {
543                        visible = visible && enabled;
544                    }
545                    if (!visible) {
546                        entries.removePreference(pref);
547                    } else if (disabledByAdmin && enforcedAdmin != null) {
548                        ((RestrictedPreference) pref).setDisabledByAdmin(enforcedAdmin);
549                    } else if (!enabled) {
550                        // we need to setDisabledByAdmin to null first to disable the padlock
551                        // in case it was set earlier.
552                        ((RestrictedPreference) pref).setDisabledByAdmin(null);
553                        pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
554                        pref.setEnabled(false);
555                    } else {
556                        ((RestrictedPreference) pref).setDisabledByAdmin(null);
557                    }
558                }
559            }
560        }
561
562        private void updatePreferenceSummaryIfNeeded() {
563            // On a default block encrypted device with accessibility, add a warning
564            // that your data is not credential encrypted
565            if (!StorageManager.isBlockEncrypted()) {
566                return;
567            }
568
569            if (StorageManager.isNonDefaultBlockEncrypted()) {
570                return;
571            }
572
573            if (AccessibilityManager.getInstance(getActivity()).getEnabledAccessibilityServiceList(
574                    AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
575                return;
576            }
577
578            setPreferenceSummary(ScreenLockType.PATTERN, R.string.secure_lock_encryption_warning);
579            setPreferenceSummary(ScreenLockType.PIN, R.string.secure_lock_encryption_warning);
580            setPreferenceSummary(ScreenLockType.PASSWORD, R.string.secure_lock_encryption_warning);
581            setPreferenceSummary(ScreenLockType.MANAGED, R.string.secure_lock_encryption_warning);
582        }
583
584        protected Intent getLockManagedPasswordIntent(String password) {
585            return mManagedPasswordProvider.createIntent(false, password);
586        }
587
588        protected Intent getLockPasswordIntent(int quality, int minLength, int maxLength) {
589            ChooseLockPassword.IntentBuilder builder =
590                    new ChooseLockPassword.IntentBuilder(getContext())
591                            .setPasswordQuality(quality)
592                            .setPasswordLengthRange(minLength, maxLength)
593                            .setForFingerprint(mForFingerprint)
594                            .setUserId(mUserId);
595            if (mHasChallenge) {
596                builder.setChallenge(mChallenge);
597            }
598            if (mUserPassword != null) {
599                builder.setPassword(mUserPassword);
600            }
601            return builder.build();
602        }
603
604        protected Intent getLockPatternIntent() {
605            ChooseLockPattern.IntentBuilder builder =
606                    new ChooseLockPattern.IntentBuilder(getContext())
607                            .setForFingerprint(mForFingerprint)
608                            .setUserId(mUserId);
609            if (mHasChallenge) {
610                builder.setChallenge(mChallenge);
611            }
612            if (mUserPassword != null) {
613                builder.setPattern(mUserPassword);
614            }
615            return builder.build();
616        }
617
618        protected Intent getEncryptionInterstitialIntent(Context context, int quality,
619                boolean required, Intent unlockMethodIntent) {
620            return EncryptionInterstitial.createStartIntent(context, quality, required,
621                    unlockMethodIntent);
622        }
623
624        /**
625         * Invokes an activity to change the user's pattern, password or PIN based on given quality
626         * and minimum quality specified by DevicePolicyManager. If quality is
627         * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared.
628         *
629         * @param quality the desired quality. Ignored if DevicePolicyManager requires more security
630         * @param disabled whether or not to show LockScreen at all. Only meaningful when quality is
631         * @param chooseLockSkipped whether or not this activity is skipped. This is true when this
632         * activity was not shown to the user at all, instead automatically proceeding based on
633         * the given intent extras, typically {@link LockPatternUtils#PASSWORD_TYPE_KEY}.
634         * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}
635         */
636        void updateUnlockMethodAndFinish(int quality, boolean disabled, boolean chooseLockSkipped) {
637            // Sanity check. We should never get here without confirming user's existing password.
638            if (!mPasswordConfirmed) {
639                throw new IllegalStateException("Tried to update password without confirming it");
640            }
641
642            quality = mController.upgradeQuality(quality);
643            Intent intent = getIntentForUnlockMethod(quality);
644            if (intent != null) {
645                if (getIntent().getBooleanExtra(EXTRA_SHOW_OPTIONS_BUTTON, false)) {
646                    intent.putExtra(EXTRA_SHOW_OPTIONS_BUTTON, chooseLockSkipped);
647                }
648                intent.putExtra(EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, getIntent().getExtras());
649                startActivityForResult(intent,
650                        mIsSetNewPassword && mHasChallenge
651                                ? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
652                                : CHOOSE_LOCK_REQUEST);
653                return;
654            }
655
656            if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
657                mChooseLockSettingsHelper.utils().clearLock(mUserPassword, mUserId);
658                mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId);
659                getActivity().setResult(Activity.RESULT_OK);
660                removeAllFingerprintForUserAndFinish(mUserId);
661            } else {
662                removeAllFingerprintForUserAndFinish(mUserId);
663            }
664        }
665
666        private Intent getIntentForUnlockMethod(int quality) {
667            Intent intent = null;
668            if (quality >= DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
669                intent = getLockManagedPasswordIntent(mUserPassword);
670            } else if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
671                int minLength = mDPM.getPasswordMinimumLength(null, mUserId);
672                if (minLength < MIN_PASSWORD_LENGTH) {
673                    minLength = MIN_PASSWORD_LENGTH;
674                }
675                final int maxLength = mDPM.getPasswordMaximumLength(quality);
676                intent = getLockPasswordIntent(quality, minLength, maxLength);
677            } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
678                intent = getLockPatternIntent();
679            }
680            if (intent != null) {
681                intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
682            }
683            return intent;
684        }
685
686        private void removeAllFingerprintForUserAndFinish(final int userId) {
687            if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
688                if (mFingerprintManager.hasEnrolledFingerprints(userId)) {
689                    mFingerprintManager.setActiveUser(userId);
690                    // For the purposes of M and N, groupId is the same as userId.
691                    final int groupId = userId;
692                    Fingerprint finger = new Fingerprint(null, groupId, 0, 0);
693                    mFingerprintManager.remove(finger, userId,
694                            new RemovalCallback() {
695                                @Override
696                                public void onRemovalError(Fingerprint fp, int errMsgId,
697                                        CharSequence errString) {
698                                    Log.e(TAG, String.format(
699                                            "Can't remove fingerprint %d in group %d. Reason: %s",
700                                            fp.getFingerId(), fp.getGroupId(), errString));
701                                    // TODO: need to proceed with the removal of managed profile
702                                    // fingerprints and finish() gracefully.
703                                }
704
705                                @Override
706                                public void onRemovalSucceeded(Fingerprint fp, int remaining) {
707                                    if (remaining == 0) {
708                                        removeManagedProfileFingerprintsAndFinishIfNecessary(userId);
709                                    }
710                                }
711                            });
712                } else {
713                    // No fingerprints in this user, we may also want to delete managed profile
714                    // fingerprints
715                    removeManagedProfileFingerprintsAndFinishIfNecessary(userId);
716                }
717            } else {
718                // The removal callback will call finish, once all fingerprints are removed.
719                // We need to wait for that to occur, otherwise, the UI will still show that
720                // fingerprints exist even though they are (about to) be removed depending on
721                // the race condition.
722                finish();
723            }
724        }
725
726        private void removeManagedProfileFingerprintsAndFinishIfNecessary(final int parentUserId) {
727            if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
728                mFingerprintManager.setActiveUser(UserHandle.myUserId());
729            }
730            boolean hasChildProfile = false;
731            if (!mUserManager.getUserInfo(parentUserId).isManagedProfile()) {
732                // Current user is primary profile, remove work profile fingerprints if necessary
733                final List<UserInfo> profiles = mUserManager.getProfiles(parentUserId);
734                final int profilesSize = profiles.size();
735                for (int i = 0; i < profilesSize; i++) {
736                    final UserInfo userInfo = profiles.get(i);
737                    if (userInfo.isManagedProfile() && !mLockPatternUtils
738                            .isSeparateProfileChallengeEnabled(userInfo.id)) {
739                        removeAllFingerprintForUserAndFinish(userInfo.id);
740                        hasChildProfile = true;
741                        break;
742                    }
743                }
744            }
745            if (!hasChildProfile) {
746                finish();
747            }
748        }
749
750        @Override
751        public void onDestroy() {
752            super.onDestroy();
753        }
754
755        @Override
756        public int getHelpResource() {
757            return R.string.help_url_choose_lockscreen;
758        }
759
760        private int getResIdForFactoryResetProtectionWarningTitle() {
761            boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mUserId);
762            return isProfile ? R.string.unlock_disable_frp_warning_title_profile
763                    : R.string.unlock_disable_frp_warning_title;
764        }
765
766        private int getResIdForFactoryResetProtectionWarningMessage() {
767            final boolean hasFingerprints;
768            if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
769                hasFingerprints = mFingerprintManager.hasEnrolledFingerprints(mUserId);
770            } else {
771                hasFingerprints = false;
772            }
773            boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mUserId);
774            switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) {
775                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
776                    if (hasFingerprints && isProfile) {
777                        return R.string
778                                .unlock_disable_frp_warning_content_pattern_fingerprint_profile;
779                    } else if (hasFingerprints && !isProfile) {
780                        return R.string.unlock_disable_frp_warning_content_pattern_fingerprint;
781                    } else if (isProfile) {
782                        return R.string.unlock_disable_frp_warning_content_pattern_profile;
783                    } else {
784                        return R.string.unlock_disable_frp_warning_content_pattern;
785                    }
786                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
787                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
788                    if (hasFingerprints && isProfile) {
789                        return R.string.unlock_disable_frp_warning_content_pin_fingerprint_profile;
790                    } else if (hasFingerprints && !isProfile) {
791                        return R.string.unlock_disable_frp_warning_content_pin_fingerprint;
792                    } else if (isProfile) {
793                        return R.string.unlock_disable_frp_warning_content_pin_profile;
794                    } else {
795                        return R.string.unlock_disable_frp_warning_content_pin;
796                    }
797                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
798                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
799                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
800                case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
801                    if (hasFingerprints && isProfile) {
802                        return R.string
803                                .unlock_disable_frp_warning_content_password_fingerprint_profile;
804                    } else if (hasFingerprints && !isProfile) {
805                        return R.string.unlock_disable_frp_warning_content_password_fingerprint;
806                    } else if (isProfile) {
807                        return R.string.unlock_disable_frp_warning_content_password_profile;
808                    } else {
809                        return R.string.unlock_disable_frp_warning_content_password;
810                    }
811                default:
812                    if (hasFingerprints && isProfile) {
813                        return R.string
814                                .unlock_disable_frp_warning_content_unknown_fingerprint_profile;
815                    } else if (hasFingerprints && !isProfile) {
816                        return R.string.unlock_disable_frp_warning_content_unknown_fingerprint;
817                    } else if (isProfile) {
818                        return R.string.unlock_disable_frp_warning_content_unknown_profile;
819                    } else {
820                        return R.string.unlock_disable_frp_warning_content_unknown;
821                    }
822            }
823        }
824
825        private boolean isUnlockMethodSecure(String unlockMethod) {
826            return !(ScreenLockType.SWIPE.preferenceKey.equals(unlockMethod) ||
827                    ScreenLockType.NONE.preferenceKey.equals(unlockMethod));
828        }
829
830        private boolean setUnlockMethod(String unlockMethod) {
831            EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
832
833            ScreenLockType lock = ScreenLockType.fromKey(unlockMethod);
834            if (lock != null) {
835                switch (lock) {
836                    case NONE:
837                    case SWIPE:
838                        updateUnlockMethodAndFinish(
839                                lock.defaultQuality,
840                                lock == ScreenLockType.NONE,
841                                false /* chooseLockSkipped */);
842                        return true;
843                    case PATTERN:
844                    case PIN:
845                    case PASSWORD:
846                    case MANAGED:
847                        maybeEnableEncryption(lock.defaultQuality, false);
848                        return true;
849                }
850            }
851            Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
852            return false;
853        }
854
855        private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) {
856            int title = getResIdForFactoryResetProtectionWarningTitle();
857            int message = getResIdForFactoryResetProtectionWarningMessage();
858            FactoryResetProtectionWarningDialog dialog =
859                    FactoryResetProtectionWarningDialog.newInstance(
860                            title, message, unlockMethodToSet);
861            dialog.show(getChildFragmentManager(), TAG_FRP_WARNING_DIALOG);
862        }
863
864        public static class FactoryResetProtectionWarningDialog extends InstrumentedDialogFragment {
865
866            private static final String ARG_TITLE_RES = "titleRes";
867            private static final String ARG_MESSAGE_RES = "messageRes";
868            private static final String ARG_UNLOCK_METHOD_TO_SET = "unlockMethodToSet";
869
870            public static FactoryResetProtectionWarningDialog newInstance(
871                    int titleRes, int messageRes, String unlockMethodToSet) {
872                FactoryResetProtectionWarningDialog frag =
873                        new FactoryResetProtectionWarningDialog();
874                Bundle args = new Bundle();
875                args.putInt(ARG_TITLE_RES, titleRes);
876                args.putInt(ARG_MESSAGE_RES, messageRes);
877                args.putString(ARG_UNLOCK_METHOD_TO_SET, unlockMethodToSet);
878                frag.setArguments(args);
879                return frag;
880            }
881
882            @Override
883            public void show(FragmentManager manager, String tag) {
884                if (manager.findFragmentByTag(tag) == null) {
885                    // Prevent opening multiple dialogs if tapped on button quickly
886                    super.show(manager, tag);
887                }
888            }
889
890            @Override
891            public Dialog onCreateDialog(Bundle savedInstanceState) {
892                final Bundle args = getArguments();
893
894                return new AlertDialog.Builder(getActivity())
895                        .setTitle(args.getInt(ARG_TITLE_RES))
896                        .setMessage(args.getInt(ARG_MESSAGE_RES))
897                        .setPositiveButton(R.string.unlock_disable_frp_warning_ok,
898                                (dialog, whichButton) -> {
899                                    String unlockMethod = args.getString(ARG_UNLOCK_METHOD_TO_SET);
900                                    ((ChooseLockGenericFragment) getParentFragment())
901                                            .setUnlockMethod(unlockMethod);
902                                })
903                        .setNegativeButton(R.string.cancel, (dialog, whichButton) -> dismiss())
904                        .create();
905            }
906
907            @Override
908            public int getMetricsCategory() {
909                return MetricsEvent.DIALOG_FRP;
910            }
911        }
912    }
913}
914