All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.biometrics.BiometricService Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2018 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.server.biometrics;

import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;

import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.IActivityTaskManager;
import android.app.KeyguardManager;
import android.app.TaskStackListener;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.StatsLog;

import com.android.internal.R;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * System service that arbitrates the modality for BiometricPrompt to use.
 */
public class BiometricService extends SystemService {

    private static final String TAG = "BiometricService";
    private static final boolean DEBUG = true;

    private static final int MSG_ON_TASK_STACK_CHANGED = 1;
    private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
    private static final int MSG_ON_AUTHENTICATION_FAILED = 3;
    private static final int MSG_ON_ERROR = 4;
    private static final int MSG_ON_ACQUIRED = 5;
    private static final int MSG_ON_DISMISSED = 6;
    private static final int MSG_ON_TRY_AGAIN_PRESSED = 7;
    private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8;
    private static final int MSG_AUTHENTICATE = 9;
    private static final int MSG_CANCEL_AUTHENTICATION = 10;
    private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11;
    private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12;
    private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13;

    private static final int[] FEATURE_ID = {
        TYPE_FINGERPRINT,
        TYPE_IRIS,
        TYPE_FACE
    };

    /**
     * Authentication either just called and we have not transitioned to the CALLED state, or
     * authentication terminated (success or error).
     */
    private static final int STATE_AUTH_IDLE = 0;
    /**
     * Authentication was called and we are waiting for the Services to return their
     * cookies before starting the hardware and showing the BiometricPrompt.
     */
    private static final int STATE_AUTH_CALLED = 1;
    /**
     * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
     */
    private static final int STATE_AUTH_STARTED = 2;
    /**
     * Authentication is paused, waiting for the user to press "try again" button. Only
     * passive modalities such as Face or Iris should have this state. Note that for passive
     * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
     * fingerprint.
     */
    private static final int STATE_AUTH_PAUSED = 3;
    /**
     * Authentication is successful, but we're waiting for the user to press "confirm" button.
     */
    private static final int STATE_AUTH_PENDING_CONFIRM = 5;
    /**
     * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential
     */
    private static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;

    private final class AuthSession implements IBinder.DeathRecipient {
        // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
        // Services before we can start authenticating. Pairs that have been returned
        // are moved to mModalitiesMatched.
        final HashMap mModalitiesWaiting;
        // Pairs that have been matched.
        final HashMap mModalitiesMatched = new HashMap<>();

        // The following variables are passed to authenticateInternal, which initiates the
        // appropriate Services.
        final IBinder mToken;
        final long mSessionId;
        final int mUserId;
        // Original receiver from BiometricPrompt.
        final IBiometricServiceReceiver mClientReceiver;
        final String mOpPackageName;
        // Info to be shown on BiometricDialog when all cookies are returned.
        final Bundle mBundle;
        final int mCallingUid;
        final int mCallingPid;
        final int mCallingUserId;
        // Continue authentication with the same modality/modalities after "try again" is
        // pressed
        final int mModality;
        final boolean mRequireConfirmation;

        // The current state, which can be either idle, called, or started
        private int mState = STATE_AUTH_IDLE;
        // For explicit confirmation, do not send to keystore until the user has confirmed
        // the authentication.
        byte[] mTokenEscrow;

        // Timestamp when hardware authentication occurred
        private long mAuthenticatedTimeMs;

        // TODO(b/123378871): Remove when moved.
        private IBiometricConfirmDeviceCredentialCallback mConfirmDeviceCredentialCallback;

        AuthSession(HashMap modalities, IBinder token, long sessionId,
                int userId, IBiometricServiceReceiver receiver, String opPackageName,
                Bundle bundle, int callingUid, int callingPid, int callingUserId,
                int modality, boolean requireConfirmation,
                IBiometricConfirmDeviceCredentialCallback callback) {
            mModalitiesWaiting = modalities;
            mToken = token;
            mSessionId = sessionId;
            mUserId = userId;
            mClientReceiver = receiver;
            mOpPackageName = opPackageName;
            mBundle = bundle;
            mCallingUid = callingUid;
            mCallingPid = callingPid;
            mCallingUserId = callingUserId;
            mModality = modality;
            mRequireConfirmation = requireConfirmation;
            mConfirmDeviceCredentialCallback = callback;

            if (isFromConfirmDeviceCredential()) {
                try {
                    token.linkToDeath(this, 0 /* flags */);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Unable to link to death", e);
                }
            }
        }

        boolean isCrypto() {
            return mSessionId != 0;
        }

        boolean isFromConfirmDeviceCredential() {
            return mBundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
        }

