Merge "3/n: Add most remaining plumbing for authenticatorId invalidation"

This commit is contained in:
Kevin Chyn
2021-01-12 22:01:01 +00:00
committed by Android (Google) Code Review
18 changed files with 364 additions and 8 deletions

View File

@@ -357,6 +357,27 @@ public class BiometricManager {
}
}
/**
* Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their
* authenticatorId invalidated for the specified user. This happens when enrollments have been
* added on devices with multiple biometric sensors.
*
* @param userId userId that the authenticatorId should be invalidated for
* @param fromSensorId sensor that triggered the invalidation request
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
@NonNull IInvalidationCallback callback) {
if (mService != null) {
try {
mService.invalidateAuthenticatorIds(userId, fromSensorId, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Get a list of AuthenticatorIDs for biometric authenticators which have 1) enrolled templates,
* and 2) meet the requirements for integrating with Keystore. The AuthenticatorIDs are known

View File

@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -57,6 +58,11 @@ interface IAuthService {
// Register callback for when keyguard biometric eligibility changes.
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
// Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
// specified user. This happens when enrollments have been added on devices with multiple
// biometric sensors.
void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback);
// Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet
// the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
// land as SIDs, and are used during key generation.

View File

@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
@@ -63,6 +64,9 @@ interface IBiometricAuthenticator {
// Return the LockoutTracker status for the specified user
int getLockoutModeForUser(int userId);
// Request the authenticatorId to be invalidated for the specified user
void invalidateAuthenticatorId(int userId, IInvalidationCallback callback);
// Gets the authenticator ID representing the current set of enrolled templates
long getAuthenticatorId(int callingUserId);
}

View File

@@ -19,6 +19,7 @@ package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -62,6 +63,11 @@ interface IBiometricService {
// Client lifecycle is still managed in <Biometric>Service.
void onReadyForAuthentication(int cookie);
// Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
// specified user. This happens when enrollments have been added on devices with multiple
// biometric sensors.
void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback);
// Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet
// the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
// land as SIDs, and are used during key generation.

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2021 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 android.hardware.biometrics;
/**
* Notifies the caller for BiometricManager#invalidateAuthenticatorIds status updates. See
* InvalidationRequesterClient for more info.
* @hide
*/
interface IInvalidationCallback {
// Notifies the caller when all authenticatorId(s) have been invalidated.
void onCompleted();
}

View File

@@ -17,6 +17,7 @@ package android.hardware.face;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -104,6 +105,9 @@ interface IFaceService {
// Return the LockoutTracker status for the specified user
int getLockoutModeForUser(int sensorId, int userId);
// Requests for the specified sensor+userId's authenticatorId to be invalidated
void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback);
// Gets the authenticator ID for face
long getAuthenticatorId(int sensorId, int callingUserId);

View File

@@ -17,6 +17,7 @@ package android.hardware.fingerprint;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -116,6 +117,9 @@ interface IFingerprintService {
// Return the LockoutTracker status for the specified user
int getLockoutModeForUser(int sensorId, int userId);
// Requests for the specified sensor+userId's authenticatorId to be invalidated
void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback);
// Gets the authenticator ID for fingerprint
long getAuthenticatorId(int sensorId, int callingUserId);

View File

@@ -38,6 +38,7 @@ import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -295,6 +296,19 @@ public class AuthService extends SystemService {
}
}
@Override
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
IInvalidationCallback callback) throws RemoteException {
checkInternalPermission();
final long identity = Binder.clearCallingIdentity();
try {
mBiometricService.invalidateAuthenticatorIds(userId, fromSensorId, callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public long[] getAuthenticatorIds() throws RemoteException {
// In this method, we're not checking whether the caller is permitted to use face

View File

@@ -42,6 +42,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -60,6 +61,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -78,6 +80,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
@@ -240,6 +243,72 @@ public class BiometricService extends SystemService {
}
};
/**
* Tracks authenticatorId invalidation. For more details, see
* {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}.
*/
@VisibleForTesting
static class InvalidationTracker {
@NonNull private final IInvalidationCallback mClientCallback;
@NonNull private final Set<Integer> mSensorsPendingInvalidation;
public static InvalidationTracker start(@NonNull ArrayList<BiometricSensor> sensors,
int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
return new InvalidationTracker(sensors, userId, fromSensorId, clientCallback);
}
private InvalidationTracker(@NonNull ArrayList<BiometricSensor> sensors, int userId,
int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
mClientCallback = clientCallback;
mSensorsPendingInvalidation = new ArraySet<>();
for (BiometricSensor sensor : sensors) {
if (sensor.id == fromSensorId) {
continue;
}
if (!Utils.isAtLeastStrength(sensor.oemStrength, Authenticators.BIOMETRIC_STRONG)) {
continue;
}
Slog.d(TAG, "Requesting authenticatorId invalidation for sensor: " + sensor.id);
synchronized (this) {
mSensorsPendingInvalidation.add(sensor.id);
}
try {
sensor.impl.invalidateAuthenticatorId(userId, new IInvalidationCallback.Stub() {
@Override
public void onCompleted() {
onInvalidated(sensor.id);
}
});
} catch (RemoteException e) {
Slog.d(TAG, "RemoteException", e);
}
}
}
@VisibleForTesting
void onInvalidated(int sensorId) {
synchronized (this) {
mSensorsPendingInvalidation.remove(sensorId);
Slog.d(TAG, "Sensor " + sensorId + " invalidated, remaining size: "
+ mSensorsPendingInvalidation.size());
if (mSensorsPendingInvalidation.isEmpty()) {
try {
mClientCallback.onCompleted();
} catch (RemoteException e) {
Slog.e(TAG, "Remote Exception", e);
}
}
}
}
}
@VisibleForTesting
public static class SettingObserver extends ContentObserver {
@@ -668,6 +737,14 @@ public class BiometricService extends SystemService {
}
}
@Override
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
IInvalidationCallback callback) {
checkInternalPermission();
InvalidationTracker.start(mSensors, userId, fromSensorId, callback);
}
@Override // Binder call
public long[] getAuthenticatorIds(int callingUserId) {
checkInternalPermission();

View File

@@ -18,8 +18,10 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
/**
* ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other
@@ -53,25 +55,39 @@ import android.hardware.biometrics.BiometricsProtoEnums;
* switches, the framework can check if any sensor has the "invalidationInProgress" flag set. If so,
* the framework should re-start the invalidation process described above.
*/
public abstract class InvalidationRequesterClient<T> extends ClientMonitor<T> {
public abstract class InvalidationRequesterClient<S extends BiometricAuthenticator.Identifier, T>
extends ClientMonitor<T> {
private final BiometricManager mBiometricManager;
@NonNull private final BiometricUtils<S> mUtils;
@NonNull private final IInvalidationCallback mInvalidationCallback =
new IInvalidationCallback.Stub() {
@Override
public void onCompleted() {
mUtils.setInvalidationInProgress(getContext(), getTargetUserId(),
false /* inProgress */);
mCallback.onClientFinished(InvalidationRequesterClient.this, true /* success */);
}
};
public InvalidationRequesterClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
int userId, int sensorId) {
int userId, int sensorId, @NonNull BiometricUtils<S> utils) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId,
context.getOpPackageName(), 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricManager = context.getSystemService(BiometricManager.class);
mUtils = utils;
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);
// TODO(b/159667191): Request BiometricManager/BiometricService to invalidate
// authenticatorIds. Be sure to invoke BiometricUtils#setInvalidationInProgress(true)
mUtils.setInvalidationInProgress(getContext(), getTargetUserId(), true /* inProgress */);
mBiometricManager.invalidateAuthenticatorIds(getTargetUserId(), getSensorId(),
mInvalidationCallback);
}
@Override

View File

@@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.face;
import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceService;
import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.biometrics.SensorConfig;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
@@ -86,6 +86,12 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
return mFaceService.hasEnrolledFaces(mSensorId, userId, opPackageName);
}
@Override
public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback)
throws RemoteException {
mFaceService.invalidateAuthenticatorId(mSensorId, userId, callback);
}
@Override
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
throws RemoteException {

View File

@@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
@@ -54,10 +55,10 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BiometricServiceCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.BiometricServiceCallback;
import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
import com.android.server.biometrics.sensors.face.hidl.Face10;
@@ -503,6 +504,19 @@ public class FaceService extends SystemService implements BiometricServiceCallba
return provider.getLockoutModeForUser(sensorId, userId);
}
@Override
public void invalidateAuthenticatorId(int sensorId, int userId,
IInvalidationCallback callback) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
return;
}
provider.scheduleInvalidateAuthenticatorId(sensorId, userId, callback);
}
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);

View File

