diff options
| author | 2020-10-20 15:17:09 -0700 | |
|---|---|---|
| committer | 2020-10-23 14:09:01 -0700 | |
| commit | 2f22b558575c9d6ba1c9beef42b195f96f69e69c (patch) | |
| tree | 1ab8eeed507abb2830844d6604dffa7562850030 | |
| parent | a524fc37db4b276cbe94e04d64e275b905064751 (diff) | |
Update IFingerprint providers for CTS
1) Adds proto dump for FingerprintProvider
2) Moves HAL ISessionCallback to static class
3) Adds AIDL-side implementation for TestApi
4) Rename TestSession to BiometricTestSessionImpl, since AIDL interface
also has a notion of session (ISession), which is unrelated.
5) Add missing start() method for FingerprintGetAuthenticatorIdClient
Fixes: 170518383
Test: atest FingerprintServiceTest
Change-Id: I530f45d794fb792fc2a64a403376b9ce2fc5f626
9 files changed, 812 insertions, 286 deletions
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 64aa7f720223..9ac12ed11ded 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 @@ -396,6 +396,8 @@ public class FingerprintService extends SystemService { for (ServiceProvider provider : mServiceProviders) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { + pw.println("Dumping for sensorId: " + props.sensorId + + ", provider: " + provider.getClass().getSimpleName()); provider.dumpInternal(props.sensorId, pw); } } @@ -613,7 +615,7 @@ public class FingerprintService extends SystemService { try { final SensorProps[] props = fp.getSensorProps(); final FingerprintProvider provider = - new FingerprintProvider(getContext(), props, fqName, + new FingerprintProvider(getContext(), props, instance, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); mServiceProviders.add(provider); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java new file mode 100644 index 000000000000..6bb40e6630bf --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java @@ -0,0 +1,186 @@ +/* + * 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.aidl; + +import static android.Manifest.permission.TEST_BIOMETRIC; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.ITestSession; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.IFingerprintServiceReceiver; +import android.os.Binder; +import android.util.Slog; + +import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +/** + * A test session implementation for {@link FingerprintProvider}. See + * {@link android.hardware.biometrics.BiometricTestSession}. + */ +class BiometricTestSessionImpl extends ITestSession.Stub { + + private static final String TAG = "BiometricTestSessionImpl"; + + @NonNull private final Context mContext; + private final int mSensorId; + @NonNull private final FingerprintProvider mProvider; + @NonNull private final Sensor mSensor; + @NonNull private final Set<Integer> mEnrollmentIds; + @NonNull private final Random mRandom; + + /** + * Internal receiver currently only used for enroll. Results do not need to be forwarded to the + * test, since enrollment is a platform-only API. The authentication path is tested through + * the public FingerprintManager APIs and does not use this receiver. + */ + private final IFingerprintServiceReceiver mReceiver = new IFingerprintServiceReceiver.Stub() { + @Override + public void onEnrollResult(Fingerprint fp, int remaining) { + + } + + @Override + public void onAcquired(int acquiredInfo, int vendorCode) { + + } + + @Override + public void onAuthenticationSucceeded(Fingerprint fp, int userId, + boolean isStrongBiometric) { + + } + + @Override + public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) { + + } + + @Override + public void onAuthenticationFailed() { + + } + + @Override + public void onError(int error, int vendorCode) { + + } + + @Override + public void onRemoved(Fingerprint fp, int remaining) { + + } + + @Override + public void onChallengeGenerated(int sensorId, long challenge) { + + } + }; + + BiometricTestSessionImpl(@NonNull Context context, int sensorId, + @NonNull FingerprintProvider provider, @NonNull Sensor sensor) { + mContext = context; + mSensorId = sensorId; + mProvider = provider; + mSensor = sensor; + mEnrollmentIds = new HashSet<>(); + mRandom = new Random(); + } + + @Override + public void setTestHalEnabled(boolean enabled) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mProvider.setTestHalEnabled(enabled); + mSensor.setTestHalEnabled(enabled); + } + + @Override + public void startEnroll(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, + mContext.getOpPackageName(), null /* surface */); + } + + @Override + public void finishEnroll(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + int nextRandomId = mRandom.nextInt(); + while (mEnrollmentIds.contains(nextRandomId)) { + nextRandomId = mRandom.nextInt(); + } + + mEnrollmentIds.add(nextRandomId); + mSensor.getSessionForUser(userId).mHalSessionCallback + .onEnrollmentProgress(nextRandomId, 0 /* remaining */); + } + + @Override + public void acceptAuthentication(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + // Fake authentication with any of the existing fingers + List<Fingerprint> fingerprints = FingerprintUtils.getInstance() + .getBiometricsForUser(mContext, userId); + if (fingerprints.isEmpty()) { + Slog.w(TAG, "No fingerprints, returning"); + return; + } + final int fid = fingerprints.get(0).getBiometricId(); + mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationSucceeded(fid, + HardwareAuthTokenUtils.toHardwareAuthToken(new byte[69])); + } + + @Override + public void rejectAuthentication(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed(); + } + + @Override + public void notifyAcquired(int userId, int acquireInfo) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mSensor.getSessionForUser(userId).mHalSessionCallback + .onAcquired((byte) acquireInfo, 0 /* vendorCode */); + } + + @Override + public void notifyError(int userId, int errorCode) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mSensor.getSessionForUser(userId).mHalSessionCallback.onError((byte) errorCode, + 0 /* vendorCode */); + } + + @Override + public void cleanupInternalState(int userId) { + Utils.checkPermission(mContext, TEST_BIOMETRIC); + + mProvider.scheduleInternalCleanup(mSensorId, userId); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java index fec3cff6d52f..2ad1fa306781 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java @@ -47,6 +47,11 @@ class FingerprintGetAuthenticatorIdClient extends ClientMonitor<ISession> { // Nothing to do here } + public void start(@NonNull Callback callback) { + super.start(callback); + startHalOperation(); + } + @Override protected void startHalOperation() { try { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index a081be7bfac6..4d07f583fdf5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -64,6 +64,8 @@ import java.util.List; @SuppressWarnings("deprecation") public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider { + private boolean mTestHalEnabled; + @NonNull private final Context mContext; @NonNull private final String mHalInstanceName; @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports @@ -134,7 +136,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi prop.commonProps.maxEnrollmentsPerUser, prop.sensorType, true /* resetLockoutRequiresHardwareAuthToken */); - final Sensor sensor = new Sensor(getTag() + "/" + sensorId, mContext, mHandler, + final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, internalProp, gestureAvailabilityDispatcher); mSensors.put(sensorId, sensor); @@ -148,6 +150,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Nullable private synchronized IFingerprint getHalInstance() { + if (mTestHalEnabled) { + // Enabling the test HAL for a single sensor in a multi-sensor HAL currently enables + // the test HAL for all sensors under that HAL. This can be updated in the future if + // necessary. + return new TestHal(); + } + if (mDaemon != null) { return mDaemon; } @@ -155,7 +164,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi Slog.d(getTag(), "Daemon was null, reconnecting"); mDaemon = IFingerprint.Stub.asInterface( - ServiceManager.waitForDeclaredService(mHalInstanceName)); + ServiceManager.waitForDeclaredService(IFingerprint.DESCRIPTOR + + "/" + mHalInstanceName)); if (mDaemon == null) { Slog.e(getTag(), "Unable to get daemon"); return null; @@ -564,7 +574,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) { - + if (mSensors.contains(sensorId)) { + mSensors.get(sensorId).dumpProtoState(sensorId, proto); + } } @Override @@ -580,7 +592,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull @Override public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) { - return null; + return mSensors.get(sensorId).createTestSession(); } @Override @@ -595,4 +607,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } }); } + + void setTestHalEnabled(boolean enabled) { + mTestHalEnabled = enabled; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index d4ce896d42ec..51c30b68e0c5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -19,7 +19,9 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.fingerprint.Error; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.ISession; @@ -31,11 +33,16 @@ import android.hardware.keymaster.HardwareAuthToken; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserManager; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.fingerprint.FingerprintServiceStateProto; +import com.android.server.biometrics.fingerprint.SensorStateProto; +import com.android.server.biometrics.fingerprint.UserStateProto; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BiometricScheduler; @@ -48,9 +55,7 @@ import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -59,7 +64,11 @@ import java.util.Map; */ @SuppressWarnings("deprecation") class Sensor implements IBinder.DeathRecipient { + + private boolean mTestHalEnabled; + @NonNull private final String mTag; + @NonNull private final FingerprintProvider mProvider; @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; @@ -91,320 +100,377 @@ class Sensor implements IBinder.DeathRecipient { }); } - private static class Session { + static class Session { @NonNull private final String mTag; @NonNull private final ISession mSession; private final int mUserId; - private final ISessionCallback mSessionCallback; + @NonNull final HalSessionCallback mHalSessionCallback; Session(@NonNull String tag, @NonNull ISession session, int userId, - @NonNull ISessionCallback sessionCallback) { + @NonNull HalSessionCallback halSessionCallback) { mTag = tag; mSession = session; mUserId = userId; - mSessionCallback = sessionCallback; + mHalSessionCallback = halSessionCallback; Slog.d(mTag, "New session created for user: " + userId); } } - Sensor(@NonNull String tag, @NonNull Context context, @NonNull Handler handler, - @NonNull FingerprintSensorPropertiesInternal sensorProperties, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - mTag = tag; - mContext = context; - mHandler = handler; - mSensorProperties = sensorProperties; - mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher); - mLockoutCache = new LockoutCache(); - mAuthenticatorIds = new HashMap<>(); - mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null; - } - - @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() { - return mLazySession; - } - - @NonNull FingerprintSensorPropertiesInternal getSensorProperties() { - return mSensorProperties; - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - boolean hasSessionForUser(int userId) { - return mCurrentSession != null && mCurrentSession.mUserId == userId; - } - - void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId) - throws RemoteException { - final ISessionCallback callback = new ISessionCallback.Stub() { - @Override - public void onStateChanged(int cookie, byte state) { - // TODO(b/162973174) - } - - @Override - public void onChallengeGenerated(long challenge) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof FingerprintGenerateChallengeClient)) { - Slog.e(mTag, "onChallengeGenerated for wrong client: " - + Utils.getClientName(client)); - return; - } - - final FingerprintGenerateChallengeClient generateChallengeClient = - (FingerprintGenerateChallengeClient) client; - generateChallengeClient.onChallengeGenerated(sensorId, userId, challenge); - }); - } + static class HalSessionCallback extends ISessionCallback.Stub { - @Override - public void onChallengeRevoked(long challenge) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof FingerprintRevokeChallengeClient)) { - Slog.e(mTag, "onChallengeRevoked for wrong client: " - + Utils.getClientName(client)); - return; - } + /** + * Interface to sends results to the HalSessionCallback's owner. + */ + public interface Callback { + /** + * Invoked when the HAL sends ERROR_HW_UNAVAILABLE. + */ + void onHardwareUnavailable(); + } - final FingerprintRevokeChallengeClient revokeChallengeClient = - (FingerprintRevokeChallengeClient) client; - revokeChallengeClient.onChallengeRevoked(sensorId, userId, challenge); - }); - } + @NonNull private final Context mContext; + @NonNull private final Handler mHandler; + @NonNull private final String mTag; + @NonNull private final BiometricScheduler mScheduler; + private final int mSensorId; + private final int mUserId; + @NonNull private final Callback mCallback; - @Override - public void onAcquired(byte info, int vendorCode) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof AcquisitionClient)) { - Slog.e(mTag, "onAcquired for non-acquisition client: " - + Utils.getClientName(client)); - return; - } + HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag, + @NonNull BiometricScheduler scheduler, int sensorId, int userId, + @NonNull Callback callback) { + mContext = context; + mHandler = handler; + mTag = tag; + mScheduler = scheduler; + mSensorId = sensorId; + mUserId = userId; + mCallback = callback; + } - final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client; - acquisitionClient.onAcquired(info, vendorCode); - }); - } + @Override + public void onStateChanged(int cookie, byte state) { + // TODO(b/162973174) + } - @Override - public void onError(byte error, int vendorCode) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - Slog.d(mTag, "onError" - + ", client: " + Utils.getClientName(client) - + ", error: " + error - + ", vendorCode: " + vendorCode); - if (!(client instanceof Interruptable)) { - Slog.e(mTag, "onError for non-error consumer: " - + Utils.getClientName(client)); - return; - } + @Override + public void onChallengeGenerated(long challenge) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintGenerateChallengeClient)) { + Slog.e(mTag, "onChallengeGenerated for wrong client: " + + Utils.getClientName(client)); + return; + } + + final FingerprintGenerateChallengeClient generateChallengeClient = + (FingerprintGenerateChallengeClient) client; + generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge); + }); + } - final Interruptable interruptable = (Interruptable) client; - interruptable.onError(error, vendorCode); + @Override + public void onChallengeRevoked(long challenge) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintRevokeChallengeClient)) { + Slog.e(mTag, "onChallengeRevoked for wrong client: " + + Utils.getClientName(client)); + return; + } + + final FingerprintRevokeChallengeClient revokeChallengeClient = + (FingerprintRevokeChallengeClient) client; + revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge); + }); + } - if (error == Error.HW_UNAVAILABLE) { - Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); - mCurrentSession = null; - } - }); - } + @Override + public void onAcquired(byte info, int vendorCode) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof AcquisitionClient)) { + Slog.e(mTag, "onAcquired for non-acquisition client: " + + Utils.getClientName(client)); + return; + } + + final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client; + acquisitionClient.onAcquired(info, vendorCode); + }); + } - @Override - public void onEnrollmentProgress(int enrollmentId, int remaining) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof FingerprintEnrollClient)) { - Slog.e(mTag, "onEnrollmentProgress for non-enroll client: " - + Utils.getClientName(client)); - return; - } + @Override + public void onError(byte error, int vendorCode) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + Slog.d(mTag, "onError" + + ", client: " + Utils.getClientName(client) + + ", error: " + error + + ", vendorCode: " + vendorCode); + if (!(client instanceof Interruptable)) { + Slog.e(mTag, "onError for non-error consumer: " + + Utils.getClientName(client)); + return; + } - final int currentUserId = client.getTargetUserId(); - final CharSequence name = FingerprintUtils.getInstance(sensorId) - .getUniqueName(mContext, currentUserId); - final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, sensorId); + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(error, vendorCode); - final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client; - enrollClient.onEnrollResult(fingerprint, remaining); - }); - } + if (error == Error.HW_UNAVAILABLE) { + mCallback.onHardwareUnavailable(); + } + }); + } - @Override - public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof AuthenticationConsumer)) { - Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: " - + Utils.getClientName(client)); - return; - } + @Override + public void onEnrollmentProgress(int enrollmentId, int remaining) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintEnrollClient)) { + Slog.e(mTag, "onEnrollmentProgress for non-enroll client: " + + Utils.getClientName(client)); + return; + } + + final int currentUserId = client.getTargetUserId(); + final CharSequence name = FingerprintUtils.getInstance(mSensorId) + .getUniqueName(mContext, currentUserId); + final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, mSensorId); + + final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client; + enrollClient.onEnrollResult(fingerprint, remaining); + }); + } - final AuthenticationConsumer authenticationConsumer = - (AuthenticationConsumer) client; - final Fingerprint fp = new Fingerprint("", enrollmentId, sensorId); - final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat); - final ArrayList<Byte> byteList = new ArrayList<>(); - for (byte b : byteArray) { - byteList.add(b); - } + @Override + public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof AuthenticationConsumer)) { + Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: " + + Utils.getClientName(client)); + return; + } + + final AuthenticationConsumer authenticationConsumer = + (AuthenticationConsumer) client; + final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId); + final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat); + final ArrayList<Byte> byteList = new ArrayList<>(); + for (byte b : byteArray) { + byteList.add(b); + } + + authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList); + }); + } - authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList); - }); - } + @Override + public void onAuthenticationFailed() { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof AuthenticationConsumer)) { + Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: " + + Utils.getClientName(client)); + return; + } + + final AuthenticationConsumer authenticationConsumer = + (AuthenticationConsumer) client; + final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, mSensorId); + authenticationConsumer + .onAuthenticated(fp, false /* authenticated */, null /* hat */); + }); + } - @Override - public void onAuthenticationFailed() { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof AuthenticationConsumer)) { - Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: " - + Utils.getClientName(client)); - return; - } + @Override + public void onLockoutTimed(long durationMillis) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof LockoutConsumer)) { + Slog.e(mTag, "onLockoutTimed for non-lockout consumer: " + + Utils.getClientName(client)); + return; + } + + final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; + lockoutConsumer.onLockoutTimed(durationMillis); + }); + } - final AuthenticationConsumer authenticationConsumer = - (AuthenticationConsumer) client; - final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, sensorId); - authenticationConsumer - .onAuthenticated(fp, false /* authenticated */, null /* hat */); - }); - } + @Override + public void onLockoutPermanent() { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof LockoutConsumer)) { + Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: " + + Utils.getClientName(client)); + return; + } + + final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; + lockoutConsumer.onLockoutPermanent(); + }); + } - @Override - public void onLockoutTimed(long durationMillis) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof LockoutConsumer)) { - Slog.e(mTag, "onLockoutTimed for non-lockout consumer: " - + Utils.getClientName(client)); - return; - } + @Override + public void onLockoutCleared() { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintResetLockoutClient)) { + Slog.e(mTag, "onLockoutCleared for non-resetLockout client: " + + Utils.getClientName(client)); + return; + } + + final FingerprintResetLockoutClient resetLockoutClient = + (FingerprintResetLockoutClient) client; + resetLockoutClient.onLockoutCleared(); + }); + } - final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; - lockoutConsumer.onLockoutTimed(durationMillis); - }); - } + @Override + public void onInteractionDetected() { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintDetectClient)) { + Slog.e(mTag, "onInteractionDetected for non-detect client: " + + Utils.getClientName(client)); + return; + } + + final FingerprintDetectClient fingerprintDetectClient = + (FingerprintDetectClient) client; + fingerprintDetectClient.onInteractionDetected(); + }); + } - @Override - public void onLockoutPermanent() { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof LockoutConsumer)) { - Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: " - + Utils.getClientName(client)); - return; + @Override + public void onEnrollmentsEnumerated(int[] enrollmentIds) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof EnumerateConsumer)) { + Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: " + + Utils.getClientName(client)); + return; + } + + final EnumerateConsumer enumerateConsumer = + (EnumerateConsumer) client; + if (enrollmentIds.length > 0) { + for (int i = 0; i < enrollmentIds.length; i++) { + final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId); + enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1); } + } else { + enumerateConsumer.onEnumerationResult(null /* identifier */, 0); + } + }); + } - final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; - lockoutConsumer.onLockoutPermanent(); - }); - } - - @Override - public void onLockoutCleared() { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof FingerprintResetLockoutClient)) { - Slog.e(mTag, "onLockoutCleared for non-resetLockout client: " - + Utils.getClientName(client)); - return; + @Override + public void onEnrollmentsRemoved(int[] enrollmentIds) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof RemovalConsumer)) { + Slog.e(mTag, "onRemoved for non-removal consumer: " + + Utils.getClientName(client)); + return; + } + + final RemovalConsumer removalConsumer = (RemovalConsumer) client; + if (enrollmentIds.length > 0) { + for (int i = 0; i < enrollmentIds.length; i++) { + final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId); + removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1); } + } else { + removalConsumer.onRemoved(null, 0); + } + }); + } - final FingerprintResetLockoutClient resetLockoutClient = - (FingerprintResetLockoutClient) client; - resetLockoutClient.onLockoutCleared(); - }); - } + @Override + public void onAuthenticatorIdRetrieved(long authenticatorId) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintGetAuthenticatorIdClient)) { + Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: " + + Utils.getClientName(client)); + return; + } + + final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient = + (FingerprintGetAuthenticatorIdClient) client; + getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId); + }); + } - @Override - public void onInteractionDetected() { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof FingerprintDetectClient)) { - Slog.e(mTag, "onInteractionDetected for non-detect client: " - + Utils.getClientName(client)); - return; - } + @Override + public void onAuthenticatorIdInvalidated() { + // TODO(159667191) + } + } - final FingerprintDetectClient fingerprintDetectClient = - (FingerprintDetectClient) client; - fingerprintDetectClient.onInteractionDetected(); - }); + Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, + @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + mTag = tag; + mProvider = provider; + mContext = context; + mHandler = handler; + mSensorProperties = sensorProperties; + mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher); + mLockoutCache = new LockoutCache(); + mAuthenticatorIds = new HashMap<>(); + mLazySession = () -> { + if (mTestHalEnabled) { + return new TestSession(mCurrentSession.mHalSessionCallback); + } else { + return mCurrentSession != null ? mCurrentSession.mSession : null; } + }; + } - @Override - public void onEnrollmentsEnumerated(int[] enrollmentIds) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof EnumerateConsumer)) { - Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: " - + Utils.getClientName(client)); - return; - } + @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() { + return mLazySession; + } - final EnumerateConsumer enumerateConsumer = - (EnumerateConsumer) client; - if (enrollmentIds.length > 0) { - for (int i = 0; i < enrollmentIds.length; i++) { - final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId); - enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1); - } - } else { - enumerateConsumer.onEnumerationResult(null /* identifier */, 0); - } - }); - } + @NonNull FingerprintSensorPropertiesInternal getSensorProperties() { + return mSensorProperties; + } - @Override - public void onEnrollmentsRemoved(int[] enrollmentIds) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof RemovalConsumer)) { - Slog.e(mTag, "onRemoved for non-removal consumer: " - + Utils.getClientName(client)); - return; - } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + boolean hasSessionForUser(int userId) { + return mCurrentSession != null && mCurrentSession.mUserId == userId; + } - final RemovalConsumer removalConsumer = (RemovalConsumer) client; - if (enrollmentIds.length > 0) { - for (int i = 0; i < enrollmentIds.length; i++) { - final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId); - removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1); - } - } else { - removalConsumer.onRemoved(null, 0); - } - }); - } + @Nullable Session getSessionForUser(int userId) { + if (mCurrentSession != null && mCurrentSession.mUserId == userId) { + return mCurrentSession; + } else { + return null; + } + } - @Override - public void onAuthenticatorIdRetrieved(long authenticatorId) { - mHandler.post(() -> { - final ClientMonitor<?> client = mScheduler.getCurrentClient(); - if (!(client instanceof FingerprintGetAuthenticatorIdClient)) { - Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: " - + Utils.getClientName(client)); - return; - } + @NonNull ITestSession createTestSession() { + return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this); + } - final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient = - (FingerprintGetAuthenticatorIdClient) client; - getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId); - }); - } + void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId) + throws RemoteException { - @Override - public void onAuthenticatorIdInvalidated() { - // TODO(159667191) - } + final HalSessionCallback.Callback callback = () -> { + Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); + mCurrentSession = null; }; + final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler, + mTag, mScheduler, sensorId, userId, callback); - final ISession newSession = daemon.createSession(sensorId, userId, callback); + final ISession newSession = daemon.createSession(sensorId, userId, resultController); newSession.asBinder().linkToDeath(this, 0 /* flags */); - mCurrentSession = new Session(mTag, newSession, userId, callback); + mCurrentSession = new Session(mTag, newSession, userId, resultController); } @NonNull BiometricScheduler getScheduler() { @@ -418,4 +484,27 @@ class Sensor implements IBinder.DeathRecipient { @NonNull Map<Integer, Long> getAuthenticatorIds() { return mAuthenticatorIds; } + + void setTestHalEnabled(boolean enabled) { + mTestHalEnabled = enabled; + } + + void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) { + final long sensorToken = proto.start(FingerprintServiceStateProto.SENSOR_STATES); + + proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); + proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null); + + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + + final long userToken = proto.start(SensorStateProto.USER_STATES); + proto.write(UserStateProto.USER_ID, userId); + proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance() + .getBiometricsForUser(mContext, userId).size()); + proto.end(userToken); + } + + proto.end(sensorToken); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java new file mode 100644 index 000000000000..8c9a269486bb --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java @@ -0,0 +1,111 @@ +/* + * 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.aidl; + +import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.fingerprint.IFingerprint; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.biometrics.fingerprint.ISessionCallback; +import android.hardware.biometrics.fingerprint.SensorProps; +import android.hardware.keymaster.HardwareAuthToken; +import android.os.Binder; +import android.os.IBinder; + +/** + * Test HAL that provides only provides no-ops. + */ +public class TestHal extends IFingerprint.Stub { + @Override + public SensorProps[] getSensorProps() { + return new SensorProps[0]; + } + + @Override + public ISession createSession(int sensorId, int userId, ISessionCallback cb) { + return new ISession() { + @Override + public void generateChallenge(int cookie, int timeoutSec) { + + } + + @Override + public void revokeChallenge(int cookie, long challenge) { + + } + + @Override + public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) { + return null; + } + + @Override + public ICancellationSignal authenticate(int cookie, long operationId) { + return null; + } + + @Override + public ICancellationSignal detectInteraction(int cookie) { + return null; + } + + @Override + public void enumerateEnrollments(int cookie) { + + } + + @Override + public void removeEnrollments(int cookie, int[] enrollmentIds) { + + } + + @Override + public void getAuthenticatorId(int cookie) { + + } + + @Override + public void invalidateAuthenticatorId(int cookie, HardwareAuthToken hat) { + + } + + @Override + public void resetLockout(int cookie, HardwareAuthToken hat) { + + } + + @Override + public void onPointerDown(int pointerId, int x, int y, float minor, float major) { + + } + + @Override + public void onPointerUp(int pointerId) { + + } + + @Override + public void onUiReady() { + + } + + @Override + public IBinder asBinder() { + return new Binder(); + } + }; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java new file mode 100644 index 000000000000..d5afd0c68ad9 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java @@ -0,0 +1,106 @@ +/* + * 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.aidl; + +import android.annotation.NonNull; +import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.keymaster.HardwareAuthToken; +import android.util.Slog; + +/** + * Test HAL that provides only provides mostly no-ops. + */ +class TestSession extends ISession.Stub { + + private static final String TAG = "TestSession"; + + @NonNull private final Sensor.HalSessionCallback mHalSessionCallback; + + TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) { + mHalSessionCallback = halSessionCallback; + } + + @Override + public void generateChallenge(int cookie, int timeoutSec) { + + } + + @Override + public void revokeChallenge(int cookie, long challenge) { + + } + + @Override + public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) { + Slog.d(TAG, "enroll"); + return null; + } + + @Override + public ICancellationSignal authenticate(int cookie, long operationId) { + Slog.d(TAG, "authenticate"); + return null; + } + + @Override + public ICancellationSignal detectInteraction(int cookie) { + return null; + } + + @Override + public void enumerateEnrollments(int cookie) { + Slog.d(TAG, "enumerate"); + } + + @Override + public void removeEnrollments(int cookie, int[] enrollmentIds) { + Slog.d(TAG, "remove"); + } + + @Override + public void getAuthenticatorId(int cookie) { + Slog.d(TAG, "getAuthenticatorId"); + // Immediately return a value so the framework can continue with subsequent requests. + mHalSessionCallback.onAuthenticatorIdRetrieved(0); + } + + @Override + public void invalidateAuthenticatorId(int cookie, HardwareAuthToken hat) { + + } + + @Override + public void resetLockout(int cookie, HardwareAuthToken hat) { + + } + + @Override + public void onPointerDown(int pointerId, int x, int y, float minor, float major) { + + } + + @Override + public void onPointerUp(int pointerId) { + + } + + @Override + public void onUiReady() { + + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index 09c0588aa7d3..e0ea99077d51 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -31,21 +31,25 @@ import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Random; +import java.util.Set; /** * A test session implementation for the {@link Fingerprint21} provider. See * {@link android.hardware.biometrics.BiometricTestSession}. */ -public class TestSession extends ITestSession.Stub { +public class BiometricTestSessionImpl extends ITestSession.Stub { - private static final String TAG = "TestSession"; + private static final String TAG = "BiometricTestSessionImpl"; - private final Context mContext; + @NonNull private final Context mContext; private final int mSensorId; - private final Fingerprint21 mFingerprint21; - private final Fingerprint21.HalResultController mHalResultController; + @NonNull private final Fingerprint21 mFingerprint21; + @NonNull private final Fingerprint21.HalResultController mHalResultController; + @NonNull private final Set<Integer> mEnrollmentIds; + @NonNull private final Random mRandom; /** * Internal receiver currently only used for enroll. Results do not need to be forwarded to the @@ -95,12 +99,15 @@ public class TestSession extends ITestSession.Stub { } }; - TestSession(@NonNull Context context, int sensorId, @NonNull Fingerprint21 fingerprint21, + BiometricTestSessionImpl(@NonNull Context context, int sensorId, + @NonNull Fingerprint21 fingerprint21, @NonNull Fingerprint21.HalResultController halResultController) { mContext = context; mSensorId = sensorId; mFingerprint21 = fingerprint21; mHalResultController = halResultController; + mEnrollmentIds = new HashSet<>(); + mRandom = new Random(); } @Override @@ -122,9 +129,14 @@ public class TestSession extends ITestSession.Stub { public void finishEnroll(int userId) { Utils.checkPermission(mContext, TEST_BIOMETRIC); - final Random random = new Random(); + int nextRandomId = mRandom.nextInt(); + while (mEnrollmentIds.contains(nextRandomId)) { + nextRandomId = mRandom.nextInt(); + } + + mEnrollmentIds.add(nextRandomId); mHalResultController.onEnrollResult(0 /* deviceId */, - random.nextInt() /* fingerId */, userId, 0); + nextRandomId /* fingerId */, userId, 0); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 616784175980..241c911ce9ab 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -96,7 +96,6 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private static final int ENROLL_TIMEOUT_SEC = 60; private boolean mTestHalEnabled; - @Nullable private TestHal mTestHal; final Context mContext; private final IActivityTaskManager mActivityTaskManager; @@ -399,7 +398,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private synchronized IBiometricsFingerprint getDaemon() { if (mTestHalEnabled) { - return mTestHal; + return new TestHal(); } if (mDaemon != null) { @@ -810,7 +809,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull @Override public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) { - mTestHal = new TestHal(); - return new TestSession(mContext, mSensorProperties.sensorId, this, mHalResultController); + return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, this, + mHalResultController); } } |