31/n: Move Fingerprint2.1 into a wrapper with its own scheduler
This is a pretty big change since it basically allows/requires us to
have FingerprintService not extend from BiometricServiceBase anymore
Bug: 157790417
Bug: 158481661
1) Move Fingerprint2.1-specific code into wrapper class. This will
A) Make it obvious where global dependencies still exist, and
B) Make room for new HIDLs
2) Sensor-ID is creeping in. Eventually there will be Manager-level
APIs for platform code to query functionality. Most likely all
IFingerprintService/IFaceService interfaces will have sensorId
as a parameter soon
3) Added Udfps interface and UdfpsHelper
4) FingerprintService no longer extends from BiometricServiceBase.
BiometricServiceBase will be removed soon. A lot of permission
checking is fingerprint-specific, since face operations all
require internal permission. Thus, moved canUseBiometric into
FingerprintService
5) Updated fingerprint retry logic in KeyguardUpdateMonitor. The
retry should keep going if isHardwareDetected()==false. Also
increased the retry counter, seems like HAL reload time has
increased.
Test: enroll, auth (settings), auth (keyguard), auth (BiometricPrompt),
rename, remove
Test: atest com.android.server.biometrics
Test: atest KeyguardUpdateMonitorTest
Test: Reset lockout
Test: atest AccessibilityFingerprintGestureTest
Test: Fingerprint stack recovers after HAL death, e.g.
1) Go to keyguard
2) adb shell ps | grep -i fingerprint
3) adb shell killall -9 android.hardware.biometrics...
4) fingerprint auth resumes after HAL reloads
Test: CtsVerifier
Test: adb shell dumpsys fingerprint
Test: Modify settings to not cancel auth when onPause, notice
task stack listener stopping settings auth
Test: Invoke assistant on keyguard, auth continues
Test: No effect on face auth devices (enroll, auth on keyguard,
BiometricPrompt)
Change-Id: Iffbdd841c093fe612e664195dcf79319d1d444ab
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
index aa5ac03..754162c 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
@@ -26,5 +26,5 @@
/**
* 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 c7fc2ad..b4546a4 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -592,7 +592,7 @@
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 @@
wakeLock.acquire();
mHandler.post(() -> {
try {
- callback.onLockoutReset();
+ callback.onLockoutReset(sensorId);
} finally {
wakeLock.release();
}
@@ -978,7 +978,7 @@
* 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 9dacca7..4ca75d9 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -347,7 +347,7 @@
* 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 @@
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 @@
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 e45cfe2..e9b58c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -295,7 +295,7 @@
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 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 @@
}
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 @@
private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback
= new FingerprintManager.LockoutResetCallback() {
@Override
- public void onLockoutReset() {
+ public void onLockoutReset(int sensorId) {
handleFingerprintLockoutReset();
}
};
@@ -1242,7 +1243,7 @@
private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
= new FaceManager.LockoutResetCallback() {
@Override
- public void onLockoutReset() {
+ public void onLockoutReset(int sensorId) {
handleFaceLockoutReset();
}
};
@@ -1838,7 +1839,7 @@
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 6be4574..29b8493 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_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 @@
}
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 ac3d867..14baaa7 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 @@
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 @@
@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 df836d5..5392f0f 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 @@
+ ", 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 @@
}
}
+ @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 0000000..c9ab313
--- /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 a3901e00..9aa72a7 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.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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
*/
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 @@
+ ", 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 @@
}
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 @@
// 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 @@
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 @@
}
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 @@
*/
/**
- * @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 @@
*/
@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 @@
// <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 @@
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 @@
"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 35f7279..8cabdf5 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 @@
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 @@
* @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 @@
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 @@
@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 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 1b8392e..35d9177 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 @@
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 9bb5c6e..22c10b3 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 @@
/**
* 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 @@
"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 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 fa490ee..9fdba4b 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 @@
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 a7f8220..7a05192 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.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 @@
* 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 @@
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 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 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
@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 @@
@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 @@
@GuardedBy("this")
private IBiometricsFace mDaemon;
private UsageStats mUsageStats;
- private RevokeChallengeClient mPendingRevokeChallenge;
+ private FaceRevokeChallengeClient mPendingRevokeChallenge;
private NotificationManager mNotificationManager;
@@ -546,7 +524,7 @@
mHandler.post(() -> {
if (duration == 0) {
- mLockoutResetTracker.notifyLockoutResetCallbacks();
+ mLockoutResetTracker.notifyLockoutResetCallbacks(getSensorId());
}
});
}
@@ -562,7 +540,7 @@
}
@Override
- protected void removeClient(ClientMonitor client) {
+ protected void removeClient(ClientMonitor<?> client) {
super.removeClient(client);
if (mPendingRevokeChallenge != null) {
revokeChallengeInternal(mPendingRevokeChallenge);
@@ -670,17 +648,6 @@
}
@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 @@
}
@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 0000000..5d48e56
--- /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 b01b856..9cc0b0d 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 @@
* {@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 @@
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 @@
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 1d56882..5282bdf 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 @@
* {@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 @@
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 1d72c2e..571d2b8 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 @@
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 b05400a..11e6bf2 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.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 @@
* 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 @@
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 @@
final long token = Binder.clearCallingIdentity();
try {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- return daemon != null;
+ return mFingerprint21.isHardwareDetected();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -285,162 +221,106 @@
@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 @@
@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 @@
}
}
+ @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();
- }
-
- @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");
- 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);
- }
- }
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
- }
- return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return MANAGE_FINGERPRINT;
- }
-
- @Override
- protected void checkUseBiometricPermission() {
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
+ int pid, int userId) {
if (getContext().checkCallingPermission(USE_FINGERPRINT)
!= PackageManager.PERMISSION_GRANTED) {
- checkPermission(USE_BIOMETRIC);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC);
}
+
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ return true; // System process (BiometricService, etc) is always allowed
+ }
+ if (Utils.isKeyguard(getContext(), opPackageName)) {
+ return true;
+ }
+ if (!Utils.isCurrentUserOrProfile(getContext(), userId)) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; not a current user or profile");
+ return false;
+ }
+ if (!checkAppOps(uid, opPackageName)) {
+ Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
+ return false;
+ }
+ 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 @@
return appOpsOk;
}
- @Override
- protected List<Fingerprint> getEnrolledTemplates(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
+ 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;
+ }
+
+ 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 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!
- }
-
- 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;
- }
- }
- 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 0000000..e1082ae
--- /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 6292ecf..5023c83 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 @@
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 @@
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 0000000..e0806ff
--- /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 0000000..e6e1533
--- /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 023ed46..58ab20d 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 @@
}
@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 @@
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 d4b299d..0c66ee7 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 @@
}
@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 @@
protected int statsModality() {
return 0;
}
-
- @Override
- protected int getLockoutMode(int userId) {
- return 0;
- }
}
private static final int CLIENT_COOKIE = 0xc00c1e;