Merge "Add challenge-less resetLockout for new AIDL interfaces" into sc-dev
This commit is contained in:
@@ -393,14 +393,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba
|
||||
final IFaceServiceReceiver receiver, final String opPackageName) {
|
||||
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
|
||||
|
||||
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
|
||||
if (provider == null) {
|
||||
Slog.w(TAG, "Null provider for removeAll");
|
||||
return;
|
||||
for (ServiceProvider provider : mServiceProviders) {
|
||||
List<FaceSensorPropertiesInternal> props = provider.getSensorProperties();
|
||||
for (FaceSensorPropertiesInternal prop : props) {
|
||||
provider.scheduleRemoveAll(prop.sensorId, token, userId, receiver,
|
||||
opPackageName);
|
||||
}
|
||||
}
|
||||
|
||||
provider.second.scheduleRemoveAll(provider.first, token, userId, receiver,
|
||||
opPackageName);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
|
||||
@@ -23,11 +23,11 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
|
||||
import android.hardware.biometrics.face.V1_0.OptionalBool;
|
||||
import android.hardware.biometrics.face.V1_0.OptionalUint64;
|
||||
import android.hardware.biometrics.face.V1_0.Status;
|
||||
import android.os.NativeHandle;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TestHal extends IBiometricsFace.Stub {
|
||||
private static final String TAG = "face.hidl.TestHal";
|
||||
@@ -107,8 +107,12 @@ public class TestHal extends IBiometricsFace.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remove(int faceId) {
|
||||
public int remove(int faceId) throws RemoteException {
|
||||
Slog.w(TAG, "remove");
|
||||
if (mCallback != null) {
|
||||
mCallback.onRemoved(0 /* deviceId */, new ArrayList<Integer>(Arrays.asList(faceId)),
|
||||
0 /* userId */);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -506,13 +506,13 @@ public class FingerprintService extends SystemService implements BiometricServic
|
||||
final IFingerprintServiceReceiver receiver, final String opPackageName) {
|
||||
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
|
||||
|
||||
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
|
||||
if (provider == null) {
|
||||
Slog.w(TAG, "Null provider for removeAll");
|
||||
return;
|
||||
for (ServiceProvider provider : mServiceProviders) {
|
||||
List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties();
|
||||
for (FingerprintSensorPropertiesInternal prop : props) {
|
||||
provider.scheduleRemoveAll(prop.sensorId, token, receiver, userId,
|
||||
opPackageName);
|
||||
}
|
||||
}
|
||||
provider.second.scheduleRemoveAll(provider.first, token, receiver, userId,
|
||||
opPackageName);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
|
||||
@@ -527,7 +527,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
|
||||
// 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);
|
||||
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
|
||||
userId, mContext.getOpPackageName(), sensorId, mLockoutTracker);
|
||||
mScheduler.scheduleClientMonitor(client);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.sensors.fingerprint.hidl;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricsProtoEnums;
|
||||
|
||||
import com.android.server.biometrics.BiometricsProto;
|
||||
import com.android.server.biometrics.sensors.BaseClientMonitor;
|
||||
|
||||
/**
|
||||
* Clears lockout, which is handled in the framework (and not the HAL) for the
|
||||
* IBiometricsFingerprint@2.1 interface.
|
||||
*/
|
||||
public class FingerprintResetLockoutClient extends BaseClientMonitor {
|
||||
|
||||
@NonNull final LockoutFrameworkImpl mLockoutTracker;
|
||||
|
||||
public FingerprintResetLockoutClient(@NonNull Context context, int userId,
|
||||
@NonNull String owner, int sensorId, @NonNull LockoutFrameworkImpl lockoutTracker) {
|
||||
super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
|
||||
sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
|
||||
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
|
||||
mLockoutTracker = lockoutTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull Callback callback) {
|
||||
super.start(callback);
|
||||
mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
|
||||
getTargetUserId());
|
||||
callback.onClientFinished(this, true /* success */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtoEnum() {
|
||||
return BiometricsProto.CM_RESET_LOCKOUT;
|
||||
}
|
||||
}
|
||||
@@ -93,8 +93,11 @@ public class TestHal extends IBiometricsFingerprint.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remove(int gid, int fid) {
|
||||
public int remove(int gid, int fid) throws RemoteException {
|
||||
Slog.w(TAG, "Remove");
|
||||
if (mCallback != null) {
|
||||
mCallback.onRemoved(0 /* deviceId */, fid, gid, 0 /* remaining */);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,19 +119,11 @@ public class BiometricDeferredQueue {
|
||||
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 == null) {
|
||||
Slog.wtf(TAG, "VerifyChallenge failed, null response");
|
||||
continue;
|
||||
final byte[] hat = requestHatFromGatekeeperPassword(spManager, userAuthInfo,
|
||||
challenge);
|
||||
if (hat != null) {
|
||||
faceManager.resetLockout(sensorId, userAuthInfo.userId, hat);
|
||||
}
|
||||
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
|
||||
Slog.wtf(TAG, "VerifyChallenge failed, response: "
|
||||
+ response.getResponseCode());
|
||||
}
|
||||
faceManager.resetLockout(sensorId, userAuthInfo.userId,
|
||||
response.getGatekeeperHAT());
|
||||
}
|
||||
|
||||
sensorIds.remove(sensorId);
|
||||
@@ -146,15 +138,6 @@ public class BiometricDeferredQueue {
|
||||
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;
|
||||
@@ -214,10 +197,19 @@ public class BiometricDeferredQueue {
|
||||
mFingerprintManager.resetLockout(prop.sensorId, user.userId,
|
||||
null /* hardwareAuthToken */);
|
||||
}
|
||||
} else if (!prop.resetLockoutRequiresChallenge) {
|
||||
for (UserAuthInfo user : pendingResetLockouts) {
|
||||
Slog.d(TAG, "Resetting fingerprint lockout for sensor: " + prop.sensorId
|
||||
+ ", user: " + user.userId);
|
||||
final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
|
||||
0 /* challenge */);
|
||||
if (hat != null) {
|
||||
mFingerprintManager.resetLockout(prop.sensorId, user.userId, hat);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Fingerprint resetLockout with HAT not supported yet");
|
||||
// TODO(b/152414803): Implement this when resetLockout is implemented below
|
||||
// the framework.
|
||||
Slog.w(TAG, "No fingerprint HAL interface requires HAT with challenge"
|
||||
+ ", sensorId: " + prop.sensorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,11 +220,6 @@ public class BiometricDeferredQueue {
|
||||
* 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) {
|
||||
@@ -251,10 +238,60 @@ public class BiometricDeferredQueue {
|
||||
mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager,
|
||||
mSpManager, sensorIds, pendingResetLockouts);
|
||||
for (final FaceSensorPropertiesInternal 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);
|
||||
if (prop.resetLockoutRequiresHardwareAuthToken) {
|
||||
if (prop.resetLockoutRequiresChallenge) {
|
||||
// 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);
|
||||
} else {
|
||||
for (UserAuthInfo user : pendingResetLockouts) {
|
||||
Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId
|
||||
+ ", user: " + user.userId);
|
||||
final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
|
||||
0 /* challenge */);
|
||||
if (hat != null) {
|
||||
mFaceManager.resetLockout(prop.sensorId, user.userId, hat);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Lockout is below the HAL for all face authentication interfaces"
|
||||
+ ", sensorId: " + prop.sensorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] requestHatFromGatekeeperPassword(
|
||||
@NonNull SyntheticPasswordManager spManager,
|
||||
@NonNull UserAuthInfo userAuthInfo, long challenge) {
|
||||
final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
|
||||
getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
|
||||
userAuthInfo.userId);
|
||||
if (response == null) {
|
||||
Slog.wtf(TAG, "VerifyChallenge failed, null response");
|
||||
return null;
|
||||
}
|
||||
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
|
||||
Slog.wtf(TAG, "VerifyChallenge failed, response: "
|
||||
+ response.getResponseCode());
|
||||
return null;
|
||||
}
|
||||
if (response.getGatekeeperHAT() == null) {
|
||||
Slog.e(TAG, "Null HAT received from spManager");
|
||||
}
|
||||
|
||||
return response.getGatekeeperHAT();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user