diff options
25 files changed, 1744 insertions, 851 deletions
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl index aa5ac035f4ee..754162ccba08 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl @@ -26,5 +26,5 @@ oneway interface IBiometricServiceLockoutResetCallback { /** * A wakelock will be held until the reciever calls back into {@param callback} */ - void onLockoutReset(IRemoteCallback callback); + void onLockoutReset(int sensorId, IRemoteCallback callback); } diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index c7fc2ad9b29a..b4546a49a07c 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -592,7 +592,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan new IBiometricServiceLockoutResetCallback.Stub() { @Override - public void onLockoutReset(IRemoteCallback serverCallback) + public void onLockoutReset(int sensorId, IRemoteCallback serverCallback) throws RemoteException { try { final PowerManager.WakeLock wakeLock = powerManager.newWakeLock( @@ -601,7 +601,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan wakeLock.acquire(); mHandler.post(() -> { try { - callback.onLockoutReset(); + callback.onLockoutReset(sensorId); } finally { wakeLock.release(); } @@ -978,7 +978,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * authentication * again. */ - public void onLockoutReset() { + public void onLockoutReset(int sensorId) { } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 9dacca769229..4ca75d9a09cf 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -347,7 +347,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * Called when lockout period expired and clients are allowed to listen for fingerprint * again. */ - public void onLockoutReset() { } + public void onLockoutReset(int sensorId) { } }; /** @@ -751,7 +751,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing new IBiometricServiceLockoutResetCallback.Stub() { @Override - public void onLockoutReset(IRemoteCallback serverCallback) + public void onLockoutReset(int sensorId, IRemoteCallback serverCallback) throws RemoteException { try { final PowerManager.WakeLock wakeLock = powerManager.newWakeLock( @@ -759,7 +759,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing wakeLock.acquire(); mHandler.post(() -> { try { - callback.onLockoutReset(); + callback.onLockoutReset(sensorId); } finally { wakeLock.release(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index e45cfe27b33f..e9b58c2c8ba3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -295,7 +295,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private int mHardwareFingerprintUnavailableRetryCount = 0; private int mHardwareFaceUnavailableRetryCount = 0; private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms - private static final int HAL_ERROR_RETRY_MAX = 10; + private static final int HAL_ERROR_RETRY_MAX = 20; private final Runnable mCancelNotReceived = new Runnable() { @Override @@ -682,7 +682,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public void run() { Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " + mHardwareFingerprintUnavailableRetryCount); - updateFingerprintListeningState(); + if (mFpm.isHardwareDetected()) { + updateFingerprintListeningState(); + } else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) { + mHardwareFingerprintUnavailableRetryCount++; + mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT); + } } }; @@ -704,11 +709,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) { - if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) { - mHardwareFingerprintUnavailableRetryCount++; - mHandler.removeCallbacks(mRetryFingerprintAuthentication); - mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT); - } + mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT); } if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) { @@ -1234,7 +1235,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback = new FingerprintManager.LockoutResetCallback() { @Override - public void onLockoutReset() { + public void onLockoutReset(int sensorId) { handleFingerprintLockoutReset(); } }; @@ -1242,7 +1243,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback = new FaceManager.LockoutResetCallback() { @Override - public void onLockoutReset() { + public void onLockoutReset(int sensorId) { handleFaceLockoutReset(); } }; @@ -1838,7 +1839,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) { return; } - mHandler.removeCallbacks(mRetryFingerprintAuthentication); + boolean shouldListenForFingerprint = shouldListenForFingerprint(); boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING; diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 6be457474630..29b8493b8035 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -16,6 +16,7 @@ package com.android.server.biometrics; +import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; import static com.android.server.biometrics.PreAuthInfo.AUTHENTICATOR_OK; @@ -30,19 +31,32 @@ import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED; import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE; import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED; +import android.app.ActivityManager; +import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType; +import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.PromptInfo; +import android.os.Binder; import android.os.Build; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.Slog; +import com.android.internal.R; + public class Utils { + + private static final String TAG = "BiometricUtils"; + public static boolean isDebugEnabled(Context context, int targetUserId) { if (targetUserId == UserHandle.USER_NULL) { return false; @@ -331,4 +345,54 @@ public class Utils { } return false; } + + public static void checkPermission(Context context, String permission) { + context.enforceCallingOrSelfPermission(permission, + "Must have " + permission + " permission."); + } + + public static boolean isCurrentUserOrProfile(Context context, int userId) { + UserManager um = UserManager.get(context); + if (um == null) { + Slog.e(TAG, "Unable to get UserManager"); + return false; + } + + final long token = Binder.clearCallingIdentity(); + try { + // Allow current user or profiles of the current user... + for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) { + if (profileId == userId) { + return true; + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + + return false; + } + + public static boolean isStrongBiometric(int sensorId) { + IBiometricService service = IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE)); + try { + return Utils.isAtLeastStrength(service.getCurrentStrength(sensorId), + Authenticators.BIOMETRIC_STRONG); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + return false; + } + } + + public static boolean isKeyguard(Context context, String clientPackage) { + final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) + == PackageManager.PERMISSION_GRANTED; + + final ComponentName keyguardComponent = ComponentName.unflattenFromString( + context.getResources().getString(R.string.config_keyguardComponent)); + final String keyguardPackage = keyguardComponent != null + ? keyguardComponent.getPackageName() : null; + return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index ac3d8675156d..14baaa72494f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -45,6 +45,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I private final PowerManager mPowerManager; private final VibrationEffect mSuccessVibrationEffect; private final VibrationEffect mErrorVibrationEffect; + private boolean mErrorAlreadySent; /** * Stops the HAL operation specific to the ClientMonitor subclass. @@ -74,15 +75,43 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I @Override public void onError(int errorCode, int vendorCode) { - logOnError(getContext(), errorCode, vendorCode, getTargetUserId()); + // Errors from the HAL always finish the client + onErrorInternal(errorCode, vendorCode, true /* finish */); + } + + protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) { + // In some cases, the framework will send an error to the caller before a true terminal + // case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint + // that do not handle lockout under the HAL. In these cases, ensure that the framework only + // sends errors once per ClientMonitor. + if (!mErrorAlreadySent) { + logOnError(getContext(), errorCode, vendorCode, getTargetUserId()); + try { + if (getListener() != null) { + mErrorAlreadySent = true; + getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode); + } + } catch (RemoteException e) { + Slog.w(TAG, "Failed to invoke sendError", e); + } + } + + if (finish) { + mFinishCallback.onClientFinished(this, false /* success */); + } + } + + @Override + public void cancelWithoutStarting(@NonNull FinishCallback finishCallback) { + final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED; try { if (getListener() != null) { - getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode); + getListener().onError(getSensorId(), getCookie(), errorCode, 0 /* vendorCode */); } } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke sendError", e); } - mFinishCallback.onClientFinished(this, false /* success */); + finishCallback.onClientFinished(this, true /* success */); } /** diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index df836d599e26..5392f0ff89af 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -119,6 +119,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> { + ", requireConfirmation: " + mRequireConfirmation + ", user: " + getTargetUserId()); + final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId()); + if (isCryptoOperation()) { + pm.incrementCryptoAuthForUser(getTargetUserId(), authenticated); + } else { + pm.incrementAuthForUser(getTargetUserId(), authenticated); + } + if (authenticated) { mAlreadyDone = true; @@ -184,6 +191,18 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> { } } + @Override + public void onAcquired(int acquiredInfo, int vendorCode) { + super.onAcquired(acquiredInfo, vendorCode); + + final @LockoutTracker.LockoutMode int lockoutMode = + mLockoutTracker.getLockoutModeForUser(getTargetUserId()); + if (lockoutMode == LockoutTracker.LOCKOUT_NONE) { + PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId()); + pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation()); + } + } + /** * Start authentication */ diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java new file mode 100644 index 000000000000..c9ab3138b410 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -0,0 +1,464 @@ +/* + * 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; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.biometrics.IBiometricService; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityTracker; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.text.SimpleDateFormat; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Queue; + +/** + * A scheduler for biometric HAL operations. Maintains a queue of {@link ClientMonitor} operations, + * without caring about its implementation details. Operations may perform one or more + * interactions with the HAL before finishing. + */ +public class BiometricScheduler { + + private static final String BASE_TAG = "BiometricScheduler"; + + /** + * Contains all the necessary information for a HAL operation. + */ + private static final class Operation { + + /** + * The operation is added to the list of pending operations and waiting for its turn. + */ + static final int STATE_WAITING_IN_QUEUE = 0; + + /** + * The operation is added to the list of pending operations, but a subsequent operation + * has been added. This state only applies to {@link Interruptable} operations. When this + * operation reaches the head of the queue, it will send ERROR_CANCELED and finish. + */ + static final int STATE_WAITING_IN_QUEUE_CANCELING = 1; + + /** + * The operation has reached the front of the queue and has started. + */ + static final int STATE_STARTED = 2; + + /** + * The operation was started, but is now canceling. Operations should wait for the HAL to + * acknowledge that the operation was canceled, at which point it finishes. + */ + static final int STATE_STARTED_CANCELING = 3; + + /** + * The operation has reached the head of the queue but is waiting for BiometricService + * to acknowledge and start the operation. + */ + static final int STATE_WAITING_FOR_COOKIE = 4; + + /** + * The {@link ClientMonitor.FinishCallback} has been invoked and the client is finished. + */ + static final int STATE_FINISHED = 5; + + @IntDef({STATE_WAITING_IN_QUEUE, + STATE_WAITING_IN_QUEUE_CANCELING, + STATE_STARTED, + STATE_STARTED_CANCELING, + STATE_WAITING_FOR_COOKIE, + STATE_FINISHED}) + @Retention(RetentionPolicy.SOURCE) + @interface OperationState {} + + @NonNull final ClientMonitor<?> clientMonitor; + @Nullable final ClientMonitor.FinishCallback clientFinishCallback; + @OperationState int state; + + Operation(@NonNull ClientMonitor<?> clientMonitor, + @Nullable ClientMonitor.FinishCallback finishCallback) { + this.clientMonitor = clientMonitor; + this.clientFinishCallback = finishCallback; + state = STATE_WAITING_IN_QUEUE; + } + + @Override + public String toString() { + return clientMonitor + ", State: " + state; + } + } + + /** + * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will + * kill the current operation and forcibly start the next. + */ + private static final class CancellationWatchdog implements Runnable { + static final int DELAY_MS = 3000; + + final String tag; + final Operation operation; + CancellationWatchdog(String tag, Operation operation) { + this.tag = tag; + this.operation = operation; + } + + @Override + public void run() { + if (operation.state != Operation.STATE_FINISHED) { + Slog.e(tag, "[Watchdog] Running for: " + operation); + operation.clientMonitor.mFinishCallback + .onClientFinished(operation.clientMonitor, false /* success */); + } + } + } + + private static final class CrashState { + static final int NUM_ENTRIES = 10; + final String timestamp; + final String currentOperation; + final List<String> pendingOperations; + + CrashState(String timestamp, String currentOperation, List<String> pendingOperations) { + this.timestamp = timestamp; + this.currentOperation = currentOperation; + this.pendingOperations = pendingOperations; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append(timestamp).append(": "); + sb.append("Current Operation: {").append(currentOperation).append("}"); + sb.append(", Pending Operations(").append(pendingOperations.size()).append(")"); + + if (!pendingOperations.isEmpty()) { + sb.append(": "); + } + for (int i = 0; i < pendingOperations.size(); i++) { + sb.append(pendingOperations.get(i)); + if (i < pendingOperations.size() - 1) { + sb.append(", "); + } + } + return sb.toString(); + } + } + + @NonNull private final String mBiometricTag; + @Nullable private final GestureAvailabilityTracker mGestureAvailabilityTracker; + @NonNull private final IBiometricService mBiometricService; + @NonNull private final Handler mHandler = new Handler(Looper.getMainLooper()); + @NonNull private final InternalFinishCallback mInternalFinishCallback; + @NonNull private final Queue<Operation> mPendingOperations; + @Nullable private Operation mCurrentOperation; + @NonNull private final ArrayDeque<CrashState> mCrashStates; + + // Internal finish callback, notified when an operation is complete. Notifies the requester + // that the operation is complete, before performing internal scheduler work (such as + // starting the next client). + private class InternalFinishCallback implements ClientMonitor.FinishCallback { + @Override + public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) { + mHandler.post(() -> { + if (mCurrentOperation == null) { + Slog.e(getTag(), "[Finishing] " + clientMonitor + + " but current operation is null, success: " + success + + ", possible lifecycle bug in clientMonitor implementation?"); + return; + } + + if (mCurrentOperation.clientFinishCallback != null) { + mCurrentOperation.clientFinishCallback.onClientFinished(clientMonitor, success); + } + + if (clientMonitor != mCurrentOperation.clientMonitor) { + throw new IllegalStateException("Mismatched operation, " + + " current: " + mCurrentOperation.clientMonitor + + " received: " + clientMonitor); + } + + Slog.d(getTag(), "[Finished] " + clientMonitor + ", success: " + success); + if (mGestureAvailabilityTracker != null) { + mGestureAvailabilityTracker.markSensorActive( + mCurrentOperation.clientMonitor.getSensorId(), false /* active */); + } + + mCurrentOperation.state = Operation.STATE_FINISHED; + mCurrentOperation = null; + startNextOperationIfIdle(); + }); + } + } + + /** + * Creates a new scheduler. + * @param tag for the specific instance of the scheduler. Should be unique. + * @param gestureAvailabilityTracker may be null if the sensor does not support gestures (such + * as fingerprint swipe). + */ + public BiometricScheduler(@NonNull String tag, + @Nullable GestureAvailabilityTracker gestureAvailabilityTracker) { + mBiometricTag = tag; + mInternalFinishCallback = new InternalFinishCallback(); + mGestureAvailabilityTracker = gestureAvailabilityTracker; + mPendingOperations = new LinkedList<>(); + mBiometricService = IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE)); + mCrashStates = new ArrayDeque<>(); + } + + private String getTag() { + return BASE_TAG + "/" + mBiometricTag; + } + + private void startNextOperationIfIdle() { + if (mCurrentOperation != null) { + Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation); + return; + } + if (mPendingOperations.isEmpty()) { + Slog.d(getTag(), "No operations, returning to idle"); + return; + } + + mCurrentOperation = mPendingOperations.poll(); + final ClientMonitor<?> currentClient = mCurrentOperation.clientMonitor; + + // If the operation at the front of the queue has been marked for cancellation, send + // ERROR_CANCELED. No need to start this client. + if (mCurrentOperation.state == Operation.STATE_WAITING_IN_QUEUE_CANCELING) { + Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation); + if (!(currentClient instanceof Interruptable)) { + throw new IllegalStateException("Mis-implemented client or scheduler, " + + "trying to cancel non-interruptable operation: " + mCurrentOperation); + } + + final Interruptable interruptable = (Interruptable) currentClient; + interruptable.cancelWithoutStarting(mInternalFinishCallback); + // Now we wait for the client to send its FinishCallback, which kicks off the next + // operation. + return; + } + + if (mGestureAvailabilityTracker != null + && mCurrentOperation.clientMonitor instanceof AcquisitionClient) { + mGestureAvailabilityTracker.markSensorActive( + mCurrentOperation.clientMonitor.getSensorId(), + true /* active */); + } + + // Not all operations start immediately. BiometricPrompt waits for its operation + // to arrive at the head of the queue, before pinging it to start. + final boolean shouldStartNow = currentClient.getCookie() == 0; + if (shouldStartNow) { + Slog.d(getTag(), "[Starting] " + mCurrentOperation); + currentClient.start(mInternalFinishCallback); + mCurrentOperation.state = Operation.STATE_STARTED; + } else { + try { + mBiometricService.onReadyForAuthentication(currentClient.getCookie()); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when contacting BiometricService", e); + } + Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation); + mCurrentOperation.state = Operation.STATE_WAITING_FOR_COOKIE; + } + } + + /** + * Starts the {@link #mCurrentOperation} if + * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and + * 2) its cookie matches this cookie + * @param cookie of the operation to be started + */ + public void startPreparedClient(int cookie) { + if (mCurrentOperation == null) { + Slog.e(getTag(), "Current operation null"); + return; + } + if (mCurrentOperation.state != Operation.STATE_WAITING_FOR_COOKIE) { + Slog.e(getTag(), "Operation in wrong state: " + mCurrentOperation); + return; + } + if (mCurrentOperation.clientMonitor.getCookie() != cookie) { + Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation + + ", received: " + cookie); + return; + } + + Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation); + mCurrentOperation.state = Operation.STATE_STARTED; + mCurrentOperation.clientMonitor.start(mInternalFinishCallback); + } + + /** + * Adds a {@link ClientMonitor} to the pending queue + * + * @param clientMonitor operation to be scheduled + */ + public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor) { + scheduleClientMonitor(clientMonitor, null /* clientFinishCallback */); + } + + /** + * Adds a {@link ClientMonitor} to the pending queue + * + * @param clientMonitor operation to be scheduled + * @param clientFinishCallback optional callback, invoked when the client is finished, but + * before it has been removed from the queue. + */ + public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor, + @Nullable ClientMonitor.FinishCallback clientFinishCallback) { + // Mark any interruptable pending clients as canceling. Once they reach the head of the + // queue, the scheduler will send ERROR_CANCELED and skip the operation. + for (Operation operation : mPendingOperations) { + if (operation.clientMonitor instanceof Interruptable + && operation.state != Operation.STATE_WAITING_IN_QUEUE_CANCELING) { + Slog.d(getTag(), "New client incoming, marking pending client as canceling: " + + operation.clientMonitor); + operation.state = Operation.STATE_WAITING_IN_QUEUE_CANCELING; + } + } + + mPendingOperations.add(new Operation(clientMonitor, clientFinishCallback)); + Slog.d(getTag(), "[Added] " + clientMonitor + + ", new queue size: " + mPendingOperations.size()); + + // If the current operation is cancellable, start the cancellation process. + if (mCurrentOperation != null && mCurrentOperation.clientMonitor instanceof Interruptable + && mCurrentOperation.state != Operation.STATE_STARTED_CANCELING) { + cancelInternal(mCurrentOperation); + } + + startNextOperationIfIdle(); + } + + private void cancelInternal(Operation operation) { + if (operation != mCurrentOperation) { + Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation); + return; + } + if (!(operation.clientMonitor instanceof Interruptable)) { + Slog.w(getTag(), "Operation not interruptable: " + operation); + return; + } + if (operation.state == Operation.STATE_STARTED_CANCELING) { + Slog.w(getTag(), "Cancel already invoked for operation: " + operation); + return; + } + Slog.d(getTag(), "[Cancelling] Current client: " + operation.clientMonitor); + final Interruptable interruptable = (Interruptable) operation.clientMonitor; + interruptable.cancel(); + operation.state = Operation.STATE_STARTED_CANCELING; + + // Add a watchdog. If the HAL does not acknowledge within the timeout, we will + // forcibly finish this client. + mHandler.postDelayed(new CancellationWatchdog(getTag(), operation), + CancellationWatchdog.DELAY_MS); + } + + /** + * Requests to cancel enrollment. + * @param token from the caller, should match the token passed in when requesting enrollment + */ + public void cancelEnrollment(IBinder token) { + if (mCurrentOperation == null) { + Slog.e(getTag(), "Unable to cancel enrollment, null operation"); + return; + } + final boolean isEnrolling = mCurrentOperation.clientMonitor instanceof EnrollClient; + final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token; + if (!isEnrolling || !tokenMatches) { + Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling + + " tokenMatches: " + tokenMatches); + return; + } + + cancelInternal(mCurrentOperation); + } + + /** + * Requests to cancel authentication. + * @param token from the caller, should match the token passed in when requesting authentication + */ + public void cancelAuthentication(IBinder token) { + if (mCurrentOperation == null) { + Slog.e(getTag(), "Unable to cancel authentication, null operation"); + return; + } + final boolean isAuthenticating = + mCurrentOperation.clientMonitor instanceof AuthenticationClient; + final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token; + if (!isAuthenticating || !tokenMatches) { + Slog.w(getTag(), "Not cancelling authentication, isEnrolling: " + isAuthenticating + + " tokenMatches: " + tokenMatches); + return; + } + + cancelInternal(mCurrentOperation); + } + + /** + * @return the current operation + */ + public ClientMonitor<?> getCurrentClient() { + if (mCurrentOperation == null) { + return null; + } + return mCurrentOperation.clientMonitor; + } + + public void recordCrashState() { + if (mCrashStates.size() >= CrashState.NUM_ENTRIES) { + mCrashStates.removeFirst(); + } + final SimpleDateFormat dateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); + final String timestamp = dateFormat.format(new Date(System.currentTimeMillis())); + final List<String> pendingOperations = new ArrayList<>(); + for (Operation operation : mPendingOperations) { + pendingOperations.add(operation.toString()); + } + + final CrashState crashState = new CrashState(timestamp, + mCurrentOperation != null ? mCurrentOperation.toString() : null, + pendingOperations); + mCrashStates.add(crashState); + Slog.e(getTag(), "Recorded crash state: " + crashState.toString()); + } + + public void dump(PrintWriter pw) { + pw.println("Dump of BiometricScheduler " + getTag()); + for (CrashState crashState : mCrashStates) { + pw.println("Crash State " + crashState); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java index a3901e005e21..9aa72a76aed2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java @@ -16,7 +16,6 @@ package com.android.server.biometrics.sensors; -import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE; import android.app.ActivityManager; @@ -25,28 +24,23 @@ import android.app.AppOpsManager; import android.app.IActivityTaskManager; import android.app.SynchronousUserSwitchObserver; import android.app.TaskStackListener; -import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; -import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricService; -import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.IHwBinder; import android.os.Looper; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.FrameworkStatsLog; @@ -74,7 +68,6 @@ public abstract class BiometricServiceBase<T> extends SystemService private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms private final Context mContext; - private final String mKeyguardPackage; protected final IActivityTaskManager mActivityTaskManager; protected final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener(); @@ -157,17 +150,6 @@ public abstract class BiometricServiceBase<T> extends SystemService */ protected abstract String getManageBiometricPermission(); - /** - * Checks if the caller has permission to use the biometric service - throws a SecurityException - * if not. - */ - protected abstract void checkUseBiometricPermission(); - - /** - * Checks if the caller passes the app ops check - */ - protected abstract boolean checkAppOps(int uid, String opPackageName); - protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates( int userId); @@ -180,11 +162,6 @@ public abstract class BiometricServiceBase<T> extends SystemService protected abstract int statsModality(); - /** - * @return one of the AuthenticationClient LOCKOUT constants - */ - protected abstract @LockoutTracker.LockoutMode int getLockoutMode(int userId); - private final Runnable mOnTaskStackChangedRunnable = new Runnable() { @Override public void run() { @@ -262,9 +239,6 @@ public abstract class BiometricServiceBase<T> extends SystemService mContext = context; mStatusBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - final ComponentName keyguardComponent = ComponentName.unflattenFromString( - context.getResources().getString(R.string.config_keyguardComponent)); - mKeyguardPackage = keyguardComponent != null ? keyguardComponent.getPackageName() : null; mAppOps = context.getSystemService(AppOpsManager.class); mActivityTaskManager = ActivityTaskManager.getService(); mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId()); @@ -298,24 +272,12 @@ public abstract class BiometricServiceBase<T> extends SystemService mSensorId = sensorId; } - protected ClientMonitor getCurrentClient() { + protected ClientMonitor<?> getCurrentClient() { return mCurrentClient; } - protected ClientMonitor getPendingClient() { - return mPendingClient; - } - protected boolean isStrongBiometric() { - IBiometricService service = IBiometricService.Stub.asInterface( - ServiceManager.getService(Context.BIOMETRIC_SERVICE)); - try { - return Utils.isAtLeastStrength(service.getCurrentStrength(mSensorId), - Authenticators.BIOMETRIC_STRONG); - } catch (RemoteException e) { - Slog.e(getTag(), "RemoteException", e); - return false; - } + return Utils.isStrongBiometric(mSensorId); } protected int getSensorId() { @@ -327,61 +289,46 @@ public abstract class BiometricServiceBase<T> extends SystemService */ protected void handleAcquired(int acquiredInfo, int vendorCode) { - final ClientMonitor client = mCurrentClient; + final ClientMonitor<?> client = mCurrentClient; if (!(client instanceof AcquisitionClient)) { final String clientName = client != null ? client.getClass().getSimpleName() : "null"; Slog.e(getTag(), "handleAcquired for non-acquire consumer: " + clientName); return; } - final AcquisitionClient acquisitionClient = (AcquisitionClient) client; + final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client; acquisitionClient.onAcquired(acquiredInfo, vendorCode); - - if (client instanceof AuthenticationClient) { - final int userId = client.getTargetUserId(); - if (getLockoutMode(userId) == LockoutTracker.LOCKOUT_NONE) { - mPerformanceTracker.incrementAcquireForUser(userId, client.isCryptoOperation()); - } - } } protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) { - final ClientMonitor client = mCurrentClient; + final ClientMonitor<?> client = mCurrentClient; if (!(client instanceof AuthenticationClient)) { final String clientName = client != null ? client.getClass().getSimpleName() : "null"; Slog.e(getTag(), "handleAuthenticated for non-authentication client: " + clientName); return; } - final AuthenticationClient authenticationClient = (AuthenticationClient) client; + final AuthenticationClient<?> authenticationClient = (AuthenticationClient<?>) client; final boolean authenticated = identifier.getBiometricId() != 0; - - final int userId = authenticationClient.getTargetUserId(); - if (authenticationClient.isCryptoOperation()) { - mPerformanceTracker.incrementCryptoAuthForUser(userId, authenticated); - } else { - mPerformanceTracker.incrementAuthForUser(userId, authenticated); - } - authenticationClient.onAuthenticated(identifier, authenticated, token); } protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { - final ClientMonitor client = mCurrentClient; + final ClientMonitor<?> client = mCurrentClient; if (!(client instanceof EnrollClient)) { final String clientName = client != null ? client.getClass().getSimpleName() : "null"; Slog.e(getTag(), "handleEnrollResult for non-enroll client: " + clientName); return; } - final EnrollClient enrollClient = (EnrollClient) client; + final EnrollClient<?> enrollClient = (EnrollClient<?>) client; enrollClient.onEnrollResult(identifier, remaining); } protected void handleError(int error, int vendorCode) { - final ClientMonitor client = mCurrentClient; + final ClientMonitor<?> client = mCurrentClient; if (DEBUG) Slog.v(getTag(), "handleError(client=" + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")"); @@ -411,7 +358,7 @@ public abstract class BiometricServiceBase<T> extends SystemService + ", dev=" + identifier.getDeviceId() + ", rem=" + remaining); - final ClientMonitor client = mCurrentClient; + final ClientMonitor<?> client = mCurrentClient; if (!(client instanceof RemovalConsumer)) { final String clientName = client != null ? client.getClass().getSimpleName() : "null"; Slog.e(getTag(), "handleRemoved for non-removal consumer: " + clientName); @@ -423,7 +370,7 @@ public abstract class BiometricServiceBase<T> extends SystemService } protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) { - final ClientMonitor client = mCurrentClient; + final ClientMonitor<?> client = mCurrentClient; if (!(client instanceof EnumerateConsumer)) { final String clientName = client != null ? client.getClass().getSimpleName() : "null"; Slog.e(getTag(), "handleEnumerate for non-enumerate consumer: " @@ -446,7 +393,7 @@ public abstract class BiometricServiceBase<T> extends SystemService // Group ID is arbitrarily set to parent profile user ID. It just represents // the default biometrics for the user. - if (!isCurrentUserOrProfile(userId)) { + if (!Utils.isCurrentUserOrProfile(mContext, userId)) { return; } @@ -457,10 +404,10 @@ public abstract class BiometricServiceBase<T> extends SystemService protected void cancelEnrollmentInternal(IBinder token) { mHandler.post(() -> { - ClientMonitor client = mCurrentClient; + ClientMonitor<?> client = mCurrentClient; if (client instanceof EnrollClient && client.getToken() == token) { if (DEBUG) Slog.v(getTag(), "Cancelling enrollment"); - ((EnrollClient) client).cancel(); + ((EnrollClient<?>) client).cancel(); } }); } @@ -478,58 +425,25 @@ public abstract class BiometricServiceBase<T> extends SystemService } protected void authenticateInternal(AuthenticationClient<T> client, String opPackageName) { - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int callingUserId = UserHandle.getCallingUserId(); - authenticateInternal(client, opPackageName, callingUid, callingPid, callingUserId); - } - - protected void authenticateInternal(AuthenticationClient<T> client, - String opPackageName, int callingUid, int callingPid, int callingUserId) { - if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid, - callingUserId)) { - if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName); - return; - } - mHandler.post(() -> { startAuthentication(client, opPackageName); }); } - protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) { - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int callingUserId = UserHandle.getCallingUserId(); - cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId, - true /* fromClient */); - } - protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName, - int callingUid, int callingPid, int callingUserId, boolean fromClient) { + boolean fromClient) { if (DEBUG) Slog.v(getTag(), "cancelAuthentication(" + opPackageName + ")"); - if (fromClient) { - // Only check this if cancel was called from the client (app). If cancel was called - // from BiometricService, it means the dialog was dismissed due to user interaction. - if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid, - callingUserId)) { - if (DEBUG) { - Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName); - } - return; - } - } mHandler.post(() -> { - ClientMonitor client = mCurrentClient; + ClientMonitor<?> client = mCurrentClient; if (client instanceof AuthenticationClient) { if (client.getToken() == token || !fromClient) { if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString() + ", fromClient: " + fromClient); // If cancel was from BiometricService, it means the dialog was dismissed // and authentication should be canceled. - ((AuthenticationClient) client).cancel(); + ((AuthenticationClient<?>) client).cancel(); } else { if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString() + " since tokens don't match. fromClient: " + fromClient); @@ -570,74 +484,10 @@ public abstract class BiometricServiceBase<T> extends SystemService */ /** - * @param opPackageName name of package for caller - * @param requireForeground only allow this call while app is in the foreground - * @return true if caller can use the biometric API - */ - protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid, - int pid, int userId) { - checkUseBiometricPermission(); - - - if (Binder.getCallingUid() == Process.SYSTEM_UID) { - return true; // System process (BiometricService, etc) is always allowed - } - if (isKeyguard(opPackageName)) { - return true; // Keyguard is always allowed - } - if (!isCurrentUserOrProfile(userId)) { - Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile"); - return false; - } - if (!checkAppOps(uid, opPackageName)) { - Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied"); - return false; - } - - if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient( - opPackageName))) { - Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground"); - return false; - } - return true; - } - - /** - * @param opPackageName package of the caller - * @return true if this is the same client currently using the biometric - */ - private boolean isCurrentClient(String opPackageName) { - return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName); - } - - /** * @return true if this is keyguard package */ public boolean isKeyguard(String clientPackage) { - return mKeyguardPackage.equals(clientPackage); - } - - private boolean isForegroundActivity(int uid, int pid) { - try { - final List<ActivityManager.RunningAppProcessInfo> procs = - ActivityManager.getService().getRunningAppProcesses(); - if (procs == null) { - Slog.e(getTag(), "Processes null, defaulting to true"); - return true; - } - - int N = procs.size(); - for (int i = 0; i < N; i++) { - ActivityManager.RunningAppProcessInfo proc = procs.get(i); - if (proc.pid == pid && proc.uid == uid - && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) { - return true; - } - } - } catch (RemoteException e) { - Slog.w(getTag(), "am.getRunningAppProcesses() failed"); - } - return false; + return Utils.isKeyguard(mContext, clientPackage); } /** @@ -650,7 +500,7 @@ public abstract class BiometricServiceBase<T> extends SystemService */ @VisibleForTesting protected void startClient(ClientMonitor<T> newClient, boolean initiatedByClient) { - ClientMonitor currentClient = mCurrentClient; + ClientMonitor<?> currentClient = mCurrentClient; if (currentClient != null) { if (DEBUG) Slog.v(getTag(), "request stop current client " + currentClient.getOwnerString()); @@ -680,7 +530,7 @@ public abstract class BiometricServiceBase<T> extends SystemService // <Biometric>Service#startPreparedClient is called. BiometricService waits until all // modalities are ready before initiating authentication. if (newClient instanceof AuthenticationClient) { - AuthenticationClient client = (AuthenticationClient) newClient; + AuthenticationClient<?> client = (AuthenticationClient<?>) newClient; if (client.isBiometricPrompt()) { if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie()); mCurrentClient = newClient; @@ -734,7 +584,7 @@ public abstract class BiometricServiceBase<T> extends SystemService notifyClientActiveCallbacks(true); } - protected void removeClient(ClientMonitor client) { + protected void removeClient(ClientMonitor<?> client) { if (client != null) { client.destroy(); if (client != mCurrentClient && mCurrentClient != null) { @@ -790,28 +640,6 @@ public abstract class BiometricServiceBase<T> extends SystemService "Must have " + permission + " permission."); } - protected boolean isCurrentUserOrProfile(int userId) { - UserManager um = UserManager.get(mContext); - if (um == null) { - Slog.e(getTag(), "Unable to acquire UserManager"); - return false; - } - - final long token = Binder.clearCallingIdentity(); - try { - // Allow current user or profiles of the current user... - for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) { - if (profileId == userId) { - return true; - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - - return false; - } - /** * @return authenticator id for the calling user */ diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java index 35f7279f6eac..8cabdf52314c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java @@ -36,6 +36,9 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde private static final String TAG = "Biometrics/ClientMonitor"; protected static final boolean DEBUG = BiometricServiceBase.DEBUG; + // Counter used to distinguish between ClientMonitor instances to help debugging. + private static int sCount = 0; + /** * Interface that ClientMonitor holders should use to receive callbacks. */ @@ -49,7 +52,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde * @param clientMonitor Reference of the ClientMonitor that finished. * @param success True if the operation completed successfully. */ - void onClientFinished(ClientMonitor clientMonitor, boolean success); + void onClientFinished(ClientMonitor<?> clientMonitor, boolean success); } /** @@ -62,6 +65,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde T getDaemon(); } + private final int mSequentialId; @NonNull private final Context mContext; @NonNull protected final LazyDaemon<T> mLazyDaemon; private final int mTargetUserId; @@ -95,6 +99,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, int statsClient) { super(statsModality, statsAction, statsClient); + mSequentialId = sCount++; mContext = context; mLazyDaemon = lazyDaemon; mToken = token; @@ -200,4 +205,12 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde public final T getFreshDaemon() { return mLazyDaemon.getDaemon(); } + + @Override + public String toString() { + return "{[" + mSequentialId + "] " + + this.getClass().getSimpleName() + + ", " + getOwnerString() + + ", " + getCookie() + "}"; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java index 1b8392ee7230..35d917747574 100644 --- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java +++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors; +import android.annotation.NonNull; + /** * Interface that {@link ClientMonitor} subclasses eligible/interested in error callbacks should * implement. @@ -27,8 +29,18 @@ public interface Interruptable { void cancel(); /** + * Notifies the client of errors from the HAL. * @param errorCode defined by the HIDL interface * @param vendorCode defined by the vendor */ void onError(int errorCode, int vendorCode); + + /** + * Notifies the client that it needs to finish before + * {@link ClientMonitor#start(ClientMonitor.FinishCallback)} was invoked. This usually happens + * if the client is still waiting in the pending queue and got notified that a subsequent + * operation is preempting it. + * @param finishCallback invoked when the operation is completed. + */ + void cancelWithoutStarting(@NonNull ClientMonitor.FinishCallback finishCallback); } diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java index 9bb5c6e77332..22c10b350e7d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java +++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java @@ -29,7 +29,8 @@ import java.util.ArrayList; /** * Allows clients (such as keyguard) to register for notifications on when biometric lockout - * ends. + * ends. This class keeps track of all client callbacks. Individual sensors should notify this + * when lockout for a specific sensor has been reset. */ public class LockoutResetTracker implements IBinder.DeathRecipient { @@ -54,11 +55,11 @@ public class LockoutResetTracker implements IBinder.DeathRecipient { "LockoutResetMonitor:SendLockoutReset"); } - void sendLockoutReset() { + void sendLockoutReset(int sensorId) { if (mCallback != null) { try { mWakeLock.acquire(WAKELOCK_TIMEOUT_MS); - mCallback.onLockoutReset(new IRemoteCallback.Stub() { + mCallback.onLockoutReset(sensorId, new IRemoteCallback.Stub() { @Override public void sendResult(Bundle data) { releaseWakelock(); @@ -114,9 +115,9 @@ public class LockoutResetTracker implements IBinder.DeathRecipient { } } - public void notifyLockoutResetCallbacks() { + public void notifyLockoutResetCallbacks(int sensorId) { for (ClientCallback callback : mClientCallbacks) { - callback.sendLockoutReset(); + callback.sendLockoutReset(sensorId); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java index fa490eef5d21..9fdba4b6d1bc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java +++ b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java @@ -107,7 +107,7 @@ public class PerformanceTracker { mAllUsersInfo.get(userId).mPermanentLockout++; } - void incrementHALDeathCount() { + public void incrementHALDeathCount() { mHALDeathCount++; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index a7f82202ca3e..7a051921f403 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -21,7 +21,6 @@ import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import android.app.ActivityManager; -import android.app.AppOpsManager; import android.app.NotificationManager; import android.content.Context; import android.content.pm.UserInfo; @@ -82,8 +81,6 @@ import java.util.List; * A service to manage multiple clients that want to access the face HAL API. * The service is responsible for maintaining a list of clients and dispatching all * face-related events. - * - * @hide */ public class FaceService extends BiometricServiceBase<IBiometricsFace> { @@ -115,7 +112,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { String opPackageName) { checkPermission(MANAGE_BIOMETRIC); - final GenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(), + final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName, getSensorId()); generateChallengeInternal(client); @@ -125,7 +122,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { public void revokeChallenge(IBinder token, String owner) { checkPermission(MANAGE_BIOMETRIC); - final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(), + final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(), mLazyDaemon, token, owner, getSensorId()); // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks. @@ -152,7 +149,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { UserHandle.CURRENT); }); - final EnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token, + final FaceEnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC, convertSurfaceToNativeHandle(surface), getSensorId()); @@ -183,7 +180,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { final boolean restricted = isRestricted(); final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD : BiometricsProtoEnums.CLIENT_UNKNOWN; - final AuthenticationClient client = new FaceAuthenticationClient(getContext(), + final FaceAuthenticationClient client = new FaceAuthenticationClient(getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId, restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(), isStrongBiometric(), statsClient, mTaskStackListener, @@ -200,13 +197,12 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { updateActiveGroup(userId); final boolean restricted = true; // BiometricPrompt is always restricted - final AuthenticationClient client = new FaceAuthenticationClient(getContext(), + final FaceAuthenticationClient client = new FaceAuthenticationClient(getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId, opId, restricted, opPackageName, cookie, requireConfirmation, getSensorId(), isStrongBiometric(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener, mLockoutTracker, mUsageStats); - authenticateInternal(client, opPackageName, callingUid, callingPid, - callingUserId); + authenticateInternal(client, opPackageName); } @Override // Binder call @@ -218,7 +214,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { @Override // Binder call public void cancelAuthentication(final IBinder token, final String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); - cancelAuthenticationInternal(token, opPackageName); + cancelAuthenticationInternal(token, opPackageName, true /* fromClient */); } @Override // Binder call @@ -226,8 +222,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { int callingUid, int callingPid, int callingUserId) { checkPermission(USE_BIOMETRIC_INTERNAL); // Cancellation is from system server in this case. - cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, - callingUserId, false /* fromClient */); + cancelAuthenticationInternal(token, opPackageName, false /* fromClient */); } @Override // Binder call @@ -241,7 +236,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { return; } - final RemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token, + final FaceRemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName, getBiometricUtils(), getSensorId(), mAuthenticatorIds); removeInternal(client); @@ -282,11 +277,6 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { @Override // Binder call public boolean isHardwareDetected(String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); - if (!canUseBiometric(opPackageName, false /* foregroundOnly */, - Binder.getCallingUid(), Binder.getCallingPid(), - UserHandle.getCallingUserId())) { - return false; - } final long token = Binder.clearCallingIdentity(); try { @@ -300,24 +290,12 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { @Override // Binder call public List<Face> getEnrolledFaces(int userId, String opPackageName) { checkPermission(MANAGE_BIOMETRIC); - if (!canUseBiometric(opPackageName, false /* foregroundOnly */, - Binder.getCallingUid(), Binder.getCallingPid(), - UserHandle.getCallingUserId())) { - return null; - } - return FaceService.this.getEnrolledTemplates(userId); } @Override // Binder call public boolean hasEnrolledFaces(int userId, String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); - if (!canUseBiometric(opPackageName, false /* foregroundOnly */, - Binder.getCallingUid(), Binder.getCallingPid(), - UserHandle.getCallingUserId())) { - return false; - } - return FaceService.this.hasEnrolledBiometrics(userId); } @@ -427,7 +405,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { @GuardedBy("this") private IBiometricsFace mDaemon; private UsageStats mUsageStats; - private RevokeChallengeClient mPendingRevokeChallenge; + private FaceRevokeChallengeClient mPendingRevokeChallenge; private NotificationManager mNotificationManager; @@ -546,7 +524,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { mHandler.post(() -> { if (duration == 0) { - mLockoutResetTracker.notifyLockoutResetCallbacks(); + mLockoutResetTracker.notifyLockoutResetCallbacks(getSensorId()); } }); } @@ -562,7 +540,7 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { } @Override - protected void removeClient(ClientMonitor client) { + protected void removeClient(ClientMonitor<?> client) { super.removeClient(client); if (mPendingRevokeChallenge != null) { revokeChallengeInternal(mPendingRevokeChallenge); @@ -670,17 +648,6 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { } @Override - protected void checkUseBiometricPermission() { - // noop for Face. The permission checks are all done on the incoming binder call. - } - - @Override - protected boolean checkAppOps(int uid, String opPackageName) { - return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName) - == AppOpsManager.MODE_ALLOWED; - } - - @Override protected List<Face> getEnrolledTemplates(int userId) { return FaceUtils.getInstance().getBiometricsForUser(getContext(), userId); } @@ -696,11 +663,6 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> { } @Override - protected @LockoutTracker.LockoutMode int getLockoutMode(int userId) { - return mLockoutTracker.getLockoutModeForUser(userId); - } - - @Override protected void doTemplateCleanupForUser(int userId) { final List<Face> enrolledList = getEnrolledTemplates(userId); final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(), diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java new file mode 100644 index 000000000000..5d48e56c7aed --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java @@ -0,0 +1,653 @@ +/* + * 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.fingerprint; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; +import android.app.SynchronousUserSwitchObserver; +import android.app.TaskStackListener; +import android.app.UserSwitchObserver; +import android.content.Context; +import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricConstants; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.IFingerprintServiceReceiver; +import android.os.Handler; +import android.os.IBinder; +import android.os.IHwBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import android.view.Surface; + +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto; +import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto; +import com.android.server.biometrics.fingerprint.PerformanceStatsProto; +import com.android.server.biometrics.sensors.AcquisitionClient; +import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.EnumerateConsumer; +import com.android.server.biometrics.sensors.Interruptable; +import com.android.server.biometrics.sensors.LockoutResetTracker; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.PerformanceTracker; +import com.android.server.biometrics.sensors.RemovalConsumer; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or + * its extended minor versions. + */ +class Fingerprint21 implements IHwBinder.DeathRecipient { + + private static final String TAG = "Fingerprint21"; + private static final int ENROLL_TIMEOUT_SEC = 60; + + private final Context mContext; + private final IActivityTaskManager mActivityTaskManager;; + private final SensorProperties mSensorProperties; + private final BiometricScheduler mScheduler; + private final Handler mHandler; + private final LockoutResetTracker mLockoutResetTracker; + private final LockoutFrameworkImpl mLockoutTracker; + private final BiometricTaskStackListener mTaskStackListener; + private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon; + private final Map<Integer, Long> mAuthenticatorIds; + + private IBiometricsFingerprint mDaemon; + private int mCurrentUserId = UserHandle.USER_NULL; + + /** + * Static properties that never change for a given sensor. + */ + private static final class SensorProperties { + final int sensorId; + final Boolean isUdfps; + + SensorProperties(int sensorId, Boolean isUdfps) { + this.sensorId = sensorId; + this.isUdfps = isUdfps; + } + } + + private final class BiometricTaskStackListener extends TaskStackListener { + @Override + public void onTaskStackChanged() { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof AuthenticationClient)) { + Slog.e(TAG, "Task stack changed for client: " + client); + return; + } + if (Utils.isKeyguard(mContext, client.getOwnerString())) { + return; // Keyguard is always allowed + } + + try { + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(TAG, "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mScheduler.cancelAuthentication(client.getToken()); + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to get running tasks", e); + } + }); + } + } + + private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback = + new LockoutFrameworkImpl.LockoutResetCallback() { + @Override + public void onLockoutReset(int userId) { + mLockoutResetTracker.notifyLockoutResetCallbacks(mSensorProperties.sensorId); + } + }; + + private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() { + @Override + public void onUserSwitching(int newUserId) { + scheduleInternalCleanup(newUserId); + } + }; + + private IBiometricsFingerprintClientCallback mDaemonCallback = + new IBiometricsFingerprintClientCallback.Stub() { + + @Override + public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) { + mHandler.post(() -> { + final CharSequence name = FingerprintUtils.getInstance() + .getUniqueName(mContext, mCurrentUserId); + final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId); + + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintEnrollClient)) { + final String clientName = client != null + ? client.getClass().getSimpleName(): "null"; + Slog.e(TAG, "onEnrollResult for non-enroll client: " + clientName); + return; + } + + final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client; + enrollClient.onEnrollResult(fingerprint, remaining); + + if (remaining == 0) { + // Update the framework's authenticatorId cache for this user + scheduleUpdateActiveUserWithoutHandler(mCurrentUserId); + } + }); + } + + @Override + public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) { + onAcquired_2_2(deviceId, acquiredInfo, vendorCode); + } + + @Override + public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof AcquisitionClient)) { + final String clientName = client != null + ? client.getClass().getSimpleName(): "null"; + Slog.e(TAG, "onAcquired for non-acquisition client: " + clientName); + return; + } + + final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client; + acquisitionClient.onAcquired(acquiredInfo, vendorCode); + }); + } + + @Override + public void onAuthenticated(long deviceId, int fingerId, int groupId, + ArrayList<Byte> token) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintAuthenticationClient)) { + final String clientName = client != null + ? client.getClass().getSimpleName(): "null"; + Slog.e(TAG, "onAuthenticated for non-authentication client: " + clientName); + return; + } + + final FingerprintAuthenticationClient authenticationClient = + (FingerprintAuthenticationClient) client; + final boolean authenticated = fingerId != 0; + final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); + authenticationClient.onAuthenticated(fp, authenticated, token); + }); + } + + @Override + public void onError(long deviceId, int error, int vendorCode) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + Slog.d(TAG, "handleError" + + ", client: " + (client != null ? client.getOwnerString() : null) + + ", error: " + error + + ", vendorCode: " + vendorCode); + if (!(client instanceof Interruptable)) { + final String clientName = client != null + ? client.getClass().getSimpleName(): "null"; + Slog.e(TAG, "onError for non-error consumer: " + clientName); + return; + } + + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(error, vendorCode); + + if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { + Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE"); + mDaemon = null; + mCurrentUserId = UserHandle.USER_NULL; + } + }); + } + + @Override + public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) { + mHandler.post(() -> { + Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining); + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof RemovalConsumer)) { + final String clientName = client != null + ? client.getClass().getSimpleName(): "null"; + Slog.e(TAG, "onRemoved for non-removal consumer: " + clientName); + return; + } + + final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); + final RemovalConsumer removalConsumer = (RemovalConsumer) client; + removalConsumer.onRemoved(fp, remaining); + }); + } + + @Override + public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof EnumerateConsumer)) { + final String clientName = client != null + ? client.getClass().getSimpleName(): "null"; + Slog.e(TAG, "onEnumerate for non-enumerate consumer: " + clientName); + return; + } + + final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); + final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client; + enumerateConsumer.onEnumerationResult(fp, remaining); + }); + } + }; + + Fingerprint21(@NonNull Context context, int sensorId, + @NonNull LockoutResetTracker lockoutResetTracker, + @NonNull GestureAvailabilityTracker gestureAvailabilityTracker) { + mContext = context; + mActivityTaskManager = ActivityTaskManager.getService(); + mHandler = new Handler(Looper.getMainLooper()); + mTaskStackListener = new BiometricTaskStackListener(); + mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>()); + mLazyDaemon = Fingerprint21.this::getDaemon; + mLockoutResetTracker = lockoutResetTracker; + mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback); + mScheduler = new BiometricScheduler(TAG, gestureAvailabilityTracker); + + try { + ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to register user switch observer"); + } + + final IBiometricsFingerprint daemon = getDaemon(); + Boolean isUdfps = false; + android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = + android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( + daemon); + if (extension != null) { + try { + isUdfps = extension.isUdfps(sensorId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception while quering udfps", e); + isUdfps = null; + } + } + mSensorProperties = new SensorProperties(sensorId, isUdfps); + } + + @Override + public void serviceDied(long cookie) { + Slog.e(TAG, "HAL died"); + mHandler.post(() -> { + PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId) + .incrementHALDeathCount(); + mDaemon = null; + mCurrentUserId = UserHandle.USER_NULL; + + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (client instanceof Interruptable) { + Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + + mScheduler.recordCrashState(); + + FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + BiometricsProtoEnums.MODALITY_FINGERPRINT, + BiometricsProtoEnums.ISSUE_HAL_DEATH); + } + }); + } + + private synchronized IBiometricsFingerprint getDaemon() { + if (mDaemon != null) { + return mDaemon; + } + + Slog.d(TAG, "Daemon was null, reconnecting, current operation: " + + mScheduler.getCurrentClient()); + try { + mDaemon = IBiometricsFingerprint.getService(); + } catch (java.util.NoSuchElementException e) { + // Service doesn't exist or cannot be opened. + Slog.w(TAG, "NoSuchElementException", e); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get fingerprint HAL", e); + } + + if (mDaemon == null) { + Slog.w(TAG, "Fingerprint HAL not available"); + return null; + } + + mDaemon.asBinder().linkToDeath(this, 0 /* flags */); + + // HAL ID for these HIDL versions are only used to determine if callbacks have been + // successfully set. + long halId = 0; + try { + halId = mDaemon.setNotify(mDaemonCallback); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set callback for fingerprint HAL", e); + mDaemon = null; + } + + Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId); + if (halId != 0) { + scheduleLoadAuthenticatorIds(); + scheduleInternalCleanup(ActivityManager.getCurrentUser()); + } else { + Slog.e(TAG, "Unable to set callback"); + mDaemon = null; + } + + return mDaemon; + } + + @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) { + return mLockoutTracker.getLockoutModeForUser(userId); + } + + private void scheduleLoadAuthenticatorIds() { + // Note that this can be performed on the scheduler (as opposed to being done immediately + // when the HAL is (re)loaded, since + // 1) If this is truly the first time it's being performed (e.g. system has just started), + // this will be run very early and way before any applications need to generate keys. + // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has + // just been reloaded), the framework already has a cache of the authenticatorIds. This + // is safe because authenticatorIds only change when A) new template has been enrolled, + // or B) all templates are removed. + mHandler.post(() -> { + for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) { + final int targetUserId = user.id; + if (!mAuthenticatorIds.containsKey(targetUserId)) { + scheduleUpdateActiveUserWithoutHandler(targetUserId); + } + } + }); + + } + + private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) { + final boolean hasEnrolled = !getEnrolledFingerprints(targetUserId).isEmpty(); + final FingerprintUpdateActiveUserClient client = + new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId, + mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId, + hasEnrolled, mAuthenticatorIds); + mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> { + if (success) { + mCurrentUserId = targetUserId; + } + }); + } + + void scheduleResetLockout(int userId, byte[] hardwareAuthToken) { + // Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler + // thread. + mHandler.post(() -> { + mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId); + }); + } + + void scheduleGenerateChallenge(@NonNull IBinder token, + @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) { + mHandler.post(() -> { + final FingerprintGenerateChallengeClient client = + new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token, + new ClientMonitorCallbackConverter(receiver), opPackageName, + mSensorProperties.sensorId); + mScheduler.scheduleClientMonitor(client); + }); + } + + void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String opPackageName) { + mHandler.post(() -> { + final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient( + mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId); + mScheduler.scheduleClientMonitor(client); + }); + } + + void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, + @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, + @Nullable Surface surface) { + mHandler.post(() -> { + scheduleUpdateActiveUserWithoutHandler(userId); + + final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext, + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, + hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(), + ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId); + mScheduler.scheduleClientMonitor(client); + }); + + } + + void cancelEnrollment(@NonNull IBinder token) { + mHandler.post(() -> { + mScheduler.cancelEnrollment(token); + }); + } + + void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie, + @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, + @Nullable Surface surface, boolean restricted, int statsClient) { + mHandler.post(() -> { + scheduleUpdateActiveUserWithoutHandler(userId); + + final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId); + final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( + mContext, mLazyDaemon, token, listener, userId, operationId, restricted, + opPackageName, cookie, false /* requireConfirmation */, + mSensorProperties.sensorId, isStrongBiometric, surface, statsClient, + mTaskStackListener, mLockoutTracker); + mScheduler.scheduleClientMonitor(client); + }); + } + + void startPreparedClient(int cookie) { + mHandler.post(() -> { + mScheduler.startPreparedClient(cookie); + }); + } + + void cancelAuthentication(@NonNull IBinder token) { + mHandler.post(() -> { + mScheduler.cancelAuthentication(token); + }); + } + + void scheduleRemove(@NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver, + int fingerId, int userId, @NonNull String opPackageName) { + mHandler.post(() -> { + scheduleUpdateActiveUserWithoutHandler(userId); + + final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext, + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId, + userId, opPackageName, FingerprintUtils.getInstance(), + mSensorProperties.sensorId, mAuthenticatorIds); + mScheduler.scheduleClientMonitor(client); + }); + } + + private void scheduleInternalCleanup(int userId) { + mHandler.post(() -> { + scheduleUpdateActiveUserWithoutHandler(userId); + + final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId); + final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient( + mContext, mLazyDaemon, userId, mContext.getOpPackageName(), + mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(), + mAuthenticatorIds); + mScheduler.scheduleClientMonitor(client); + }); + } + + boolean isHardwareDetected() { + IBiometricsFingerprint daemon = getDaemon(); + return daemon != null; + } + + void rename(int fingerId, int userId, String name) { + mHandler.post(() -> { + FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name); + }); + } + + List<Fingerprint> getEnrolledFingerprints(int userId) { + return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId); + } + + long getAuthenticatorId(int userId) { + return mAuthenticatorIds.get(userId); + } + + void onFingerDown(int x, int y, float minor, float major) { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof Udfps)) { + Slog.w(TAG, "onFingerDown received during client: " + client); + return; + } + final Udfps udfps = (Udfps) client; + udfps.onFingerDown(x, y, minor, major); + } + + void onFingerUp() { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof Udfps)) { + Slog.w(TAG, "onFingerDown received during client: " + client); + return; + } + final Udfps udfps = (Udfps) client; + udfps.onFingerUp(); + } + + boolean isUdfps() { + return mSensorProperties.isUdfps; + } + + void dumpProto(FileDescriptor fd) { + PerformanceTracker tracker = + PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId); + + final ProtoOutputStream proto = new ProtoOutputStream(fd); + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + + final long userToken = proto.start(FingerprintServiceDumpProto.USERS); + + proto.write(FingerprintUserStatsProto.USER_ID, userId); + proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS, + FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId).size()); + + // Normal fingerprint authentications (e.g. lockscreen) + long countsToken = proto.start(FingerprintUserStatsProto.NORMAL); + proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId)); + proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId)); + proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId)); + proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId)); + proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, + tracker.getPermanentLockoutForUser(userId)); + proto.end(countsToken); + + // Statistics about secure fingerprint transactions (e.g. to unlock password + // storage, make secure purchases, etc.) + countsToken = proto.start(FingerprintUserStatsProto.CRYPTO); + proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId)); + proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId)); + proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId)); + proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto + proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto + proto.end(countsToken); + + proto.end(userToken); + } + proto.flush(); + tracker.clear(); + } + + void dumpInternal(PrintWriter pw) { + PerformanceTracker performanceTracker = + PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId); + + JSONObject dump = new JSONObject(); + try { + dump.put("service", "Fingerprint Manager"); + + JSONArray sets = new JSONArray(); + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + final int N = FingerprintUtils.getInstance() + .getBiometricsForUser(mContext, userId).size(); + JSONObject set = new JSONObject(); + set.put("id", userId); + set.put("count", N); + set.put("accept", performanceTracker.getAcceptForUser(userId)); + set.put("reject", performanceTracker.getRejectForUser(userId)); + set.put("acquire", performanceTracker.getAcquireForUser(userId)); + set.put("lockout", performanceTracker.getTimedLockoutForUser(userId)); + set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId)); + // cryptoStats measures statistics about secure fingerprint transactions + // (e.g. to unlock password storage, make secure purchases, etc.) + set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId)); + set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId)); + set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId)); + sets.put(set); + } + + dump.put("prints", sets); + } catch (JSONException e) { + Slog.e(TAG, "dump formatting failure", e); + } + pw.println(dump); + pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); + mScheduler.dump(pw); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java index b01b8564466b..9cc0b0dd06b8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java @@ -41,7 +41,8 @@ import java.util.ArrayList; * {@link android.hardware.biometrics.fingerprint.V2_1} and * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces. */ -class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint> { +class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint> + implements Udfps { private static final String TAG = "Biometrics/FingerprintAuthClient"; @@ -79,12 +80,14 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId()); if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { Slog.w(TAG, "Fingerprint locked out, lockoutMode(" + lockoutMode + ")"); - cancel(); final int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; - onError(errorCode, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, true /* success */); + // Send the error, but do not invoke the FinishCallback yet. Since lockout is not + // controlled by the HAL, the framework must stop the sensor before finishing the + // client. + onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */); + cancel(); } } } @@ -123,4 +126,14 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi mFinishCallback.onClientFinished(this, false /* success */); } } + + @Override + public void onFingerDown(int x, int y, float minor, float major) { + UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major); + } + + @Override + public void onFingerUp() { + UdfpsHelper.onFingerUp(getFreshDaemon()); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java index 1d56882c0fd2..5282bdf774ab 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java @@ -34,7 +34,8 @@ import com.android.server.biometrics.sensors.EnrollClient; * {@link android.hardware.biometrics.fingerprint.V2_1} and * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces. */ -public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint> { +public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint> + implements Udfps { private static final String TAG = "FingerprintEnrollClient"; @@ -72,4 +73,14 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint mFinishCallback.onClientFinished(this, false /* success */); } } + + @Override + public void onFingerDown(int x, int y, float minor, float major) { + UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major); + } + + @Override + public void onFingerUp() { + UdfpsHelper.onFingerUp(getFreshDaemon()); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java index 1d72c2e0a2ca..571d2b80fd3e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java @@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.fingerprint; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index b05400a0e426..11e6bf24339e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -23,64 +23,38 @@ import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; 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.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; -import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; -import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.IFingerprintClientActiveCallback; import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; -import android.os.Build; -import android.os.Environment; import android.os.IBinder; import android.os.NativeHandle; +import android.os.Process; import android.os.RemoteException; -import android.os.SELinux; import android.os.UserHandle; -import android.os.UserManager; import android.util.Slog; -import android.util.proto.ProtoOutputStream; import android.view.Surface; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; -import com.android.server.SystemServerInitThreadPool; -import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto; -import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto; -import com.android.server.biometrics.fingerprint.PerformanceStatsProto; -import com.android.server.biometrics.sensors.AuthenticationClient; -import com.android.server.biometrics.sensors.BiometricServiceBase; -import com.android.server.biometrics.sensors.BiometricUtils; -import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.SystemService; +import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; -import com.android.server.biometrics.sensors.EnrollClient; -import com.android.server.biometrics.sensors.GenerateChallengeClient; import com.android.server.biometrics.sensors.LockoutResetTracker; import com.android.server.biometrics.sensors.LockoutTracker; -import com.android.server.biometrics.sensors.PerformanceTracker; -import com.android.server.biometrics.sensors.RemovalClient; -import com.android.server.biometrics.sensors.RevokeChallengeClient; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -88,159 +62,127 @@ import java.util.List; * A service to manage multiple clients that want to access the fingerprint HAL API. * The service is responsible for maintaining a list of clients and dispatching all * fingerprint-related events. - * - * @hide */ -public class FingerprintService extends BiometricServiceBase<IBiometricsFingerprint> { +public class FingerprintService extends SystemService { protected static final String TAG = "FingerprintService"; - private static final boolean DEBUG = true; - private static final String FP_DATA_DIR = "fpdata"; + private final AppOpsManager mAppOps; private final LockoutResetTracker mLockoutResetTracker; - private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon; private final GestureAvailabilityTracker mGestureAvailabilityTracker; + private Fingerprint21 mFingerprint21; + private IUdfpsOverlayController mUdfpsOverlayController; /** * Receives the incoming binder calls from FingerprintManager. */ private final class FingerprintServiceWrapper extends IFingerprintService.Stub { - private static final int ENROLL_TIMEOUT_SEC = 60; - - /** - * The following methods contain common code which is shared in biometrics/common. - */ - @Override // Binder call public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver, String opPackageName) { - checkPermission(MANAGE_FINGERPRINT); - - final GenerateChallengeClient client = new FingerprintGenerateChallengeClient( - getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), - opPackageName, getSensorId()); - generateChallengeInternal(client); + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); + mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName); } @Override // Binder call public void revokeChallenge(IBinder token, String owner) { - checkPermission(MANAGE_FINGERPRINT); - - final RevokeChallengeClient client = new FingerprintRevokeChallengeClient(getContext(), - mLazyDaemon, token, owner, getSensorId()); - revokeChallengeInternal(client); + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); + mFingerprint21.scheduleRevokeChallenge(token, owner); } @Override // Binder call - public void enroll(final IBinder token, final byte[] cryptoToken, final int userId, + public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, - final Surface surface) { - checkPermission(MANAGE_FINGERPRINT); - updateActiveGroup(userId); - - final EnrollClient client = new FingerprintEnrollClient(getContext(), mLazyDaemon, - token, new ClientMonitorCallbackConverter(receiver), userId, cryptoToken, - opPackageName, getBiometricUtils(), ENROLL_TIMEOUT_SEC, getSensorId()); - - enrollInternal(client, userId); + Surface surface) { + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); + mFingerprint21.scheduleEnroll(token, hardwareAuthToken, userId, receiver, opPackageName, + surface); } @Override // Binder call public void cancelEnrollment(final IBinder token) { - checkPermission(MANAGE_FINGERPRINT); - cancelEnrollmentInternal(token); + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); + mFingerprint21.cancelEnrollment(token); } @Override // Binder call - public void authenticate(final IBinder token, final long opId, final int userId, + public void authenticate(final IBinder token, final long operationId, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, - final Surface surface) { - updateActiveGroup(userId); + Surface surface) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int callingUserId = UserHandle.getCallingUserId(); - final boolean isStrongBiometric; - final long ident = Binder.clearCallingIdentity(); - try { - isStrongBiometric = isStrongBiometric(); - } finally { - Binder.restoreCallingIdentity(ident); + if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid, + callingPid, callingUserId)) { + Slog.w(TAG, "Authenticate rejecting package: " + opPackageName); + return; } - final boolean restricted = isRestricted(); - final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD + final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT) + != PackageManager.PERMISSION_GRANTED; + final int statsClient = Utils.isKeyguard(getContext(), opPackageName) + ? BiometricsProtoEnums.CLIENT_KEYGUARD : BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER; - final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(), - mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId, - restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */, - getSensorId(), isStrongBiometric, surface, statsClient, mTaskStackListener, - mLockoutTracker); - authenticateInternal(client, opPackageName); + mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */, + new ClientMonitorCallbackConverter(receiver), opPackageName, surface, + restricted, statsClient); } @Override // Binder call - public void prepareForAuthentication(IBinder token, long opId, int userId, + public void prepareForAuthentication(IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId, Surface surface) { - checkPermission(MANAGE_BIOMETRIC); - updateActiveGroup(userId); + Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final boolean restricted = true; // BiometricPrompt is always restricted - final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(), - mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId, - opId, restricted, opPackageName, cookie, false /* requireConfirmation */, - getSensorId(), isStrongBiometric(), surface, - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener, - mLockoutTracker); - authenticateInternal(client, opPackageName, callingUid, callingPid, - callingUserId); + mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie, + new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, surface, + restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT); } @Override // Binder call public void startPreparedClient(int cookie) { - checkPermission(MANAGE_BIOMETRIC); - startCurrentClient(cookie); + Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); + mFingerprint21.startPreparedClient(cookie); } @Override // Binder call public void cancelAuthentication(final IBinder token, final String opPackageName) { - cancelAuthenticationInternal(token, opPackageName); + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int callingUserId = UserHandle.getCallingUserId(); + + if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid, + callingPid, callingUserId)) { + Slog.w(TAG, "cancelAuthentication rejecting package: " + opPackageName); + return; + } + + mFingerprint21.cancelAuthentication(token); } @Override // Binder call public void cancelAuthenticationFromService(final IBinder token, final String opPackageName, int callingUid, int callingPid, int callingUserId) { - checkPermission(MANAGE_BIOMETRIC); - // Cancellation is from system server in this case. - cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, - callingUserId, false /* fromClient */); + Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); + mFingerprint21.cancelAuthentication(token); } @Override // Binder call public void remove(final IBinder token, final int fingerId, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName) - throws RemoteException { - checkPermission(MANAGE_FINGERPRINT); - updateActiveGroup(userId); - - if (token == null) { - Slog.w(TAG, "remove(): token is null"); - return; - } - - final RemovalClient client = new FingerprintRemovalClient(getContext(), mLazyDaemon, - token, new ClientMonitorCallbackConverter(receiver), fingerId, userId, - opPackageName, getBiometricUtils(), getSensorId(), mAuthenticatorIds); - removeInternal(client); + final IFingerprintServiceReceiver receiver, final String opPackageName) { + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); + mFingerprint21.scheduleRemove(token, receiver, fingerId, userId, opPackageName); } @Override public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback, final String opPackageName) { - checkPermission(USE_BIOMETRIC_INTERNAL); - mHandler.post(() -> { - mLockoutResetTracker.addCallback(callback, opPackageName); - }); + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + mLockoutResetTracker.addCallback(callback, opPackageName); } @Override // Binder call @@ -252,23 +194,18 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr final long ident = Binder.clearCallingIdentity(); try { if (args.length > 0 && "--proto".equals(args[0])) { - dumpProto(fd); + mFingerprint21.dumpProto(fd); } else { - dumpInternal(pw); + mFingerprint21.dumpInternal(pw); } } finally { Binder.restoreCallingIdentity(ident); } } - /** - * The following methods don't use any common code from BiometricService - */ - - // TODO: refactor out common code here @Override // Binder call public boolean isHardwareDetected(String opPackageName) { - if (!canUseBiometric(opPackageName, false /* foregroundOnly */, + if (!canUseFingerprint(opPackageName, false /* foregroundOnly */, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getCallingUserId())) { return false; @@ -276,8 +213,7 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr final long token = Binder.clearCallingIdentity(); try { - IBiometricsFingerprint daemon = getFingerprintDaemon(); - return daemon != null; + return mFingerprint21.isHardwareDetected(); } finally { Binder.restoreCallingIdentity(token); } @@ -285,162 +221,106 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr @Override // Binder call public void rename(final int fingerId, final int userId, final String name) { - checkPermission(MANAGE_FINGERPRINT); - if (!isCurrentUserOrProfile(userId)) { + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); + if (!Utils.isCurrentUserOrProfile(getContext(), userId)) { return; } - mHandler.post(new Runnable() { - @Override - public void run() { - getBiometricUtils().renameBiometricForUser(getContext(), userId, fingerId, - name); - } - }); + + mFingerprint21.rename(fingerId, userId, name); } @Override // Binder call public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) { - if (!canUseBiometric(opPackageName, false /* foregroundOnly */, + if (!canUseFingerprint(opPackageName, false /* foregroundOnly */, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getCallingUserId())) { return Collections.emptyList(); } - return FingerprintService.this.getEnrolledTemplates(userId); + if (userId != UserHandle.getCallingUserId()) { + Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); + } + return mFingerprint21.getEnrolledFingerprints(userId); } @Override // Binder call public boolean hasEnrolledFingerprints(int userId, String opPackageName) { - if (!canUseBiometric(opPackageName, false /* foregroundOnly */, + if (!canUseFingerprint(opPackageName, false /* foregroundOnly */, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getCallingUserId())) { return false; } - return FingerprintService.this.hasEnrolledBiometrics(userId); + if (userId != UserHandle.getCallingUserId()) { + Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); + } + return mFingerprint21.getEnrolledFingerprints(userId).size() > 0; } @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) { - checkPermission(USE_BIOMETRIC_INTERNAL); - return mLockoutTracker.getLockoutModeForUser(userId); + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + return mFingerprint21.getLockoutModeForUser(userId); } @Override // Binder call - public long getAuthenticatorId(int callingUserId) { - checkPermission(USE_BIOMETRIC_INTERNAL); - return FingerprintService.this.getAuthenticatorId(callingUserId); + public long getAuthenticatorId(int userId) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + return mFingerprint21.getAuthenticatorId(userId); } @Override // Binder call - public void resetLockout(int userId, byte [] hardwareAuthToken) throws RemoteException { - checkPermission(RESET_FINGERPRINT_LOCKOUT); - if (!FingerprintService.this.hasEnrolledBiometrics(userId)) { - Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId); - return; - } - - // TODO: confirm security token when we move timeout management into the HAL layer. - mHandler.post(() -> { - mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId); - }); + public void resetLockout(int userId, byte [] hardwareAuthToken) { + Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT); + mFingerprint21.scheduleResetLockout(userId, hardwareAuthToken); } @Override public boolean isClientActive() { - checkPermission(MANAGE_FINGERPRINT); - synchronized(FingerprintService.this) { - return (getCurrentClient() != null) || (getPendingClient() != null); - } + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); + return mGestureAvailabilityTracker.isAnySensorActive(); } @Override public void addClientActiveCallback(IFingerprintClientActiveCallback callback) { - checkPermission(MANAGE_FINGERPRINT); + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); mGestureAvailabilityTracker.registerCallback(callback); } @Override public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) { - checkPermission(MANAGE_FINGERPRINT); + Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); mGestureAvailabilityTracker.removeCallback(callback); } @Override // Binder call public void initializeConfiguration(int sensorId) { - checkPermission(USE_BIOMETRIC_INTERNAL); - initializeConfigurationInternal(sensorId); + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetTracker, + mGestureAvailabilityTracker); } @Override public void onFingerDown(int x, int y, float minor, float major) { - checkPermission(USE_BIOMETRIC_INTERNAL); - IBiometricsFingerprint daemon = getFingerprintDaemon(); - if (daemon == null) { - Slog.e(TAG, "onFingerDown | daemon is null"); - } else { - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( - daemon); - if (extension == null) { - Slog.v(TAG, "onFingerDown | failed to cast the HIDL to V2_3"); - } else { - try { - extension.onFingerDown(x, y, minor, major); - } catch (RemoteException e) { - Slog.e(TAG, "onFingerDown | RemoteException: ", e); - } - } - } + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + mFingerprint21.onFingerDown(x, y, minor, major); } @Override public void onFingerUp() { - checkPermission(USE_BIOMETRIC_INTERNAL); - IBiometricsFingerprint daemon = getFingerprintDaemon(); - if (daemon == null) { - Slog.e(TAG, "onFingerUp | daemon is null"); - } else { - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( - daemon); - if (extension == null) { - Slog.v(TAG, "onFingerUp | failed to cast the HIDL to V2_3"); - } else { - try { - extension.onFingerUp(); - } catch (RemoteException e) { - Slog.e(TAG, "onFingerUp | RemoteException: ", e); - } - } - } + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + mFingerprint21.onFingerUp(); } @Override public boolean isUdfps(int sensorId) { - checkPermission(USE_BIOMETRIC_INTERNAL); - IBiometricsFingerprint daemon = getFingerprintDaemon(); - if (daemon == null) { - Slog.e(TAG, "isUdfps | daemon is null"); - } else { - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( - daemon); - if (extension == null) { - Slog.v(TAG, "isUdfps | failed to cast the HIDL to V2_3"); - } else { - try { - return extension.isUdfps(sensorId); - } catch (RemoteException e) { - Slog.e(TAG, "isUdfps | RemoteException: ", e); - } - } - } - return false; + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + return mFingerprint21.isUdfps(); } @Override public void showUdfpsOverlay() { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); if (mUdfpsOverlayController == null) { Slog.e(TAG, "showUdfpsOverlay | mUdfpsOverlayController is null"); return; @@ -454,6 +334,7 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr @Override public void hideUdfpsOverlay() { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); if (mUdfpsOverlayController == null) { Slog.e(TAG, "hideUdfpsOverlay | mUdfpsOverlayController is null"); return; @@ -465,209 +346,55 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr } } + @Override public void setUdfpsOverlayController(IUdfpsOverlayController controller) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); mUdfpsOverlayController = controller; } } - private final LockoutFrameworkImpl mLockoutTracker; - private IUdfpsOverlayController mUdfpsOverlayController; - - @GuardedBy("this") - private IBiometricsFingerprint mDaemon; - - /** - * Receives callbacks from the HAL. - */ - private IBiometricsFingerprintClientCallback mDaemonCallback = - new IBiometricsFingerprintClientCallback.Stub() { - @Override - public void onEnrollResult(final long deviceId, final int fingerId, final int groupId, - final int remaining) { - mHandler.post(() -> { - final Fingerprint fingerprint = - new Fingerprint(getBiometricUtils().getUniqueName(getContext(), groupId), - groupId, fingerId, deviceId); - FingerprintService.super.handleEnrollResult(fingerprint, remaining); - }); - } - - @Override - public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) { - onAcquired_2_2(deviceId, acquiredInfo, vendorCode); - } - - @Override - public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) { - mHandler.post(() -> { - FingerprintService.super.handleAcquired(acquiredInfo, vendorCode); - }); - } - - @Override - public void onAuthenticated(final long deviceId, final int fingerId, final int groupId, - ArrayList<Byte> token) { - mHandler.post(() -> { - Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); - FingerprintService.super.handleAuthenticated(fp, token); - }); - } - - @Override - public void onError(final long deviceId, final int error, final int vendorCode) { - mHandler.post(() -> { - FingerprintService.super.handleError(error, vendorCode); - // TODO: this chunk of code should be common to all biometric services - if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { - // If we get HW_UNAVAILABLE, try to connect again later... - Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client."); - synchronized (this) { - mDaemon = null; - mCurrentUserId = UserHandle.USER_NULL; - } - } - }); - } - - @Override - public void onRemoved(final long deviceId, final int fingerId, final int groupId, - final int remaining) { - mHandler.post(() -> { - final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); - FingerprintService.super.handleRemoved(fp, remaining); - }); - } - - @Override - public void onEnumerate(final long deviceId, final int fingerId, final int groupId, - final int remaining) { - mHandler.post(() -> { - final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); - FingerprintService.super.handleEnumerate(fp, remaining); - }); - - } - }; - public FingerprintService(Context context) { super(context); - mLazyDaemon = FingerprintService.this::getFingerprintDaemon; + mAppOps = context.getSystemService(AppOpsManager.class); mGestureAvailabilityTracker = new GestureAvailabilityTracker(); mLockoutResetTracker = new LockoutResetTracker(context); - final LockoutFrameworkImpl.LockoutResetCallback lockoutResetCallback = userId -> { - mLockoutResetTracker.notifyLockoutResetCallbacks(); - }; - mLockoutTracker = new LockoutFrameworkImpl(context, lockoutResetCallback); } @Override public void onStart() { - super.onStart(); publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper()); - SystemServerInitThreadPool.submit(this::getFingerprintDaemon, TAG + ".onStart"); - } - - @Override - protected String getTag() { - return TAG; } - @Override - protected IBiometricsFingerprint getDaemon() { - return getFingerprintDaemon(); - } - - @Override - protected BiometricUtils getBiometricUtils() { - return FingerprintUtils.getInstance(); - } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid, + int pid, int userId) { + if (getContext().checkCallingPermission(USE_FINGERPRINT) + != PackageManager.PERMISSION_GRANTED) { + Utils.checkPermission(getContext(), USE_BIOMETRIC); + } - @Override - protected boolean hasReachedEnrollmentLimit(int userId) { - final int limit = getContext().getResources().getInteger( - com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser); - final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size(); - if (enrolled >= limit) { - Slog.w(TAG, "Too many fingerprints registered"); + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + return true; // System process (BiometricService, etc) is always allowed + } + if (Utils.isKeyguard(getContext(), opPackageName)) { return true; } - return false; - } - - @Override - public void serviceDied(long cookie) { - super.serviceDied(cookie); - mDaemon = null; - } - - @Override - protected void updateActiveGroup(int userId) { - IBiometricsFingerprint daemon = getFingerprintDaemon(); - - if (daemon != null) { - try { - if (userId != mCurrentUserId) { - int firstSdkInt = Build.VERSION.FIRST_SDK_INT; - if (firstSdkInt < Build.VERSION_CODES.BASE) { - Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " + - "at least VERSION_CODES.BASE"); - } - File baseDir; - if (firstSdkInt <= Build.VERSION_CODES.O_MR1) { - baseDir = Environment.getUserSystemDirectory(userId); - } else { - baseDir = Environment.getDataVendorDeDirectory(userId); - } - - File fpDir = new File(baseDir, FP_DATA_DIR); - if (!fpDir.exists()) { - if (!fpDir.mkdir()) { - Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath()); - return; - } - // Calling mkdir() from this process will create a directory with our - // permissions (inherited from the containing dir). This command fixes - // the label. - if (!SELinux.restorecon(fpDir)) { - Slog.w(TAG, "Restorecons failed. Directory will have wrong label."); - return; - } - } - - daemon.setActiveGroup(userId, fpDir.getAbsolutePath()); - mCurrentUserId = userId; - } - mAuthenticatorIds.put(userId, - hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId() : 0L); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to setActiveGroup():", e); - } + if (!Utils.isCurrentUserOrProfile(getContext(), userId)) { + Slog.w(TAG, "Rejecting " + opPackageName + "; not a current user or profile"); + return false; } - } - - @Override - protected boolean hasEnrolledBiometrics(int userId) { - if (userId != UserHandle.getCallingUserId()) { - checkPermission(INTERACT_ACROSS_USERS); + if (!checkAppOps(uid, opPackageName)) { + Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied"); + return false; } - return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0; - } - - @Override - protected String getManageBiometricPermission() { - return MANAGE_FINGERPRINT; - } - - @Override - protected void checkUseBiometricPermission() { - if (getContext().checkCallingPermission(USE_FINGERPRINT) - != PackageManager.PERMISSION_GRANTED) { - checkPermission(USE_BIOMETRIC); + if (requireForeground && !(isForegroundActivity(uid, pid))) { + Slog.w(TAG, "Rejecting " + opPackageName + "; not in foreground"); + return false; } + return true; } - @Override - protected boolean checkAppOps(int uid, String opPackageName) { + private boolean checkAppOps(int uid, String opPackageName) { boolean appOpsOk = false; if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName) == AppOpsManager.MODE_ALLOWED) { @@ -679,154 +406,28 @@ public class FingerprintService extends BiometricServiceBase<IBiometricsFingerpr return appOpsOk; } - @Override - protected List<Fingerprint> getEnrolledTemplates(int userId) { - if (userId != UserHandle.getCallingUserId()) { - checkPermission(INTERACT_ACROSS_USERS); - } - return FingerprintUtils.getInstance().getBiometricsForUser(getContext(), userId); - } - - @Override - protected void notifyClientActiveCallbacks(boolean isActive) { - mGestureAvailabilityTracker.notifyClientActiveCallbacks(isActive); - } - - @Override - protected int statsModality() { - return BiometricsProtoEnums.MODALITY_FINGERPRINT; - } - - @Override - protected @LockoutTracker.LockoutMode int getLockoutMode(int userId) { - return mLockoutTracker.getLockoutModeForUser(userId); - } - - @Override - protected void doTemplateCleanupForUser(int userId) { - final List<Fingerprint> enrolledList = getEnrolledTemplates(userId); - final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient( - getContext(), mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), - enrolledList, getBiometricUtils(), mAuthenticatorIds); - cleanupInternal(client); - } - - /** Gets the fingerprint daemon */ - private synchronized IBiometricsFingerprint getFingerprintDaemon() { - if (mDaemon == null) { - Slog.v(TAG, "mDaemon was null, reconnect to fingerprint"); - try { - mDaemon = IBiometricsFingerprint.getService(); - } catch (java.util.NoSuchElementException e) { - // Service doesn't exist or cannot be opened. Logged below. - } catch (RemoteException e) { - Slog.e(TAG, "Failed to get biometric interface", e); - } - if (mDaemon == null) { - Slog.w(TAG, "fingerprint HIDL not available"); - return null; - } - - mDaemon.asBinder().linkToDeath(this, 0); - - long halId = 0; - try { - halId = mDaemon.setNotify(mDaemonCallback); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to open fingerprint HAL", e); - mDaemon = null; // try again later! + private boolean isForegroundActivity(int uid, int pid) { + try { + final List<ActivityManager.RunningAppProcessInfo> procs = + ActivityManager.getService().getRunningAppProcesses(); + if (procs == null) { + Slog.e(TAG, "Processes null, defaulting to true"); + return true; } - if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + halId); - if (halId != 0) { - loadAuthenticatorIds(); - final int userId = ActivityManager.getCurrentUser(); - updateActiveGroup(userId); - doTemplateCleanupForUser(userId); - } else { - Slog.w(TAG, "Failed to open Fingerprint HAL!"); - MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1); - mDaemon = null; + int N = procs.size(); + for (int i = 0; i < N; i++) { + ActivityManager.RunningAppProcessInfo proc = procs.get(i); + if (proc.pid == pid && proc.uid == uid + && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) { + return true; + } } + } catch (RemoteException e) { + Slog.w(TAG, "am.getRunningAppProcesses() failed"); } - return mDaemon; + return false; } private native NativeHandle convertSurfaceToNativeHandle(Surface surface); - - private void dumpInternal(PrintWriter pw) { - PerformanceTracker performanceTracker = - PerformanceTracker.getInstanceForSensorId(getSensorId()); - - JSONObject dump = new JSONObject(); - try { - dump.put("service", "Fingerprint Manager"); - - JSONArray sets = new JSONArray(); - for (UserInfo user : UserManager.get(getContext()).getUsers()) { - final int userId = user.getUserHandle().getIdentifier(); - final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size(); - JSONObject set = new JSONObject(); - set.put("id", userId); - set.put("count", N); - set.put("accept", performanceTracker.getAcceptForUser(userId)); - set.put("reject", performanceTracker.getRejectForUser(userId)); - set.put("acquire", performanceTracker.getAcquireForUser(userId)); - set.put("lockout", performanceTracker.getTimedLockoutForUser(userId)); - set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId)); - // cryptoStats measures statistics about secure fingerprint transactions - // (e.g. to unlock password storage, make secure purchases, etc.) - set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId)); - set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId)); - set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId)); - sets.put(set); - } - - dump.put("prints", sets); - } catch (JSONException e) { - Slog.e(TAG, "dump formatting failure", e); - } - pw.println(dump); - pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); - } - - private void dumpProto(FileDescriptor fd) { - PerformanceTracker tracker = - PerformanceTracker.getInstanceForSensorId(getSensorId()); - - final ProtoOutputStream proto = new ProtoOutputStream(fd); - for (UserInfo user : UserManager.get(getContext()).getUsers()) { - final int userId = user.getUserHandle().getIdentifier(); - - final long userToken = proto.start(FingerprintServiceDumpProto.USERS); - - proto.write(FingerprintUserStatsProto.USER_ID, userId); - proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS, - getBiometricUtils().getBiometricsForUser(getContext(), userId).size()); - - // Normal fingerprint authentications (e.g. lockscreen) - long countsToken = proto.start(FingerprintUserStatsProto.NORMAL); - proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId)); - proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId)); - proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId)); - proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId)); - proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, - tracker.getPermanentLockoutForUser(userId)); - proto.end(countsToken); - - // Statistics about secure fingerprint transactions (e.g. to unlock password - // storage, make secure purchases, etc.) - countsToken = proto.start(FingerprintUserStatsProto.CRYPTO); - proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId)); - proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId)); - proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId)); - proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto - proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto - proto.end(countsToken); - - proto.end(userToken); - } - proto.flush(); - tracker.clear(); - } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java new file mode 100644 index 000000000000..e1082ae51575 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java @@ -0,0 +1,125 @@ +/* + * 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.fingerprint; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.os.Build; +import android.os.Environment; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SELinux; +import android.util.Slog; + +import com.android.server.biometrics.sensors.ClientMonitor; + +import java.io.File; +import java.util.Map; + +/** + * Sets the HAL's current active user, and updates the framework's authenticatorId cache. + */ +public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometricsFingerprint> { + + private static final String TAG = "FingerprintUpdateActiveUserClient"; + private static final String FP_DATA_DIR = "fpdata"; + + private final int mCurrentUserId; + private final boolean mHasEnrolledBiometrics; + private final Map<Integer, Long> mAuthenticatorIds; + private File mDirectory; + + FingerprintUpdateActiveUserClient(@NonNull Context context, + @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId, + @NonNull String owner, int sensorId, int currentUserId, boolean hasEnrolledBiometrics, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, + 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + mCurrentUserId = currentUserId; + mHasEnrolledBiometrics = hasEnrolledBiometrics; + mAuthenticatorIds = authenticatorIds; + } + + @Override + public void start(@NonNull FinishCallback finishCallback) { + super.start(finishCallback); + + if (mCurrentUserId == getTargetUserId()) { + Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId"); + try { + mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics + ? getFreshDaemon().getAuthenticatorId() : 0L); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to refresh authenticatorId", e); + } + finishCallback.onClientFinished(this, true /* success */); + return; + } + + int firstSdkInt = Build.VERSION.FIRST_SDK_INT; + if (firstSdkInt < Build.VERSION_CODES.BASE) { + Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " + + "at least VERSION_CODES.BASE"); + } + File baseDir; + if (firstSdkInt <= Build.VERSION_CODES.O_MR1) { + baseDir = Environment.getUserSystemDirectory(getTargetUserId()); + } else { + baseDir = Environment.getDataVendorDeDirectory(getTargetUserId()); + } + + mDirectory = new File(baseDir, FP_DATA_DIR); + if (!mDirectory.exists()) { + if (!mDirectory.mkdir()) { + Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath()); + finishCallback.onClientFinished(this, false /* success */); + return; + } + // Calling mkdir() from this process will create a directory with our + // permissions (inherited from the containing dir). This command fixes + // the label. + if (!SELinux.restorecon(mDirectory)) { + Slog.e(TAG, "Restorecons failed. Directory will have wrong label."); + finishCallback.onClientFinished(this, false /* success */); + return; + } + } + + startHalOperation(); + } + + @Override + public void unableToStart() { + // Nothing to do here + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath()); + mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics + ? getFreshDaemon().getAuthenticatorId() : 0L); + mFinishCallback.onClientFinished(this, true /* success */); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to setActiveGroup: " + e); + mFinishCallback.onClientFinished(this, false /* success */); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java index 6292ecf0e207..5023c83cd585 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java @@ -18,16 +18,54 @@ package com.android.server.biometrics.sensors.fingerprint; import android.hardware.fingerprint.IFingerprintClientActiveCallback; import android.os.RemoteException; +import android.util.Slog; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; /** * Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its * availability changes */ -class GestureAvailabilityTracker { - private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks = - new CopyOnWriteArrayList<>(); +public class GestureAvailabilityTracker { + private static final String TAG = "GestureAvailabilityTracker"; + + private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks; + private final Map<Integer, Boolean> mActiveSensors; + + private boolean mIsActive; + + GestureAvailabilityTracker() { + mClientActiveCallbacks = new CopyOnWriteArrayList<>(); + mActiveSensors = new HashMap<>(); + } + + /** + * @return true if any sensor is active. + */ + public boolean isAnySensorActive() { + return mIsActive; + } + + public void markSensorActive(int sensorId, boolean active) { + mActiveSensors.put(sensorId, active); + + final boolean wasActive = mIsActive; + boolean isActive = false; + for (Boolean b : mActiveSensors.values()) { + if (b) { + isActive = true; + break; + } + } + + if (wasActive != isActive) { + Slog.d(TAG, "Notifying gesture availability, active=" + mIsActive); + mIsActive = isActive; + notifyClientActiveCallbacks(mIsActive); + } + } void registerCallback(IFingerprintClientActiveCallback callback) { mClientActiveCallbacks.add(callback); @@ -37,7 +75,7 @@ class GestureAvailabilityTracker { mClientActiveCallbacks.remove(callback); } - void notifyClientActiveCallbacks(boolean isActive) { + private void notifyClientActiveCallbacks(boolean isActive) { for (IFingerprintClientActiveCallback callback : mClientActiveCallbacks) { try { callback.onClientActiveChanged(isActive); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java new file mode 100644 index 000000000000..e0806ff0fdb0 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java @@ -0,0 +1,27 @@ +/* + * 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.fingerprint; + +/** + * Interface for under-display fingerprint sensors. + * {@link com.android.server.biometrics.sensors.ClientMonitor} subclass that require knowledge of + * finger position (e.g. enroll, authenticate) should implement this. + */ +public interface Udfps { + void onFingerDown(int x, int y, float minor, float major); + void onFingerUp(); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java new file mode 100644 index 000000000000..e6e15337d062 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java @@ -0,0 +1,62 @@ +/* + * 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.fingerprint; + +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Contains helper methods for under-display fingerprint HIDL. + */ +public class UdfpsHelper { + + private static final String TAG = "UdfpsHelper"; + + static void onFingerDown(IBiometricsFingerprint daemon, int x, int y, float minor, + float major) { + android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = + android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( + daemon); + if (extension == null) { + Slog.v(TAG, "onFingerDown | failed to cast the HIDL to V2_3"); + return; + } + + try { + extension.onFingerDown(x, y, minor, major); + } catch (RemoteException e) { + Slog.e(TAG, "onFingerDown | RemoteException: ", e); + } + } + + static void onFingerUp(IBiometricsFingerprint daemon) { + android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = + android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( + daemon); + if (extension == null) { + Slog.v(TAG, "onFingerUp | failed to cast the HIDL to V2_3"); + return; + } + + try { + extension.onFingerUp(); + } catch (RemoteException e) { + Slog.e(TAG, "onFingerUp | RemoteException: ", e); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java index 023ed460429c..58ab20d8d56d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java +++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java @@ -115,16 +115,6 @@ public class IrisService extends BiometricServiceBase { } @Override - protected void checkUseBiometricPermission() { - - } - - @Override - protected boolean checkAppOps(int uid, String opPackageName) { - return false; - } - - @Override protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) { return null; } @@ -133,9 +123,4 @@ public class IrisService extends BiometricServiceBase { protected int statsModality() { return BiometricsProtoEnums.MODALITY_IRIS; } - - @Override - protected int getLockoutMode(int userId) { - return LockoutTracker.LOCKOUT_NONE; - } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java index d4b299db8c4d..0c66ee75eab0 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java @@ -80,15 +80,6 @@ public class BiometricServiceBaseTest { } @Override - protected void checkUseBiometricPermission() { - } - - @Override - protected boolean checkAppOps(int uid, String opPackageName) { - return false; - } - - @Override protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates( int userId) { return null; @@ -98,11 +89,6 @@ public class BiometricServiceBaseTest { protected int statsModality() { return 0; } - - @Override - protected int getLockoutMode(int userId) { - return 0; - } } private static final int CLIENT_COOKIE = 0xc00c1e; |