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

Commit 5e318156 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

16/n: Introduce ClientMonitor#FinishCallback

The main purpose of this CL is to decouple ClientMonitor's "finishing"
from the onError, onAcquired, onAuthenticated, etc callback handlers.
This will allow us to eventually move all operations into ClientMonitor
subclasses (think generateChallenge, revokeChallenge, etc which are
synchronous).

We can then introduce a scheduler to ensure __all__ HAL operations are
performed serially, not just the current ClientMonitor implementations.

Bug: 157790417

However, since things are still a bit intertwined, a few additional
changes are also necessary.

1) Added FinishCallback - All ClientMonitors are responsible for
   invoking this callback, notifying its owner that the current
   operation is complete
2) Added Cancellable - only Authenticate, Enroll (in other words,
   AcquisitionClient subclasses) are cancellable. Remainder of
   operations are "system" things and should never be canceled. In
   addition, the current HIDL contract is vague as to what the expected
   behavior would be if cancel() was invoked while one of the other
   operations are in process
3) Added ErrorConsumer - only Authenticate, Enroll (in other words,
   AcquisitionClient subclasses) receive onError from the HAL

This change should be functionally equivalent, except the following:
1) onError is now only applicable to Authenticate, Enroll
2) Cancel is now only applicable to Authenticate, Enroll

Test: On fingerprint and face devices, do the following:
      1) Internal cleanup (unknown framework template, unknown HAL
         template) by modifying Fingerprint/FaceUtils
      2) Enroll
      3) Lockout on fingerprint/face devices
      4) Auth (lockscreen, BiometricPrompt) success/reject
      5) Remove templates from settings

Change-Id: Icce99dbdb0aa076bc1dddd3f3c9ea74d4960df93
parent 9f7512ea
Loading
Loading
Loading
Loading
+29 −17
Original line number Diff line number Diff line
@@ -32,7 +32,8 @@ import android.util.Slog;
 * Abstract {@link ClientMonitor} subclass that operations eligible/interested in acquisition
 * messages should extend.
 */
public abstract class AcquisitionClient extends ClientMonitor {
public abstract class AcquisitionClient extends ClientMonitor
        implements ErrorConsumer, Cancellable {

    private static final String TAG = "Biometrics/AcquisitionClient";

@@ -46,29 +47,41 @@ public abstract class AcquisitionClient extends ClientMonitor {
    private final VibrationEffect mSuccessVibrationEffect;
    private final VibrationEffect mErrorVibrationEffect;

    AcquisitionClient(@NonNull Context context, @NonNull IBinder token,
            @NonNull ClientMonitorCallbackConverter listener, int userId, boolean restricted,
            @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
            int statsClient) {
        super(context, token, listener, userId, restricted, owner, cookie, sensorId,
    AcquisitionClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
            boolean restricted, @NonNull String owner, int cookie, int sensorId, int statsModality,
            int statsAction, int statsClient) {
        super(finishCallback, context, token, listener, userId, restricted, owner, cookie, sensorId,
                statsModality, statsAction, statsClient);
        mPowerManager = context.getSystemService(PowerManager.class);
        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
        mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
    }

    @Override
    public void onError(int errorCode, int vendorCode) {
        logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
        try {
            if (getListener() != null) {
                getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to invoke sendError", e);
        }
        mFinishCallback.onClientFinished(this);
    }

    /**
     * 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) {
    public void onAcquired(int acquiredInfo, int vendorCode) {
        // Default is to always send acquire messages to clients.
        return onAcquiredInternal(acquiredInfo, vendorCode, true /* shouldSend */);
        onAcquiredInternal(acquiredInfo, vendorCode, true /* shouldSend */);
    }

    protected final boolean onAcquiredInternal(int acquiredInfo, int vendorCode,
    protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
            boolean shouldSend) {
        super.logOnAcquired(getContext(), acquiredInfo, vendorCode, getTargetUserId());
        if (DEBUG) {
@@ -76,19 +89,18 @@ public abstract class AcquisitionClient extends ClientMonitor {
                    + ", shouldSend: " + shouldSend);
        }

        // Good scans will keep the device awake
        if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
            notifyUserActivity();
        }

        try {
            if (getListener() != null && shouldSend) {
                getListener().onAcquired(getSensorId(), acquiredInfo, vendorCode);
            }
            return false; // acquisition continues...
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to invoke sendAcquired", e);
            return true;
        } finally {
            // Good scans will keep the device awake
            if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
                notifyUserActivity();
            }
            mFinishCallback.onClientFinished(this);
        }
    }

+35 −64
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
@@ -47,14 +46,16 @@ public abstract class AuthenticationClient extends AcquisitionClient {
    protected final long mOperationId;

    private long mStartTimeMs;

    public AuthenticationClient(@NonNull Context context, @NonNull IBinder token,
            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
            boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
            int sensorId, boolean isStrongBiometric, int statsModality, int statsClient,
            @NonNull TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker) {
        super(context, token, listener, targetUserId, restricted, owner, cookie, sensorId,
                statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
    private boolean mAlreadyCancelled;

    public AuthenticationClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
            int targetUserId, long operationId, boolean restricted, @NonNull String owner,
            int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
            int statsModality, int statsClient, @NonNull TaskStackListener taskStackListener,
            @NonNull LockoutTracker lockoutTracker) {
        super(finishCallback, context, token, listener, targetUserId, restricted, owner, cookie,
                sensorId, statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
        mIsStrongBiometric = isStrongBiometric;
        mOperationId = operationId;
        mRequireConfirmation = requireConfirmation;
@@ -97,15 +98,13 @@ public abstract class AuthenticationClient extends AcquisitionClient {
        return mOperationId != 0;
    }

    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
            boolean authenticated, ArrayList<Byte> token) {
    public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
            boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
        super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
                getTargetUserId(), isBiometricPrompt());

        final ClientMonitorCallbackConverter listener = getListener();

        boolean result = false;

        try {
            if (DEBUG) Slog.v(TAG, "onAuthenticated(" + authenticated + ")"
                    + ", ID:" + identifier.getBiometricId()
@@ -121,7 +120,6 @@ public abstract class AuthenticationClient extends AcquisitionClient {
                if (listener != null) {
                    vibrateSuccess();
                }
                result = true;

                try {
                    mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
@@ -129,9 +127,9 @@ public abstract class AuthenticationClient extends AcquisitionClient {
                    Slog.e(TAG, "Could not unregister task stack listener", e);
                }

                final byte[] byteToken = new byte[token.size()];
                for (int i = 0; i < token.size(); i++) {
                    byteToken[i] = token.get(i);
                final byte[] byteToken = new byte[hardwareAuthToken.size()];
                for (int i = 0; i < hardwareAuthToken.size(); i++) {
                    byteToken[i] = hardwareAuthToken.get(i);
                }
                if (isBiometricPrompt() && listener != null) {
                    // BiometricService will add the token to keystore
@@ -144,7 +142,6 @@ public abstract class AuthenticationClient extends AcquisitionClient {
                        Slog.d(TAG, "Skipping addAuthToken");
                    }

                    try {
                    // Explicitly have if/else here to make it super obvious in case the code is
                    // touched in the future.
                    if (!getIsRestricted()) {
@@ -154,13 +151,10 @@ public abstract class AuthenticationClient extends AcquisitionClient {
                        listener.onAuthenticationSucceeded(getSensorId(), null /* identifier */,
                                byteToken, getTargetUserId(), mIsStrongBiometric);
                    }
                    } catch (RemoteException e) {
                        Slog.e(TAG, "Remote exception", e);
                    }

                } else {
                    // Client not listening
                    Slog.w(TAG, "Client not listening");
                    result = true;
                }
            } else {
                if (listener != null) {
@@ -178,47 +172,35 @@ public abstract class AuthenticationClient extends AcquisitionClient {
                        listener.onAuthenticationFailed(getSensorId());
                    }
                }
                result = lockoutMode != LockoutTracker.LOCKOUT_NONE; // in a lockout mode
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Remote exception", e);
            result = true;
            Slog.e(TAG, "Unable to notify listener, finishing", e);
            mFinishCallback.onClientFinished(this);
        }
        return result;
    }

    /**
     * Start authentication
     */
    @Override
    public int start() {
    public void start() {
        try {
            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
        } catch (RemoteException e) {
            Slog.e(TAG, "Could not register task stack listener", e);
        }

        try {
        if (DEBUG) Slog.w(TAG, "Requesting auth for " + getOwnerString());

        mStartTimeMs = System.currentTimeMillis();
            final int result = startHalOperation();
            if (result != 0) {
                Slog.w(TAG, "startAuthentication failed, result=" + result);
                onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
                return result;
            }
            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
        } catch (RemoteException e) {
            Slog.e(TAG, "startAuthentication failed", e);
            return BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS;
        }
        return 0; // success
        startHalOperation();
    }

    @Override
    public int stop(boolean initiatedByClient) {
    public void cancel() {
        if (mAlreadyCancelled) {
            Slog.w(TAG, "stopAuthentication: already cancelled!");
            return 0;
            return;
        }

        try {
@@ -227,20 +209,9 @@ public abstract class AuthenticationClient extends AcquisitionClient {
            Slog.e(TAG, "Could not unregister task stack listener", e);
        }

        try {
            final int result = stopHalOperation();
            if (result != 0) {
                Slog.w(TAG, "stopAuthentication failed, result=" + result);
                return result;
            }
            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() +
                    " is no longer authenticating");
        } catch (RemoteException e) {
            Slog.e(TAG, "stopAuthentication failed", e);
            return BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS;
        }
        if (DEBUG) Slog.w(TAG, "Requesting cancel for " + getOwnerString());

        stopHalOperation();
        mAlreadyCancelled = true;
        return 0; // success
    }
}
+35 −37
Original line number Diff line number Diff line
@@ -110,6 +110,23 @@ public abstract class BiometricServiceBase extends SystemService
        }
    };

    protected final ClientMonitor.FinishCallback mClientFinishCallback = clientMonitor -> {
        if (clientMonitor instanceof RemovalConsumer) {
            // When the last biometric of a group is removed, update the authenticator id.
            // Note that 1) multiple ClientMonitors may be cause onRemoved (e.g. internal cleanup),
            // and 2) updateActiveGroup updates/relies on global state, so there's no good way to
            // compartmentalize this yet.
            final int userId = clientMonitor.getTargetUserId();
            if (!hasEnrolledBiometrics(userId)) {
                Slog.d(getTag(), "Last biometric removed for user: " + userId
                        + ", updating active group");
                updateActiveGroup(userId, null);
            }
        }

        removeClient(clientMonitor);
    };

    private IBiometricService mBiometricService;
    private ClientMonitor mCurrentClient;
    private ClientMonitor mPendingClient;
@@ -199,7 +216,7 @@ public abstract class BiometricServiceBase extends SystemService
                            && !mCurrentClient.isAlreadyDone()) {
                        Slog.e(getTag(), "Stopping background authentication, top: "
                                + topPackage + " currentClient: " + currentClient);
                        mCurrentClient.stop(false /* initiatedByClient */);
                        ((AuthenticationClient) mCurrentClient).cancel();
                    }
                }
            } catch (RemoteException e) {
@@ -391,9 +408,7 @@ public abstract class BiometricServiceBase extends SystemService
        }

        final AcquisitionClient acquisitionClient = (AcquisitionClient) client;
        if (acquisitionClient.onAcquired(acquiredInfo, vendorCode)) {
            removeClient(client);
        }
        acquisitionClient.onAcquired(acquiredInfo, vendorCode);

        if (client instanceof AuthenticationClient) {
            final int userId = client.getTargetUserId();
@@ -421,9 +436,8 @@ public abstract class BiometricServiceBase extends SystemService
        } else {
            mPerformanceTracker.incrementAuthForUser(userId, authenticated);
        }
        if (authenticationClient.onAuthenticated(identifier, authenticated, token)) {
            removeClient(authenticationClient);
        }

        authenticationClient.onAuthenticated(identifier, authenticated, token);
    }

    protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
@@ -453,11 +467,14 @@ public abstract class BiometricServiceBase extends SystemService
        if (DEBUG) Slog.v(getTag(), "handleError(client="
                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");

        if (client != null) {
            client.onError(error, vendorCode);
            removeClient(client);
        if (!(client instanceof ErrorConsumer)) {
            Slog.e(getTag(), "error received for non-ErrorConsumer");
            return;
        }

        final ErrorConsumer errorConsumer = (ErrorConsumer) client;
        errorConsumer.onError(error, vendorCode);

        if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
            mHandler.removeCallbacks(mResetClientState);
            if (mPendingClient != null) {
@@ -483,17 +500,7 @@ public abstract class BiometricServiceBase extends SystemService
        }

        final RemovalConsumer removalConsumer = (RemovalConsumer) client;
        if (removalConsumer.onRemoved(identifier, remaining)) {
            removeClient(client);
            // When the last biometric of a group is removed, update the authenticator id
            int userId = mCurrentUserId;
            if (identifier instanceof Fingerprint) {
                userId = ((Fingerprint) identifier).getGroupId();
            }
            if (!hasEnrolledBiometrics(userId)) {
                updateActiveGroup(userId, null);
            }
        }
        removalConsumer.onRemoved(identifier, remaining);
    }

    protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
@@ -506,9 +513,7 @@ public abstract class BiometricServiceBase extends SystemService
        }

        final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
        if (enumerateConsumer.onEnumerationResult(identifier, remaining)) {
            removeClient(client);
        }
        enumerateConsumer.onEnumerationResult(identifier, remaining);
    }

    /**
@@ -536,7 +541,7 @@ public abstract class BiometricServiceBase extends SystemService
            ClientMonitor client = mCurrentClient;
            if (client instanceof EnrollClient && client.getToken() == token) {
                if (DEBUG) Slog.v(getTag(), "Cancelling enrollment");
                client.stop(client.getToken() == token);
                ((EnrollClient) client).cancel();
            }
        });
    }
@@ -593,7 +598,7 @@ public abstract class BiometricServiceBase extends SystemService
                            + ", fromClient: " + fromClient);
                    // If cancel was from BiometricService, it means the dialog was dismissed
                    // and authentication should be canceled.
                    client.stop(client.getToken() == token);
                    ((AuthenticationClient) client).cancel();
                } else {
                    if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
                            + " since tokens don't match. fromClient: " + fromClient);
@@ -739,8 +744,6 @@ public abstract class BiometricServiceBase extends SystemService
        if (currentClient != null) {
            if (DEBUG) Slog.v(getTag(), "request stop current client " +
                    currentClient.getOwnerString());
            // This check only matters for FingerprintService, since enumerate may call back
            // multiple times.
            if (currentClient instanceof InternalCleanupClient) {
                // This condition means we're currently running internal diagnostics to
                // remove extra templates in the hardware and/or the software
@@ -751,8 +754,8 @@ public abstract class BiometricServiceBase extends SystemService
                            + "(" + newClient.getOwnerString() + ")"
                            + ", initiatedByClient = " + initiatedByClient);
                }
            } else {
                currentClient.stop(initiatedByClient);
            } else if (currentClient instanceof Cancellable) {
                ((Cancellable) currentClient).cancel();

                // Only post the reset runnable for non-cleanup clients. Cleanup clients should
                // never be forcibly stopped since they ensure synchronization between HAL and
@@ -808,13 +811,8 @@ public abstract class BiometricServiceBase extends SystemService
            return;
        }

        int status = mCurrentClient.start();
        if (status == 0) {
        mCurrentClient.start();
        notifyClientActiveCallbacks(true);
        } else {
            mCurrentClient.onError(BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
            removeClient(mCurrentClient);
        }
    }

    protected void removeClient(ClientMonitor client) {
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.sensors;

/**
 * Interface that cancellable {@link ClientMonitor} subclasses should implement.
 */
public interface Cancellable {
    /**
     * Requests to end the ClientMonitor's lifecycle.
     */
    void cancel();
}
+32 −56
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
@@ -37,6 +36,23 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    private static final String TAG = "Biometrics/ClientMonitor";
    protected static final boolean DEBUG = BiometricServiceBase.DEBUG;

    /**
     * Interface that ClientMonitor holders should use to receive callbacks.
     */
    public interface FinishCallback {
        /**
         * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
         * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
         * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
         * implementation.
         *
         * @param clientMonitor Reference of the ClientMonitor that finished.
         */
        void onClientFinished(ClientMonitor clientMonitor);
    }

    protected final FinishCallback mFinishCallback;

    private final Context mContext;
    private final int mTargetUserId;
    // True if client does not have MANAGE_FINGERPRINT permission
@@ -49,11 +65,11 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    // Currently only used for authentication client. The cookie generated by BiometricService
    // is never 0.
    private final int mCookie;

    boolean mAlreadyCancelled;
    boolean mAlreadyDone;

    /**
     * @param finishCallback    used to notify the ClientMonitor's holder when its lifecycle has
     *                          completed and can be removed from the scheduler
     * @param context    system_server context
     * @param token      a unique token for the client
     * @param listener   recipient of related events (e.g. authentication)
@@ -67,11 +83,12 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
     * @param statsAction   One of {@link BiometricsProtoEnums} ACTION_* constants
     * @param statsClient   One of {@link BiometricsProtoEnums} CLIENT_* constants
     */
    public ClientMonitor(@NonNull Context context, IBinder token,
            @Nullable ClientMonitorCallbackConverter listener, int userId, boolean restricted,
            @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
            int statsClient) {
    public ClientMonitor(@NonNull FinishCallback finishCallback, @NonNull Context context,
            IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
            boolean restricted, @NonNull String owner, int cookie, int sensorId, int statsModality,
            int statsAction, int statsClient) {
        super(statsModality, statsAction, statsClient);
        mFinishCallback = finishCallback;
        mContext = context;
        mToken = token;
        mListener = listener;
@@ -97,54 +114,23 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    /**
     * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
     * keeping is complete.
     * @return 0 on success, errno from driver on failure
     */
    public abstract int start();

    /**
     * Requests to end the ClientMonitor's lifecycle. Invokes {@link #stopHalOperation()} when
     * internal book keeping is complete.
     * @param initiatedByClient whether the operation is at the request of a client
     */
    public abstract int stop(boolean initiatedByClient);
    public abstract void start();

    /**
     * Starts the HAL operation specific to the ClientMonitor subclass.
     * @return status code specific to the HIDL interface. ClientMonitor subclasses currently
     * assume that non-zero codes are errors, and 0 == success.
     * @throws RemoteException
     */
    protected abstract int startHalOperation() throws RemoteException;
    protected abstract void startHalOperation();

    /**
     * Stops the HAL operation specific to the ClientMonitor subclass.
     * @return status code specific to the HIDL interface. ClientMonitor subclasses currently
     * assume that non-zero codes are errors, and 0 == success.
     * @throws RemoteException
     */
    protected abstract int stopHalOperation() throws RemoteException;
    protected abstract void stopHalOperation();

    public boolean isAlreadyDone() {
        return mAlreadyDone;
    }

    /**
     * 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 void onError(int error, int vendorCode) {
        super.logOnError(mContext, error, vendorCode, getTargetUserId());
        try {
            if (mListener != null) {
                mListener.onError(getSensorId(), getCookie(), error, vendorCode);
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to invoke sendError", e);
        }
    }

    public void destroy() {
        if (mToken != null) {
            try {
@@ -163,29 +149,19 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
        binderDiedInternal(true /* clearListener */);
    }

    // TODO(b/157790417): Move this to the scheduler
    void binderDiedInternal(boolean clearListener) {
        // If the current client dies we should cancel the current operation.
        if (this instanceof Cancellable) {
            Slog.e(TAG, "Binder died, cancelling client");
        stop(false /* initiatedByClient */);
            ((Cancellable) this).cancel();
        }
        mToken = null;
        if (clearListener) {
            mListener = null;
        }
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (mToken != null) {
                if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
                onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                        0 /* vendorCode */);
            }
        } finally {
            super.finalize();
        }
    }

    public final Context getContext() {
        return mContext;
    }
Loading