Merge "3/n: Move generateChallenge/resetLockout/revokeChallenge off critical path"
This commit is contained in:
@@ -299,26 +299,6 @@ public class BiometricManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
*
|
||||
* @param userId this operation takes effect for.
|
||||
* @param hardwareAuthToken an opaque token returned by password confirmation.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
|
||||
public void resetLockout(int userId, byte[] hardwareAuthToken) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.resetLockout(userId, hardwareAuthToken);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "resetLockout(): Service not connected");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -46,9 +46,6 @@ interface IAuthService {
|
||||
// Register callback for when keyguard biometric eligibility changes.
|
||||
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
|
||||
|
||||
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
void resetLockout(int userId, in byte [] hardwareAuthToken);
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -53,9 +53,6 @@ interface IBiometricAuthenticator {
|
||||
// Return the LockoutTracker status for the specified user
|
||||
int getLockoutModeForUser(int userId);
|
||||
|
||||
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
void resetLockout(int userId, in byte [] hardwareAuthToken);
|
||||
|
||||
// Gets the authenticator ID representing the current set of enrolled templates
|
||||
long getAuthenticatorId(int callingUserId);
|
||||
}
|
||||
|
||||
@@ -56,9 +56,6 @@ interface IBiometricService {
|
||||
// Client lifecycle is still managed in <Biometric>Service.
|
||||
void onReadyForAuthentication(int cookie);
|
||||
|
||||
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
void resetLockout(int userId, in byte [] hardwareAuthToken);
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -143,14 +143,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChallengeGenerated(long challenge) {
|
||||
if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) {
|
||||
// Perform this on system_server thread, since the application's thread is
|
||||
// blocked waiting for the result
|
||||
mGenerateChallengeCallback.onGenerateChallengeResult(challenge);
|
||||
} else {
|
||||
mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget();
|
||||
}
|
||||
public void onChallengeGenerated(int sensorId, long challenge) {
|
||||
mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -416,35 +411,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the
|
||||
* TEE/hardware operation is complete.
|
||||
* @return challenge generated in the TEE/hardware
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(MANAGE_BIOMETRIC)
|
||||
public long generateChallengeBlocking() {
|
||||
final AtomicReference<Long> result = new AtomicReference<>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() {
|
||||
@Override
|
||||
public void onGenerateChallengeResult(long challenge) {
|
||||
result.set(challenge);
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
|
||||
generateChallenge(callback);
|
||||
|
||||
try {
|
||||
latch.await(1, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Slog.e(TAG, "Interrupted while generatingChallenge", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a
|
||||
* HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification.
|
||||
@@ -458,11 +424,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(MANAGE_BIOMETRIC)
|
||||
public void generateChallenge(GenerateChallengeCallback callback) {
|
||||
public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mGenerateChallengeCallback = callback;
|
||||
mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName());
|
||||
mService.generateChallenge(mToken, sensorId, mServiceReceiver,
|
||||
mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -470,15 +437,66 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the current auth token.
|
||||
* Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
|
||||
* enumerated sensor.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(MANAGE_BIOMETRIC)
|
||||
public void generateChallenge(GenerateChallengeCallback callback) {
|
||||
final List<FaceSensorProperties> faceSensorProperties = getSensorProperties();
|
||||
if (faceSensorProperties.isEmpty()) {
|
||||
Slog.e(TAG, "No sensors");
|
||||
return;
|
||||
}
|
||||
|
||||
final int sensorId = faceSensorProperties.get(0).sensorId;
|
||||
generateChallenge(sensorId, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the current challenge.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(MANAGE_BIOMETRIC)
|
||||
public void revokeChallenge() {
|
||||
final List<FaceSensorProperties> faceSensorProperties = getSensorProperties();
|
||||
if (faceSensorProperties.isEmpty()) {
|
||||
Slog.e(TAG, "No sensors during revokeChallenge");
|
||||
}
|
||||
revokeChallenge(faceSensorProperties.get(0).sensorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the current challenge.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(MANAGE_BIOMETRIC)
|
||||
public void revokeChallenge(int sensorId) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.revokeChallenge(mToken, mContext.getOpPackageName());
|
||||
mService.revokeChallenge(mToken, sensorId, mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
*
|
||||
* @param sensorId Sensor ID that this operation takes effect for
|
||||
* @param userId User ID that this operation takes effect for.
|
||||
* @param hardwareAuthToken An opaque token returned by password confirmation.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
|
||||
public void resetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.resetLockout(mToken, sensorId, userId, hardwareAuthToken,
|
||||
mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -1083,18 +1101,18 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback structure provided to {@link #generateChallenge(GenerateChallengeCallback)}.
|
||||
* Callback structure provided to {@link #generateChallenge(int, GenerateChallengeCallback)}.
|
||||
* @hide
|
||||
*/
|
||||
public interface GenerateChallengeCallback {
|
||||
/**
|
||||
* Invoked when a challenge has been generated.
|
||||
*/
|
||||
void onGenerateChallengeResult(long challenge);
|
||||
void onGenerateChallengeResult(int sensorId, long challenge);
|
||||
|
||||
/**
|
||||
* Invoked if the challenge has not been revoked and a subsequent caller/owner invokes
|
||||
* {@link #generateChallenge(GenerateChallengeCallback)}, but
|
||||
* {@link #generateChallenge(int, GenerateChallengeCallback)}, but
|
||||
*/
|
||||
default void onChallengeInterrupted(int sensorId) {}
|
||||
|
||||
@@ -1104,9 +1122,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
default void onChallengeInterruptFinished(int sensorId) {}
|
||||
}
|
||||
|
||||
private abstract static class InternalGenerateChallengeCallback
|
||||
implements GenerateChallengeCallback {}
|
||||
|
||||
private class OnEnrollCancelListener implements OnCancelListener {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
@@ -1178,7 +1193,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
args.recycle();
|
||||
break;
|
||||
case MSG_CHALLENGE_GENERATED:
|
||||
sendChallengeGenerated((long) msg.obj /* challenge */);
|
||||
sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
|
||||
break;
|
||||
case MSG_FACE_DETECTED:
|
||||
sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
|
||||
@@ -1211,11 +1226,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
mGetFeatureCallback.onCompleted(success, feature, value);
|
||||
}
|
||||
|
||||
private void sendChallengeGenerated(long challenge) {
|
||||
private void sendChallengeGenerated(int sensorId, long challenge) {
|
||||
if (mGenerateChallengeCallback == null) {
|
||||
return;
|
||||
}
|
||||
mGenerateChallengeCallback.onGenerateChallengeResult(challenge);
|
||||
mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, challenge);
|
||||
}
|
||||
|
||||
private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
|
||||
|
||||
@@ -83,10 +83,10 @@ interface IFaceService {
|
||||
boolean isHardwareDetected(String opPackageName);
|
||||
|
||||
// Get a pre-enrollment authentication token
|
||||
void generateChallenge(IBinder token, IFaceServiceReceiver receiver, String opPackageName);
|
||||
void generateChallenge(IBinder token, int sensorId, IFaceServiceReceiver receiver, String opPackageName);
|
||||
|
||||
// Finish an enrollment sequence and invalidate the authentication token
|
||||
void revokeChallenge(IBinder token, String opPackageName);
|
||||
void revokeChallenge(IBinder token, int sensorId, String opPackageName);
|
||||
|
||||
// Determine if a user has at least one enrolled face
|
||||
boolean hasEnrolledFaces(int userId, String opPackageName);
|
||||
@@ -98,7 +98,7 @@ interface IFaceService {
|
||||
long getAuthenticatorId(int callingUserId);
|
||||
|
||||
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
void resetLockout(int userId, in byte [] hardwareAuthToken);
|
||||
void resetLockout(IBinder token, int sensorId, int userId, in byte [] hardwareAuthToken, String opPackageName);
|
||||
|
||||
// Add a callback which gets notified when the face lockout period expired.
|
||||
void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
|
||||
|
||||
@@ -31,7 +31,7 @@ oneway interface IFaceServiceReceiver {
|
||||
void onRemoved(in Face face, int remaining);
|
||||
void onFeatureSet(boolean success, int feature);
|
||||
void onFeatureGet(boolean success, int feature, boolean value);
|
||||
void onChallengeGenerated(long challenge);
|
||||
void onChallengeGenerated(int sensorId, long challenge);
|
||||
void onChallengeInterrupted(int sensorId);
|
||||
void onChallengeInterruptFinished(int sensorId);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.hardware.fingerprint;
|
||||
|
||||
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
|
||||
import static android.Manifest.permission.MANAGE_FINGERPRINT;
|
||||
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
|
||||
import static android.Manifest.permission.USE_BIOMETRIC;
|
||||
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
|
||||
import static android.Manifest.permission.USE_FINGERPRINT;
|
||||
@@ -378,12 +379,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
||||
* @hide
|
||||
*/
|
||||
public interface GenerateChallengeCallback {
|
||||
void onChallengeGenerated(long challenge);
|
||||
void onChallengeGenerated(int sensorId, long challenge);
|
||||
}
|
||||
|
||||
private abstract static class InternalGenerateChallengeCallback
|
||||
implements GenerateChallengeCallback {}
|
||||
|
||||
/**
|
||||
* Request authentication of a crypto object. This call warms up the fingerprint hardware
|
||||
* and starts scanning for a fingerprint. It terminates when
|
||||
@@ -593,15 +591,33 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(MANAGE_FINGERPRINT)
|
||||
public void generateChallenge(GenerateChallengeCallback callback) {
|
||||
public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
|
||||
if (mService != null) try {
|
||||
mGenerateChallengeCallback = callback;
|
||||
mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName());
|
||||
mService.generateChallenge(mToken, sensorId, mServiceReceiver,
|
||||
mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
|
||||
* enumerated sensor.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(MANAGE_FINGERPRINT)
|
||||
public void generateChallenge(GenerateChallengeCallback callback) {
|
||||
final List<FingerprintSensorProperties> fingerprintSensorProperties = getSensorProperties();
|
||||
if (fingerprintSensorProperties.isEmpty()) {
|
||||
Slog.e(TAG, "No sensors");
|
||||
return;
|
||||
}
|
||||
|
||||
final int sensorId = fingerprintSensorProperties.get(0).sensorId;
|
||||
generateChallenge(sensorId, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes enrollment and cancels the current auth token.
|
||||
* @hide
|
||||
@@ -615,6 +631,26 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
*
|
||||
* @param sensorId Sensor ID that this operation takes effect for
|
||||
* @param userId User ID that this operation takes effect for.
|
||||
* @param hardwareAuthToken An opaque token returned by password confirmation.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(RESET_FINGERPRINT_LOCKOUT)
|
||||
public void resetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.resetLockout(mToken, sensorId, userId, hardwareAuthToken,
|
||||
mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove given fingerprint template from fingerprint hardware and/or protected storage.
|
||||
* @param fp the fingerprint item to remove
|
||||
@@ -901,7 +937,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
||||
sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
|
||||
break;
|
||||
case MSG_CHALLENGE_GENERATED:
|
||||
sendChallengeGenerated((long) msg.obj /* challenge */);
|
||||
sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
|
||||
break;
|
||||
case MSG_FINGERPRINT_DETECTED:
|
||||
sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
|
||||
@@ -989,12 +1025,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
||||
}
|
||||
}
|
||||
|
||||
private void sendChallengeGenerated(long challenge) {
|
||||
private void sendChallengeGenerated(int sensorId, long challenge) {
|
||||
if (mGenerateChallengeCallback == null) {
|
||||
Slog.e(TAG, "sendChallengeGenerated, callback null");
|
||||
return;
|
||||
}
|
||||
mGenerateChallengeCallback.onChallengeGenerated(challenge);
|
||||
mGenerateChallengeCallback.onChallengeGenerated(sensorId, challenge);
|
||||
}
|
||||
|
||||
private void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
|
||||
@@ -1178,14 +1214,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
||||
}
|
||||
|
||||
@Override // binder call
|
||||
public void onChallengeGenerated(long challenge) {
|
||||
if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) {
|
||||
// Perform this on system_server thread, since the application's thread is
|
||||
// blocked waiting for the result
|
||||
mGenerateChallengeCallback.onChallengeGenerated(challenge);
|
||||
} else {
|
||||
mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget();
|
||||
}
|
||||
public void onChallengeGenerated(int sensorId, long challenge) {
|
||||
mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
|
||||
.sendToTarget();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -45,18 +45,24 @@ public class FingerprintSensorProperties implements Parcelable {
|
||||
|
||||
public final int sensorId;
|
||||
public final @SensorType int sensorType;
|
||||
// IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
|
||||
// cannot be checked
|
||||
public final boolean resetLockoutRequiresHardwareAuthToken;
|
||||
|
||||
/**
|
||||
* Initializes SensorProperties with specified values
|
||||
*/
|
||||
public FingerprintSensorProperties(int sensorId, @SensorType int sensorType) {
|
||||
public FingerprintSensorProperties(int sensorId, @SensorType int sensorType,
|
||||
boolean resetLockoutRequiresHardwareAuthToken) {
|
||||
this.sensorId = sensorId;
|
||||
this.sensorType = sensorType;
|
||||
this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
|
||||
}
|
||||
|
||||
protected FingerprintSensorProperties(Parcel in) {
|
||||
sensorId = in.readInt();
|
||||
sensorType = in.readInt();
|
||||
resetLockoutRequiresHardwareAuthToken = in.readBoolean();
|
||||
}
|
||||
|
||||
public static final Creator<FingerprintSensorProperties> CREATOR =
|
||||
@@ -81,5 +87,6 @@ public class FingerprintSensorProperties implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(sensorId);
|
||||
dest.writeInt(sensorType);
|
||||
dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ interface IFingerprintService {
|
||||
boolean isHardwareDetected(String opPackageName);
|
||||
|
||||
// Get a pre-enrollment authentication token
|
||||
void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver, String opPackageName);
|
||||
void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName);
|
||||
|
||||
// Finish an enrollment sequence and invalidate the authentication token
|
||||
void revokeChallenge(IBinder token, String opPackageName);
|
||||
@@ -103,7 +103,7 @@ interface IFingerprintService {
|
||||
long getAuthenticatorId(int callingUserId);
|
||||
|
||||
// Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
|
||||
void resetLockout(int userId, in byte [] hardwareAuthToken);
|
||||
void resetLockout(IBinder token, int sensorId, int userId, in byte[] hardwareAuthToken, String opPackageNAame);
|
||||
|
||||
// Add a callback which gets notified when the fingerprint lockout period expired.
|
||||
void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
|
||||
|
||||
@@ -29,5 +29,5 @@ oneway interface IFingerprintServiceReceiver {
|
||||
void onAuthenticationFailed();
|
||||
void onError(int error, int vendorCode);
|
||||
void onRemoved(in Fingerprint fp, int remaining);
|
||||
void onChallengeGenerated(long challenge);
|
||||
void onChallengeGenerated(int sensorId, long challenge);
|
||||
}
|
||||
|
||||
@@ -258,17 +258,6 @@ public class AuthService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
|
||||
checkInternalPermission();
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mBiometricService.resetLockout(userId, hardwareAuthToken);
|
||||
} 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
|
||||
|
||||
@@ -637,19 +637,6 @@ public class BiometricService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void resetLockout(int userId, byte[] hardwareAuthToken) {
|
||||
checkInternalPermission();
|
||||
|
||||
try {
|
||||
for (BiometricSensor sensor : mSensors) {
|
||||
sensor.impl.resetLockout(userId, hardwareAuthToken);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public long[] getAuthenticatorIds(int callingUserId) {
|
||||
checkInternalPermission();
|
||||
|
||||
@@ -128,11 +128,11 @@ public final class ClientMonitorCallbackConverter {
|
||||
}
|
||||
}
|
||||
|
||||
public void onChallengeGenerated(long challenge) throws RemoteException {
|
||||
public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
|
||||
if (mFaceServiceReceiver != null) {
|
||||
mFaceServiceReceiver.onChallengeGenerated(challenge);
|
||||
mFaceServiceReceiver.onChallengeGenerated(sensorId, challenge);
|
||||
} else if (mFingerprintServiceReceiver != null) {
|
||||
mFingerprintServiceReceiver.onChallengeGenerated(challenge);
|
||||
mFingerprintServiceReceiver.onChallengeGenerated(sensorId, challenge);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> {
|
||||
@Override
|
||||
public void unableToStart() {
|
||||
try {
|
||||
getListener().onChallengeGenerated(0L);
|
||||
getListener().onChallengeGenerated(getSensorId(), 0L);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Unable to send error", e);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> {
|
||||
|
||||
startHalOperation();
|
||||
try {
|
||||
getListener().onChallengeGenerated(mChallenge);
|
||||
getListener().onChallengeGenerated(getSensorId(), mChallenge);
|
||||
mCallback.onClientFinished(this, true /* success */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
|
||||
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
|
||||
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
|
||||
import android.hardware.face.Face;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.face.FaceSensorProperties;
|
||||
import android.hardware.face.IFaceServiceReceiver;
|
||||
import android.os.Build;
|
||||
@@ -464,6 +465,21 @@ class Face10 implements IHwBinder.DeathRecipient {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link IBiometricsFace} only supports a single in-flight challenge. In cases where two
|
||||
* callers both need challenges (e.g. resetLockout right before enrollment), we need to ensure
|
||||
* that either:
|
||||
* 1) generateChallenge/operation/revokeChallenge is complete before the next generateChallenge
|
||||
* is processed by the scheduler, or
|
||||
* 2) the generateChallenge callback provides a mechanism for notifying the caller that its
|
||||
* challenge has been invalidated by a subsequent caller, as well as a mechanism for
|
||||
* notifying the previous caller that the interrupting operation is complete (e.g. the
|
||||
* interrupting client's challenge has been revoked, so that the interrupted client can
|
||||
* start retry logic if necessary). See
|
||||
* {@link FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)}
|
||||
* The only case of conflicting challenges is currently resetLockout --> enroll. So, the second
|
||||
* option seems better as it prioritizes the new operation, which is user-facing.
|
||||
*/
|
||||
void scheduleGenerateChallenge(@NonNull IBinder token, @NonNull IFaceServiceReceiver receiver,
|
||||
@NonNull String opPackageName) {
|
||||
mHandler.post(() -> {
|
||||
@@ -498,7 +514,8 @@ class Face10 implements IHwBinder.DeathRecipient {
|
||||
void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String owner) {
|
||||
mHandler.post(() -> {
|
||||
if (!mCurrentChallengeOwner.getOwnerString().contentEquals(owner)) {
|
||||
Slog.e(TAG, "Package: " + owner + " attempting to revoke challenge owned by: "
|
||||
Slog.e(TAG, "scheduleRevokeChallenge, package: " + owner
|
||||
+ " attempting to revoke challenge owned by: "
|
||||
+ mCurrentChallengeOwner.getOwnerString());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,11 +74,6 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
|
||||
return mFaceService.getLockoutModeForUser(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
|
||||
mFaceService.resetLockout(userId, hardwareAuthToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAuthenticatorId(int callingUserId) throws RemoteException {
|
||||
return mFaceService.getAuthenticatorId(callingUserId);
|
||||
|
||||
@@ -80,16 +80,27 @@ public class FaceService extends SystemService {
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void generateChallenge(IBinder token, IFaceServiceReceiver receiver,
|
||||
public void generateChallenge(IBinder token, int sensorId, IFaceServiceReceiver receiver,
|
||||
String opPackageName) {
|
||||
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
|
||||
mFace10.scheduleGenerateChallenge(token, receiver, opPackageName);
|
||||
if (sensorId == mFace10.getFaceSensorProperties().sensorId) {
|
||||
mFace10.scheduleGenerateChallenge(token, receiver, opPackageName);
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void revokeChallenge(IBinder token, String owner) {
|
||||
public void revokeChallenge(IBinder token, int sensorId, String owner) {
|
||||
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
|
||||
mFace10.scheduleRevokeChallenge(token, owner);
|
||||
|
||||
if (sensorId == mFace10.getFaceSensorProperties().sensorId) {
|
||||
mFace10.scheduleRevokeChallenge(token, owner);
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@@ -267,9 +278,16 @@ public class FaceService extends SystemService {
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void resetLockout(int userId, byte[] hardwareAuthToken) {
|
||||
public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken,
|
||||
String opPackageName) {
|
||||
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
|
||||
mFace10.scheduleResetLockout(userId, hardwareAuthToken);
|
||||
|
||||
if (sensorId == mFace10.getFaceSensorProperties().sensorId) {
|
||||
mFace10.scheduleResetLockout(userId, hardwareAuthToken);
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -336,7 +336,10 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
final @FingerprintSensorProperties.SensorType int sensorType =
|
||||
isUdfps ? FingerprintSensorProperties.TYPE_UDFPS
|
||||
: FingerprintSensorProperties.TYPE_REAR;
|
||||
mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType);
|
||||
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
|
||||
final boolean resetLockoutRequiresHardwareAuthToken = false;
|
||||
mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType,
|
||||
resetLockoutRequiresHardwareAuthToken);
|
||||
}
|
||||
|
||||
static Fingerprint21 newInstance(@NonNull Context context, int sensorId,
|
||||
@@ -469,7 +472,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
|
||||
});
|
||||
}
|
||||
|
||||
void scheduleResetLockout(int userId, byte[] hardwareAuthToken) {
|
||||
void scheduleResetLockout(int userId) {
|
||||
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
|
||||
// thread.
|
||||
mHandler.post(() -> {
|
||||
|
||||
@@ -406,8 +406,10 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
|
||||
mScheduler = scheduler;
|
||||
mScheduler.init(this);
|
||||
mHandler = handler;
|
||||
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
|
||||
final boolean resetLockoutRequiresHardwareAuthToken = false;
|
||||
mSensorProperties = new FingerprintSensorProperties(sensorId,
|
||||
FingerprintSensorProperties.TYPE_UDFPS);
|
||||
FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken);
|
||||
mMockHalResultController = controller;
|
||||
mUserHasTrust = new SparseBooleanArray();
|
||||
mTrustManager = context.getSystemService(TrustManager.class);
|
||||
|
||||
@@ -74,11 +74,6 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
|
||||
return mFingerprintService.getLockoutModeForUser(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
|
||||
mFingerprintService.resetLockout(userId, hardwareAuthToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAuthenticatorId(int callingUserId) throws RemoteException {
|
||||
return mFingerprintService.getAuthenticatorId(callingUserId);
|
||||
|
||||
@@ -25,6 +25,7 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
|
||||
import static android.Manifest.permission.USE_FINGERPRINT;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -97,10 +98,16 @@ public class FingerprintService extends SystemService {
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver,
|
||||
String opPackageName) {
|
||||
public void generateChallenge(IBinder token, int sensorId,
|
||||
IFingerprintServiceReceiver receiver, String opPackageName) {
|
||||
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
|
||||
mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
|
||||
|
||||
if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
|
||||
mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@@ -346,9 +353,16 @@ public class FingerprintService extends SystemService {
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void resetLockout(int userId, byte [] hardwareAuthToken) {
|
||||
public void resetLockout(IBinder token, int sensorId, int userId,
|
||||
@Nullable byte [] hardwareAuthToken, String opPackageName) {
|
||||
Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT);
|
||||
mFingerprint21.scheduleResetLockout(userId, hardwareAuthToken);
|
||||
|
||||
if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
|
||||
mFingerprint21.scheduleResetLockout(userId);
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -69,10 +69,6 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
|
||||
return LockoutTracker.LOCKOUT_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAuthenticatorId(int callingUserId) throws RemoteException {
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.locksettings;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.face.FaceSensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.ServiceManager;
|
||||
import android.service.gatekeeper.IGateKeeperService;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Class that handles biometric-related work in the {@link LockSettingsService} area, for example
|
||||
* resetLockout.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class BiometricDeferredQueue {
|
||||
private static final String TAG = "BiometricDeferredQueue";
|
||||
|
||||
@NonNull private final Context mContext;
|
||||
@NonNull private final SyntheticPasswordManager mSpManager;
|
||||
@NonNull private final Handler mHandler;
|
||||
@Nullable private FingerprintManager mFingerprintManager;
|
||||
@Nullable private FaceManager mFaceManager;
|
||||
|
||||
// Entries added by LockSettingsService once a user's synthetic password is known. At this point
|
||||
// things are still keyed by userId.
|
||||
@NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts;
|
||||
|
||||
/**
|
||||
* Authentication info for a successful user unlock via Synthetic Password. This can be used to
|
||||
* perform multiple operations (e.g. resetLockout for multiple HALs/Sensors) by sending the
|
||||
* Gatekeeper Password to Gatekeer multiple times, each with a sensor-specific challenge.
|
||||
*/
|
||||
private static class UserAuthInfo {
|
||||
final int userId;
|
||||
@NonNull final byte[] gatekeeperPassword;
|
||||
|
||||
UserAuthInfo(int userId, @NonNull byte[] gatekeeperPassword) {
|
||||
this.userId = userId;
|
||||
this.gatekeeperPassword = gatekeeperPassword;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-authentication callback.
|
||||
*/
|
||||
private static class FaceResetLockoutTask implements FaceManager.GenerateChallengeCallback {
|
||||
interface FinishCallback {
|
||||
void onFinished();
|
||||
}
|
||||
|
||||
@NonNull FinishCallback finishCallback;
|
||||
@NonNull FaceManager faceManager;
|
||||
@NonNull SyntheticPasswordManager spManager;
|
||||
@NonNull Set<Integer> sensorIds; // IDs of sensors waiting for challenge
|
||||
@NonNull List<UserAuthInfo> pendingResetLockuts;
|
||||
|
||||
FaceResetLockoutTask(
|
||||
@NonNull FinishCallback finishCallback,
|
||||
@NonNull FaceManager faceManager,
|
||||
@NonNull SyntheticPasswordManager spManager,
|
||||
@NonNull Set<Integer> sensorIds,
|
||||
@NonNull List<UserAuthInfo> pendingResetLockouts) {
|
||||
this.finishCallback = finishCallback;
|
||||
this.faceManager = faceManager;
|
||||
this.spManager = spManager;
|
||||
this.sensorIds = sensorIds;
|
||||
this.pendingResetLockuts = pendingResetLockouts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChallengeInterrupted(int sensorId) {
|
||||
Slog.w(TAG, "Challenge interrupted, sensor: " + sensorId);
|
||||
// Consider re-attempting generateChallenge/resetLockout/revokeChallenge
|
||||
// when onChallengeInterruptFinished is invoked
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChallengeInterruptFinished(int sensorId) {
|
||||
Slog.w(TAG, "Challenge interrupt finished, sensor: " + sensorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGenerateChallengeResult(int sensorId, long challenge) {
|
||||
if (!sensorIds.contains(sensorId)) {
|
||||
Slog.e(TAG, "Unknown sensorId received: " + sensorId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Challenge received for a sensor. For each sensor, reset lockout for all users.
|
||||
for (UserAuthInfo userAuthInfo : pendingResetLockuts) {
|
||||
Slog.d(TAG, "Resetting face lockout for sensor: " + sensorId
|
||||
+ ", user: " + userAuthInfo.userId);
|
||||
final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
|
||||
getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
|
||||
userAuthInfo.userId);
|
||||
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
|
||||
Slog.wtf(TAG, "VerifyChallenge failed, response: "
|
||||
+ response.getResponseCode());
|
||||
}
|
||||
faceManager.resetLockout(sensorId, userAuthInfo.userId,
|
||||
response.getGatekeeperHAT());
|
||||
}
|
||||
|
||||
sensorIds.remove(sensorId);
|
||||
faceManager.revokeChallenge(sensorId);
|
||||
|
||||
if (sensorIds.isEmpty()) {
|
||||
Slog.d(TAG, "Done requesting resetLockout for all face sensors");
|
||||
finishCallback.onFinished();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized IGateKeeperService getGatekeeperService() {
|
||||
final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
|
||||
if (service == null) {
|
||||
Slog.e(TAG, "Unable to acquire GateKeeperService");
|
||||
return null;
|
||||
}
|
||||
return IGateKeeperService.Stub.asInterface(service);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable private FaceResetLockoutTask mFaceResetLockoutTask;
|
||||
|
||||
private final FaceResetLockoutTask.FinishCallback mFaceFinishCallback = () -> {
|
||||
mFaceResetLockoutTask = null;
|
||||
};
|
||||
|
||||
BiometricDeferredQueue(@NonNull Context context, @NonNull SyntheticPasswordManager spManager,
|
||||
@NonNull Handler handler) {
|
||||
mContext = context;
|
||||
mSpManager = spManager;
|
||||
mHandler = handler;
|
||||
mPendingResetLockouts = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void systemReady() {
|
||||
mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
|
||||
mFaceManager = mContext.getSystemService(FaceManager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a request for resetLockout on all biometric sensors for the user specified. The queue
|
||||
* owner must invoke {@link #processPendingLockoutResets()} at some point to kick off the
|
||||
* operations.
|
||||
*
|
||||
* Note that this should only ever be invoked for successful authentications, otherwise it will
|
||||
* consume a Gatekeeper authentication attempt and potentially wipe the user/device.
|
||||
*
|
||||
* @param userId The user that the operation will apply for.
|
||||
* @param gatekeeperPassword The Gatekeeper Password
|
||||
*/
|
||||
void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) {
|
||||
mHandler.post(() -> {
|
||||
Slog.d(TAG, "addPendingLockoutResetForUser: " + userId);
|
||||
mPendingResetLockouts.add(new UserAuthInfo(userId, gatekeeperPassword));
|
||||
});
|
||||
}
|
||||
|
||||
void processPendingLockoutResets() {
|
||||
mHandler.post(() -> {
|
||||
Slog.d(TAG, "processPendingLockoutResets: " + mPendingResetLockouts.size());
|
||||
processPendingLockoutsForFingerprint(new ArrayList<>(mPendingResetLockouts));
|
||||
processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockouts));
|
||||
mPendingResetLockouts.clear();
|
||||
});
|
||||
}
|
||||
|
||||
private void processPendingLockoutsForFingerprint(List<UserAuthInfo> pendingResetLockouts) {
|
||||
if (mFingerprintManager != null) {
|
||||
final List<FingerprintSensorProperties> fingerprintSensorProperties =
|
||||
mFingerprintManager.getSensorProperties();
|
||||
for (FingerprintSensorProperties prop : fingerprintSensorProperties) {
|
||||
if (!prop.resetLockoutRequiresHardwareAuthToken) {
|
||||
for (UserAuthInfo user : pendingResetLockouts) {
|
||||
mFingerprintManager.resetLockout(prop.sensorId, user.userId,
|
||||
null /* hardwareAuthToken */);
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Fingerprint resetLockout with HAT not supported yet");
|
||||
// TODO(b/152414803): Implement this when resetLockout is implemented below
|
||||
// the framework.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For devices on {@link android.hardware.biometrics.face.V1_0} which only support a single
|
||||
* in-flight challenge, we generate a single challenge to reset lockout for all profiles. This
|
||||
* hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked
|
||||
* challenge, or other race conditions.
|
||||
*
|
||||
* TODO(b/162965646) This logic can be avoided if multiple in-flight challenges are supported.
|
||||
* Though it will need to continue to exist to support existing HIDLs, each profile that
|
||||
* requires resetLockout could have its own challenge, and the `mPendingResetLockouts` queue
|
||||
* can be avoided.
|
||||
*/
|
||||
private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
|
||||
if (mFaceManager != null) {
|
||||
if (mFaceResetLockoutTask != null) {
|
||||
// This code will need to be updated if this problem ever occurs.
|
||||
Slog.w(TAG, "mFaceGenerateChallengeCallback not null, previous operation may be"
|
||||
+ " stuck");
|
||||
}
|
||||
final List<FaceSensorProperties> faceSensorProperties =
|
||||
mFaceManager.getSensorProperties();
|
||||
final Set<Integer> sensorIds = new ArraySet<>();
|
||||
for (FaceSensorProperties prop : faceSensorProperties) {
|
||||
sensorIds.add(prop.sensorId);
|
||||
}
|
||||
|
||||
mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager,
|
||||
mSpManager, sensorIds, pendingResetLockouts);
|
||||
for (final FaceSensorProperties prop : faceSensorProperties) {
|
||||
// Generate a challenge for each sensor. The challenge does not need to be
|
||||
// per-user, since the HAT returned by gatekeeper contains userId.
|
||||
mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,6 @@ import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_RETURN_GK
|
||||
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
|
||||
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
@@ -187,23 +186,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
|
||||
private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
|
||||
|
||||
// TODO (b/145978626) LockSettingsService no longer accepts challenges in the verifyCredential
|
||||
// paths. These are temporarily left around to ensure that resetLockout works. It will be
|
||||
// removed once resetLockout is compartmentalized.
|
||||
// No challenge provided
|
||||
private static final int CHALLENGE_NONE = 0;
|
||||
// Challenge was provided from the external caller (non-LockSettingsService)
|
||||
private static final int CHALLENGE_FROM_CALLER = 1;
|
||||
// Challenge was generated from within LockSettingsService, for resetLockout. When challenge
|
||||
// type is set to internal, LSS will revokeChallenge after all profiles for that user are
|
||||
// unlocked.
|
||||
private static final int CHALLENGE_INTERNAL = 2;
|
||||
|
||||
@IntDef({CHALLENGE_NONE,
|
||||
CHALLENGE_FROM_CALLER,
|
||||
CHALLENGE_INTERNAL})
|
||||
@interface ChallengeType {}
|
||||
|
||||
// Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
|
||||
// Do not call into ActivityManager while holding mSpManager lock.
|
||||
private final Object mSeparateChallengeLock = new Object();
|
||||
@@ -219,6 +201,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
protected final LockSettingsStorage mStorage;
|
||||
private final LockSettingsStrongAuth mStrongAuth;
|
||||
private final SynchronizedStrongAuthTracker mStrongAuthTracker;
|
||||
private final BiometricDeferredQueue mBiometricDeferredQueue;
|
||||
|
||||
private final NotificationManager mNotificationManager;
|
||||
private final UserManager mUserManager;
|
||||
@@ -321,15 +304,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private class PendingResetLockout {
|
||||
final int mUserId;
|
||||
final byte[] mHAT;
|
||||
PendingResetLockout(int userId, byte[] hat) {
|
||||
mUserId = userId;
|
||||
mHAT = hat;
|
||||
}
|
||||
}
|
||||
|
||||
private LockscreenCredential generateRandomProfilePassword() {
|
||||
byte[] randomLockSeed = new byte[] {};
|
||||
try {
|
||||
@@ -588,6 +562,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
mSpManager = injector.getSyntheticPasswordManager(mStorage);
|
||||
mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache();
|
||||
mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler);
|
||||
|
||||
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
|
||||
mStorage);
|
||||
@@ -740,8 +715,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// If boot took too long and the password in vold got expired, parent keystore will
|
||||
// be still locked, we ignore this case since the user will be prompted to unlock
|
||||
// the device after boot.
|
||||
unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */,
|
||||
CHALLENGE_NONE, 0 /* challenge */, null /* resetLockouts */);
|
||||
unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,6 +804,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
mRebootEscrowManager.loadRebootEscrowDataIfAvailable();
|
||||
// TODO: maybe skip this for split system user mode.
|
||||
mStorage.prefetchUser(UserHandle.USER_SYSTEM);
|
||||
mBiometricDeferredQueue.systemReady();
|
||||
}
|
||||
|
||||
private void getAuthSecretHal() {
|
||||
@@ -1306,13 +1281,10 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return credential;
|
||||
}
|
||||
|
||||
private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated,
|
||||
@ChallengeType int challengeType, long challenge,
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
|
||||
private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated) {
|
||||
try {
|
||||
doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
|
||||
challengeType, challenge, profileHandle, null /* progressCallback */,
|
||||
resetLockouts, 0 /* flags */);
|
||||
profileHandle, null /* progressCallback */, 0 /* flags */);
|
||||
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
|
||||
| NoSuchAlgorithmException | NoSuchPaddingException
|
||||
| InvalidAlgorithmParameterException | IllegalBlockSizeException
|
||||
@@ -1327,10 +1299,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void unlockUser(int userId, byte[] token, byte[] secret) {
|
||||
unlockUser(userId, token, secret, CHALLENGE_NONE, 0 /* challenge */, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock the user (both storage and user state) and its associated managed profiles
|
||||
* synchronously.
|
||||
@@ -1339,9 +1307,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* can end up calling into other system services to process user unlock request (via
|
||||
* {@link com.android.server.SystemServiceManager#unlockUser} </em>
|
||||
*/
|
||||
private void unlockUser(int userId, byte[] token, byte[] secret,
|
||||
@ChallengeType int challengeType, long challenge,
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
|
||||
private void unlockUser(int userId, byte[] token, byte[] secret) {
|
||||
Slog.i(TAG, "Unlocking user " + userId + " with secret only, length "
|
||||
+ (secret != null ? secret.length : 0));
|
||||
// TODO: make this method fully async so we can update UI with progress strings
|
||||
@@ -1378,6 +1344,9 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
|
||||
if (!hasUnifiedChallenge(userId)) {
|
||||
mBiometricDeferredQueue.processPendingLockoutResets();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1388,10 +1357,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
if (hasUnifiedChallenge(profile.id)) {
|
||||
if (mUserManager.isUserRunning(profile.id)) {
|
||||
// Unlock managed profile with unified lock
|
||||
// Must pass the challenge on for resetLockout, so it's not over-written, which
|
||||
// causes LockSettingsService to revokeChallenge inappropriately.
|
||||
unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */,
|
||||
challengeType, challenge, resetLockouts);
|
||||
unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */);
|
||||
} else {
|
||||
try {
|
||||
// Profile not ready for unlock yet, but decrypt the unified challenge now
|
||||
@@ -1412,22 +1378,9 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (resetLockouts != null && !resetLockouts.isEmpty()) {
|
||||
mHandler.post(() -> {
|
||||
final BiometricManager bm = mContext.getSystemService(BiometricManager.class);
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
for (int i = 0; i < resetLockouts.size(); i++) {
|
||||
bm.resetLockout(resetLockouts.get(i).mUserId, resetLockouts.get(i).mHAT);
|
||||
}
|
||||
if (challengeType == CHALLENGE_INTERNAL
|
||||
&& pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
|
||||
mContext.getSystemService(FaceManager.class).revokeChallenge();
|
||||
}
|
||||
});
|
||||
}
|
||||
mBiometricDeferredQueue.processPendingLockoutResets();
|
||||
}
|
||||
|
||||
private boolean hasUnifiedChallenge(int userId) {
|
||||
@@ -1727,8 +1680,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
setUserKeyProtection(userId, credential, convertResponse(gkResponse));
|
||||
fixateNewestUserKeyAuth(userId);
|
||||
// Refresh the auth token
|
||||
doVerifyCredential(credential, CHALLENGE_FROM_CALLER, 0, userId,
|
||||
null /* progressCallback */, 0 /* flags */);
|
||||
doVerifyCredential(credential, userId, null /* progressCallback */, 0 /* flags */);
|
||||
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
|
||||
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
|
||||
return true;
|
||||
@@ -1972,8 +1924,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
ICheckCredentialProgressCallback progressCallback) {
|
||||
checkPasswordReadPermission(userId);
|
||||
try {
|
||||
return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId, progressCallback,
|
||||
0 /* flags */);
|
||||
return doVerifyCredential(credential, userId, progressCallback, 0 /* flags */);
|
||||
} finally {
|
||||
scheduleGc();
|
||||
}
|
||||
@@ -1984,10 +1935,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
|
||||
int userId, int flags) {
|
||||
checkPasswordReadPermission(userId);
|
||||
|
||||
try {
|
||||
return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId,
|
||||
null /* progressCallback */, flags);
|
||||
return doVerifyCredential(credential, userId, null /* progressCallback */, flags);
|
||||
} finally {
|
||||
scheduleGc();
|
||||
}
|
||||
@@ -2006,32 +1955,17 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
|
||||
* format.
|
||||
* @param credential User's lockscreen credential
|
||||
* @param challengeType Owner of the challenge
|
||||
* @param challenge Challenge to be wrapped within Gatekeeper's HAT, if the credential is
|
||||
* verified
|
||||
* @param userId User to verify the credential for
|
||||
* @param progressCallback Receive progress callbacks
|
||||
* @param flags See {@link LockPatternUtils.VerifyFlag}
|
||||
* @return See {@link VerifyCredentialResponse}
|
||||
*/
|
||||
private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
|
||||
@ChallengeType int challengeType, long challenge, int userId,
|
||||
ICheckCredentialProgressCallback progressCallback,
|
||||
@LockPatternUtils.VerifyFlag int flags) {
|
||||
return doVerifyCredential(credential, challengeType, challenge, userId,
|
||||
progressCallback, null /* resetLockouts */, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
|
||||
* format.
|
||||
*/
|
||||
private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
|
||||
@ChallengeType int challengeType, long challenge, int userId,
|
||||
ICheckCredentialProgressCallback progressCallback,
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts,
|
||||
int userId, ICheckCredentialProgressCallback progressCallback,
|
||||
@LockPatternUtils.VerifyFlag int flags) {
|
||||
if (credential == null || credential.isNone()) {
|
||||
throw new IllegalArgumentException("Credential can't be null or empty");
|
||||
@@ -2041,9 +1975,10 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
|
||||
return VerifyCredentialResponse.ERROR;
|
||||
}
|
||||
VerifyCredentialResponse response = null;
|
||||
response = spBasedDoVerifyCredential(credential, challengeType, challenge,
|
||||
userId, progressCallback, resetLockouts, flags);
|
||||
|
||||
VerifyCredentialResponse response = spBasedDoVerifyCredential(credential, userId,
|
||||
progressCallback, flags);
|
||||
|
||||
// The user employs synthetic password based credential.
|
||||
if (response != null) {
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
@@ -2064,8 +1999,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return VerifyCredentialResponse.ERROR;
|
||||
}
|
||||
|
||||
response = verifyCredential(userId, storedHash, credential,
|
||||
challengeType, challenge, progressCallback);
|
||||
response = verifyCredential(userId, storedHash, credential, progressCallback);
|
||||
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
|
||||
@@ -2085,8 +2019,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// Unlock parent by using parent's challenge
|
||||
final VerifyCredentialResponse parentResponse = doVerifyCredential(
|
||||
credential,
|
||||
CHALLENGE_NONE,
|
||||
0L,
|
||||
parentProfileId,
|
||||
null /* progressCallback */,
|
||||
flags);
|
||||
@@ -2098,10 +2030,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
try {
|
||||
// Unlock work profile, and work profile with unified lock must use password only
|
||||
return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
|
||||
CHALLENGE_NONE,
|
||||
0L,
|
||||
userId, null /* progressCallback */,
|
||||
flags);
|
||||
userId, null /* progressCallback */, flags);
|
||||
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
|
||||
| NoSuchAlgorithmException | NoSuchPaddingException
|
||||
| InvalidAlgorithmParameterException | IllegalBlockSizeException
|
||||
@@ -2119,8 +2048,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* hash to GK.
|
||||
*/
|
||||
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
|
||||
LockscreenCredential credential, @ChallengeType int challengeType, long challenge,
|
||||
ICheckCredentialProgressCallback progressCallback) {
|
||||
LockscreenCredential credential, ICheckCredentialProgressCallback progressCallback) {
|
||||
if ((storedHash == null || storedHash.hash.length == 0) && credential.isNone()) {
|
||||
// don't need to pass empty credentials to GateKeeper
|
||||
return VerifyCredentialResponse.OK;
|
||||
@@ -2137,7 +2065,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
GateKeeperResponse gateKeeperResponse;
|
||||
try {
|
||||
gateKeeperResponse = getGateKeeperService().verifyChallenge(
|
||||
userId, challenge, storedHash.hash, credential.getCredential());
|
||||
userId, 0L /* challenge */, storedHash.hash, credential.getCredential());
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "gatekeeper verify failed", e);
|
||||
gateKeeperResponse = GateKeeperResponse.ERROR;
|
||||
@@ -2710,28 +2638,13 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
|
||||
@ChallengeType int challengeType, long challenge,
|
||||
int userId, ICheckCredentialProgressCallback progressCallback,
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts,
|
||||
@LockPatternUtils.VerifyFlag int flags) {
|
||||
|
||||
final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);
|
||||
|
||||
Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " challengeType=" + challengeType
|
||||
Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId
|
||||
+ " hasEnrolledBiometrics=" + hasEnrolledBiometrics);
|
||||
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
// TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
|
||||
// we need to generate challenge for each one, have it signed by GK and reset lockout
|
||||
// for each modality.
|
||||
if (challengeType == CHALLENGE_NONE && pm.hasSystemFeature(PackageManager.FEATURE_FACE)
|
||||
&& hasEnrolledBiometrics) {
|
||||
// If there are multiple profiles in the same account, ensure we only generate the
|
||||
// challenge once.
|
||||
challengeType = CHALLENGE_INTERNAL;
|
||||
challenge = mContext.getSystemService(FaceManager.class).generateChallengeBlocking();
|
||||
}
|
||||
|
||||
final AuthenticationResult authResult;
|
||||
VerifyCredentialResponse response;
|
||||
final boolean returnGkPw = (flags & VERIFY_FLAG_RETURN_GK_PW) != 0;
|
||||
@@ -2752,10 +2665,13 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
// credential has matched
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
|
||||
authResult.authToken.deriveGkPassword());
|
||||
|
||||
// perform verifyChallenge with synthetic password which generates the real GK auth
|
||||
// token and response for the current user
|
||||
response = mSpManager.verifyChallenge(getGateKeeperService(), authResult.authToken,
|
||||
challenge, userId);
|
||||
0L /* challenge */, userId);
|
||||
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
|
||||
// This shouldn't really happen: the unwrapping of SP succeeds, but SP doesn't
|
||||
// match the recorded GK password handle.
|
||||
@@ -2765,15 +2681,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
// Do resetLockout / revokeChallenge when all profiles are unlocked
|
||||
if (hasEnrolledBiometrics) {
|
||||
if (resetLockouts == null) {
|
||||
resetLockouts = new ArrayList<>();
|
||||
}
|
||||
resetLockouts.add(new PendingResetLockout(userId, response.getGatekeeperHAT()));
|
||||
}
|
||||
|
||||
onCredentialVerified(authResult.authToken, challengeType, challenge, resetLockouts,
|
||||
onCredentialVerified(authResult.authToken,
|
||||
PasswordMetrics.computeForCredential(userCredential), userId);
|
||||
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||
if (response.getTimeout() > 0) {
|
||||
@@ -2789,9 +2697,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void onCredentialVerified(AuthenticationToken authToken,
|
||||
@ChallengeType int challengeType, long challenge,
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts, PasswordMetrics metrics,
|
||||
private void onCredentialVerified(AuthenticationToken authToken, PasswordMetrics metrics,
|
||||
int userId) {
|
||||
|
||||
if (metrics != null) {
|
||||
@@ -2806,7 +2712,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
{
|
||||
final byte[] secret = authToken.deriveDiskEncryptionKey();
|
||||
unlockUser(userId, null, secret, challengeType, challenge, resetLockouts);
|
||||
unlockUser(userId, null, secret);
|
||||
Arrays.fill(secret, (byte) 0);
|
||||
}
|
||||
activateEscrowTokens(authToken, userId);
|
||||
@@ -3193,11 +3099,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO: Reset biometrics lockout here. Ideally that should be self-contained inside
|
||||
// onCredentialVerified(), which will require some refactoring on the current lockout
|
||||
// reset logic.
|
||||
|
||||
onCredentialVerified(authResult.authToken, CHALLENGE_NONE, 0, null,
|
||||
onCredentialVerified(authResult.authToken,
|
||||
loadPasswordMetrics(authResult.authToken, userId), userId);
|
||||
return true;
|
||||
}
|
||||
@@ -3208,8 +3111,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
if (cred == null) {
|
||||
return false;
|
||||
}
|
||||
return doVerifyCredential(cred, CHALLENGE_NONE, 0, userId,
|
||||
null /* progressCallback */, 0 /* flags */)
|
||||
return doVerifyCredential(cred, userId, null /* progressCallback */, 0 /* flags */)
|
||||
.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
|
||||
}
|
||||
}
|
||||
@@ -3532,8 +3434,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
SyntheticPasswordManager.AuthenticationToken
|
||||
authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion);
|
||||
authToken.recreateDirectly(syntheticPassword);
|
||||
onCredentialVerified(authToken, CHALLENGE_NONE, 0, null,
|
||||
loadPasswordMetrics(authToken, userId), userId);
|
||||
onCredentialVerified(authToken, loadPasswordMetrics(authToken, userId), userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,21 +269,6 @@ public class AuthServiceTest {
|
||||
eq(callback), eq(UserHandle.getCallingUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetLockout_callsBiometricServiceResetLockout() throws
|
||||
Exception {
|
||||
mAuthService = new AuthService(mContext, mInjector);
|
||||
mAuthService.onStart();
|
||||
|
||||
final int userId = 100;
|
||||
final byte[] token = new byte[0];
|
||||
|
||||
mAuthService.mImpl.resetLockout(userId, token);
|
||||
|
||||
waitForIdle();
|
||||
verify(mBiometricService).resetLockout(eq(userId), AdditionalMatchers.aryEq(token));
|
||||
}
|
||||
|
||||
private static void waitForIdle() {
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user