        boolean containsCookie(int cookie) {
            if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
                return true;
            }
            if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
                return true;
            }
            return false;
        }

        // TODO(b/123378871): Remove when moved.
        @Override
        public void binderDied() {
            mHandler.post(() -> {
                Slog.e(TAG, "Binder died, killing ConfirmDeviceCredential");
                if (mConfirmDeviceCredentialCallback == null) {
                    Slog.e(TAG, "Callback is null");
                    return;
                }

                try {
                    mConfirmDeviceCredentialCallback.cancel();
                    mConfirmDeviceCredentialCallback = null;
                } catch (RemoteException e) {
                    Slog.e(TAG, "Unable to send cancel", e);
                }
            });
        }
    }

    private final class BiometricTaskStackListener extends TaskStackListener {
        @Override
        public void onTaskStackChanged() {
            mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED);
        }
    }

    private final AppOpsManager mAppOps;
    private final boolean mHasFeatureFingerprint;
    private final boolean mHasFeatureIris;
    private final boolean mHasFeatureFace;
    private final SettingObserver mSettingObserver;
    private final List mEnabledOnKeyguardCallbacks;
    private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
    private final Random mRandom = new Random();

    private IFingerprintService mFingerprintService;
    private IFaceService mFaceService;
    private IActivityTaskManager mActivityTaskManager;
    private IStatusBarService mStatusBarService;

    // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
    // polymorphism :/
    final ArrayList mAuthenticators = new ArrayList<>();

    // Cache the current service that's being used. This is the service which
    // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
    // check (is caller the current client) is done in the Service.
    // Since Settings/System (not application) is responsible for changing preference, this
    // should be safe.
    private int mCurrentModality;

    // The current authentication session, null if idle/done. We need to track both the current
    // and pending sessions since errors may be sent to either.
    private AuthSession mCurrentAuthSession;
    private AuthSession mPendingAuthSession;

    // TODO(b/123378871): Remove when moved.
    // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
    // client (app) receiver. BiometricService internally launches CDCA which invokes
    // BiometricService to start authentication (normal path). When auth is success/rejected,
    // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded
    // to this receiver.
    private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;

    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ON_TASK_STACK_CHANGED: {
                    handleTaskStackChanged();
                    break;
                }

                case MSG_ON_AUTHENTICATION_SUCCEEDED: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleAuthenticationSucceeded(
                            (boolean) args.arg1 /* requireConfirmation */,
                            (byte[]) args.arg2 /* token */);
                    args.recycle();
                    break;
                }

                case MSG_ON_AUTHENTICATION_FAILED: {
                    handleAuthenticationFailed((String) msg.obj /* failureReason */);
                    break;
                }

                case MSG_ON_ERROR: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleOnError(
                            args.argi1 /* cookie */,
                            args.argi2 /* error */,
                            (String) args.arg1 /* message */);
                    args.recycle();
                    break;
                }

                case MSG_ON_ACQUIRED: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleOnAcquired(
                            args.argi1 /* acquiredInfo */,
                            (String) args.arg1 /* message */);
                    args.recycle();
                    break;
                }

                case MSG_ON_DISMISSED: {
                    handleOnDismissed(msg.arg1);
                    break;
                }

                case MSG_ON_TRY_AGAIN_PRESSED: {
                    handleOnTryAgainPressed();
                    break;
                }

                case MSG_ON_READY_FOR_AUTHENTICATION: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleOnReadyForAuthentication(
                            args.argi1 /* cookie */,
                            (boolean) args.arg1 /* requireConfirmation */,
                            args.argi2 /* userId */);
                    args.recycle();
                    break;
                }

                case MSG_AUTHENTICATE: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleAuthenticate(
                            (IBinder) args.arg1 /* token */,
                            (long) args.arg2 /* sessionId */,
                            args.argi1 /* userid */,
                            (IBiometricServiceReceiver) args.arg3 /* receiver */,
                            (String) args.arg4 /* opPackageName */,
                            (Bundle) args.arg5 /* bundle */,
                            args.argi2 /* callingUid */,
                            args.argi3 /* callingPid */,
                            args.argi4 /* callingUserId */,
                            (IBiometricConfirmDeviceCredentialCallback) args.arg6 /* callback */);
                    args.recycle();
                    break;
                }

                case MSG_CANCEL_AUTHENTICATION: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleCancelAuthentication(
                            (IBinder) args.arg1 /* token */,
                            (String) args.arg2 /* opPackageName */);
                    args.recycle();
                    break;
                }

                case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS: {
                    handleOnConfirmDeviceCredentialSuccess();
                    break;
                }

                case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleOnConfirmDeviceCredentialError(
                            args.argi1 /* error */,
                            (String) args.arg1 /* errorMsg */);
                    args.recycle();
                    break;
                }

                case MSG_REGISTER_CANCELLATION_CALLBACK: {
                    handleRegisterCancellationCallback(
                            (IBiometricConfirmDeviceCredentialCallback) msg.obj /* callback */);
                    break;
                }

                default:
                    Slog.e(TAG, "Unknown message: " + msg);
                    break;
            }
        }
    };

    private final class Authenticator {
        int mType;
        BiometricAuthenticator mAuthenticator;

        Authenticator(int type, BiometricAuthenticator authenticator) {
            mType = type;
            mAuthenticator = authenticator;
        }

        int getType() {
            return mType;
        }

        BiometricAuthenticator getAuthenticator() {
            return mAuthenticator;
        }
    }

    private final class SettingObserver extends ContentObserver {

        private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
        private static final boolean DEFAULT_APP_ENABLED = true;
        private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;

        private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
                Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
        private final Uri FACE_UNLOCK_APP_ENABLED =
                Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
        private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
                Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);

        private final ContentResolver mContentResolver;

        private Map mFaceEnabledOnKeyguard = new HashMap<>();
        private Map mFaceEnabledForApps = new HashMap<>();
        private Map mFaceAlwaysRequireConfirmation = new HashMap<>();

        /**
         * Creates a content observer.
         *
         * @param handler The handler to run {@link #onChange} on, or null if none.
         */
        SettingObserver(Handler handler) {
            super(handler);
            mContentResolver = getContext().getContentResolver();
            updateContentObserver();
        }

        void updateContentObserver() {
            mContentResolver.unregisterContentObserver(this);
            mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
                    false /* notifyForDescendents */,
                    this /* observer */,
                    UserHandle.USER_ALL);
            mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
                    false /* notifyForDescendents */,
                    this /* observer */,
                    UserHandle.USER_ALL);
            mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
                    false /* notifyForDescendents */,
                    this /* observer */,
                    UserHandle.USER_ALL);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri, int userId) {
            if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
                mFaceEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser(
                                mContentResolver,
                                Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
                                DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */,
                                userId) != 0);

                if (userId == ActivityManager.getCurrentUser() && !selfChange) {
                    notifyEnabledOnKeyguardCallbacks(userId);
                }
            } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
                mFaceEnabledForApps.put(userId, Settings.Secure.getIntForUser(
                                mContentResolver,
                                Settings.Secure.FACE_UNLOCK_APP_ENABLED,
                                DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
                                userId) != 0);
            } else if (FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION.equals(uri)) {
                mFaceAlwaysRequireConfirmation.put(userId, Settings.Secure.getIntForUser(
                                mContentResolver,
                                Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
                                DEFAULT_ALWAYS_REQUIRE_CONFIRMATION ? 1 : 0 /* default */,
                                userId) != 0);
            }
        }

        boolean getFaceEnabledOnKeyguard() {
            final int user = ActivityManager.getCurrentUser();
            if (!mFaceEnabledOnKeyguard.containsKey(user)) {
                onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
            }
            return mFaceEnabledOnKeyguard.get(user);
        }

        boolean getFaceEnabledForApps(int userId) {
            if (!mFaceEnabledForApps.containsKey(userId)) {
                onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
            }
            return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
        }

        boolean getFaceAlwaysRequireConfirmation(int userId) {
            if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
                onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
            }
            return mFaceAlwaysRequireConfirmation.get(userId);
        }

        void notifyEnabledOnKeyguardCallbacks(int userId) {
            List callbacks = mEnabledOnKeyguardCallbacks;
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).notify(BiometricSourceType.FACE,
                        mFaceEnabledOnKeyguard.getOrDefault(userId,
                                DEFAULT_KEYGUARD_ENABLED));
            }
        }
    }

    private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {

        private final IBiometricEnabledOnKeyguardCallback mCallback;

        EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
            mCallback = callback;
            try {
                mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
            } catch (RemoteException e) {
                Slog.w(TAG, "Unable to linkToDeath", e);
            }
        }

        void notify(BiometricSourceType sourceType, boolean enabled) {
            try {
                mCallback.onChanged(sourceType, enabled);
            } catch (DeadObjectException e) {
                Slog.w(TAG, "Death while invoking notify", e);
                mEnabledOnKeyguardCallbacks.remove(this);
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to invoke onChanged", e);
            }
        }

        @Override
        public void binderDied() {
            Slog.e(TAG, "Enabled callback binder died");
            mEnabledOnKeyguardCallbacks.remove(this);
        }
    }

    // Wrap the client's receiver so we can do things with the BiometricDialog first
    private final IBiometricServiceReceiverInternal mInternalReceiver =
            new IBiometricServiceReceiverInternal.Stub() {
        @Override
        public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
                throws RemoteException {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = requireConfirmation;
            args.arg2 = token;
            mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
        }

        @Override
        public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
                throws RemoteException {
            String failureReason = getContext().getString(R.string.biometric_not_recognized);
            mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
        }

        @Override
        public void onError(int cookie, int error, String message) throws RemoteException {
            // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
            // soft errors and we should allow the user to try authenticating again instead of
            // dismissing BiometricPrompt.
            if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
                mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
            } else {
                SomeArgs args = SomeArgs.obtain();
                args.argi1 = cookie;
                args.argi2 = error;
                args.arg1 = message;
                mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
            }
        }

        @Override
        public void onAcquired(int acquiredInfo, String message) throws RemoteException {
            SomeArgs args = SomeArgs.obtain();
            args.argi1 = acquiredInfo;
            args.arg1 = message;
            mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget();
        }

        @Override
        public void onDialogDismissed(int reason) throws RemoteException {
            mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget();
        }

        @Override
        public void onTryAgainPressed() {
            mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
        }
    };


    /**
     * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
     * should not carry any state. The reality is we need to keep a tiny amount of state so that
     * cancelAuthentication() can go to the right place.
     */
    private final class BiometricServiceWrapper extends IBiometricService.Stub {
        @Override // Binder call
        public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
            checkInternalPermission();

            SomeArgs args = SomeArgs.obtain();
            args.argi1 = cookie;
            args.arg1 = requireConfirmation;
            args.argi2 = userId;
            mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget();
        }

        @Override // Binder call
        public void authenticate(IBinder token, long sessionId, int userId,
                IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
                IBiometricConfirmDeviceCredentialCallback callback)
                throws RemoteException {
            final int callingUid = Binder.getCallingUid();
            final int callingPid = Binder.getCallingPid();
            final int callingUserId = UserHandle.getCallingUserId();

            // TODO(b/123378871): Remove when moved.
            if (callback != null) {
                checkInternalPermission();
            }

            // In the BiometricServiceBase, check do the AppOps and foreground check.
            if (userId == callingUserId) {
                // Check the USE_BIOMETRIC permission here.
                checkPermission();
            } else {
                // Only allow internal clients to authenticate with a different userId
                Slog.w(TAG, "User " + callingUserId + " is requesting authentication of userid: "
                        + userId);
                checkInternalPermission();
            }

            if (token == null || receiver == null || opPackageName == null || bundle == null) {
                Slog.e(TAG, "Unable to authenticate, one or more null arguments");
                return;
            }

            final boolean isFromConfirmDeviceCredential =
                    bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
            if (isFromConfirmDeviceCredential) {
                checkInternalPermission();
            }

            // Check the usage of this in system server. Need to remove this check if it becomes
            // a public API.
            final boolean useDefaultTitle =
                    bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
            if (useDefaultTitle) {
                checkInternalPermission();
                // Set the default title if necessary
                if (TextUtils.isEmpty(bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
                    bundle.putCharSequence(BiometricPrompt.KEY_TITLE,
                            getContext().getString(R.string.biometric_dialog_default_title));
                }
            }

            // Launch CDC instead if necessary. CDC will return results through an AIDL call, since
            // we can't get activity results. Store the receiver somewhere so we can forward the
            // result back to the client.
            // TODO(b/123378871): Remove when moved.
            if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)) {
                mHandler.post(() -> {
                    final KeyguardManager kgm = getContext().getSystemService(
                            KeyguardManager.class);
                    if (!kgm.isDeviceSecure()) {
                        try {
                            receiver.onError(
                                    BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
                                    getContext().getString(
                                            R.string.biometric_error_device_not_secured));
                        } catch (RemoteException e) {
                            Slog.e(TAG, "Remote exception", e);
                        }
                        return;
                    }
                    mConfirmDeviceCredentialReceiver = receiver;
                    // Use this so we don't need to duplicate logic..
                    final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
                            null /* description */, userId);
                    // Then give it the bundle to do magic behavior..
                    intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
                    // Create a new task with this activity located at the root.
                    intent.setFlags(
                            Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
                    getContext().startActivityAsUser(intent, UserHandle.CURRENT);
                });
                return;
            }

            SomeArgs args = SomeArgs.obtain();
            args.arg1 = token;
            args.arg2 = sessionId;
            args.argi1 = userId;
            args.arg3 = receiver;
            args.arg4 = opPackageName;
            args.arg5 = bundle;
            args.argi2 = callingUid;
            args.argi3 = callingPid;
            args.argi4 = callingUserId;
            args.arg6 = callback;

            mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
        }

        @Override // Binder call
        public void onConfirmDeviceCredentialSuccess() {
            checkInternalPermission();

            mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS);
        }

        @Override // Binder call
        public void onConfirmDeviceCredentialError(int error, String message) {
            checkInternalPermission();

            SomeArgs args = SomeArgs.obtain();
            args.argi1 = error;
            args.arg1 = message;
            mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget();
        }

        @Override // Binder call
        public void registerCancellationCallback(
                IBiometricConfirmDeviceCredentialCallback callback) {
            // TODO(b/123378871): Remove when moved.
            // This callback replaces the one stored in the current session. If the session is null
            // we can ignore this, since it means ConfirmDeviceCredential was launched by something
            // else (not BiometricPrompt)
            checkInternalPermission();

            mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget();
        }

        @Override // Binder call
        public void cancelAuthentication(IBinder token, String opPackageName)
                throws RemoteException {
            checkPermission();

            SomeArgs args = SomeArgs.obtain();
            args.arg1 = token;
            args.arg2 = opPackageName;
            mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
        }

        @Override // Binder call
        public int canAuthenticate(String opPackageName) {
            checkPermission();

            final int userId = UserHandle.getCallingUserId();
            final long ident = Binder.clearCallingIdentity();
            int error;
            try {
                final Pair result = checkAndGetBiometricModality(userId);
                error = result.second;
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
            return error;
        }

        @Override // Binder call
        public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
                throws RemoteException {
            checkInternalPermission();
            mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
            try {
                callback.onChanged(BiometricSourceType.FACE,
                        mSettingObserver.getFaceEnabledOnKeyguard());
            } catch (RemoteException e) {
                Slog.w(TAG, "Remote exception", e);
            }
        }

        @Override // Binder call
        public void setActiveUser(int userId) {
            checkInternalPermission();
            final long ident = Binder.clearCallingIdentity();
            try {
                for (int i = 0; i < mAuthenticators.size(); i++) {
                    mAuthenticators.get(i).getAuthenticator().setActiveUser(userId);
                }
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override // Binder call
        public void resetLockout(byte[] token) {
            checkInternalPermission();
            final long ident = Binder.clearCallingIdentity();
            try {
                if (mFingerprintService != null) {
                    mFingerprintService.resetTimeout(token);
                }
                if (mFaceService != null) {
                    mFaceService.resetLockout(token);
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Remote exception", e);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

    private void checkAppOp(String opPackageName, int callingUid) {
        if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
                opPackageName) != AppOpsManager.MODE_ALLOWED) {
            Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
            throw new SecurityException("Permission denied");
        }
    }

    private void checkInternalPermission() {
        getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
                "Must have USE_BIOMETRIC_INTERNAL permission");
    }

    private void checkPermission() {
        if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
                != PackageManager.PERMISSION_GRANTED) {
            getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
                    "Must have USE_BIOMETRIC permission");
        }
    }

    /**
     * Initializes the system service.
     * 

* Subclasses must define a single argument constructor that accepts the context * and passes it to super. *

* * @param context The system server context. */ public BiometricService(Context context) { super(context); mAppOps = context.getSystemService(AppOpsManager.class); mEnabledOnKeyguardCallbacks = new ArrayList<>(); mSettingObserver = new SettingObserver(mHandler); final PackageManager pm = context.getPackageManager(); mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS); mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); try { ActivityManager.getService().registerUserSwitchObserver( new UserSwitchObserver() { @Override public void onUserSwitchComplete(int newUserId) { mSettingObserver.updateContentObserver(); mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId); } }, BiometricService.class.getName() ); } catch (RemoteException e) { Slog.e(TAG, "Failed to register user switch observer", e); } } @Override public void onStart() { // TODO: maybe get these on-demand if (mHasFeatureFingerprint) { mFingerprintService = IFingerprintService.Stub.asInterface( ServiceManager.getService(Context.FINGERPRINT_SERVICE)); } if (mHasFeatureFace) { mFaceService = IFaceService.Stub.asInterface( ServiceManager.getService(Context.FACE_SERVICE)); } mActivityTaskManager = ActivityTaskManager.getService(); mStatusBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); // Cache the authenticators for (int i = 0; i < FEATURE_ID.length; i++) { if (hasFeature(FEATURE_ID[i])) { Authenticator authenticator = new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i])); mAuthenticators.add(authenticator); } } publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper()); } /** * Checks if there are any available biometrics, and returns the modality. This method also * returns errors through the callback (no biometric feature, hardware not detected, no * templates enrolled, etc). This service must not start authentication if errors are sent. * * @Returns A pair [Modality, Error] with Modality being one of * {@link BiometricAuthenticator#TYPE_NONE}, * {@link BiometricAuthenticator#TYPE_FINGERPRINT}, * {@link BiometricAuthenticator#TYPE_IRIS}, * {@link BiometricAuthenticator#TYPE_FACE} * and the error containing one of the {@link BiometricConstants} errors. */ private Pair checkAndGetBiometricModality(int userId) { int modality = TYPE_NONE; // No biometric features, send error if (mAuthenticators.isEmpty()) { return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT); } // Assuming that authenticators are listed in priority-order, the rest of this function // will go through and find the first authenticator that's available, enrolled, and enabled. // The tricky part is returning the correct error. Error strings that are modality-specific // should also respect the priority-order. // Find first authenticator that's detected, enrolled, and enabled. boolean isHardwareDetected = false; boolean hasTemplatesEnrolled = false; boolean enabledForApps = false; int firstHwAvailable = TYPE_NONE; for (int i = 0; i < mAuthenticators.size(); i++) { modality = mAuthenticators.get(i).getType(); BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator(); if (authenticator.isHardwareDetected()) { isHardwareDetected = true; if (firstHwAvailable == TYPE_NONE) { // Store the first one since we want to return the error in correct priority // order. firstHwAvailable = modality; } if (authenticator.hasEnrolledTemplates(userId)) { hasTemplatesEnrolled = true; if (isEnabledForApp(modality, userId)) { // TODO(b/110907543): When face settings (and other settings) have both a // user toggle as well as a work profile settings page, this needs to be // updated to reflect the correct setting. enabledForApps = true; break; } } } } // Check error conditions if (!isHardwareDetected) { return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); } else if (!hasTemplatesEnrolled) { // Return the modality here so the correct error string can be sent. This error is // preferred over !enabledForApps return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS); } else if (!enabledForApps) { return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); } return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS); } private boolean isEnabledForApp(int modality, int userId) { switch(modality) { case TYPE_FINGERPRINT: return true; case TYPE_IRIS: return true; case TYPE_FACE: return mSettingObserver.getFaceEnabledForApps(userId); default: Slog.w(TAG, "Unsupported modality: " + modality); return false; } } private String getErrorString(int type, int error, int vendorCode) { switch (type) { case TYPE_FINGERPRINT: return FingerprintManager.getErrorString(getContext(), error, vendorCode); case TYPE_IRIS: Slog.w(TAG, "Modality not supported"); return null; // not supported case TYPE_FACE: return FaceManager.getErrorString(getContext(), error, vendorCode); default: Slog.w(TAG, "Unable to get error string for modality: " + type); return null; } } private BiometricAuthenticator getAuthenticator(int type) { switch (type) { case TYPE_FINGERPRINT: return (FingerprintManager) getContext().getSystemService(Context.FINGERPRINT_SERVICE); case TYPE_IRIS: return null; case TYPE_FACE: return (FaceManager) getContext().getSystemService(Context.FACE_SERVICE); default: return null; } } private boolean hasFeature(int type) { switch (type) { case TYPE_FINGERPRINT: return mHasFeatureFingerprint; case TYPE_IRIS: return mHasFeatureIris; case TYPE_FACE: return mHasFeatureFace; default: return false; } } private void logDialogDismissed(int reason) { if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { // Explicit auth, authentication confirmed. // Latency in this case is authenticated -> confirmed. Service // should have the first half (first acquired -> authenticated). final long latency = System.currentTimeMillis() - mCurrentAuthSession.mAuthenticatedTimeMs; if (LoggableMonitor.DEBUG) { Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality() + ", User: " + mCurrentAuthSession.mUserId + ", IsCrypto: " + mCurrentAuthSession.isCrypto() + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT + ", RequireConfirmation: " + mCurrentAuthSession.mRequireConfirmation + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED + ", Latency: " + latency); } StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED, statsModality(), mCurrentAuthSession.mUserId, mCurrentAuthSession.isCrypto(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mCurrentAuthSession.mRequireConfirmation, StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED, latency, Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId)); } else { int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED : 0; if (LoggableMonitor.DEBUG) { Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality() + ", User: " + mCurrentAuthSession.mUserId + ", IsCrypto: " + mCurrentAuthSession.isCrypto() + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT + ", Error: " + error); } // Auth canceled StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED, statsModality(), mCurrentAuthSession.mUserId, mCurrentAuthSession.isCrypto(), BiometricsProtoEnums.ACTION_AUTHENTICATE, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, error, 0 /* vendorCode */, Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId)); } } private int statsModality() { int modality = 0; if (mCurrentAuthSession == null) { return BiometricsProtoEnums.MODALITY_UNKNOWN; } if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT) != 0) { modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT; } if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) { modality |= BiometricsProtoEnums.MODALITY_IRIS; } if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) { modality |= BiometricsProtoEnums.MODALITY_FACE; } return modality; } private void handleTaskStackChanged() { try { final List runningTasks = mActivityTaskManager.getTasks(1); if (!runningTasks.isEmpty()) { final String topPackage = runningTasks.get(0).topActivity.getPackageName(); if (mCurrentAuthSession != null && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) { mStatusBarService.hideBiometricDialog(); mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); mCurrentAuthSession.mClientReceiver.onError( BiometricConstants.BIOMETRIC_ERROR_CANCELED, getContext().getString( com.android.internal.R.string.biometric_error_canceled) ); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; } } } catch (RemoteException e) { Slog.e(TAG, "Unable to get running tasks", e); } } private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) { try { // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded // after user dismissed/canceled dialog). if (mCurrentAuthSession == null) { Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null"); return; } if (!requireConfirmation) { mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); KeyStore.getInstance().addAuthToken(token); mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; } else { mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis(); // Store the auth token and submit it to keystore after the confirmation // button has been pressed. mCurrentAuthSession.mTokenEscrow = token; mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM; } // Notify SysUI that the biometric has been authenticated. SysUI already knows // the implicit/explicit state and will react accordingly. mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } private void handleAuthenticationFailed(String failureReason) { try { // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded // after user dismissed/canceled dialog). if (mCurrentAuthSession == null) { Slog.e(TAG, "onAuthenticationFailed(): Auth session is null"); return; } mStatusBarService.onBiometricAuthenticated(false, failureReason); // TODO: This logic will need to be updated if BP is multi-modal if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { // Pause authentication. onBiometricAuthenticated(false) causes the // dialog to show a "try again" button for passive modalities. mCurrentAuthSession.mState = STATE_AUTH_PAUSED; } mCurrentAuthSession.mClientReceiver.onAuthenticationFailed(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } private void handleOnConfirmDeviceCredentialSuccess() { if (mConfirmDeviceCredentialReceiver == null) { Slog.w(TAG, "onCDCASuccess null!"); return; } try { mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded(); if (mCurrentAuthSession != null) { mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; } } catch (RemoteException e) { Slog.e(TAG, "RemoteException", e); } mConfirmDeviceCredentialReceiver = null; } private void handleOnConfirmDeviceCredentialError(int error, String message) { if (mConfirmDeviceCredentialReceiver == null) { Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message); return; } try { mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); mConfirmDeviceCredentialReceiver.onError(error, message); if (mCurrentAuthSession != null) { mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; } } catch (RemoteException e) { Slog.e(TAG, "RemoteException", e); } mConfirmDeviceCredentialReceiver = null; } private void handleRegisterCancellationCallback( IBiometricConfirmDeviceCredentialCallback callback) { if (mCurrentAuthSession == null) { Slog.d(TAG, "Current auth session null"); return; } Slog.d(TAG, "Updating cancel callback"); mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback; } private void handleOnError(int cookie, int error, String message) { Slog.d(TAG, "Error: " + error + " cookie: " + cookie); // Errors can either be from the current auth session or the pending auth session. // The pending auth session may receive errors such as ERROR_LOCKOUT before // it becomes the current auth session. Similarly, the current auth session may // receive errors such as ERROR_CANCELED while the pending auth session is preparing // to be started. Thus we must match error messages with their cookies to be sure // of their intended receivers. try { if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) { if (mCurrentAuthSession.isFromConfirmDeviceCredential()) { // If we were invoked by ConfirmDeviceCredential, do not delete the current // auth session since we still need to respond to cancel signal while if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state"); // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass // screen mCurrentAuthSession.mClientReceiver.onError(error, message); mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC; mStatusBarService.hideBiometricDialog(); } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { mStatusBarService.onBiometricError(message); if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { mActivityTaskManager.unregisterTaskStackListener( mTaskStackListener); mCurrentAuthSession.mClientReceiver.onError(error, message); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; mStatusBarService.hideBiometricDialog(); } else { // Send errors after the dialog is dismissed. mHandler.postDelayed(() -> { try { if (mCurrentAuthSession != null) { mActivityTaskManager.unregisterTaskStackListener( mTaskStackListener); mCurrentAuthSession.mClientReceiver.onError(error, message); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } }, BiometricPrompt.HIDE_DIALOG_DELAY); } } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) { // In the "try again" state, we should forward canceled errors to // the client and and clean up. mCurrentAuthSession.mClientReceiver.onError(error, message); mStatusBarService.onBiometricError(message); mActivityTaskManager.unregisterTaskStackListener( mTaskStackListener); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; } else { Slog.e(TAG, "Impossible session error state: " + mCurrentAuthSession.mState); } } else if (mPendingAuthSession != null && mPendingAuthSession.containsCookie(cookie)) { if (mPendingAuthSession.mState == STATE_AUTH_CALLED) { mPendingAuthSession.mClientReceiver.onError(error, message); mPendingAuthSession.mState = STATE_AUTH_IDLE; mPendingAuthSession = null; } else { Slog.e(TAG, "Impossible pending session error state: " + mPendingAuthSession.mState); } } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } private void handleOnAcquired(int acquiredInfo, String message) { // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded // after user dismissed/canceled dialog). if (mCurrentAuthSession == null) { Slog.e(TAG, "onAcquired(): Auth session is null"); return; } if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) { if (message == null) { Slog.w(TAG, "Ignoring null message: " + acquiredInfo); return; } try { mStatusBarService.onBiometricHelp(message); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } } private void handleOnDismissed(int reason) { if (mCurrentAuthSession == null) { Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null"); return; } logDialogDismissed(reason); try { if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) { // Positive button is used by passive modalities as a "confirm" button, // do not send to client mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason); // Cancel authentication. Skip the token/package check since we are cancelling // from system server. The interface is permission protected so this is fine. cancelInternal(null /* token */, null /* package */, false /* fromClient */); } if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) { mCurrentAuthSession.mClientReceiver.onError( BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, getContext().getString( com.android.internal.R.string.biometric_error_user_canceled)); } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { // Have the service send the token to KeyStore, and send onAuthenticated // to the application KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow); mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); } // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when // ConfirmDeviceCredential is confirmed or canceled. // TODO(b/123378871): Remove when moved if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) { mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } private void handleOnTryAgainPressed() { Slog.d(TAG, "onTryAgainPressed"); // No need to check permission, since it can only be invoked by SystemUI // (or system server itself). authenticateInternal(mCurrentAuthSession.mToken, mCurrentAuthSession.mSessionId, mCurrentAuthSession.mUserId, mCurrentAuthSession.mClientReceiver, mCurrentAuthSession.mOpPackageName, mCurrentAuthSession.mBundle, mCurrentAuthSession.mCallingUid, mCurrentAuthSession.mCallingPid, mCurrentAuthSession.mCallingUserId, mCurrentAuthSession.mModality, mCurrentAuthSession.mConfirmDeviceCredentialCallback); } private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) { Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); if (pair.getValue() == cookie) { mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue()); mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey()); Slog.d(TAG, "Matched cookie: " + cookie + ", " + mPendingAuthSession.mModalitiesWaiting.size() + " remaining"); break; } } if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) { final boolean continuing = mCurrentAuthSession != null && mCurrentAuthSession.mState == STATE_AUTH_PAUSED; mCurrentAuthSession = mPendingAuthSession; mPendingAuthSession = null; mCurrentAuthSession.mState = STATE_AUTH_STARTED; try { int modality = TYPE_NONE; it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); if (pair.getKey() == TYPE_FINGERPRINT) { mFingerprintService.startPreparedClient(pair.getValue()); } else if (pair.getKey() == TYPE_IRIS) { Slog.e(TAG, "Iris unsupported"); } else if (pair.getKey() == TYPE_FACE) { mFaceService.startPreparedClient(pair.getValue()); } else { Slog.e(TAG, "Unknown modality: " + pair.getKey()); } modality |= pair.getKey(); } if (!continuing) { mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle, mInternalReceiver, modality, requireConfirmation, userId); mActivityTaskManager.registerTaskStackListener(mTaskStackListener); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } } private void handleAuthenticate(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, int callingUid, int callingPid, int callingUserId, IBiometricConfirmDeviceCredentialCallback callback) { mHandler.post(() -> { final Pair result = checkAndGetBiometricModality(userId); final int modality = result.first; final int error = result.second; // Check for errors, notify callback, and return if (error != BiometricConstants.BIOMETRIC_SUCCESS) { try { final String hardwareUnavailable = getContext().getString(R.string.biometric_error_hw_unavailable); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT: receiver.onError(error, hardwareUnavailable); break; case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE: receiver.onError(error, hardwareUnavailable); break; case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS: receiver.onError(error, getErrorString(modality, error, 0 /* vendorCode */)); break; default: Slog.e(TAG, "Unhandled error"); break; } } catch (RemoteException e) { Slog.e(TAG, "Unable to send error", e); } return; } mCurrentModality = modality; // Start preparing for authentication. Authentication starts when // all modalities requested have invoked onReadyForAuthentication. authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, modality, callback); }); } /** * authenticate() (above) which is called from BiometricPrompt determines which * modality/modalities to start authenticating with. authenticateInternal() should only be * used for: * 1) Preparing Services for authentication when BiometricPrompt#authenticate is, * invoked, shortly after which BiometricPrompt is shown and authentication starts * 2) Preparing Services for authentication when BiometricPrompt is already shown * and the user has pressed "try again" */ private void authenticateInternal(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, int callingUid, int callingPid, int callingUserId, int modality, IBiometricConfirmDeviceCredentialCallback callback) { try { boolean requireConfirmation = bundle.getBoolean( BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); if ((modality & TYPE_FACE) != 0) { // Check if the user has forced confirmation to be required in Settings. requireConfirmation = requireConfirmation || mSettingObserver.getFaceAlwaysRequireConfirmation(userId); } // Generate random cookies to pass to the services that should prepare to start // authenticating. Store the cookie here and wait for all services to "ack" // with the cookie. Once all cookies are received, we can show the prompt // and let the services start authenticating. The cookie should be non-zero. final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; Slog.d(TAG, "Creating auth session. Modality: " + modality + ", cookie: " + cookie); final HashMap authenticators = new HashMap<>(); authenticators.put(modality, cookie); mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, modality, requireConfirmation, callback); mPendingAuthSession.mState = STATE_AUTH_CALLED; // No polymorphism :( if ((modality & TYPE_FINGERPRINT) != 0) { mFingerprintService.prepareForAuthentication(token, sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId); } if ((modality & TYPE_IRIS) != 0) { Slog.w(TAG, "Iris unsupported"); } if ((modality & TYPE_FACE) != 0) { mFaceService.prepareForAuthentication(requireConfirmation, token, sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId); } } catch (RemoteException e) { Slog.e(TAG, "Unable to start authentication", e); } } private void handleCancelAuthentication(IBinder token, String opPackageName) { if (token == null || opPackageName == null) { Slog.e(TAG, "Unable to cancel, one or more null arguments"); return; } if (mCurrentAuthSession != null && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) { if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing"); try { mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel(); } catch (RemoteException e) { Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e); } // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up. handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, getContext().getString(R.string.biometric_error_canceled)); } else if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { // We need to check the current authenticators state. If we're pending confirm // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, // since we won't be getting an onError from the driver. try { // Send error to client mCurrentAuthSession.mClientReceiver.onError( BiometricConstants.BIOMETRIC_ERROR_CANCELED, getContext().getString( com.android.internal.R.string.biometric_error_user_canceled) ); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; mStatusBarService.hideBiometricDialog(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } } else { boolean fromCDC = false; if (mCurrentAuthSession != null) { fromCDC = mCurrentAuthSession.mBundle.getBoolean( BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false); } if (fromCDC) { if (DEBUG) Slog.d(TAG, "Cancelling from CDC"); cancelInternal(token, opPackageName, false /* fromClient */); } else { cancelInternal(token, opPackageName, true /* fromClient */); } } } void cancelInternal(IBinder token, String opPackageName, boolean fromClient) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); mHandler.post(() -> { try { // TODO: For multiple modalities, send a single ERROR_CANCELED only when all // drivers have canceled authentication. if ((mCurrentModality & TYPE_FINGERPRINT) != 0) { mFingerprintService.cancelAuthenticationFromService(token, opPackageName, callingUid, callingPid, callingUserId, fromClient); } if ((mCurrentModality & TYPE_IRIS) != 0) { Slog.w(TAG, "Iris unsupported"); } if ((mCurrentModality & TYPE_FACE) != 0) { mFaceService.cancelAuthenticationFromService(token, opPackageName, callingUid, callingPid, callingUserId, fromClient); } } catch (RemoteException e) { Slog.e(TAG, "Unable to cancel authentication"); } }); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy