Merge "Add get/set feature implementation" into sc-dev

This commit is contained in:
Joshua Mccloskey
2021-04-25 23:20:35 +00:00
committed by Android (Google) Code Review
11 changed files with 337 additions and 18 deletions

View File

@@ -133,11 +133,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
@Override
public void onFeatureGet(boolean success, int feature, boolean value) {
public void onFeatureGet(boolean success, int[] features, boolean[] featureState) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = success;
args.argi1 = feature;
args.arg2 = value;
args.arg2 = features;
args.arg3 = featureState;
mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
}
@@ -1088,7 +1088,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @hide
*/
public abstract static class GetFeatureCallback {
public abstract void onCompleted(boolean success, int feature, boolean value);
public abstract void onCompleted(boolean success, int[] features, boolean[] featureState);
}
/**
@@ -1179,8 +1179,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
case MSG_GET_FEATURE_COMPLETED:
SomeArgs args = (SomeArgs) msg.obj;
sendGetFeatureCompleted((boolean) args.arg1 /* success */,
args.argi1 /* feature */,
(boolean) args.arg2 /* value */);
(int[]) args.arg2 /* features */,
(boolean[]) args.arg3 /* featureState */);
args.recycle();
break;
case MSG_CHALLENGE_GENERATED:
@@ -1216,11 +1216,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
mSetFeatureCallback.onCompleted(success, feature);
}
private void sendGetFeatureCompleted(boolean success, int feature, boolean value) {
private void sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState) {
if (mGetFeatureCallback == null) {
return;
}
mGetFeatureCallback.onCompleted(success, feature, value);
mGetFeatureCallback.onCompleted(success, features, featureState);
}
private void sendChallengeGenerated(int sensorId, long challenge) {

View File

@@ -66,7 +66,8 @@ public class FaceServiceReceiver extends IFaceServiceReceiver.Stub {
}
@Override
public void onFeatureGet(boolean success, int feature, boolean value) throws RemoteException {
public void onFeatureGet(boolean success, int[] features, boolean[] featureState)
throws RemoteException {
}

View File

@@ -32,7 +32,7 @@ oneway interface IFaceServiceReceiver {
void onError(int error, int vendorCode);
void onRemoved(in Face face, int remaining);
void onFeatureSet(boolean success, int feature);
void onFeatureGet(boolean success, int feature, boolean value);
void onFeatureGet(boolean success, in int[] features, in boolean[] featureState);
void onChallengeGenerated(int sensorId, long challenge);
void onChallengeInterrupted(int sensorId);
void onChallengeInterruptFinished(int sensorId);

View File

@@ -146,9 +146,10 @@ public class ClientMonitorCallbackConverter {
}
}
public void onFeatureGet(boolean success, int feature, boolean value) throws RemoteException {
public void onFeatureGet(boolean success, int[] features, boolean[] featureState)
throws RemoteException {
if (mFaceServiceReceiver != null) {
mFaceServiceReceiver.onFeatureGet(success, feature, value);
mFaceServiceReceiver.onFeatureGet(success, features, featureState);
}
}

View File

@@ -105,7 +105,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
}
@Override
public void onFeatureGet(boolean success, int feature, boolean value) {
public void onFeatureGet(boolean success, int[] features, boolean[] featureState) {
}

View File

@@ -0,0 +1,146 @@
/*
* 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.face.aidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.HashMap;
import java.util.Map;
/**
* Face-specific get feature client for the {@link IFace} AIDL HAL interface.
*/
public class FaceGetFeatureClient extends HalClientMonitor<ISession> implements ErrorConsumer {
private static final String TAG = "FaceGetFeatureClient";
private final int mUserId;
FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mUserId = userId;
}
@Override
public void unableToStart() {
mCallback.onClientFinished(this, false /* success */);
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);
startHalOperation();
}
@Override
protected void startHalOperation() {
try {
getFreshDaemon().getFeatures();
} catch (RemoteException e) {
Slog.e(TAG, "Unable to getFeature", e);
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_GET_FEATURE;
}
public void onFeatureGet(boolean success, byte[] features) {
HashMap<Integer, Boolean> featureMap = getFeatureMap();
int[] featuresToSend = new int[featureMap.size()];
boolean[] featureState = new boolean[featureMap.size()];
// The AIDL get feature api states that the presence of a feature means
// it is enabled, while the lack thereof means its disabled.
for (int i = 0; i < features.length; i++) {
Integer feature = convertAidlToFrameworkFeature(features[i]);
if (feature != null) {
featureMap.put(feature, true);
}
}
int i = 0;
for (Map.Entry<Integer, Boolean> entry : featureMap.entrySet()) {
featuresToSend[i] = entry.getKey();
featureState[i] = entry.getValue();
i++;
}
boolean attentionEnabled = featureMap.get(BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION);
Slog.d(TAG, "Updating attention value for user: " + mUserId
+ " to value: " + attentionEnabled);
Settings.Secure.putIntForUser(getContext().getContentResolver(),
Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
attentionEnabled ? 1 : 0, mUserId);
try {
getListener().onFeatureGet(success, featuresToSend, featureState);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
mCallback.onClientFinished(this, true /* success */);
}
private @NonNull HashMap<Integer, Boolean> getFeatureMap() {
HashMap<Integer, Boolean> featureMap = new HashMap<>();
featureMap.put(BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, false);
return featureMap;
}
private Integer convertAidlToFrameworkFeature(byte feature) {
switch (feature) {
case Feature.REQUIRE_ATTENTION:
return new Integer(BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION);
default:
return null;
}
}
@Override
public void onError(int errorCode, int vendorCode) {
try {
getListener().onFeatureGet(false /* success */, new int[0], new boolean[0]);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
mCallback.onClientFinished(this, false /* success */);
}
}

View File

@@ -435,13 +435,36 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public void scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
boolean enabled, @NonNull byte[] hardwareAuthToken,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
// TODO(b/171335732): implement this.
mHandler.post(() -> {
final List<Face> faces = FaceUtils.getInstance(sensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
Slog.w(getTag(), "Ignoring setFeature, no templates enrolled for user: " + userId);
return;
}
final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId,
mContext.getOpPackageName(), sensorId, feature, enabled, hardwareAuthToken);
scheduleForSensor(sensorId, client);
});
}
@Override
public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName) {
// TODO(b/171335732): implement this.
mHandler.post(() -> {
final List<Face> faces = FaceUtils.getInstance(sensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
Slog.w(getTag(), "Ignoring getFeature, no templates enrolled for user: " + userId);
return;
}
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
mSensors.get(sensorId).getLazySession(), token, callback, userId,
mContext.getOpPackageName(), sensorId);
scheduleForSensor(sensorId, client);
});
}
@Override

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2022 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.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
/**
* Face-specific get feature client for the {@link IFace} AIDL HAL interface.
*/
public class FaceSetFeatureClient extends HalClientMonitor<ISession> implements ErrorConsumer {
private static final String TAG = "FaceSetFeatureClient";
private final int mFeature;
private final boolean mEnabled;
private final HardwareAuthToken mHardwareAuthToken;
FaceSetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId, int feature, boolean enabled,
byte[] hardwareAuthToken) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mFeature = feature;
mEnabled = enabled;
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
}
@Override
public void unableToStart() {
try {
getListener().onFeatureSet(false /* success */, mFeature);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send error", e);
}
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);
startHalOperation();
}
@Override
protected void startHalOperation() {
try {
getFreshDaemon()
.setFeature(mHardwareAuthToken,
convertFrameworkToAidlFeature(mFeature), mEnabled);
} catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_SET_FEATURE;
}
public void onFeatureSet(boolean success) {
try {
getListener().onFeatureSet(success, mFeature);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
mCallback.onClientFinished(this, true /* success */);
}
private byte convertFrameworkToAidlFeature(int feature) throws IllegalArgumentException {
switch (feature) {
case BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION:
return Feature.REQUIRE_ATTENTION;
default:
Slog.e(TAG, "Unsupported feature : " + feature);
throw new IllegalArgumentException();
}
}
@Override
public void onError(int errorCode, int vendorCode) {
try {
getListener().onFeatureSet(false /* success */, mFeature);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
mCallback.onClientFinished(this, false /* success */);
}
}

View File

@@ -377,12 +377,32 @@ public class Sensor {
@Override
public void onFeaturesRetrieved(byte[] features) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FaceGetFeatureClient)) {
Slog.e(mTag, "onFeaturesRetrieved for non-get feature consumer: "
+ Utils.getClientName(client));
return;
}
final FaceGetFeatureClient faceGetFeatureClient = (FaceGetFeatureClient) client;
faceGetFeatureClient.onFeatureGet(true /* success */, features);
});
}
@Override
public void onFeatureSet(byte feature) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FaceSetFeatureClient)) {
Slog.e(mTag, "onFeatureSet for non-set consumer: "
+ Utils.getClientName(client));
return;
}
final FaceSetFeatureClient faceSetFeatureClient = (FaceSetFeatureClient) client;
faceSetFeatureClient.onFeatureSet(true /* success */);
});
}
@Override

View File

@@ -94,7 +94,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
}
@Override
public void onFeatureGet(boolean success, int feature, boolean value) {
public void onFeatureGet(boolean success, int[] features, boolean[] featureState) {
}

View File

@@ -58,7 +58,7 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
public void unableToStart() {
try {
if (getListener() != null) {
getListener().onFeatureGet(false /* success */, mFeature, false /* value */);
getListener().onFeatureGet(false /* success */, new int[0], new boolean[0]);
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send error", e);
@@ -75,9 +75,14 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
protected void startHalOperation() {
try {
final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
int[] features = new int[1];
boolean[] featureState = new boolean[1];
features[0] = mFeature;
featureState[0] = result.value;
mValue = result.value;
if (getListener() != null) {
getListener().onFeatureGet(result.status == Status.OK, mFeature, mValue);
getListener().onFeatureGet(result.status == Status.OK, features, featureState);
}
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {