Merge "Fix enrollment preview surface leak" into sc-dev

This commit is contained in:
Ilya Matyukhin
2021-07-21 17:41:34 +00:00
committed by Android (Google) Code Review
11 changed files with 206 additions and 89 deletions

View File

@@ -74,7 +74,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private final IFaceService mService; private final IFaceService mService;
private final Context mContext; private final Context mContext;
private IBinder mToken = new Binder(); private final IBinder mToken = new Binder();
@Nullable private AuthenticationCallback mAuthenticationCallback; @Nullable private AuthenticationCallback mAuthenticationCallback;
@Nullable private FaceDetectionCallback mFaceDetectionCallback; @Nullable private FaceDetectionCallback mFaceDetectionCallback;
@Nullable private EnrollmentCallback mEnrollmentCallback; @Nullable private EnrollmentCallback mEnrollmentCallback;
@@ -86,7 +86,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private Face mRemovalFace; private Face mRemovalFace;
private Handler mHandler; private Handler mHandler;
private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() { private final IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
@Override // binder call @Override // binder call
public void onEnrollResult(Face face, int remaining) { public void onEnrollResult(Face face, int remaining) {
@@ -293,7 +293,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
/** /**
* Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, * Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback,
* int[], Surface)} with {@code surface} set to null. * int[], Surface)} with {@code previewSurface} set to null.
* *
* @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], Surface) * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], Surface)
* @hide * @hide
@@ -301,8 +301,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
@RequiresPermission(MANAGE_BIOMETRIC) @RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures) { EnrollmentCallback callback, int[] disabledFeatures) {
enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures, null /* surface */, enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures,
false /* debugConsent */); null /* previewSurface */, false /* debugConsent */);
} }
/** /**
@@ -319,14 +319,14 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @param cancel an object that can be used to cancel enrollment * @param cancel an object that can be used to cancel enrollment
* @param userId the user to whom this face will belong to * @param userId the user to whom this face will belong to
* @param callback an object to receive enrollment events * @param callback an object to receive enrollment events
* @param surface optional camera preview surface for a single-camera device. * @param previewSurface optional camera preview surface for a single-camera device.
* Must be null if not used. * Must be null if not used.
* @param debugConsent a feature flag that the user has consented to debug. * @param debugConsent a feature flag that the user has consented to debug.
* @hide * @hide
*/ */
@RequiresPermission(MANAGE_BIOMETRIC) @RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface surface, EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface previewSurface,
boolean debugConsent) { boolean debugConsent) {
if (callback == null) { if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback"); throw new IllegalArgumentException("Must supply an enrollment callback");
@@ -346,7 +346,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
mEnrollmentCallback = callback; mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enroll"); Trace.beginSection("FaceManager#enroll");
mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver, mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
mContext.getOpPackageName(), disabledFeatures, surface, debugConsent); mContext.getOpPackageName(), disabledFeatures, previewSurface,
debugConsent);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e); Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or // Though this may not be a hardware issue, it will cause apps to give up or
@@ -853,10 +854,10 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @hide * @hide
*/ */
public static class AuthenticationResult { public static class AuthenticationResult {
private Face mFace; private final Face mFace;
private CryptoObject mCryptoObject; private final CryptoObject mCryptoObject;
private int mUserId; private final int mUserId;
private boolean mIsStrongBiometric; private final boolean mIsStrongBiometric;
/** /**
* Authentication result * Authentication result
@@ -1127,7 +1128,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
} }
private class OnAuthenticationCancelListener implements OnCancelListener { private class OnAuthenticationCancelListener implements OnCancelListener {
private CryptoObject mCrypto; private final CryptoObject mCrypto;
OnAuthenticationCancelListener(CryptoObject crypto) { OnAuthenticationCancelListener(CryptoObject crypto) {
mCrypto = crypto; mCrypto = crypto;

View File

@@ -75,7 +75,7 @@ interface IFaceService {
// Start face enrollment // Start face enrollment
void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures, in Surface surface, boolean debugConsent); String opPackageName, in int [] disabledFeatures, in Surface previewSurface, boolean debugConsent);
// Start remote face enrollment // Start remote face enrollment
void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,

View File

@@ -215,7 +215,7 @@ public class FaceService extends SystemService {
@Override // Binder call @Override // Binder call
public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken, public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName, final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface surface, boolean debugConsent) { final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final Pair<Integer, ServiceProvider> provider = getSingleProvider(); final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -225,8 +225,7 @@ public class FaceService extends SystemService {
} }
provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, disabledFeatures, receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
convertSurfaceToNativeHandle(surface), debugConsent);
} }
@Override // Binder call @Override // Binder call
@@ -703,5 +702,27 @@ public class FaceService extends SystemService {
publishBinderService(Context.FACE_SERVICE, mServiceWrapper); publishBinderService(Context.FACE_SERVICE, mServiceWrapper);
} }
private native NativeHandle convertSurfaceToNativeHandle(Surface surface); /**
* Acquires a NativeHandle that can be used to access the provided surface. The returned handle
* must be explicitly released with {@link #releaseSurfaceHandle(NativeHandle)} to avoid memory
* leaks.
*
* The caller is responsible for ensuring that the surface is valid while using the handle.
* This method provides no lifecycle synchronization between the surface and the handle.
*
* @param surface a valid Surface.
* @return {@link android.os.NativeHandle} a NativeHandle for the provided surface.
*/
public static native NativeHandle acquireSurfaceHandle(@NonNull Surface surface);
/**
* Releases resources associated with a NativeHandle that was acquired with
* {@link #acquireSurfaceHandle(Surface)}.
*
* This method has no affect on the surface for which the handle was acquired. It only frees up
* the resources that are associated with the handle.
*
* @param handle a handle that was obtained from {@link #acquireSurfaceHandle(Surface)}.
*/
public static native void releaseSurfaceHandle(@NonNull NativeHandle handle);
} }