@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.face;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
@@ -71,6 +72,16 @@ public interface ServiceProvider {
@LockoutTracker.LockoutMode
int getLockoutModeForUser(int sensorId, int userId);
/**
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
* be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
*/
default void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) {
throw new IllegalStateException("Providers that support invalidation must override"
+ " this method");
}
long getAuthenticatorId(int sensorId, int userId);
boolean isHardwareDetected(int sensorId);

View File

@@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint;
import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.biometrics.SensorConfig;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
@@ -93,6 +93,12 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
return mFingerprintService.getLockoutModeForUser(mSensorId, userId);
}
@Override
public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback)
throws RemoteException {
mFingerprintService.invalidateAuthenticatorId(mSensorId, userId, callback);
}
@Override
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFingerprintService.getAuthenticatorId(mSensorId, callingUserId);

View File

@@ -36,6 +36,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
@@ -571,6 +572,19 @@ public class FingerprintService extends SystemService implements BiometricServic
return provider.getLockoutModeForUser(sensorId, userId);
}
@Override
public void invalidateAuthenticatorId(int sensorId, int userId,
IInvalidationCallback callback) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
return;
}
provider.scheduleInvalidateAuthenticatorId(sensorId, userId, callback);
}
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);

View File

@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -109,6 +110,16 @@ public interface ServiceProvider {
@LockoutTracker.LockoutMode
int getLockoutModeForUser(int sensorId, int userId);
/**
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
* be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
*/
default void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) {
throw new IllegalStateException("Providers that support invalidation must override"
+ " this method");
}
long getAuthenticatorId(int sensorId, int userId);
void onPointerDown(int sensorId, int x, int y, float minor, float major);

View File

@@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.iris;
import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.iris.IIrisService;
import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.biometrics.SensorConfig;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
@@ -85,6 +85,10 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
return LockoutTracker.LOCKOUT_NONE;
}
@Override
public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback) {
}
@Override
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return 0;

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IInvalidationCallback;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.BiometricService.InvalidationTracker;
import org.junit.Test;
import java.util.ArrayList;
@Presubmit
@SmallTest
public class InvalidationTrackerTest {
@Test
public void testCallbackReceived_whenAllStrongSensorsInvalidated() throws Exception {
final IBiometricAuthenticator authenticator1 = mock(IBiometricAuthenticator.class);
final TestSensor sensor1 = new TestSensor(0 /* id */,
BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
authenticator1);
final IBiometricAuthenticator authenticator2 = mock(IBiometricAuthenticator.class);
final TestSensor sensor2 = new TestSensor(1 /* id */,
BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
authenticator2);
final IBiometricAuthenticator authenticator3 = mock(IBiometricAuthenticator.class);
final TestSensor sensor3 = new TestSensor(2 /* id */,
BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG,
authenticator3);
final IBiometricAuthenticator authenticator4 = mock(IBiometricAuthenticator.class);
final TestSensor sensor4 = new TestSensor(3 /* id */,
BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK,
authenticator4);
final ArrayList<BiometricSensor> sensors = new ArrayList<>();
sensors.add(sensor1);
sensors.add(sensor2);
sensors.add(sensor3);
sensors.add(sensor4);
final IInvalidationCallback callback = mock(IInvalidationCallback.class);
final InvalidationTracker tracker =
InvalidationTracker.start(sensors, 0 /* userId */, 0 /* fromSensorId */, callback);
// The sensor which the request originated from should not be requested to invalidate
// its authenticatorId.
verify(authenticator1, never()).invalidateAuthenticatorId(anyInt(), any());
// All other strong sensors should be requested to invalidate authenticatorId
verify(authenticator2).invalidateAuthenticatorId(eq(0) /* userId */, any());
verify(authenticator3).invalidateAuthenticatorId(eq(0) /* userId */, any());
// Weak sensors are not requested to invalidate authenticatorId
verify(authenticator4, never()).invalidateAuthenticatorId(anyInt(), any());
// Client is not notified until invalidation for all required sensors have completed
verify(callback, never()).onCompleted();
tracker.onInvalidated(1);
verify(callback, never()).onCompleted();
tracker.onInvalidated(2);
verify(callback).onCompleted();
}
private static class TestSensor extends BiometricSensor {
TestSensor(int id, int modality, int strength, IBiometricAuthenticator impl) {
super(id, modality, strength, impl);
}
@Override
boolean confirmationAlwaysRequired(int userId) {
return false;
}
@Override
boolean confirmationSupported() {
return false;
}
}
}