diff options
17 files changed, 584 insertions, 130 deletions
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index b64ce999ff82..9876de11a2bb 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -41,7 +41,6 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; -import android.util.Log; import android.util.Slog; import android.view.Surface; @@ -49,6 +48,9 @@ import com.android.internal.R; import com.android.internal.os.SomeArgs; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; /** * A class that coordinates access to the face authentication hardware. @@ -67,6 +69,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan private static final int MSG_REMOVED = 105; private static final int MSG_GET_FEATURE_COMPLETED = 106; private static final int MSG_SET_FEATURE_COMPLETED = 107; + private static final int MSG_CHALLENGE_GENERATED = 108; private IFaceService mService; private final Context mContext; @@ -76,6 +79,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan private RemovalCallback mRemovalCallback; private SetFeatureCallback mSetFeatureCallback; private GetFeatureCallback mGetFeatureCallback; + private GenerateChallengeCallback mGenerateChallengeCallback; private CryptoObject mCryptoObject; private Face mRemovalFace; private Handler mHandler; @@ -126,6 +130,17 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan args.arg2 = value; mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget(); } + + @Override + public void onChallengeGenerated(long challenge) { + if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) { + // Perform this on system_server thread, since the application's thread is + // blocked waiting for the result + mGenerateChallengeCallback.onGenerateChallengeResult(challenge); + } else { + mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget(); + } + } }; /** @@ -207,7 +222,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (cancel != null) { if (cancel.isCanceled()) { - Log.w(TAG, "authentication already canceled"); + Slog.w(TAG, "authentication already canceled"); return; } else { cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto)); @@ -224,7 +239,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mService.authenticate(mToken, operationId, userId, mServiceReceiver, flags, mContext.getOpPackageName()); } catch (RemoteException e) { - Log.w(TAG, "Remote exception while authenticating: ", e); + Slog.w(TAG, "Remote exception while authenticating: ", e); if (callback != null) { // Though this may not be a hardware issue, it will cause apps to give up or // try again later. @@ -278,7 +293,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (cancel != null) { if (cancel.isCanceled()) { - Log.w(TAG, "enrollment already canceled"); + Slog.w(TAG, "enrollment already canceled"); return; } else { cancel.setOnCancelListener(new OnEnrollCancelListener()); @@ -292,7 +307,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mService.enroll(userId, mToken, token, mServiceReceiver, mContext.getOpPackageName(), disabledFeatures, surface); } catch (RemoteException e) { - Log.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 // try again later. callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, @@ -330,7 +345,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (cancel != null) { if (cancel.isCanceled()) { - Log.w(TAG, "enrollRemotely is already canceled."); + Slog.w(TAG, "enrollRemotely is already canceled."); return; } else { cancel.setOnCancelListener(new OnEnrollCancelListener()); @@ -344,7 +359,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mService.enrollRemotely(userId, mToken, token, mServiceReceiver, mContext.getOpPackageName(), disabledFeatures); } catch (RemoteException e) { - Log.w(TAG, "Remote exception in enrollRemotely: ", e); + Slog.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, @@ -357,22 +372,56 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Requests an auth token to tie sensitive operations to the confirmation of - * existing device credentials (e.g. pin/pattern/password). + * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the + * TEE/hardware operation is complete. + * @return challenge generated in the TEE/hardware + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public long generateChallengeBlocking() { + final AtomicReference<Long> result = new AtomicReference<>(); + final CountDownLatch latch = new CountDownLatch(1); + final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() { + @Override + public void onGenerateChallengeResult(long challenge) { + result.set(challenge); + latch.countDown(); + } + }; + + generateChallenge(callback); + + try { + latch.await(1, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Slog.e(TAG, "Interrupted while generatingChallenge", e); + e.printStackTrace(); + } + return result.get(); + } + + /** + * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a + * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification. + * The HardwareAuthenticationToken can then be sent to the biometric HAL together with a + * request to perform sensitive operation(s) (for example enroll or setFeature), represented + * by the challenge. Doing this ensures that a the sensitive operation cannot be performed + * unless the user has entered confirmed PIN/Pattern/Password. + * + * @see com.android.server.locksettings.LockSettingsService * * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public long generateChallenge() { - long result = 0; + public void generateChallenge(GenerateChallengeCallback callback) { if (mService != null) { try { - result = mService.generateChallenge(mToken); + mGenerateChallengeCallback = callback; + mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - return result; } /** @@ -381,16 +430,14 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public int revokeChallenge() { - int result = 0; + public void revokeChallenge() { if (mService != null) { try { - result = mService.revokeChallenge(mToken); + mService.revokeChallenge(mToken, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - return result; } /** @@ -458,7 +505,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver, mContext.getOpPackageName()); } catch (RemoteException e) { - Log.w(TAG, "Remote exception in remove: ", e); + Slog.w(TAG, "Remote exception in remove: ", e); if (callback != null) { callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE, getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, @@ -546,7 +593,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan throw e.rethrowFromSystemServer(); } } else { - Log.w(TAG, "isFaceHardwareDetected(): Service not connected!"); + Slog.w(TAG, "isFaceHardwareDetected(): Service not connected!"); } return false; } @@ -586,7 +633,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan throw e.rethrowFromSystemServer(); } } else { - Log.w(TAG, "addLockoutResetCallback(): Service not connected!"); + Slog.w(TAG, "addLockoutResetCallback(): Service not connected!"); } } @@ -967,6 +1014,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public abstract void onCompleted(boolean success, int feature, boolean value); } + /** + * @hide + */ + public abstract static class GenerateChallengeCallback { + public abstract void onGenerateChallengeResult(long challenge); + } + + private abstract static class InternalGenerateChallengeCallback + extends GenerateChallengeCallback {} + private class OnEnrollCancelListener implements OnCancelListener { @Override public void onCancel() { @@ -1030,8 +1087,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan (boolean) args.arg2 /* value */); args.recycle(); break; + case MSG_CHALLENGE_GENERATED: + sendChallengeGenerated((long) msg.obj /* challenge */); + break; default: - Log.w(TAG, "Unknown message: " + msg.what); + Slog.w(TAG, "Unknown message: " + msg.what); } Trace.endSection(); } @@ -1051,12 +1111,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mGetFeatureCallback.onCompleted(success, feature, value); } + private void sendChallengeGenerated(long challenge) { + if (mGenerateChallengeCallback == null) { + return; + } + mGenerateChallengeCallback.onGenerateChallengeResult(challenge); + } + private void sendRemovedResult(Face face, int remaining) { if (mRemovalCallback == null) { return; } if (face == null) { - Log.e(TAG, "Received MSG_REMOVED, but face is null"); + Slog.e(TAG, "Received MSG_REMOVED, but face is null"); return; } mRemovalCallback.onRemovalSucceeded(face, remaining); diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index f5ac047885ca..8dbaf2115fe5 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -72,10 +72,10 @@ interface IFaceService { boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token - long generateChallenge(IBinder token); + void generateChallenge(IBinder token, IFaceServiceReceiver receiver, String opPackageName); // Finish an enrollment sequence and invalidate the authentication token - int revokeChallenge(IBinder token); + void revokeChallenge(IBinder token, String opPackageName); // Determine if a user has at least one enrolled face boolean hasEnrolledFaces(int userId, String opPackageName); diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl index ff3f6f39586e..844657e7453f 100644 --- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl +++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl @@ -30,4 +30,5 @@ oneway interface IFaceServiceReceiver { void onRemoved(in Face face, int remaining); void onFeatureSet(boolean success, int feature); void onFeatureGet(boolean success, int feature, boolean value); + void onChallengeGenerated(long challenge); } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 713c169352d0..4787984cdf41 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -50,7 +50,10 @@ import android.view.Surface; import java.security.Signature; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -75,6 +78,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private static final int MSG_AUTHENTICATION_FAILED = 103; private static final int MSG_ERROR = 104; private static final int MSG_REMOVED = 105; + private static final int MSG_CHALLENGE_GENERATED = 106; private IFingerprintService mService; private Context mContext; @@ -82,6 +86,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private AuthenticationCallback mAuthenticationCallback; private EnrollmentCallback mEnrollmentCallback; private RemovalCallback mRemovalCallback; + private GenerateChallengeCallback mGenerateChallengeCallback; private CryptoObject mCryptoObject; private Fingerprint mRemovalFingerprint; private Handler mHandler; @@ -346,6 +351,16 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing }; /** + * @hide + */ + public abstract static class GenerateChallengeCallback { + public abstract void onChallengeGenerated(long challenge); + } + + private abstract static class InternalGenerateChallengeCallback + extends GenerateChallengeCallback {} + + /** * Request authentication of a crypto object. This call warms up the fingerprint hardware * and starts scanning for a fingerprint. It terminates when * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or @@ -514,19 +529,56 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** - * Requests a pre-enrollment auth token to tie enrollment to the confirmation of - * existing device credentials (e.g. pin/pattern/password). + * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the + * TEE/hardware operation is complete. + * @return challenge generated in the TEE/hardware + * @hide + */ + @RequiresPermission(MANAGE_FINGERPRINT) + public long generateChallengeBlocking() { + final AtomicReference<Long> result = new AtomicReference<>(); + final CountDownLatch latch = new CountDownLatch(1); + final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() { + @Override + public void onChallengeGenerated(long challenge) { + result.set(challenge); + latch.countDown(); + } + }; + + generateChallenge(callback); + + try { + latch.await(1, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Slog.e(TAG, "Interrupted while generatingChallenge", e); + e.printStackTrace(); + } + + return result.get(); + } + + + /** + * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a + * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification. + * The HardwareAuthenticationToken can then be sent to the biometric HAL together with a + * request to perform sensitive operation(s) (for example enroll), represented by the challenge. + * Doing this ensures that a the sensitive operation cannot be performed unless the user has + * entered confirmed PIN/Pattern/Password. + * + * @see com.android.server.locksettings.LockSettingsService + * * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) - public long preEnroll() { - long result = 0; + public void generateChallenge(GenerateChallengeCallback callback) { if (mService != null) try { - result = mService.preEnroll(mToken); + mGenerateChallengeCallback = callback; + mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return result; } /** @@ -534,14 +586,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) - public int postEnroll() { - int result = 0; + public void revokeChallenge() { if (mService != null) try { - result = mService.postEnroll(mToken); + mService.revokeChallenge(mToken, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return result; } /** @@ -760,6 +810,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing case MSG_REMOVED: sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */); break; + case MSG_CHALLENGE_GENERATED: + sendChallengeGenerated((long) msg.obj /* challenge */); + break; + default: + Slog.w(TAG, "Unknown message: " + msg.what); + } } }; @@ -839,6 +895,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } + private void sendChallengeGenerated(long challenge) { + if (mGenerateChallengeCallback == null) { + Slog.e(TAG, "sendChallengeGenerated, callback null"); + return; + } + mGenerateChallengeCallback.onChallengeGenerated(challenge); + } + /** * @hide */ @@ -846,7 +910,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing mContext = context; mService = service; if (mService == null) { - Slog.v(TAG, "FingerprintManagerService was null"); + Slog.v(TAG, "FingerprintService was null"); } mHandler = new MyHandler(context); } @@ -992,6 +1056,17 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public void onRemoved(Fingerprint fp, int remaining) { mHandler.obtainMessage(MSG_REMOVED, remaining, 0, fp).sendToTarget(); } + + @Override // binder call + public void onChallengeGenerated(long challenge) { + if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) { + // Perform this on system_server thread, since the application's thread is + // blocked waiting for the result + mGenerateChallengeCallback.onChallengeGenerated(challenge); + } else { + mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget(); + } + } }; } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index f0c5781f60cb..b72881ace9df 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -76,10 +76,10 @@ interface IFingerprintService { boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token - long preEnroll(IBinder token); + void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver, String opPackageName); // Finish an enrollment sequence and invalidate the authentication token - int postEnroll(IBinder token); + void revokeChallenge(IBinder token, String opPackageName); // Determine if a user has at least one enrolled fingerprint boolean hasEnrolledFingerprints(int userId, String opPackageName); diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index 87748a32b40b..3da07b05efb6 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -28,4 +28,5 @@ oneway interface IFingerprintServiceReceiver { void onAuthenticationFailed(); void onError(int error, int vendorCode); void onRemoved(in Fingerprint fp, int remaining); + void onChallengeGenerated(long challenge); } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java index 301bd522e345..4c24d1a61e45 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java @@ -546,6 +546,18 @@ public abstract class BiometricServiceBase extends SystemService }); } + protected void generateChallengeInternal(GenerateChallengeClient client) { + mHandler.post(() -> { + startClient(client, true /* initiatedByClient */); + }); + } + + protected void revokeChallengeInternal(RevokeChallengeClient client) { + mHandler.post(() -> { + startClient(client, true /* initiatedByClient */); + }); + } + protected void authenticateInternal(AuthenticationClient client, String opPackageName) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java index 342fc7e54434..f4863f59db4d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java @@ -119,12 +119,11 @@ public final class ClientMonitorCallbackConverter { } } - // The following are only used internally within system_server - specifically, within - // BiometricServiceBase and their <Biometric>Service implementations. - - void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining) - throws RemoteException { - // Currently unused, BiometricServiceBase#handleEnumerate everything internally without - // needing to propagate this to any receiver. + public void onChallengeGenerated(long challenge) throws RemoteException { + if (mFaceServiceReceiver != null) { + mFaceServiceReceiver.onChallengeGenerated(challenge); + } else if (mFingerprintServiceReceiver != null) { + mFingerprintServiceReceiver.onChallengeGenerated(challenge); + } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java new file mode 100644 index 000000000000..98e83daceb6e --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +public abstract class GenerateChallengeClient extends ClientMonitor { + + private static final String TAG = "GenerateChallengeClient"; + + protected long mChallenge; + + public GenerateChallengeClient(FinishCallback finishCallback, Context context, IBinder token, + ClientMonitorCallbackConverter listener, String owner, int sensorId) { + super(finishCallback, context, token, listener, 0 /* userId */, false /* restricted */, + owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + } + + @Override + public void start() { + startHalOperation(); + try { + getListener().onChallengeGenerated(mChallenge); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + mFinishCallback.onClientFinished(this); + } + + @Override + protected void stopHalOperation() { + // Not supported for GenerateChallenge + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java new file mode 100644 index 000000000000..024ff576d230 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.os.IBinder; + +public abstract class RevokeChallengeClient extends ClientMonitor { + + public RevokeChallengeClient(FinishCallback finishCallback, Context context, IBinder token, + String owner, int sensorId) { + super(finishCallback, context, token, null /* listener */, 0 /* userId */, + false /* restricted */, owner, 0 /* cookie */, sensorId, + BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN); + } + + @Override + public void start() { + startHalOperation(); + mFinishCallback.onClientFinished(this); + } + + @Override + protected void stopHalOperation() { + // Not supported for RevokeChallenge + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java new file mode 100644 index 000000000000..7d10c59bf828 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.face.V1_0.IBiometricsFace; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.GenerateChallengeClient; + +/** + * Face-specific generateChallenge client supporting the + * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1} + * HIDL interfaces. + */ +public class FaceGenerateChallengeClient extends GenerateChallengeClient { + + private static final String TAG = "FaceGenerateChallengeClient"; + private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes + + private final IBiometricsFace mDaemon; + + FaceGenerateChallengeClient(@NonNull FinishCallback finishCallback, + @NonNull Context context, @NonNull IBiometricsFace daemon, @NonNull IBinder token, + @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) { + super(finishCallback, context, token, listener, owner, sensorId); + mDaemon = daemon; + } + + @Override + protected void startHalOperation() { + try { + mChallenge = mDaemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value; + } catch (RemoteException e) { + Slog.e(TAG, "generateChallenge failed", e); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java new file mode 100644 index 000000000000..102efda8a45d --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.face.V1_0.IBiometricsFace; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.RevokeChallengeClient; + +/** + * Face-specific revokeChallenge client supporting the {@link android.hardware.biometrics.face.V1_0} + * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces. + */ +public class FaceRevokeChallengeClient extends RevokeChallengeClient { + + private static final String TAG = "FaceRevokeChallengeClient"; + + private final IBiometricsFace mDaemon; + + FaceRevokeChallengeClient(@NonNull FinishCallback finishCallback, @NonNull Context context, + @NonNull IBiometricsFace daemon, @NonNull IBinder token, @NonNull String owner, + int sensorId) { + super(finishCallback, context, token, owner, sensorId); + mDaemon = daemon; + } + + @Override + protected void startHalOperation() { + try { + mDaemon.revokeChallenge(); + } catch (RemoteException e) { + Slog.e(TAG, "revokeChallenge failed", e); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 360acd1bf6e4..088762c1f39d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -61,9 +61,11 @@ import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnrollClient; +import com.android.server.biometrics.sensors.GenerateChallengeClient; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.RemovalClient; +import com.android.server.biometrics.sensors.RevokeChallengeClient; import org.json.JSONArray; import org.json.JSONException; @@ -92,7 +94,7 @@ public class FaceService extends BiometricServiceBase { private static final String FACE_DATA_DIR = "facedata"; private static final String ACTION_LOCKOUT_RESET = "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET"; - private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes + static final String NOTIFICATION_TAG = "FaceService"; static final int NOTIFICATION_ID = 1; @@ -108,27 +110,46 @@ public class FaceService extends BiometricServiceBase { */ @Override // Binder call - public long generateChallenge(IBinder token) { + public void generateChallenge(IBinder token, IFaceServiceReceiver receiver, + String opPackageName) throws RemoteException { checkPermission(MANAGE_BIOMETRIC); - return startGenerateChallenge(token); + + final IBiometricsFace daemon = getFaceDaemon(); + if (daemon == null) { + Slog.e(TAG, "Unable to generateChallenge, daemon null"); + receiver.onChallengeGenerated(0L); + return; + } + + final GenerateChallengeClient client = new FaceGenerateChallengeClient( + mClientFinishCallback, getContext(), daemon, token, + new ClientMonitorCallbackConverter(receiver), opPackageName, getSensorId()); + generateChallengeInternal(client); } @Override // Binder call - public int revokeChallenge(IBinder token) { + public void revokeChallenge(IBinder token, String owner) { checkPermission(MANAGE_BIOMETRIC); - mHandler.post(() -> { - // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks. - if (getCurrentClient() == null) { - // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke - // the challenge right away. - startRevokeChallenge(token); - } else { - // postpone revoking the challenge until we finish processing the current HIDL - // call. - mRevokeChallengePending = true; - } - }); - return Status.OK; + + final IBiometricsFace daemon = getFaceDaemon(); + if (daemon == null) { + Slog.e(TAG, "Unable to revokeChallenge, daemon null"); + return; + } + + final RevokeChallengeClient client = new FaceRevokeChallengeClient( + mClientFinishCallback, getContext(), daemon, token, owner, getSensorId()); + + // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks. + if (getCurrentClient() == null) { + // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke + // the challenge right away. + revokeChallengeInternal(client); + } else { + // postpone revoking the challenge until we finish processing the current HIDL + // call. + mPendingRevokeChallenge = client; + } } @Override // Binder call @@ -480,7 +501,7 @@ public class FaceService extends BiometricServiceBase { @GuardedBy("this") private IBiometricsFace mDaemon; private UsageStats mUsageStats; - private boolean mRevokeChallengePending = false; + private RevokeChallengeClient mPendingRevokeChallenge; private NotificationManager mNotificationManager; @@ -615,9 +636,9 @@ public class FaceService extends BiometricServiceBase { @Override protected void removeClient(ClientMonitor client) { super.removeClient(client); - if (mRevokeChallengePending) { - startRevokeChallenge(null); - mRevokeChallengePending = false; + if (mPendingRevokeChallenge != null) { + revokeChallengeInternal(mPendingRevokeChallenge); + mPendingRevokeChallenge = null; } } @@ -806,38 +827,6 @@ public class FaceService extends BiometricServiceBase { return mDaemon; } - private long startGenerateChallenge(IBinder token) { - IBiometricsFace daemon = getFaceDaemon(); - if (daemon == null) { - Slog.w(TAG, "startGenerateChallenge: no face HAL!"); - return 0; - } - try { - return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value; - } catch (RemoteException e) { - Slog.e(TAG, "startGenerateChallenge failed", e); - } - return 0; - } - - private int startRevokeChallenge(IBinder token) { - IBiometricsFace daemon = getFaceDaemon(); - if (daemon == null) { - Slog.w(TAG, "startRevokeChallenge: no face HAL!"); - return 0; - } - try { - final int res = daemon.revokeChallenge(); - if (res != Status.OK) { - Slog.e(TAG, "revokeChallenge returned " + res); - } - return res; - } catch (RemoteException e) { - Slog.e(TAG, "startRevokeChallenge failed", e); - } - return 0; - } - private native NativeHandle convertSurfaceToNativeHandle(Surface surface); private void dumpInternal(PrintWriter pw) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java new file mode 100644 index 000000000000..5ff49f377f0a --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.GenerateChallengeClient; + +/** + * Fingerprint-specific generateChallenge/preEnroll client supporting the + * {@link android.hardware.biometrics.fingerprint.V2_1} and + * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces. + */ +public class FingerprintGenerateChallengeClient extends GenerateChallengeClient { + + private static final String TAG = "FingerprintGenerateChallengeClient"; + + private final IBiometricsFingerprint mDaemon; + + FingerprintGenerateChallengeClient(@NonNull FinishCallback finishCallback, + @NonNull Context context, @NonNull IBiometricsFingerprint daemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, + @NonNull String owner, int sensorId) { + super(finishCallback, context, token, listener, owner, sensorId); + mDaemon = daemon; + } + + @Override + protected void startHalOperation() { + try { + mChallenge = mDaemon.preEnroll(); + } catch (RemoteException e) { + Slog.e(TAG, "preEnroll failed", e); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java new file mode 100644 index 000000000000..99e9d54b6a5e --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.RevokeChallengeClient; + +/** + * Fingerprint-specific revokeChallenge client supporting the + * {@link android.hardware.biometrics.fingerprint.V2_1} and + * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces. + */ +public class FingerprintRevokeChallengeClient extends RevokeChallengeClient { + + private static final String TAG = "FingerprintRevokeChallengeClient"; + + private final IBiometricsFingerprint mDaemon; + + FingerprintRevokeChallengeClient(@NonNull FinishCallback finishCallback, + @NonNull Context context, @NonNull IBiometricsFingerprint daemon, + @NonNull IBinder token, @NonNull String owner, int sensorId) { + super(finishCallback, context, token, owner, sensorId); + mDaemon = daemon; + } + + @Override + protected void startHalOperation() { + try { + mDaemon.postEnroll(); + } catch (RemoteException e) { + Slog.e(TAG, "revokeChallenge/postEnroll failed", e); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 893ec6c73674..a4bf22566e98 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -65,9 +65,11 @@ import com.android.server.biometrics.sensors.BiometricServiceBase; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnrollClient; +import com.android.server.biometrics.sensors.GenerateChallengeClient; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.RemovalClient; +import com.android.server.biometrics.sensors.RevokeChallengeClient; import org.json.JSONArray; import org.json.JSONException; @@ -105,15 +107,36 @@ public class FingerprintService extends BiometricServiceBase { */ @Override // Binder call - public long preEnroll(IBinder token) { + public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver, + String opPackageName) throws RemoteException { checkPermission(MANAGE_FINGERPRINT); - return startPreEnroll(token); + + final IBiometricsFingerprint daemon = getFingerprintDaemon(); + if (daemon == null) { + Slog.e(TAG, "Unable to generateChallenge, daemon null"); + receiver.onChallengeGenerated(0L); + return; + } + + final GenerateChallengeClient client = new FingerprintGenerateChallengeClient( + mClientFinishCallback, getContext(), daemon, token, + new ClientMonitorCallbackConverter(receiver), opPackageName, getSensorId()); + generateChallengeInternal(client); } @Override // Binder call - public int postEnroll(IBinder token) { + public void revokeChallenge(IBinder token, String owner) { checkPermission(MANAGE_FINGERPRINT); - return startPostEnroll(token); + + final IBiometricsFingerprint daemon = getFingerprintDaemon(); + if (daemon == null) { + Slog.e(TAG, "startPostEnroll: no fingerprint HAL!"); + return; + } + + final RevokeChallengeClient client = new FingerprintRevokeChallengeClient( + mClientFinishCallback, getContext(), daemon, token, owner, getSensorId()); + revokeChallengeInternal(client); } @Override // Binder call @@ -674,34 +697,6 @@ public class FingerprintService extends BiometricServiceBase { return mDaemon; } - private long startPreEnroll(IBinder token) { - IBiometricsFingerprint daemon = getFingerprintDaemon(); - if (daemon == null) { - Slog.w(TAG, "startPreEnroll: no fingerprint HAL!"); - return 0; - } - try { - return daemon.preEnroll(); - } catch (RemoteException e) { - Slog.e(TAG, "startPreEnroll failed", e); - } - return 0; - } - - private int startPostEnroll(IBinder token) { - IBiometricsFingerprint daemon = getFingerprintDaemon(); - if (daemon == null) { - Slog.w(TAG, "startPostEnroll: no fingerprint HAL!"); - return 0; - } - try { - return daemon.postEnroll(); - } catch (RemoteException e) { - Slog.e(TAG, "startPostEnroll failed", e); - } - return 0; - } - private native NativeHandle convertSurfaceToNativeHandle(Surface surface); private void dumpInternal(PrintWriter pw) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index bacc43bfd502..66c9c917c567 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2687,7 +2687,7 @@ public class LockSettingsService extends ILockSettings.Stub { // If there are multiple profiles in the same account, ensure we only generate the // challenge once. challengeType = CHALLENGE_INTERNAL; - challenge = mContext.getSystemService(FaceManager.class).generateChallenge(); + challenge = mContext.getSystemService(FaceManager.class).generateChallengeBlocking(); } final AuthenticationResult authResult; |