From ef410e3c1e64ca77f2897bfc79b5c86e2b75ad21 Mon Sep 17 00:00:00 2001 From: Ilya Matyukhin Date: Tue, 4 Feb 2020 13:39:48 -0800 Subject: Add support for fingerprint@2.2 and face@1.1 This CL adds the necessary plumbing, but not implementation, to do the following: biometrics.fingerprint@2.2: - enroll_2_2(..., handle windowId) - authenticate_2_2(..., handle windowId) biometrics.face@1.1: - enroll_1_1(..., handle windowId) - enrollRemotely(...) Bug: 148493694 Bug: 145562442 Test: enrolled and authenticated with Keyguard and BiometricPrompt Test: compiled face@1.0 HAL as face@1.1 and made sure it works Change-Id: I22e25df4d880d16d21a940e7dff6d233b64743d1 --- .../biometrics/BiometricNativeHandleUtils.java | 79 ++++++++++++++++++++ .../biometrics/IBiometricNativeHandle.aidl | 26 +++++++ core/java/android/hardware/face/FaceManager.java | 86 ++++++++++++++++++++-- core/java/android/hardware/face/IFaceService.aidl | 5 ++ .../hardware/fingerprint/FingerprintManager.java | 84 +++++++++++++++------ .../hardware/fingerprint/IFingerprintService.aidl | 8 +- services/core/Android.bp | 4 +- .../server/biometrics/AuthenticationClient.java | 22 +++++- .../server/biometrics/BiometricServiceBase.java | 16 ++-- .../android/server/biometrics/EnrollClient.java | 21 +++++- .../java/com/android/server/biometrics/Utils.java | 32 ++++++++ .../server/biometrics/face/FaceService.java | 39 +++++++--- .../fingerprint/FingerprintAuthenticator.java | 2 +- .../biometrics/fingerprint/FingerprintService.java | 54 +++++++++++--- 14 files changed, 412 insertions(+), 66 deletions(-) create mode 100644 core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java create mode 100644 core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl diff --git a/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java b/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java new file mode 100644 index 000000000000..5544eaeca7f3 --- /dev/null +++ b/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java @@ -0,0 +1,79 @@ +/* + * 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 android.hardware.biometrics; + +import android.os.NativeHandle; +import android.os.ParcelFileDescriptor; + +import java.io.IOException; + +/** + * A class that contains utilities for IBiometricNativeHandle. + * + * @hide + */ +public final class BiometricNativeHandleUtils { + + private BiometricNativeHandleUtils() { + } + + /** + * Converts a {@link NativeHandle} into an {@link IBiometricNativeHandle} by duplicating the + * underlying file descriptors. + * + * Both the original and new handle must be closed after use. + * + * @param h {@link NativeHandle}. Usually used to identify a WindowManager window. Can be null. + * @return A {@link IBiometricNativeHandle} representation of {@code h}. Will be null if + * {@code h} or its raw file descriptors are null. + */ + public static IBiometricNativeHandle dup(NativeHandle h) { + IBiometricNativeHandle handle = null; + if (h != null && h.getFileDescriptors() != null && h.getInts() != null) { + handle = new IBiometricNativeHandle(); + handle.ints = h.getInts().clone(); + handle.fds = new ParcelFileDescriptor[h.getFileDescriptors().length]; + for (int i = 0; i < h.getFileDescriptors().length; ++i) { + try { + handle.fds[i] = ParcelFileDescriptor.dup(h.getFileDescriptors()[i]); + } catch (IOException e) { + return null; + } + } + } + return handle; + } + + /** + * Closes the handle's file descriptors. + * + * @param h {@link IBiometricNativeHandle} handle. + */ + public static void close(IBiometricNativeHandle h) { + if (h != null) { + for (ParcelFileDescriptor fd : h.fds) { + if (fd != null) { + try { + fd.close(); + } catch (IOException e) { + // do nothing. + } + } + } + } + } +} diff --git a/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl b/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl new file mode 100644 index 000000000000..6dcdc1be3a50 --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl @@ -0,0 +1,26 @@ +/* + * 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 android.hardware.biometrics; + +/** + * Representation of a native handle. + * Copied from /common/aidl/android/hardware/common/NativeHandle.aidl + * @hide + */ +parcelable IBiometricNativeHandle { + ParcelFileDescriptor[] fds; + int[] ints; +} diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 55ebe285af1e..6bda46b0b692 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -29,7 +29,9 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.BiometricNativeHandleUtils; import android.hardware.biometrics.CryptoObject; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; import android.os.CancellationSignal; @@ -38,6 +40,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; +import android.os.NativeHandle; import android.os.PowerManager; import android.os.RemoteException; import android.os.Trace; @@ -244,6 +247,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } + /** + * Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, + * int[], NativeHandle)} with {@code windowId} set to null. + * + * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], + * NativeHandle) + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void enroll(int userId, byte[] token, CancellationSignal cancel, + EnrollmentCallback callback, int[] disabledFeatures) { + enroll(userId, token, cancel, callback, disabledFeatures, null /* windowId */); + } + /** * Request face authentication enrollment. This call operates the face authentication hardware * and starts capturing images. Progress will be indicated by callbacks to the @@ -259,11 +275,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @param flags optional flags * @param userId the user to whom this face will belong to * @param callback an object to receive enrollment events + * @param windowId optional ID of a camera preview window for a single-camera device. Must be + * null if not used. * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) public void enroll(int userId, byte[] token, CancellationSignal cancel, - EnrollmentCallback callback, int[] disabledFeatures) { + EnrollmentCallback callback, int[] disabledFeatures, @Nullable NativeHandle windowId) { if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); } @@ -278,20 +296,72 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } if (mService != null) { + IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId); try { mEnrollmentCallback = callback; Trace.beginSection("FaceManager#enroll"); mService.enroll(userId, mToken, token, mServiceReceiver, - mContext.getOpPackageName(), disabledFeatures); + mContext.getOpPackageName(), disabledFeatures, handle); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); - if (callback != null) { - // Though this may not be a hardware issue, it will cause apps to give up or - // try again later. - callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, - getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, + // Though this may not be a hardware issue, it will cause apps to give up or + // try again later. + callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, + getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */)); + } finally { + Trace.endSection(); + BiometricNativeHandleUtils.close(handle); + } + } + } + + /** + * Request face authentication enrollment for a remote client, for example Android Auto. + * This call operates the face authentication hardware and starts capturing images. + * Progress will be indicated by callbacks to the + * {@link EnrollmentCallback} object. It terminates when + * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or + * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at + * which point the object is no longer valid. The operation can be canceled by using the + * provided cancel object. + * + * @param token a unique token provided by a recent creation or verification of device + * credentials (e.g. pin, pattern or password). + * @param cancel an object that can be used to cancel enrollment + * @param userId the user to whom this face will belong to + * @param callback an object to receive enrollment events + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void enrollRemotely(int userId, byte[] token, CancellationSignal cancel, + EnrollmentCallback callback, int[] disabledFeatures) { + if (callback == null) { + throw new IllegalArgumentException("Must supply an enrollment callback"); + } + + if (cancel != null) { + if (cancel.isCanceled()) { + Log.w(TAG, "enrollRemotely is already canceled."); + return; + } else { + cancel.setOnCancelListener(new OnEnrollCancelListener()); + } + } + + if (mService != null) { + try { + mEnrollmentCallback = callback; + Trace.beginSection("FaceManager#enrollRemotely"); + mService.enrollRemotely(userId, mToken, token, mServiceReceiver, + mContext.getOpPackageName(), disabledFeatures); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in enrollRemotely: ", e); + // Though this may not be a hardware issue, it will cause apps to give up or + // try again later. + callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, + getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */)); - } } finally { Trace.endSection(); } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 68a4aef7af08..8ba24735c039 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -15,6 +15,7 @@ */ package android.hardware.face; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.face.IFaceServiceReceiver; @@ -51,6 +52,10 @@ interface IFaceService { // Start face enrollment void enroll(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver, + String opPackageName, in int [] disabledFeatures, in IBiometricNativeHandle windowId); + + // Start remote face enrollment + void enrollRemotely(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver, String opPackageName, in int [] disabledFeatures); // Cancel enrollment in progress diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index ff9d14510d4b..f6717c77f90e 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -32,7 +32,9 @@ import android.content.Context; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.BiometricNativeHandleUtils; import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; import android.os.CancellationSignal; @@ -41,6 +43,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; +import android.os.NativeHandle; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; @@ -402,16 +405,34 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } + /** + * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, + * AuthenticationCallback, Handler, int, NativeHandle)} with {@code windowId} set to null. + * + * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, + * AuthenticationCallback, Handler, int, NativeHandle) + * + * @hide + */ + @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) + public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, + int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) { + authenticate(crypto, cancel, flags, callback, handler, userId, null /* windowId */); + } + /** * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not * display the BiometricPrompt. * @param userId the user ID that the fingerprint hardware will authenticate for. + * @param windowId for optical fingerprint sensors that require active illumination by the OLED + * display. Should be null for devices that don't require illumination. * @hide */ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) { + int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId, + @Nullable NativeHandle windowId) { if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } @@ -425,25 +446,43 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - if (mService != null) try { - useHandler(handler); - mAuthenticationCallback = callback; - mCryptoObject = crypto; - long sessionId = crypto != null ? crypto.getOpId() : 0; - mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags, - mContext.getOpPackageName()); - } catch (RemoteException e) { - Slog.w(TAG, "Remote exception while authenticating: ", e); - if (callback != null) { + if (mService != null) { + IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId); + try { + useHandler(handler); + mAuthenticationCallback = callback; + mCryptoObject = crypto; + long sessionId = crypto != null ? crypto.getOpId() : 0; + mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags, + mContext.getOpPackageName(), handle); + } catch (RemoteException e) { + Slog.w(TAG, "Remote exception while authenticating: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try // again later. callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE, getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */)); + 0 /* vendorCode */)); + } finally { + BiometricNativeHandleUtils.close(handle); } } } + /** + * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int, int, + * EnrollmentCallback, NativeHandle)} with {@code windowId} set to null. + * + * @see FingerprintManager#enroll(byte[], CancellationSignal, int, int, EnrollmentCallback, + * NativeHandle) + * + * @hide + */ + @RequiresPermission(MANAGE_FINGERPRINT) + public void enroll(byte [] token, CancellationSignal cancel, int flags, + int userId, EnrollmentCallback callback) { + enroll(token, cancel, flags, userId, callback, null /* windowId */); + } + /** * Request fingerprint enrollment. This call warms up the fingerprint hardware * and starts scanning for fingerprints. Progress will be indicated by callbacks to the @@ -462,7 +501,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] token, CancellationSignal cancel, int flags, - int userId, EnrollmentCallback callback) { + int userId, EnrollmentCallback callback, @Nullable NativeHandle windowId) { if (userId == UserHandle.USER_CURRENT) { userId = getCurrentUserId(); } @@ -479,18 +518,21 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - if (mService != null) try { - mEnrollmentCallback = callback; - mService.enroll(mToken, token, userId, mServiceReceiver, flags, - mContext.getOpPackageName()); - } catch (RemoteException e) { - Slog.w(TAG, "Remote exception in enroll: ", e); - if (callback != null) { + if (mService != null) { + IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId); + try { + mEnrollmentCallback = callback; + mService.enroll(mToken, token, userId, mServiceReceiver, flags, + mContext.getOpPackageName(), handle); + } catch (RemoteException 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 try // again later. callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE, getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */)); + 0 /* vendorCode */)); + } finally { + BiometricNativeHandleUtils.close(handle); } } } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 1a7e12856753..f2ffd08d5bc8 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -15,6 +15,7 @@ */ package android.hardware.fingerprint; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.fingerprint.IFingerprintClientActiveCallback; @@ -31,7 +32,8 @@ interface IFingerprintService { // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes // through FingerprintManager now. void authenticate(IBinder token, long sessionId, int userId, - IFingerprintServiceReceiver receiver, int flags, String opPackageName); + IFingerprintServiceReceiver receiver, int flags, String opPackageName, + in IBiometricNativeHandle windowId); // This method prepares the service to start authenticating, but doesn't start authentication. // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be @@ -40,7 +42,7 @@ interface IFingerprintService { // startPreparedClient(). void prepareForAuthentication(IBinder token, long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie, - int callingUid, int callingPid, int callingUserId); + int callingUid, int callingPid, int callingUserId, in IBiometricNativeHandle windowId); // Starts authentication with the previously prepared client. void startPreparedClient(int cookie); @@ -55,7 +57,7 @@ interface IFingerprintService { // Start fingerprint enrollment void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver, - int flags, String opPackageName); + int flags, String opPackageName, in IBiometricNativeHandle windowId); // Cancel enrollment in progress void cancelEnrollment(IBinder token); diff --git a/services/core/Android.bp b/services/core/Android.bp index a603fa975b74..f33237f490e4 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -115,8 +115,8 @@ java_library_static { "android.hardware.health-V2.0-java", "android.hardware.light-java", "android.hardware.weaver-V1.0-java", - "android.hardware.biometrics.face-V1.0-java", - "android.hardware.biometrics.fingerprint-V2.1-java", + "android.hardware.biometrics.face-V1.1-java", + "android.hardware.biometrics.fingerprint-V2.2-java", "android.hardware.oemlock-V1.0-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 766e5c4d638f..7bbda9f3f732 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -20,11 +20,14 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.os.IBinder; +import android.os.NativeHandle; import android.os.RemoteException; import android.security.KeyStore; import android.util.Slog; +import java.io.IOException; import java.util.ArrayList; /** @@ -41,6 +44,7 @@ public abstract class AuthenticationClient extends ClientMonitor { public static final int LOCKOUT_PERMANENT = 2; private final boolean mRequireConfirmation; + private final NativeHandle mWindowId; // We need to track this state since it's possible for applications to request for // authentication while the device is already locked out. In that case, the client is created @@ -69,11 +73,25 @@ public abstract class AuthenticationClient extends ClientMonitor { public AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, int cookie, boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation, + IBiometricNativeHandle windowId) { super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId, restricted, owner, cookie); mOpId = opId; mRequireConfirmation = requireConfirmation; + mWindowId = Utils.dupNativeHandle(windowId); + } + + @Override + public void destroy() { + if (mWindowId != null && mWindowId.getFileDescriptors() != null) { + try { + mWindowId.close(); + } catch (IOException e) { + Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e); + } + } + super.destroy(); } protected long getStartTimeMs() { @@ -233,7 +251,7 @@ public abstract class AuthenticationClient extends ClientMonitor { onStart(); try { mStartTimeMs = System.currentTimeMillis(); - final int result = getDaemonWrapper().authenticate(mOpId, getGroupId()); + final int result = getDaemonWrapper().authenticate(mOpId, getGroupId(), mWindowId); if (result != 0) { Slog.w(getLogTag(), "startAuthentication failed, result=" + result); mMetricsLogger.histogram(mConstants.tagAuthStartError(), result); diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 687d9353ebd1..0e709944b03f 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -32,6 +32,7 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.biometrics.IBiometricServiceReceiverInternal; @@ -43,6 +44,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IHwBinder; import android.os.IRemoteCallback; +import android.os.NativeHandle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -220,9 +222,10 @@ public abstract class BiometricServiceBase extends SystemService public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, int cookie, boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation, + IBiometricNativeHandle windowId) { super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId, - groupId, opId, restricted, owner, cookie, requireConfirmation); + groupId, opId, restricted, owner, cookie, requireConfirmation, windowId); } @Override @@ -283,10 +286,10 @@ public abstract class BiometricServiceBase extends SystemService public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, - final int[] disabledFeatures, int timeoutSec) { + final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) { super(context, getConstants(), daemon, halDeviceId, token, listener, userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(), - disabledFeatures, timeoutSec); + disabledFeatures, timeoutSec, windowId); } @Override @@ -472,12 +475,13 @@ public abstract class BiometricServiceBase extends SystemService */ protected interface DaemonWrapper { int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h. - int authenticate(long operationId, int groupId) throws RemoteException; + int authenticate(long operationId, int groupId, NativeHandle windowId) + throws RemoteException; int cancel() throws RemoteException; int remove(int groupId, int biometricId) throws RemoteException; int enumerate() throws RemoteException; int enroll(byte[] token, int groupId, int timeout, - ArrayList disabledFeatures) throws RemoteException; + ArrayList disabledFeatures, NativeHandle windowId) throws RemoteException; void resetLockout(byte[] token) throws RemoteException; } diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java index 7ebb7c059b4c..684795ec66b5 100644 --- a/services/core/java/com/android/server/biometrics/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/EnrollClient.java @@ -20,10 +20,13 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.os.IBinder; +import android.os.NativeHandle; import android.os.RemoteException; import android.util.Slog; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -35,6 +38,7 @@ public abstract class EnrollClient extends ClientMonitor { private final BiometricUtils mBiometricUtils; private final int[] mDisabledFeatures; private final int mTimeoutSec; + private final NativeHandle mWindowId; private long mEnrollmentStartTimeMs; @@ -44,13 +48,26 @@ public abstract class EnrollClient extends ClientMonitor { BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils, - final int[] disabledFeatures, int timeoutSec) { + final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) { super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); mBiometricUtils = utils; mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length); mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); mTimeoutSec = timeoutSec; + mWindowId = Utils.dupNativeHandle(windowId); + } + + @Override + public void destroy() { + if (mWindowId != null && mWindowId.getFileDescriptors() != null) { + try { + mWindowId.close(); + } catch (IOException e) { + Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e); + } + } + super.destroy(); } @Override @@ -102,7 +119,7 @@ public abstract class EnrollClient extends ClientMonitor { } final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec, - disabledFeatures); + disabledFeatures, mWindowId); if (result != 0) { Slog.w(getLogTag(), "startEnroll failed, result=" + result); mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result); diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 389763b5377a..2d4ab6308a8b 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -23,12 +23,17 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType; +import android.hardware.biometrics.IBiometricNativeHandle; import android.os.Build; import android.os.Bundle; +import android.os.NativeHandle; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; +import java.io.FileDescriptor; +import java.io.IOException; + public class Utils { public static boolean isDebugEnabled(Context context, int targetUserId) { if (targetUserId == UserHandle.USER_NULL) { @@ -237,4 +242,31 @@ public class Utils { throw new IllegalArgumentException("Unsupported dismissal reason: " + reason); } } + + /** + * Converts an {@link IBiometricNativeHandle} to a {@link NativeHandle} by duplicating the + * the underlying file descriptors. + * + * Both the original and new handle must be closed after use. + * + * @param h {@link IBiometricNativeHandle} received as a binder call argument. Usually used to + * identify a WindowManager window. Can be null. + * @return A {@link NativeHandle} representation of {@code h}. Will be null if either {@code h} + * or its contents are null. + */ + public static NativeHandle dupNativeHandle(IBiometricNativeHandle h) { + NativeHandle handle = null; + if (h != null && h.fds != null && h.ints != null) { + FileDescriptor[] fds = new FileDescriptor[h.fds.length]; + for (int i = 0; i < h.fds.length; ++i) { + try { + fds[i] = h.fds[i].dup().getFileDescriptor(); + } catch (IOException e) { + return null; + } + } + handle = new NativeHandle(fds, h.ints, true /* own */); + } + return handle; + } } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index b512475e7971..31c3d4d7b24e 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -34,6 +34,7 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.face.V1_0.IBiometricsFace; @@ -214,9 +215,10 @@ public class FaceService extends BiometricServiceBase { public FaceAuthClient(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, int cookie, boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation, + IBiometricNativeHandle windowId) { super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId, - restricted, owner, cookie, requireConfirmation); + restricted, owner, cookie, requireConfirmation, windowId); } @Override @@ -373,7 +375,7 @@ public class FaceService extends BiometricServiceBase { @Override // Binder call public void enroll(int userId, final IBinder token, final byte[] cryptoToken, final IFaceServiceReceiver receiver, final String opPackageName, - final int[] disabledFeatures) { + final int[] disabledFeatures, IBiometricNativeHandle windowId) { checkPermission(MANAGE_BIOMETRIC); updateActiveGroup(userId, opPackageName); @@ -384,7 +386,7 @@ public class FaceService extends BiometricServiceBase { final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures, - ENROLL_TIMEOUT_SEC) { + ENROLL_TIMEOUT_SEC, windowId) { @Override public int[] getAcquireIgnorelist() { @@ -410,6 +412,14 @@ public class FaceService extends BiometricServiceBase { enrollInternal(client, mCurrentUserId); } + @Override // Binder call + public void enrollRemotely(int userId, final IBinder token, final byte[] cryptoToken, + final IFaceServiceReceiver receiver, final String opPackageName, + final int[] disabledFeatures) { + checkPermission(MANAGE_BIOMETRIC); + // TODO(b/145027036): Implement this. + } + @Override // Binder call public void cancelEnrollment(final IBinder token) { checkPermission(MANAGE_BIOMETRIC); @@ -426,7 +436,7 @@ public class FaceService extends BiometricServiceBase { final AuthenticationClientImpl client = new FaceAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, - 0 /* cookie */, false /* requireConfirmation */); + 0 /* cookie */, false /* requireConfirmation */, null /* windowId */); authenticateInternal(client, opId, opPackageName); } @@ -442,7 +452,7 @@ public class FaceService extends BiometricServiceBase { mDaemonWrapper, mHalDeviceId, token, new BiometricPromptServiceListenerImpl(wrapperReceiver), mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie, - requireConfirmation); + requireConfirmation, null /* windowId */); authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId); } @@ -985,7 +995,8 @@ public class FaceService extends BiometricServiceBase { */ private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() { @Override - public int authenticate(long operationId, int groupId) throws RemoteException { + public int authenticate(long operationId, int groupId, NativeHandle windowId) + throws RemoteException { IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "authenticate(): no face HAL!"); @@ -1026,7 +1037,7 @@ public class FaceService extends BiometricServiceBase { @Override public int enroll(byte[] cryptoToken, int groupId, int timeout, - ArrayList disabledFeatures) throws RemoteException { + ArrayList disabledFeatures, NativeHandle windowId) throws RemoteException { IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "enroll(): no face HAL!"); @@ -1036,7 +1047,17 @@ public class FaceService extends BiometricServiceBase { for (int i = 0; i < cryptoToken.length; i++) { token.add(cryptoToken[i]); } - return daemon.enroll(token, timeout, disabledFeatures); + android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 = + android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom( + daemon); + if (daemon11 != null) { + return daemon11.enroll_1_1(token, timeout, disabledFeatures, windowId); + } else if (windowId == null) { + return daemon.enroll(token, timeout, disabledFeatures); + } else { + Slog.e(TAG, "enroll(): windowId is only supported in @1.1 HAL"); + return ERROR_ESRCH; + } } @Override diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java index 6150de151ccc..7a4e62e18474 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java @@ -38,7 +38,7 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) throws RemoteException { mFingerprintService.prepareForAuthentication(token, sessionId, userId, wrapperReceiver, - opPackageName, cookie, callingUid, callingPid, callingUserId); + opPackageName, cookie, callingUid, callingPid, callingUserId, null /* windowId */); } @Override diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 44797ad97b37..57d1867b3aca 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -37,6 +37,7 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; @@ -50,6 +51,7 @@ import android.os.Binder; import android.os.Build; import android.os.Environment; import android.os.IBinder; +import android.os.NativeHandle; import android.os.RemoteException; import android.os.SELinux; import android.os.SystemClock; @@ -132,9 +134,9 @@ public class FingerprintService extends BiometricServiceBase { DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, - boolean requireConfirmation) { + boolean requireConfirmation, IBiometricNativeHandle windowId) { super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId, - restricted, owner, cookie, requireConfirmation); + restricted, owner, cookie, requireConfirmation, windowId); } @Override @@ -198,7 +200,7 @@ public class FingerprintService extends BiometricServiceBase { @Override // Binder call public void enroll(final IBinder token, final byte[] cryptoToken, final int userId, final IFingerprintServiceReceiver receiver, final int flags, - final String opPackageName) { + final String opPackageName, IBiometricNativeHandle windowId) { checkPermission(MANAGE_FINGERPRINT); final boolean restricted = isRestricted(); @@ -206,7 +208,7 @@ public class FingerprintService extends BiometricServiceBase { final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId, cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */, - ENROLL_TIMEOUT_SEC) { + ENROLL_TIMEOUT_SEC, windowId) { @Override public boolean shouldVibrate() { return true; @@ -230,20 +232,22 @@ public class FingerprintService extends BiometricServiceBase { @Override // Binder call public void authenticate(final IBinder token, final long opId, final int groupId, final IFingerprintServiceReceiver receiver, final int flags, - final String opPackageName) { + final String opPackageName, IBiometricNativeHandle windowId) { updateActiveGroup(groupId, opPackageName); final boolean restricted = isRestricted(); final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId, opId, restricted, opPackageName, - 0 /* cookie */, false /* requireConfirmation */); + 0 /* cookie */, false /* requireConfirmation */, + windowId); authenticateInternal(client, opId, opPackageName); } @Override // Binder call public void prepareForAuthentication(IBinder token, long opId, int groupId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, - int cookie, int callingUid, int callingPid, int callingUserId) { + int cookie, int callingUid, int callingPid, int callingUserId, + IBiometricNativeHandle windowId) { checkPermission(MANAGE_BIOMETRIC); updateActiveGroup(groupId, opPackageName); final boolean restricted = true; // BiometricPrompt is always restricted @@ -251,7 +255,8 @@ public class FingerprintService extends BiometricServiceBase { mDaemonWrapper, mHalDeviceId, token, new BiometricPromptServiceListenerImpl(wrapperReceiver), mCurrentUserId, groupId, opId, restricted, opPackageName, cookie, - false /* requireConfirmation */); + false /* requireConfirmation */, + windowId); authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId); } @@ -654,13 +659,24 @@ public class FingerprintService extends BiometricServiceBase { */ private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() { @Override - public int authenticate(long operationId, int groupId) throws RemoteException { + public int authenticate(long operationId, int groupId, NativeHandle windowId) + throws RemoteException { IBiometricsFingerprint daemon = getFingerprintDaemon(); if (daemon == null) { Slog.w(TAG, "authenticate(): no fingerprint HAL!"); return ERROR_ESRCH; } - return daemon.authenticate(operationId, groupId); + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 = + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom( + daemon); + if (daemon22 != null) { + return daemon22.authenticate_2_2(operationId, groupId, windowId); + } else if (windowId == null) { + return daemon.authenticate(operationId, groupId); + } else { + Slog.e(TAG, "authenticate(): windowId is only supported in @2.2 HAL"); + return ERROR_ESRCH; + } } @Override @@ -695,13 +711,27 @@ public class FingerprintService extends BiometricServiceBase { @Override public int enroll(byte[] cryptoToken, int groupId, int timeout, - ArrayList disabledFeatures) throws RemoteException { + ArrayList disabledFeatures, NativeHandle windowId) throws RemoteException { IBiometricsFingerprint daemon = getFingerprintDaemon(); if (daemon == null) { Slog.w(TAG, "enroll(): no fingerprint HAL!"); return ERROR_ESRCH; } - return daemon.enroll(cryptoToken, groupId, timeout); + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 = + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom( + daemon); + if (daemon22 != null) { + ArrayList cryptoTokenAsList = new ArrayList<>(cryptoToken.length); + for (byte b : cryptoToken) { + cryptoTokenAsList.add(b); + } + return daemon22.enroll_2_2(cryptoTokenAsList, groupId, timeout, windowId); + } else if (windowId == null) { + return daemon.enroll(cryptoToken, groupId, timeout); + } else { + Slog.e(TAG, "enroll(): windowId is only supported in @2.2 HAL"); + return ERROR_ESRCH; + } } @Override -- cgit v1.2.3-59-g8ed1b