Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7dfe9187 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Launch settings for clicking fingerprint unlock"

parents 465e7c4c 160661dc
Loading
Loading
Loading
Loading
+33 −3
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;

import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
@@ -58,6 +59,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
    private static final String KEY_CONFIRMING_CREDENTIALS = "confirming_credentials";
    private static final String KEY_SCROLLED_TO_BOTTOM = "scrolled";

    private GatekeeperPasswordProvider mGatekeeperPasswordProvider;
    private UserManager mUserManager;
    private boolean mHasPassword;
    private boolean mBiometricUnlockDisabledByAdmin;
@@ -178,7 +180,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase

        mErrorText = getErrorTextView();

        mUserManager = UserManager.get(this);
        mUserManager = getUserManager();
        updatePasswordQuality();

        if (!mConfirmingCredentials) {
@@ -253,8 +255,28 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
        return super.shouldFinishWhenBackgrounded() && !mConfirmingCredentials && !mNextClicked;
    }

    @VisibleForTesting
    @NonNull
    protected GatekeeperPasswordProvider getGatekeeperPasswordProvider() {
        if (mGatekeeperPasswordProvider == null) {
            mGatekeeperPasswordProvider = new GatekeeperPasswordProvider(getLockPatternUtils());
        }
        return mGatekeeperPasswordProvider;
    }

    @VisibleForTesting
    protected UserManager getUserManager() {
        return UserManager.get(this);
    }

    @VisibleForTesting
    @NonNull
    protected LockPatternUtils getLockPatternUtils() {
        return new LockPatternUtils(this);
    }

    private void updatePasswordQuality() {
        final int passwordQuality = new LockPatternUtils(this)
        final int passwordQuality = getLockPatternUtils()
                .getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId));
        mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
    }
@@ -301,6 +323,14 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
        startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
    }

    /**
     * Returns the intent extra data for setResult(), null means nothing need to been sent back
     */
    @Nullable
    protected Intent getSetResultIntentExtra(@Nullable Intent activityResultIntent) {
        return activityResultIntent;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG,
@@ -310,7 +340,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
                && BiometricUtils.isMultiBiometricFingerprintEnrollmentFlow(this);
        if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) {
            if (isResultFinished(resultCode)) {
                handleBiometricResultSkipOrFinished(resultCode, data);
                handleBiometricResultSkipOrFinished(resultCode, getSetResultIntentExtra(data));
            } else if (isResultSkipped(resultCode)) {
                if (!BiometricUtils.tryStartingNextBiometricEnroll(this,
                        ENROLL_NEXT_BIOMETRIC_REQUEST, "BIOMETRIC_FIND_SENSOR_SKIPPED")) {
+22 −0
Original line number Diff line number Diff line
@@ -72,6 +72,8 @@ public class BiometricUtils {
    };

    /**
     * @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
     *
     * Given the result from confirming or choosing a credential, request Gatekeeper to generate
     * a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
     *
@@ -83,6 +85,7 @@ public class BiometricUtils {
     * @throws GatekeeperCredentialNotMatchException if Gatekeeper response is not match
     * @throws IllegalStateException if Gatekeeper Password is missing
     */
    @Deprecated
    public static byte[] requestGatekeeperHat(@NonNull Context context, @NonNull Intent result,
            int userId, long challenge) {
        if (!containsGatekeeperPasswordHandle(result)) {
@@ -93,6 +96,10 @@ public class BiometricUtils {
        return requestGatekeeperHat(context, gatekeeperPasswordHandle, userId, challenge);
    }

    /**
     * @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
     */
    @Deprecated
    public static byte[] requestGatekeeperHat(@NonNull Context context, long gkPwHandle, int userId,
            long challenge) {
        final LockPatternUtils utils = new LockPatternUtils(context);
@@ -104,15 +111,25 @@ public class BiometricUtils {
        return response.getGatekeeperHAT();
    }

    /**
     * @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
     */
    @Deprecated
    public static boolean containsGatekeeperPasswordHandle(@Nullable Intent data) {
        return data != null && data.hasExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
    }

    /**
     * @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
     */
    @Deprecated
    public static long getGatekeeperPasswordHandle(@NonNull Intent data) {
        return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
    }

    /**
     * @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
     *
     * Requests {@link com.android.server.locksettings.LockSettingsService} to remove the
     * gatekeeper password associated with a previous
     * {@link ChooseLockSettingsHelper.Builder#setRequestGatekeeperPasswordHandle(boolean)}
@@ -120,6 +137,7 @@ public class BiometricUtils {
     * @param context Caller's context
     * @param data The onActivityResult intent from ChooseLock* or ConfirmLock*
     */
    @Deprecated
    public static void removeGatekeeperPasswordHandle(@NonNull Context context,
            @Nullable Intent data) {
        if (data == null) {
@@ -131,6 +149,10 @@ public class BiometricUtils {
        removeGatekeeperPasswordHandle(context, getGatekeeperPasswordHandle(data));
    }

    /**
     * @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
     */
    @Deprecated
    public static void removeGatekeeperPasswordHandle(@NonNull Context context, long handle) {
        final LockPatternUtils utils = new LockPatternUtils(context);
        utils.removeGatekeeperPasswordHandle(handle);
+128 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.biometrics;

import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;

import android.content.Intent;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.password.ChooseLockSettingsHelper;

/**
 * Gatekeeper hat related methods
 */
public class GatekeeperPasswordProvider {

    private static final String TAG = "GatekeeperPasswordProvider";

    private final LockPatternUtils mLockPatternUtils;

    public GatekeeperPasswordProvider(LockPatternUtils lockPatternUtils) {
        mLockPatternUtils = lockPatternUtils;
    }

    /**
     * Given the result from confirming or choosing a credential, request Gatekeeper to generate
     * a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
     *
     * @param result The onActivityResult intent from ChooseLock* or ConfirmLock*
     * @param challenge Unique biometric challenge from FingerprintManager/FaceManager
     * @param userId User ID that the credential/biometric operation applies to
     * @throws GatekeeperCredentialNotMatchException if Gatekeeper response is not match
     * @throws IllegalStateException if Gatekeeper Password is missing
     */
    public byte[] requestGatekeeperHat(@NonNull Intent result, long challenge, int userId) {
        if (!containsGatekeeperPasswordHandle(result)) {
            throw new IllegalStateException("Gatekeeper Password is missing!!");
        }
        final long gatekeeperPasswordHandle = result.getLongExtra(
                ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
        return requestGatekeeperHat(gatekeeperPasswordHandle, challenge, userId);
    }

    /**
     * Given the result from confirming or choosing a credential, request Gatekeeper to generate
     * a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
     *
     * @param gkPwHandle The Gatekeeper password handle from ChooseLock* or ConfirmLock*
     * @param challenge Unique biometric challenge from FingerprintManager/FaceManager
     * @param userId User ID that the credential/biometric operation applies to
     * @throws GatekeeperCredentialNotMatchException if Gatekeeper response is not match
     */
    public byte[] requestGatekeeperHat(long gkPwHandle, long challenge, int userId) {
        final VerifyCredentialResponse response = mLockPatternUtils.verifyGatekeeperPasswordHandle(
                gkPwHandle, challenge, userId);
        if (!response.isMatched()) {
            throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT");
        }
        return response.getGatekeeperHAT();
    }

    /**
     * Intent data contains gatekeeper password handle or not
     */
    public static boolean containsGatekeeperPasswordHandle(@Nullable Intent data) {
        return data != null && data.hasExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
    }

    /**
     * Returns the gatekeeper password handle from intent
     */
    public static long getGatekeeperPasswordHandle(@NonNull Intent data) {
        return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
    }

    /**
     * Requests {@link com.android.server.locksettings.LockSettingsService} to remove the
     * gatekeeper password associated with a previous
     * {@link ChooseLockSettingsHelper.Builder#setRequestGatekeeperPasswordHandle(boolean)}
     *
     * @param data The onActivityResult intent from ChooseLock* or ConfirmLock*
     * @param alsoRemoveItFromIntent set it to true if gkPwHandle needs to be removed from intent
     */
    public void removeGatekeeperPasswordHandle(@Nullable Intent data,
            boolean alsoRemoveItFromIntent) {
        if (data == null) {
            return;
        }
        if (!containsGatekeeperPasswordHandle(data)) {
            return;
        }
        removeGatekeeperPasswordHandle(getGatekeeperPasswordHandle(data));
        if (alsoRemoveItFromIntent) {
            data.removeExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
        }
    }

    /**
     * Requests {@link com.android.server.locksettings.LockSettingsService} to remove the
     * gatekeeper password associated with a previous
     * {@link ChooseLockSettingsHelper.Builder#setRequestGatekeeperPasswordHandle(boolean)}
     *
     * @param handle The Gatekeeper password handle from ChooseLock* or ConfirmLock*
     */
    public void removeGatekeeperPasswordHandle(long handle) {
        mLockPatternUtils.removeGatekeeperPasswordHandle(handle);
        Log.d(TAG, "Removed handle");
    }
}
+43 −8
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.android.settings.biometrics.MultiBiometricEnrollHelper;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.HelpUtils;
@@ -68,7 +69,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
        mFingerprintManager = getFingerprintManager();
        if (mFingerprintManager == null) {
            Log.e(TAG, "Null FingerprintManager");
            finish();
@@ -127,11 +128,50 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {

        final ScrollView scrollView = findViewById(R.id.sud_scroll_view);
        scrollView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);

        final Intent intent = getIntent();
        if (mFromSettingsSummary
                && GatekeeperPasswordProvider.containsGatekeeperPasswordHandle(intent)) {
            overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
            getNextButton().setEnabled(false);
            getChallenge(((sensorId, userId, challenge) -> {
                if (isFinishing()) {
                    // Do nothing if activity is finishing
                    Log.w(TAG, "activity finished before challenge callback launched.");
                    return;
                }

                mSensorId = sensorId;
                mChallenge = challenge;
                final GatekeeperPasswordProvider provider = getGatekeeperPasswordProvider();
                mToken = provider.requestGatekeeperHat(intent, challenge, mUserId);
                provider.removeGatekeeperPasswordHandle(intent, true);
                getNextButton().setEnabled(true);
            }));
        }
    }

    @VisibleForTesting
    @Nullable
    protected FingerprintManager getFingerprintManager() {
        return Utils.getFingerprintManagerOrNull(this);
    }

    /**
     * Returns the intent extra data for setResult(), null means nothing need to been sent back
     */
    @Nullable
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    protected Intent getSetResultIntentExtra(@Nullable Intent activityResultIntent) {
        Intent intent = super.getSetResultIntentExtra(activityResultIntent);
        if (mFromSettingsSummary && mToken != null && mChallenge != -1L) {
            if (intent == null) {
                intent = new Intent();
            }
            intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
            intent.putExtra(EXTRA_KEY_CHALLENGE, mChallenge);
        }
        return intent;
    }

    @Override
@@ -295,11 +335,6 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {

    @Override
    protected void getChallenge(GenerateChallengeCallback callback) {
        mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
        if (mFingerprintManager == null) {
            callback.onChallengeGenerated(0, 0, 0L);
            return;
        }
        mFingerprintManager.generateChallenge(mUserId, callback::onChallengeGenerated);
    }

+61 −22
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.app.admin.DevicePolicyResources.UNDEFINED;

import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;

import android.app.Activity;
import android.app.Dialog;
@@ -60,11 +61,13 @@ import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;

import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -410,7 +413,7 @@ public class FingerprintSettings extends SubSettings {
                    launchChooseOrConfirmLock();
                } else if (!mHasFirstEnrolled) {
                    mIsEnrolling = true;
                    addFirstFingerprint();
                    addFirstFingerprint(null);
                }
            }
            updateFooterColumns(activity);
@@ -776,7 +779,7 @@ public class FingerprintSettings extends SubSettings {
            if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
                mLaunchedConfirm = false;
                if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
                    if (data != null && BiometricUtils.containsGatekeeperPasswordHandle(data)) {
                    if (BiometricUtils.containsGatekeeperPasswordHandle(data)) {
                        if (!mHasFirstEnrolled && !mIsEnrolling) {
                            final Activity activity = getActivity();
                            if (activity != null) {
@@ -784,21 +787,34 @@ public class FingerprintSettings extends SubSettings {
                                activity.overridePendingTransition(R.anim.sud_slide_next_in,
                                        R.anim.sud_slide_next_out);
                            }
                        }

                            // To have smoother animation, change flow to let next visible activity
                            // to generateChallenge, then pass it back through activity result.
                            // Token and challenge will be updated later through the activity result
                            // of AUTO_ADD_FIRST_FINGERPRINT_REQUEST.
                            mIsEnrolling = true;
                            addFirstFingerprint(
                                    BiometricUtils.getGatekeeperPasswordHandle(data));
                        } else {
                            mFingerprintManager.generateChallenge(mUserId,
                                    (sensorId, userId, challenge) -> {
                                    mToken = BiometricUtils.requestGatekeeperHat(getActivity(),
                                            data,
                                            mUserId, challenge);
                                        final Activity activity = getActivity();
                                        if (activity == null || activity.isFinishing()) {
                                            // Stop everything
                                            Log.w(TAG, "activity detach or finishing");
                                            return;
                                        }

                                        final GatekeeperPasswordProvider provider =
                                                new GatekeeperPasswordProvider(
                                                        new LockPatternUtils(activity));
                                        mToken = provider.requestGatekeeperHat(data, challenge,
                                                mUserId);
                                        mChallenge = challenge;
                                    BiometricUtils.removeGatekeeperPasswordHandle(getActivity(),
                                            data);
                                        provider.removeGatekeeperPasswordHandle(data, false);
                                        updateAddPreference();
                                    if (!mHasFirstEnrolled && !mIsEnrolling) {
                                        mIsEnrolling = true;
                                        addFirstFingerprint();
                                    }
                                    });
                        }
                    } else {
                        Log.d(TAG, "Data null or GK PW missing");
                        finish();
@@ -815,12 +831,29 @@ public class FingerprintSettings extends SubSettings {
                    activity.finish();
                }
            } else if (requestCode == AUTO_ADD_FIRST_FINGERPRINT_REQUEST) {
                mIsEnrolling = false;
                mHasFirstEnrolled = true;
                if (resultCode != RESULT_FINISHED) {
                    Log.d(TAG, "Add first fingerprint fail, result:" + resultCode);
                if (resultCode != RESULT_FINISHED || data == null) {
                    Log.d(TAG, "Add first fingerprint, fail or null data, result:" + resultCode);
                    finish();
                    return;
                }

                mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
                if (mToken == null) {
                    Log.w(TAG, "Add first fingerprint, null token");
                    finish();
                    return;
                }

                mChallenge = data.getLongExtra(EXTRA_KEY_CHALLENGE, -1L);
                if (mChallenge == -1L) {
                    Log.w(TAG, "Add first fingerprint, invalid challenge");
                    finish();
                    return;
                }

                mIsEnrolling = false;
                mHasFirstEnrolled = true;
                updateAddPreference();
            }
        }

@@ -892,7 +925,7 @@ public class FingerprintSettings extends SubSettings {
            }
        }

        private void addFirstFingerprint() {
        private void addFirstFingerprint(@Nullable Long gkPwHandle) {
            Intent intent = new Intent();
            intent.setClassName(SETTINGS_PACKAGE_NAME,
                    FeatureFlagUtils.isEnabled(getActivity(),
@@ -906,7 +939,13 @@ public class FingerprintSettings extends SubSettings {
                    SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);

            intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
            if (gkPwHandle != null) {
                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
                        gkPwHandle.longValue());
            } else {
                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
                intent.putExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, mChallenge);
            }
            startActivityForResult(intent, AUTO_ADD_FIRST_FINGERPRINT_REQUEST);
        }

Loading