View File

@@ -26,8 +26,8 @@ import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.IFaceServiceReceiver;
import android.os.IBinder; import android.os.IBinder;
import android.os.NativeHandle;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -75,8 +75,8 @@ public interface ServiceProvider {
int getLockoutModeForUser(int sensorId, int userId); int getLockoutModeForUser(int sensorId, int userId);
/** /**
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to be
* be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} * invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
*/ */
default void scheduleInvalidateAuthenticatorId(int sensorId, int userId, default void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) { @NonNull IInvalidationCallback callback) {
@@ -96,7 +96,7 @@ public interface ServiceProvider {
void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
@NonNull int[] disabledFeatures, @Nullable NativeHandle surfaceHandle, @NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
boolean debugConsent); boolean debugConsent);
void cancelEnrollment(int sensorId, @NonNull IBinder token); void cancelEnrollment(int sensorId, @NonNull IBinder token);

View File

@@ -150,8 +150,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC); Utils.checkPermission(mContext, TEST_BIOMETRIC);
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */, mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
false /* debugConsent */); null /* previewSurface */, false /* debugConsent */);
} }
@Override @Override

View File

@@ -26,21 +26,24 @@ import android.hardware.biometrics.face.EnrollmentType;
import android.hardware.biometrics.face.Feature; import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession; import android.hardware.biometrics.face.ISession;
import android.hardware.common.NativeHandle;
import android.hardware.face.Face; import android.hardware.face.Face;
import android.hardware.face.FaceEnrollFrame; import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.FaceManager; import android.hardware.face.FaceManager;
import android.os.IBinder; import android.os.IBinder;
import android.os.NativeHandle;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import android.view.Surface;
import com.android.internal.R; import com.android.internal.R;
import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils; import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient; import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.face.FaceService;
import com.android.server.biometrics.sensors.face.FaceUtils; import com.android.server.biometrics.sensors.face.FaceUtils;
import java.io.IOException; import java.io.IOException;
@@ -57,16 +60,31 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
@NonNull private final int[] mEnrollIgnoreList; @NonNull private final int[] mEnrollIgnoreList;
@NonNull private final int[] mEnrollIgnoreListVendor; @NonNull private final int[] mEnrollIgnoreListVendor;
@NonNull private final int[] mDisabledFeatures; @NonNull private final int[] mDisabledFeatures;
@Nullable private final Surface mPreviewSurface;
@Nullable private android.os.NativeHandle mOsPreviewHandle;
@Nullable private NativeHandle mHwPreviewHandle;
@Nullable private ICancellationSignal mCancellationSignal; @Nullable private ICancellationSignal mCancellationSignal;
@Nullable private android.hardware.common.NativeHandle mPreviewSurface;
private final int mMaxTemplatesPerUser; private final int mMaxTemplatesPerUser;
private final boolean mDebugConsent; private final boolean mDebugConsent;
private final BaseClientMonitor.Callback mPreviewHandleDeleterCallback =
new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
releaseSurfaceHandlesIfNeeded();
}
};
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable NativeHandle previewSurface, int sensorId, int maxTemplatesPerUser, @Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
boolean debugConsent) { boolean debugConsent) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils, super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
@@ -78,14 +96,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
mMaxTemplatesPerUser = maxTemplatesPerUser; mMaxTemplatesPerUser = maxTemplatesPerUser;
mDebugConsent = debugConsent; mDebugConsent = debugConsent;
mDisabledFeatures = disabledFeatures; mDisabledFeatures = disabledFeatures;
try { mPreviewSurface = previewSurface;
// We must manually close the duplicate handle after it's no longer needed.
// The caller is responsible for closing the original handle.
mPreviewSurface = AidlNativeHandleUtils.dup(previewSurface);
} catch (IOException e) {
mPreviewSurface = null;
Slog.e(TAG, "Failed to dup previewSurface", e);
}
} }
@Override @Override
@@ -98,17 +109,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
@NonNull @NonNull
@Override @Override
protected Callback wrapCallbackForStart(@NonNull Callback callback) { protected Callback wrapCallbackForStart(@NonNull Callback callback) {
return new CompositeCallback(createALSCallback(), callback); return new CompositeCallback(mPreviewHandleDeleterCallback, createALSCallback(), callback);
}
@Override
public void destroy() {
try {
AidlNativeHandleUtils.close(mPreviewSurface);
} catch (IOException e) {
Slog.e(TAG, "Failed to close mPreviewSurface", e);
}
super.destroy();
} }
@Override @Override
@@ -153,22 +154,23 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
@Override @Override
protected void startHalOperation() { protected void startHalOperation() {
obtainSurfaceHandlesIfNeeded();
try { try {
List<Byte> featureList = new ArrayList<Byte>(); List<Byte> featureList = new ArrayList<Byte>();
if (mDebugConsent) { if (mDebugConsent) {
featureList.add(new Byte(Feature.DEBUG)); featureList.add(Feature.DEBUG);
} }
boolean shouldAddDiversePoses = true; boolean shouldAddDiversePoses = true;
for (int i = 0; i < mDisabledFeatures.length; i++) { for (int disabledFeature : mDisabledFeatures) {
if (AidlConversionUtils.convertFrameworkToAidlFeature(mDisabledFeatures[i]) if (AidlConversionUtils.convertFrameworkToAidlFeature(disabledFeature)
== Feature.REQUIRE_DIVERSE_POSES) { == Feature.REQUIRE_DIVERSE_POSES) {
shouldAddDiversePoses = false; shouldAddDiversePoses = false;
} }
} }
if (shouldAddDiversePoses) { if (shouldAddDiversePoses) {
featureList.add(new Byte(Feature.REQUIRE_DIVERSE_POSES)); featureList.add(Feature.REQUIRE_DIVERSE_POSES);
} }
byte[] features = new byte[featureList.size()]; byte[] features = new byte[featureList.size()];
@@ -178,7 +180,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
mCancellationSignal = getFreshDaemon().enroll( mCancellationSignal = getFreshDaemon().enroll(
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken), HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken),
EnrollmentType.DEFAULT, features, mPreviewSurface); EnrollmentType.DEFAULT, features, mHwPreviewHandle);
} catch (RemoteException | IllegalArgumentException e) { } catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Exception when requesting enroll", e); Slog.e(TAG, "Exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
@@ -198,4 +200,55 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
} }
} }
} }
private void obtainSurfaceHandlesIfNeeded() {
if (mPreviewSurface != null) {
// There is no direct way to convert Surface to android.hardware.common.NativeHandle. We
// first convert Surface to android.os.NativeHandle, and then android.os.NativeHandle to
// android.hardware.common.NativeHandle, which can be passed to the HAL.
// The resources for both handles must be explicitly freed to avoid memory leaks.
mOsPreviewHandle = FaceService.acquireSurfaceHandle(mPreviewSurface);
try {
// We must manually free up the resources for both handles after they are no longer
// needed. mHwPreviewHandle must be closed, but mOsPreviewHandle must be released
// through FaceService.
mHwPreviewHandle = AidlNativeHandleUtils.dup(mOsPreviewHandle);
Slog.v(TAG, "Obtained handles for the preview surface.");
} catch (IOException e) {
mHwPreviewHandle = null;
Slog.e(TAG, "Failed to dup mOsPreviewHandle", e);
}
}
}
private void releaseSurfaceHandlesIfNeeded() {
if (mPreviewSurface != null && mHwPreviewHandle == null) {
Slog.w(TAG, "mHwPreviewHandle is null even though mPreviewSurface is not null.");
}
if (mHwPreviewHandle != null) {
try {
Slog.v(TAG, "Closing mHwPreviewHandle");
AidlNativeHandleUtils.close(mHwPreviewHandle);
} catch (IOException e) {
Slog.e(TAG, "Failed to close mPreviewSurface", e);
}
mHwPreviewHandle = null;
}
if (mOsPreviewHandle != null) {
Slog.v(TAG, "Releasing mOsPreviewHandle");
FaceService.releaseSurfaceHandle(mOsPreviewHandle);
mOsPreviewHandle = null;
}
if (mPreviewSurface != null) {
Slog.v(TAG, "Releasing mPreviewSurface");
// We need to manually release this surface because it's a copy of the original surface
// that was sent to us by an app (e.g. Settings). The app cleans up its own surface (as
// part of the SurfaceView lifecycle, for example), but there is no mechanism in place
// that will clean up this copy.
// If this copy isn't cleaned up, it will eventually be garbage collected. However, this
// surface could be holding onto the native buffers that the GC is not aware of,
// exhausting the native memory before the GC feels the need to garbage collect.
mPreviewSurface.release();
}
}
} }

View File

@@ -37,13 +37,13 @@ import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.NativeHandle;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.UserManager; import android.os.UserManager;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils; import com.android.server.biometrics.Utils;
@@ -327,7 +327,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public void scheduleEnroll(int sensorId, @NonNull IBinder token, public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures, @NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable NativeHandle previewSurface, boolean debugConsent) { @Nullable Surface previewSurface, boolean debugConsent) {
mHandler.post(() -> { mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get( final int maxTemplatesPerUser = mSensors.get(
sensorId).getSensorProperties().maxEnrollmentsPerUser; sensorId).getSensorProperties().maxEnrollmentsPerUser;
@@ -400,7 +400,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override @Override
public void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId, public void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
scheduleRemoveSpecifiedIds(sensorId, token, new int[] {faceId}, userId, receiver, scheduleRemoveSpecifiedIds(sensorId, token, new int[]{faceId}, userId, receiver,
opPackageName); opPackageName);
} }
@@ -561,7 +561,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
} }
@Override @Override
public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {} public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {
}
@Override @Override
public void binderDied() { public void binderDied() {

View File

@@ -139,7 +139,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */, mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
null /* surfaceHandle */, false /* debugConsent */); null /* previewSurface */, false /* debugConsent */);
} }
@Override @Override

View File

@@ -47,6 +47,7 @@ import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.util.Slog; import android.util.Slog;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.FrameworkStatsLog;
@@ -88,8 +89,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or * Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or its extended
* its extended minor versions. * minor versions.
*/ */
public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@@ -325,7 +326,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
} }
} }
@VisibleForTesting Face10(@NonNull Context context, @VisibleForTesting
Face10(@NonNull Context context,
@NonNull FaceSensorPropertiesInternal sensorProps, @NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull BiometricScheduler scheduler) { @NonNull BiometricScheduler scheduler) {
@@ -571,7 +573,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public void scheduleEnroll(int sensorId, @NonNull IBinder token, public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures, @NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable NativeHandle surfaceHandle, boolean debugConsent) { @Nullable Surface previewSurface, boolean debugConsent) {
mHandler.post(() -> { mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId); scheduleUpdateActiveUserWithoutHandler(userId);
@@ -580,7 +582,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, surfaceHandle, mSensorId); ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override @Override
@@ -858,8 +860,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
} }
/** /**
* Schedules the {@link FaceUpdateActiveUserClient} without posting the work onto the * Schedules the {@link FaceUpdateActiveUserClient} without posting the work onto the handler.
* handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser" * Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
* invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
* this operation on the same lambda/runnable as those operations so that the ordering is * this operation on the same lambda/runnable as those operations so that the ordering is
* correct. * correct.

View File

@@ -26,9 +26,9 @@ import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face; import android.hardware.face.Face;
import android.hardware.face.FaceManager; import android.hardware.face.FaceManager;
import android.os.IBinder; import android.os.IBinder;
import android.os.NativeHandle;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import android.view.Surface;
import com.android.internal.R; import com.android.internal.R;
import com.android.server.biometrics.Utils; import com.android.server.biometrics.Utils;
@@ -40,15 +40,14 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
/** /**
* Face-specific enroll client supporting the {@link android.hardware.biometrics.face.V1_0} * Face-specific enroll client supporting the {@link android.hardware.biometrics.face.V1_0} HIDL
* HIDL interface. * interface.
*/ */
public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
private static final String TAG = "FaceEnrollClient"; private static final String TAG = "FaceEnrollClient";
@NonNull private final int[] mDisabledFeatures; @NonNull private final int[] mDisabledFeatures;
@Nullable private final NativeHandle mSurfaceHandle;
@NonNull private final int[] mEnrollIgnoreList; @NonNull private final int[] mEnrollIgnoreList;
@NonNull private final int[] mEnrollIgnoreListVendor; @NonNull private final int[] mEnrollIgnoreListVendor;
@@ -56,12 +55,11 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable NativeHandle surfaceHandle, int sensorId) { @Nullable Surface previewSurface, int sensorId) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */); false /* shouldVibrate */);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mSurfaceHandle = surfaceHandle;
mEnrollIgnoreList = getContext().getResources() mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist); .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources() mEnrollIgnoreListVendor = getContext().getResources()

View File

@@ -16,18 +16,19 @@
#define LOG_TAG "SurfaceToNativeHandleConverter" #define LOG_TAG "SurfaceToNativeHandleConverter"
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include <android/native_window_jni.h>
#include <android_os_NativeHandle.h> #include <android_os_NativeHandle.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <gui/IGraphicBufferProducer.h> #include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h> #include <gui/Surface.h>
#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h> #include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <utils/Log.h>
#include "jni.h"
namespace android { namespace android {
namespace { namespace {
constexpr int WINDOW_HAL_TOKEN_SIZE_MAX = 256; constexpr int WINDOW_HAL_TOKEN_SIZE_MAX = 256;
native_handle_t* convertHalTokenToNativeHandle(const HalToken& halToken) { native_handle_t* convertHalTokenToNativeHandle(const HalToken& halToken) {
@@ -54,33 +55,73 @@ native_handle_t* convertHalTokenToNativeHandle(const HalToken& halToken) {
memcpy(&(nh->data[1]), halToken.data(), nhDataByteSize); memcpy(&(nh->data[1]), halToken.data(), nhDataByteSize);
return nh; return nh;
} }
} // namespace
using ::android::sp; HalToken convertNativeHandleToHalToken(native_handle_t* handle) {
int size = handle->data[0];
auto data = reinterpret_cast<uint8_t*>(&handle->data[1]);
HalToken halToken;
halToken.setToExternal(data, size);
return halToken;
}
static jobject convertSurfaceToNativeHandle(JNIEnv* env, jobject /* clazz */, jobject acquireSurfaceHandle(JNIEnv* env, jobject /* clazz */, jobject jSurface) {
jobject previewSurface) { ALOGD("%s", __func__);
if (previewSurface == nullptr) { if (jSurface == nullptr) {
ALOGE("%s: jSurface is null", __func__);
return nullptr; return nullptr;
} }
ANativeWindow* previewAnw = ANativeWindow_fromSurface(env, previewSurface);
sp<Surface> surface = static_cast<Surface*>(previewAnw); sp<Surface> surface = android_view_Surface_getSurface(env, jSurface);
if (surface == nullptr) {
ALOGE("%s: surface is null", __func__);
return nullptr;
}
sp<IGraphicBufferProducer> igbp = surface->getIGraphicBufferProducer(); sp<IGraphicBufferProducer> igbp = surface->getIGraphicBufferProducer();
sp<HGraphicBufferProducer> hgbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(igbp); sp<HGraphicBufferProducer> hgbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(igbp);
// The HAL token will be closed in releaseSurfaceHandle.
HalToken halToken; HalToken halToken;
createHalToken(hgbp, &halToken); createHalToken(hgbp, &halToken);
native_handle_t* native_handle = convertHalTokenToNativeHandle(halToken); native_handle_t* native_handle = convertHalTokenToNativeHandle(halToken);
return JNativeHandle::MakeJavaNativeHandleObj(env, native_handle); if (native_handle == nullptr) {
ALOGE("%s: native_handle is null", __func__);
return nullptr;
}
jobject jHandle = JNativeHandle::MakeJavaNativeHandleObj(env, native_handle);
native_handle_delete(native_handle);
return jHandle;
} }
static const JNINativeMethod method_table[] = { void releaseSurfaceHandle(JNIEnv* env, jobject /* clazz */, jobject jHandle) {
{"convertSurfaceToNativeHandle", "(Landroid/view/Surface;)Landroid/os/NativeHandle;", ALOGD("%s", __func__);
reinterpret_cast<void*>(convertSurfaceToNativeHandle)}, // Creates a native handle from a Java handle. We must call native_handle_delete when we're done
// with it because we created it, but we shouldn't call native_handle_close because we don't own
// the underlying FDs.
native_handle_t* handle =
JNativeHandle::MakeCppNativeHandle(env, jHandle, nullptr /* storage */);
if (handle == nullptr) {
ALOGE("%s: handle is null", __func__);
return;
}
HalToken token = convertNativeHandleToHalToken(handle);
ALOGD("%s: deleteHalToken, success: %d", __func__, deleteHalToken(token));
ALOGD("%s: native_handle_delete, success: %d", __func__, !native_handle_delete(handle));
}
const JNINativeMethod method_table[] = {
{"acquireSurfaceHandle", "(Landroid/view/Surface;)Landroid/os/NativeHandle;",
reinterpret_cast<void*>(acquireSurfaceHandle)},
{"releaseSurfaceHandle", "(Landroid/os/NativeHandle;)V",
reinterpret_cast<void*>(releaseSurfaceHandle)},
}; };
} // namespace
int register_android_server_FaceService(JNIEnv* env) { int register_android_server_FaceService(JNIEnv* env) {
return jniRegisterNativeMethods(env, "com/android/server/biometrics/sensors/face/FaceService", return AndroidRuntime::
method_table, NELEM(method_table)); registerNativeMethods(env, "com/android/server/biometrics/sensors/face/FaceService",
method_table, NELEM(method_table));
} }
} // namespace android
}; // namespace android