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

Commit 037c4d5a authored by Kevin Chyn's avatar Kevin Chyn
Browse files

2/n: Refactor out common BiometricService code

Bug: 109900227

Test: fingerprint enrolls (up to 5)
Test: fingerprint authenticates in Settings/Keyguard
Test: removing fingerprints one by one works
Test: removing all fingerprints works
Test: enumerate works (update fingerprintutils to not store in framework)
      "extra fingerprint in hw" situation
Test: enumerate works (update fingerprintutils to store extra in framework)
      "extra fingerprint in framework" situation
Test: launch FP settings, lock screen, auth twice, repeat many times
Test: multi-user - fp for one user does not work for another
Test: multi-user - fp for secondary user works
Test: lockout reset works, per-user
Test: adb shell dumpsys fingerprint
Test: test app works
Test: test app gets automatically canceled when losing foreground
Test: test app without fingerprint/biometric permission is not allowed
Test: manually inspected that each incoming binder call does the heavy
      work on a handler
Test: log tags are per specific biometric for easier log tracking

Change-Id: Id9812e290e6a8098f73cd9902eaca43c02685990
parent 95d628ef
Loading
Loading
Loading
Loading
+50 −59
Original line number Diff line number Diff line
/**
 * Copyright (C) 2016 The Android Open Source Project
/*
 * 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.
@@ -11,25 +11,22 @@
 * 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.
 * limitations under the License
 */

package com.android.server.biometrics.fingerprint;
package com.android.server.biometrics.common;

import android.content.Context;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;

/**
@@ -51,6 +48,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
    private Bundle mBundle;
    private IStatusBarService mStatusBarService;
    private boolean mInLockout;
    // TODO: BiometricManager, after other biometric modalities are introduced.
    private final FingerprintManager mFingerprintManager;
    protected boolean mDialogDismissed;

@@ -62,12 +60,12 @@ public abstract class AuthenticationClient extends ClientMonitor {
                try {
                    mDialogReceiverFromClient.onDialogDismissed(reason);
                    if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
                        onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
                        onError(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
                                0 /* vendorCode */);
                    }
                    mDialogDismissed = true;
                } catch (RemoteException e) {
                    Slog.e(TAG, "Unable to notify dialog dismissed", e);
                    Slog.e(getLogTag(), "Unable to notify dialog dismissed", e);
                }
                stop(true /* initiatedByClient */);
            }
@@ -85,11 +83,13 @@ public abstract class AuthenticationClient extends ClientMonitor {
     */
    public abstract void onStop();

    public AuthenticationClient(Context context, long halDeviceId, IBinder token,
            IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
    public AuthenticationClient(Context context, Metrics metrics,
            BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
            BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
            boolean restricted, String owner, Bundle bundle,
            IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
        super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
        super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
                restricted, owner);
        mOpId = opId;
        mBundle = bundle;
        mDialogReceiverFromClient = dialogReceiver;
@@ -112,17 +112,17 @@ public abstract class AuthenticationClient extends ClientMonitor {
        // If the dialog is showing, the client doesn't need to receive onAcquired messages.
        if (mBundle != null) {
            try {
                if (acquiredInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
                if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
                    mStatusBarService.onFingerprintHelp(
                            mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
                }
                return false; // acquisition continues
            } catch (RemoteException e) {
                Slog.e(TAG, "Remote exception when sending acquired message", e);
                Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
                return true; // client failed
            } finally {
                // Good scans will keep the device awake
                if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
                if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
                    notifyUserActivity();
                }
            }
@@ -145,7 +145,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
                mStatusBarService.onFingerprintError(
                        mFingerprintManager.getErrorString(error, vendorCode));
            } catch (RemoteException e) {
                Slog.e(TAG, "Remote exception when sending error", e);
                Slog.e(getLogTag(), "Remote exception when sending error", e);
            }
        }
        return super.onError(error, vendorCode);
@@ -166,36 +166,35 @@ public abstract class AuthenticationClient extends ClientMonitor {
                            com.android.internal.R.string.fingerprint_not_recognized));
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to notify Authenticated:", e);
                Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
            }
        }

        IFingerprintServiceReceiver receiver = getReceiver();
        if (receiver != null) {
        final BiometricService.ServiceListener listener = getListener();
        if (listener != null) {
            try {
                MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
                        authenticated);
                mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
                if (!authenticated) {
                    receiver.onAuthenticationFailed(getHalDeviceId());
                    listener.onAuthenticationFailed(getHalDeviceId());
                } else {
                    if (DEBUG) {
                        Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
                        Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
                                + ", id=" + fingerId + ", gp=" + groupId + ")");
                    }
                    Fingerprint fp = !getIsRestricted()
                            ? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
                            : null;
                    receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
                    listener.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to notify Authenticated:", e);
                Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
                result = true; // client failed
            }
        } else {
            result = true; // client not listening
        }
        if (!authenticated) {
            if (receiver != null) {
            if (listener != null) {
                vibrateError();
            }
            // allow system-defined limit of number of attempts before giving up
@@ -203,17 +202,17 @@ public abstract class AuthenticationClient extends ClientMonitor {
            if (lockoutMode != LOCKOUT_NONE) {
                try {
                    mInLockout = true;
                    Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
                    Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
                            lockoutMode + ")");
                    stop(false);
                    int errorCode = lockoutMode == LOCKOUT_TIMED ?
                            FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
                            FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
                            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
                            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;

                    // TODO: if the dialog is showing, this error should be delayed. On a similar
                    // note, AuthenticationClient should override onError and delay all other errors
                    // as well, if the dialog is showing
                    receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
                    listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);

                    // Send the lockout message to the system dialog
                    if (mBundle != null) {
@@ -221,12 +220,12 @@ public abstract class AuthenticationClient extends ClientMonitor {
                                mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
                    }
                } catch (RemoteException e) {
                    Slog.w(TAG, "Failed to notify lockout:", e);
                    Slog.w(getLogTag(), "Failed to notify lockout:", e);
                }
            }
            result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
        } else {
            if (receiver != null) {
            if (listener != null) {
                vibrateSuccess();
            }
            result |= true; // we have a valid fingerprint, done
@@ -241,32 +240,27 @@ public abstract class AuthenticationClient extends ClientMonitor {
     */
    @Override
    public int start() {
        IBiometricsFingerprint daemon = getFingerprintDaemon();
        if (daemon == null) {
            Slog.w(TAG, "start authentication: no fingerprint HAL!");
            return ERROR_ESRCH;
        }
        onStart();
        try {
            final int result = daemon.authenticate(mOpId, getGroupId());
            final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
            if (result != 0) {
                Slog.w(TAG, "startAuthentication failed, result=" + result);
                MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
                Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
                mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result);
                onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
                return result;
            }
            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
            if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");

            // If authenticating with system dialog, show the dialog
            if (mBundle != null) {
                try {
                    mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Unable to show fingerprint dialog", e);
                    Slog.e(getLogTag(), "Unable to show fingerprint dialog", e);
                }
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "startAuthentication failed", e);
            Slog.e(getLogTag(), "startAuthentication failed", e);
            return ERROR_ESRCH;
        }
        return 0; // success
@@ -275,25 +269,21 @@ public abstract class AuthenticationClient extends ClientMonitor {
    @Override
    public int stop(boolean initiatedByClient) {
        if (mAlreadyCancelled) {
            Slog.w(TAG, "stopAuthentication: already cancelled!");
            Slog.w(getLogTag(), "stopAuthentication: already cancelled!");
            return 0;
        }

        onStop();
        IBiometricsFingerprint daemon = getFingerprintDaemon();
        if (daemon == null) {
            Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
            return ERROR_ESRCH;
        }

        try {
            final int result = daemon.cancel();
            final int result = getDaemonWrapper().cancel();
            if (result != 0) {
                Slog.w(TAG, "stopAuthentication failed, result=" + result);
                Slog.w(getLogTag(), "stopAuthentication failed, result=" + result);
                return result;
            }
            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
            if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is no longer authenticating");
        } catch (RemoteException e) {
            Slog.e(TAG, "stopAuthentication failed", e);
            Slog.e(getLogTag(), "stopAuthentication failed", e);
            return ERROR_ESRCH;
        } finally {
            // If the user already cancelled authentication (via some interaction with the
@@ -304,29 +294,30 @@ public abstract class AuthenticationClient extends ClientMonitor {
                try {
                    mStatusBarService.hideFingerprintDialog();
                } catch (RemoteException e) {
                    Slog.e(TAG, "Unable to hide fingerprint dialog", e);
                    Slog.e(getLogTag(), "Unable to hide fingerprint dialog", e);
                }
            }
        }

        mAlreadyCancelled = true;
        return 0; // success
    }

    @Override
    public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
        if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for authenticate!");
        return true; // Invalid for Authenticate
    }

    @Override
    public boolean onRemoved(int fingerId, int groupId, int remaining) {
        if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
        if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for authenticate!");
        return true; // Invalid for Authenticate
    }

    @Override
    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
        if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for authenticate!");
        return true; // Invalid for Authenticate
    }
}
+973 −0

File added.

Preview size limit exceeded, changes collapsed.

+61 −54
Original line number Diff line number Diff line
/**
 * Copyright (C) 2016 The Android Open Source Project
/*
 * 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.
@@ -11,16 +11,13 @@
 * 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.
 * limitations under the License
 */

package com.android.server.biometrics.fingerprint;
package com.android.server.biometrics.common;

import android.Manifest;
import android.content.Context;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
import android.os.IBinder;
import android.os.RemoteException;
@@ -28,23 +25,24 @@ import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Slog;

import com.android.internal.logging.MetricsLogger;

import java.util.NoSuchElementException;

/**
 * Abstract base class for keeping track and dispatching events from fingerprint HAL to the
 * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
 * the current client.  Subclasses are responsible for coordinating the interaction with
 * fingerprint HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
 * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
 */
public abstract class ClientMonitor implements IBinder.DeathRecipient {
    protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
    protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
    protected static final boolean DEBUG = FingerprintService.DEBUG;
    private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
    protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
    protected static final boolean DEBUG = BiometricService.DEBUG;
    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
            new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                    .build();

    private final Context mContext;
    private final long mHalDeviceId;
    private final int mTargetUserId;
@@ -54,51 +52,65 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
    private final String mOwner;
    private final VibrationEffect mSuccessVibrationEffect;
    private final VibrationEffect mErrorVibrationEffect;
    private final BiometricService.DaemonWrapper mDaemon;

    private IBinder mToken;
    private IFingerprintServiceReceiver mReceiver;
    private BiometricService.ServiceListener mListener;

    protected final MetricsLogger mMetricsLogger;
    protected final Metrics mMetrics;

    protected boolean mAlreadyCancelled;

    /**
     * @param context context of FingerprintService
     * @param halDeviceId the HAL device ID of the associated fingerprint hardware
     * @param context context of BiometricService
     * @param daemon interface to call back to a specific biometric's daemon
     * @param halDeviceId the HAL device ID of the associated biometric hardware
     * @param token a unique token for the client
     * @param receiver recipient of related events (e.g. authentication)
     * @param listener recipient of related events (e.g. authentication)
     * @param userId target user id for operation
     * @param groupId groupId for the fingerprint set
     * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
     * @param restricted whether or not client has the MANAGE_* permission
     * permission
     * @param owner name of the client that owns this
     */
    public ClientMonitor(Context context, long halDeviceId, IBinder token,
            IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
            String owner) {
    public ClientMonitor(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
            long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
            int groupId, boolean restricted, String owner) {
        mContext = context;
        mMetrics = metrics;
        mDaemon = daemon;
        mHalDeviceId = halDeviceId;
        mToken = token;
        mReceiver = receiver;
        mListener = listener;
        mTargetUserId = userId;
        mGroupId = groupId;
        mIsRestricted = restricted;
        mOwner = owner;
        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
        mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
        mMetricsLogger = new MetricsLogger();
        try {
            if (token != null) {
                token.linkToDeath(this, 0);
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
            Slog.w(getLogTag(), "caught remote exception in linkToDeath: ", e);
        }
    }

    protected String getLogTag() {
        return mMetrics.logTag();
    }

    /**
     * Contacts fingerprint HAL to start the client.
     * Contacts the biometric's HAL to start the client.
     * @return 0 on success, errno from driver on failure
     */
    public abstract int start();

    /**
     * Contacts fingerprint HAL to stop the client.
     * Contacts the biometric's HAL to stop the client.
     * @param initiatedByClient whether the operation is at the request of a client
     */
    public abstract int stop(boolean initiatedByClient);
@@ -108,56 +120,47 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
     */
    public abstract void notifyUserActivity();

    /**
     * Gets the fingerprint daemon from the cached state in the container class.
     */
    public abstract IBiometricsFingerprint getFingerprintDaemon();

    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
    // respective client (e.g. enrolling shouldn't get authenticate events).
    // All of these return 'true' if the operation is completed and it's ok to move
    // to the next client (e.g. authentication accepts or rejects a fingerprint).
    // to the next client (e.g. authentication accepts or rejects a biometric).
    public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
    public abstract boolean onAuthenticated(int fingerId, int groupId);
    public abstract boolean onRemoved(int fingerId, int groupId, int remaining);
    public abstract boolean onEnumerationResult(int fingerId, int groupId, int remaining);

    /**
     * Called when we get notification from fingerprint HAL that an image has been acquired.
     * Called when we get notification from the biometric's HAL that an image has been acquired.
     * Common to authenticate and enroll.
     * @param acquiredInfo info about the current image acquisition
     * @return true if client should be removed
     */
    public boolean onAcquired(int acquiredInfo, int vendorCode) {
        if (mReceiver == null)
            return true; // client not connected
        try {
            mReceiver.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
            mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
            return false; // acquisition continues...
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to invoke sendAcquired:", e);
            return true; // client failed
            Slog.w(getLogTag(), "Failed to invoke sendAcquired", e);
            return true;
        } finally {
            // Good scans will keep the device awake
            if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
            if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
                notifyUserActivity();
            }
        }
    }

    /**
     * Called when we get notification from fingerprint HAL that an error has occurred with the
     * Called when we get notification from the biometric's HAL that an error has occurred with the
     * current operation. Common to authenticate, enroll, enumerate and remove.
     * @param error
     * @return true if client should be removed
     */
    public boolean onError(int error, int vendorCode) {
        if (mReceiver != null) {
        try {
                mReceiver.onError(getHalDeviceId(), error, vendorCode);
            mListener.onError(getHalDeviceId(), error, vendorCode);
        } catch (RemoteException e) {
                Slog.w(TAG, "Failed to invoke sendError:", e);
            }
            Slog.w(getLogTag(), "Failed to invoke sendError", e);
        }
        return true; // errors always remove current client
    }
@@ -168,26 +171,26 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
                mToken.unlinkToDeath(this, 0);
            } catch (NoSuchElementException e) {
                // TODO: remove when duplicate call bug is found
                Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
                Slog.e(getLogTag(), "destroy(): " + this + ":", new Exception("here"));
            }
            mToken = null;
        }
        mReceiver = null;
        mListener = null;
    }

    @Override
    public void binderDied() {
        mToken = null;
        mReceiver = null;
        onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
        mListener = null;
        onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (mToken != null) {
                if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
                if (DEBUG) Slog.w(getLogTag(), "removing leaked reference: " + mToken);
                onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
            }
        } finally {
            super.finalize();
@@ -206,8 +209,12 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
        return mOwner;
    }

    public final IFingerprintServiceReceiver getReceiver() {
        return mReceiver;
    public final BiometricService.ServiceListener getListener() {
        return mListener;
    }

    public final BiometricService.DaemonWrapper getDaemonWrapper() {
        return mDaemon;
    }

    public final boolean getIsRestricted() {
+30 −44

File changed and moved.

Preview size limit exceeded, changes collapsed.

+27 −37

File changed and moved.

Preview size limit exceeded, changes collapsed.

Loading