diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 7c42dc0da7c78..d48d562b6692e 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -849,26 +849,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing return hasEnrolledFingerprints(userId); } - /** - * Checks if the specified user has enrollments in any of the specified sensors. - * @hide - */ - @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public boolean hasEnrolledTemplatesForAnySensor(int userId, - @NonNull List sensors) { - if (mService == null) { - Slog.w(TAG, "hasEnrolledTemplatesForAnySensor: no fingerprint service"); - return false; - } - - try { - return mService.hasEnrolledTemplatesForAnySensor(userId, sensors, - mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** * @hide */ diff --git a/core/java/android/hardware/fingerprint/FingerprintStateListener.java b/core/java/android/hardware/fingerprint/FingerprintStateListener.java index 6e607a269cb26..cf914c5acd1a1 100644 --- a/core/java/android/hardware/fingerprint/FingerprintStateListener.java +++ b/core/java/android/hardware/fingerprint/FingerprintStateListener.java @@ -49,5 +49,10 @@ public abstract class FingerprintStateListener extends IFingerprintStateListener * Defines behavior in response to state update * @param newState new state of fingerprint sensor */ - public abstract void onStateChanged(@FingerprintStateListener.State int newState); + public void onStateChanged(@FingerprintStateListener.State int newState) {}; + + /** + * Invoked when enrollment state changes for the specified user + */ + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {}; } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 4774827576994..de94b2fbb5b59 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -120,9 +120,6 @@ interface IFingerprintService { // Determine if a user has at least one enrolled fingerprint. boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName); - // Determine if a user has at least one enrolled fingerprint in any of the specified sensors - boolean hasEnrolledTemplatesForAnySensor(int userId, in List sensors, String opPackageName); - // Return the LockoutTracker status for the specified user int getLockoutModeForUser(int sensorId, int userId); diff --git a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl index 56dba7ea1d9ae..1aa6fa197066c 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl @@ -24,4 +24,5 @@ import android.hardware.fingerprint.Fingerprint; */ oneway interface IFingerprintStateListener { void onStateChanged(int newState); + void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index bcc0530687122..0790af94f287a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -42,6 +42,7 @@ import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.FingerprintStateListener; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.Bundle; @@ -49,6 +50,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.Log; +import android.util.SparseBooleanArray; import android.view.MotionEvent; import android.view.WindowManager; @@ -76,6 +78,9 @@ import kotlin.Unit; /** * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the * appropriate biometric UI (e.g. BiometricDialogView). + * + * Also coordinates biometric-related things, such as UDFPS, with + * {@link com.android.keyguard.KeyguardUpdateMonitor} */ @SysUISingleton public class AuthController extends SystemUI implements CommandQueue.Callbacks, @@ -115,6 +120,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Nullable private List mUdfpsProps; @Nullable private List mSidefpsProps; + @NonNull private final SparseBooleanArray mUdfpsEnrolledForUser; + private class BiometricTaskStackListener extends TaskStackListener { @Override public void onTaskStackChanged() { @@ -122,6 +129,21 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } } + private final FingerprintStateListener mFingerprintStateListener = + new FingerprintStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + Log.d(TAG, "onEnrollmentsChanged, userId: " + userId + + ", sensorId: " + sensorId + + ", hasEnrollments: " + hasEnrollments); + for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) { + if (prop.sensorId == sensorId) { + mUdfpsEnrolledForUser.put(userId, hasEnrollments); + } + } + } + }; + @NonNull private final IFingerprintAuthenticatorsRegisteredCallback mFingerprintAuthenticatorsRegisteredCallback = @@ -436,6 +458,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mUdfpsControllerFactory = udfpsControllerFactory; mSidefpsControllerFactory = sidefpsControllerFactory; mWindowManager = windowManager; + mUdfpsEnrolledForUser = new SparseBooleanArray(); mOrientationListener = new BiometricOrientationEventListener(context, () -> { onOrientationChanged(); @@ -474,6 +497,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, if (mFingerprintManager != null) { mFingerprintManager.addAuthenticatorsRegisteredCallback( mFingerprintAuthenticatorsRegisteredCallback); + mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener); } mTaskStackListener = new BiometricTaskStackListener(); @@ -673,7 +697,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, return false; } - return mFingerprintManager.hasEnrolledTemplatesForAnySensor(userId, mUdfpsProps); + return mUdfpsEnrolledForUser.get(userId); } private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) { diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 0a1c77bf10a26..9764a167fbbff 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -26,6 +26,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import java.util.ArrayList; +import java.util.List; import java.util.NoSuchElementException; /** @@ -70,26 +72,32 @@ public abstract class BaseClientMonitor extends LoggableMonitor } /** Holder for wrapping multiple handlers into a single Callback. */ - protected static class CompositeCallback implements Callback { + public static class CompositeCallback implements Callback { @NonNull - private final Callback[] mCallbacks; + private final List mCallbacks; public CompositeCallback(@NonNull Callback... callbacks) { - mCallbacks = callbacks; + mCallbacks = new ArrayList<>(); + + for (Callback callback : callbacks) { + if (callback != null) { + mCallbacks.add(callback); + } + } } @Override public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { - for (int i = 0; i < mCallbacks.length; i++) { - mCallbacks[i].onClientStarted(clientMonitor); + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).onClientStarted(clientMonitor); } } @Override public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { - for (int i = mCallbacks.length - 1; i >= 0; i--) { - mCallbacks[i].onClientFinished(clientMonitor, success); + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).onClientFinished(clientMonitor, success); } } } @@ -256,7 +264,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor return mToken; } - public final int getSensorId() { + public int getSensorId() { return mSensorId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index a15e14b79e301..9191b8b559899 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -31,7 +31,7 @@ import java.util.Arrays; /** * A class to keep track of the enrollment state for a given client. */ -public abstract class EnrollClient extends AcquisitionClient { +public abstract class EnrollClient extends AcquisitionClient implements EnrollmentModifier { private static final String TAG = "Biometrics/EnrollClient"; @@ -40,6 +40,7 @@ public abstract class EnrollClient extends AcquisitionClient { protected final BiometricUtils mBiometricUtils; private long mEnrollmentStartTimeMs; + private final boolean mHasEnrollmentsBeforeStarting; /** * @return true if the user has already enrolled the maximum number of templates. @@ -56,6 +57,18 @@ public abstract class EnrollClient extends AcquisitionClient { mBiometricUtils = utils; mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length); mTimeoutSec = timeoutSec; + mHasEnrollmentsBeforeStarting = hasEnrollments(); + } + + @Override + public boolean hasEnrollmentStateChanged() { + final boolean hasEnrollmentsNow = hasEnrollments(); + return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting; + } + + @Override + public boolean hasEnrollments() { + return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty(); } public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java new file mode 100644 index 0000000000000..c2f909b08bb69 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +/** + * Interface for {@link BaseClientMonitor} subclasses that affect the state of enrollment. + */ +public interface EnrollmentModifier { + + /** + * Callers should typically check this after + * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} + * + * @return true if the user has gone from: + * 1) none-enrolled --> enrolled + * 2) enrolled --> none-enrolled + * but NOT any-enrolled --> more-enrolled + */ + boolean hasEnrollmentStateChanged(); + + /** + * @return true if the user has any enrollments + */ + boolean hasEnrollments(); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index 282261e6556f8..579dfd69ec66b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -40,7 +40,8 @@ import java.util.Map; * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/ */ public abstract class InternalCleanupClient - extends HalClientMonitor implements EnumerateConsumer, RemovalConsumer { + extends HalClientMonitor implements EnumerateConsumer, RemovalConsumer, + EnrollmentModifier { private static final String TAG = "Biometrics/InternalCleanupClient"; @@ -61,6 +62,7 @@ public abstract class InternalCleanupClient mBiometricUtils; private final Map mAuthenticatorIds; private final List mEnrolledList; + private final boolean mHasEnrollmentsBeforeStarting; private BaseClientMonitor mCurrentTask; private final Callback mEnumerateCallback = new Callback() { @@ -115,6 +117,7 @@ public abstract class InternalCleanupClient) mCurrentTask).onRemoved(identifier, remaining); } + @Override + public boolean hasEnrollmentStateChanged() { + final boolean hasEnrollmentsNow = !mBiometricUtils + .getBiometricsForUser(getContext(), getTargetUserId()).isEmpty(); + return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting; + } + + @Override + public boolean hasEnrollments() { + return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty(); + } + @Override public void onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining) { diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index 383efce5bb2a4..2a6677e55d602 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -33,12 +33,13 @@ import java.util.Map; * A class to keep track of the remove state for a given client. */ public abstract class RemovalClient - extends HalClientMonitor implements RemovalConsumer { + extends HalClientMonitor implements RemovalConsumer, EnrollmentModifier { private static final String TAG = "Biometrics/RemovalClient"; private final BiometricUtils mBiometricUtils; private final Map mAuthenticatorIds; + private final boolean mHasEnrollmentsBeforeStarting; public RemovalClient(@NonNull Context context, @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, @@ -49,6 +50,7 @@ public abstract class RemovalClient mServiceProviders; @NonNull private final FingerprintStateCallback mFingerprintStateCallback; + @NonNull private final Handler mHandler; @GuardedBy("mLock") @NonNull private final RemoteCallbackList @@ -125,6 +129,37 @@ public class FingerprintService extends SystemService { */ public void registerFingerprintStateListener(@NonNull IFingerprintStateListener listener) { mFingerprintStateCallback.registerFingerprintStateListener(listener); + broadcastCurrentEnrollmentState(listener); + } + + /** + * @param listener if non-null, notifies only this listener. if null, notifies all listeners + * in {@link FingerprintStateCallback}. This is slightly ugly, but reduces + * redundant code. + */ + private void broadcastCurrentEnrollmentState(@Nullable IFingerprintStateListener listener) { + final UserManager um = UserManager.get(getContext()); + synchronized (mLock) { + // Update the new listener with current state of all sensors + for (FingerprintSensorPropertiesInternal prop : mSensorProps) { + final ServiceProvider provider = getProviderForSensor(prop.sensorId); + for (UserInfo userInfo : um.getAliveUsers()) { + final boolean enrolled = !provider + .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty(); + + // Defer this work and allow the loop to release the lock sooner + mHandler.post(() -> { + if (listener != null) { + mFingerprintStateCallback.notifyFingerprintEnrollmentStateChanged( + listener, userInfo.id, prop.sensorId, enrolled); + } else { + mFingerprintStateCallback.notifyAllFingerprintEnrollmentStateChanged( + userInfo.id, prop.sensorId, enrolled); + } + }); + } + } + } } /** @@ -143,8 +178,7 @@ public class FingerprintService extends SystemService { return null; } - return provider.createTestSession(sensorId, callback, mFingerprintStateCallback, - opPackageName); + return provider.createTestSession(sensorId, callback, opPackageName); } @Override @@ -227,7 +261,7 @@ public class FingerprintService extends SystemService { } provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, - receiver, opPackageName, enrollReason, mFingerprintStateCallback); + receiver, opPackageName, enrollReason); } @Override // Binder call @@ -306,7 +340,7 @@ public class FingerprintService extends SystemService { } return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, - restricted, statsClient, isKeyguard, mFingerprintStateCallback); + restricted, statsClient, isKeyguard); } private long authenticateWithPrompt( @@ -414,7 +448,7 @@ public class FingerprintService extends SystemService { return provider.second.scheduleFingerDetect(provider.first, token, userId, new ClientMonitorCallbackConverter(receiver), opPackageName, - BiometricsProtoEnums.CLIENT_KEYGUARD, mFingerprintStateCallback); + BiometricsProtoEnums.CLIENT_KEYGUARD); } @Override // Binder call @@ -433,7 +467,7 @@ public class FingerprintService extends SystemService { provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie, new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId, restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - allowBackgroundAuthentication, mFingerprintStateCallback); + allowBackgroundAuthentication); } @Override // Binder call @@ -687,27 +721,6 @@ public class FingerprintService extends SystemService { .isEmpty(); } - @Override // Binder call - public boolean hasEnrolledTemplatesForAnySensor(int userId, - @NonNull List sensors, - @NonNull String opPackageName) { - Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - - for (FingerprintSensorPropertiesInternal prop : sensors) { - final ServiceProvider provider = getProviderForSensor(prop.sensorId); - if (provider == null) { - Slog.w(TAG, "Null provider for sensorId: " + prop.sensorId - + ", caller: " + opPackageName); - continue; - } - - if (!provider.getEnrolledFingerprints(prop.sensorId, userId).isEmpty()) { - return true; - } - } - return false; - } - public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); @@ -797,10 +810,12 @@ public class FingerprintService extends SystemService { && Settings.Secure.getIntForUser(getContext().getContentResolver(), Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */, UserHandle.USER_CURRENT) != 0) { - fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), hidlSensor, + fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), + mFingerprintStateCallback, hidlSensor, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } else { - fingerprint21 = Fingerprint21.newInstance(getContext(), hidlSensor, + fingerprint21 = Fingerprint21.newInstance(getContext(), + mFingerprintStateCallback, hidlSensor, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } mServiceProviders.add(fingerprint21); @@ -823,8 +838,9 @@ public class FingerprintService extends SystemService { try { final SensorProps[] props = fp.getSensorProps(); final FingerprintProvider provider = - new FingerprintProvider(getContext(), props, instance, - mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + new FingerprintProvider(getContext(), mFingerprintStateCallback, props, + instance, mLockoutResetDispatcher, + mGestureAvailabilityDispatcher); mServiceProviders.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); @@ -878,6 +894,7 @@ public class FingerprintService extends SystemService { } } + broadcastCurrentEnrollmentState(null); // broadcasts to all listeners broadcastAllAuthenticatorsRegistered(); }); } @@ -975,6 +992,7 @@ public class FingerprintService extends SystemService { mFingerprintStateCallback = new FingerprintStateCallback(); mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>(); mSensorProps = new ArrayList<>(); + mHandler = new Handler(Looper.getMainLooper()); } // Notifies the callbacks that all of the authenticators have been registered and removes the diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java index 5f998d81fb833..0050a895034f1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java @@ -23,6 +23,7 @@ import static android.hardware.fingerprint.FingerprintStateListener.STATE_IDLE; import static android.hardware.fingerprint.FingerprintStateListener.STATE_KEYGUARD_AUTH; import android.annotation.NonNull; +import android.content.Context; import android.hardware.fingerprint.FingerprintStateListener; import android.hardware.fingerprint.IFingerprintStateListener; import android.os.RemoteException; @@ -31,6 +32,9 @@ import android.util.Slog; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.EnrollClient; +import com.android.server.biometrics.sensors.EnrollmentModifier; +import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.fingerprint.hidl.FingerprintEnrollClient; import java.util.concurrent.CopyOnWriteArrayList; @@ -39,9 +43,11 @@ import java.util.concurrent.CopyOnWriteArrayList; * A callback for receiving notifications about changes in fingerprint state. */ public class FingerprintStateCallback implements BaseClientMonitor.Callback { - private @FingerprintStateListener.State int mFingerprintState; + @NonNull private final CopyOnWriteArrayList - mFingerprintStateListeners = new CopyOnWriteArrayList<>(); + mFingerprintStateListeners = new CopyOnWriteArrayList<>(); + + private @FingerprintStateListener.State int mFingerprintState; public FingerprintStateCallback() { mFingerprintState = STATE_IDLE; @@ -54,8 +60,9 @@ public class FingerprintStateCallback implements BaseClientMonitor.Callback { @Override public void onClientStarted(@NonNull BaseClientMonitor client) { final int previousFingerprintState = mFingerprintState; + if (client instanceof AuthenticationClient) { - AuthenticationClient authClient = (AuthenticationClient) client; + final AuthenticationClient authClient = (AuthenticationClient) client; if (authClient.isKeyguard()) { mFingerprintState = STATE_KEYGUARD_AUTH; } else if (authClient.isBiometricPrompt()) { @@ -70,6 +77,7 @@ public class FingerprintStateCallback implements BaseClientMonitor.Callback { "Other authentication client: " + Utils.getClientName(client)); mFingerprintState = STATE_IDLE; } + Slog.d(FingerprintService.TAG, "Fps state updated from " + previousFingerprintState + " to " + mFingerprintState + ", client " + client); notifyFingerprintStateListeners(mFingerprintState); @@ -81,6 +89,18 @@ public class FingerprintStateCallback implements BaseClientMonitor.Callback { Slog.d(FingerprintService.TAG, "Client finished, fps state updated to " + mFingerprintState + ", client " + client); + + if (client instanceof EnrollmentModifier) { + EnrollmentModifier enrollmentModifier = (EnrollmentModifier) client; + final boolean enrollmentStateChanged = enrollmentModifier.hasEnrollmentStateChanged(); + Slog.d(FingerprintService.TAG, "Enrollment state changed: " + enrollmentStateChanged); + if (enrollmentStateChanged) { + notifyAllFingerprintEnrollmentStateChanged(client.getTargetUserId(), + client.getSensorId(), + enrollmentModifier.hasEnrollments()); + } + } + notifyFingerprintStateListeners(mFingerprintState); } @@ -94,6 +114,32 @@ public class FingerprintStateCallback implements BaseClientMonitor.Callback { } } + /** + * This should be invoked when: + * 1) Enrolled --> None-enrolled + * 2) None-enrolled --> enrolled + * 3) HAL becomes ready + * 4) Listener is registered + */ + void notifyAllFingerprintEnrollmentStateChanged(int userId, int sensorId, + boolean hasEnrollments) { + for (IFingerprintStateListener listener : mFingerprintStateListeners) { + notifyFingerprintEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments); + } + } + + /** + * Notifies the listener of enrollment state changes. + */ + void notifyFingerprintEnrollmentStateChanged(@NonNull IFingerprintStateListener listener, + int userId, int sensorId, boolean hasEnrollments) { + try { + listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments); + } catch (RemoteException e) { + Slog.e(FingerprintService.TAG, "Remote exception", e); + } + } + /** * Enables clients to register a FingerprintStateListener. Used by FingerprintService to forward * updates in fingerprint sensor state to the SideFpNsEventHandler diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index b9fcd8ef4e424..1772f814dd102 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -90,27 +90,23 @@ public interface ServiceProvider { */ void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, - @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason, - @NonNull FingerprintStateCallback fingerprintStateCallback); + @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason); void cancelEnrollment(int sensorId, @NonNull IBinder token); long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, - int statsClient, - @NonNull FingerprintStateCallback fingerprintStateCallback); + int statsClient); void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, - @NonNull FingerprintStateCallback fingerprintStateCallback); + boolean allowBackgroundAuthentication); long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, - @NonNull FingerprintStateCallback fingerprintStateCallback); + boolean allowBackgroundAuthentication); void startPreparedClient(int sensorId, int cookie); @@ -169,6 +165,5 @@ public interface ServiceProvider { @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull String opPackageName); } 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 index 29f2f20b8a754..2b50b96c69a17 100644 --- 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 @@ -143,8 +143,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL, - mFingerprintStateCallback); + mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL); } @Override 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 377fecac61177..ca83dda3bc4ed 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 @@ -82,6 +82,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi private boolean mTestHalEnabled; @NonNull private final Context mContext; + @NonNull private final FingerprintStateCallback mFingerprintStateCallback; @NonNull private final String mHalInstanceName; @NonNull @VisibleForTesting final SparseArray mSensors; // Map of sensors that this HAL supports @@ -130,10 +131,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } } - public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props, - @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, + public FingerprintProvider(@NonNull Context context, + @NonNull FingerprintStateCallback fingerprintStateCallback, + @NonNull SensorProps[] props, @NonNull String halInstanceName, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { mContext = context; + mFingerprintStateCallback = fingerprintStateCallback; mHalInstanceName = halInstanceName; mSensors = new SparseArray<>(); mHandler = new Handler(Looper.getMainLooper()); @@ -335,8 +339,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - @FingerprintManager.EnrollReason int enrollReason, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + @FingerprintManager.EnrollReason int enrollReason) { mHandler.post(() -> { final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties() .maxEnrollmentsPerUser; @@ -350,13 +353,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { - fingerprintStateCallback.onClientStarted(clientMonitor); + mFingerprintStateCallback.onClientStarted(clientMonitor); } @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { - fingerprintStateCallback.onClientFinished(clientMonitor, success); + mFingerprintStateCallback.onClientFinished(clientMonitor, success); if (success) { scheduleLoadAuthenticatorIdsForUser(sensorId, userId); scheduleInvalidationRequest(sensorId, userId); @@ -374,17 +377,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, - int statsClient, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + int statsClient) { final long id = mRequestCounter.incrementAndGet(); - mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FingerprintDetectClient client = new FingerprintDetectClient(mContext, mSensors.get(sensorId).getLazySession(), token, id, callback, userId, opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric, statsClient); - scheduleForSensor(sensorId, client, fingerprintStateCallback); + scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); return id; @@ -394,8 +395,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + boolean allowBackgroundAuthentication) { mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( @@ -405,7 +405,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), mUdfpsOverlayController, allowBackgroundAuthentication, mSensors.get(sensorId).getSensorProperties()); - scheduleForSensor(sensorId, client, fingerprintStateCallback); + scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); } @@ -413,13 +413,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + boolean allowBackgroundAuthentication) { final long id = mRequestCounter.incrementAndGet(); scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback, - opPackageName, id, restricted, statsClient, allowBackgroundAuthentication, - fingerprintStateCallback); + opPackageName, id, restricted, statsClient, allowBackgroundAuthentication); return id; } @@ -466,7 +464,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId, opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, mSensors.get(sensorId).getAuthenticatorIds()); - scheduleForSensor(sensorId, client); + scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); } @@ -481,7 +479,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mContext.getOpPackageName(), sensorId, enrolledList, FingerprintUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); - scheduleForSensor(sensorId, client, callback); + scheduleForSensor(sensorId, client, new BaseClientMonitor.CompositeCallback(callback, + mFingerprintStateCallback)); }); } @@ -626,9 +625,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull String opPackageName) { - return mSensors.get(sensorId).createTestSession(callback, fingerprintStateCallback); + return mSensors.get(sensorId).createTestSession(callback, mFingerprintStateCallback); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index c00daffb867ff..79c6b1b30d5bf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -143,8 +143,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL, - mFingerprintStateCallback); + mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL); } @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 f17bcc8c63546..d2882aa4094c7 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 @@ -102,6 +102,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private boolean mTestHalEnabled; final Context mContext; + @NonNull private final FingerprintStateCallback mFingerprintStateCallback; private final ActivityTaskManager mActivityTaskManager; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; private final BiometricScheduler mScheduler; @@ -317,11 +318,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } Fingerprint21(@NonNull Context context, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull BiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull HalResultController controller) { mContext = context; + mFingerprintStateCallback = fingerprintStateCallback; mSensorProperties = sensorProps; mSensorId = sensorProps.sensorId; @@ -351,6 +354,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } public static Fingerprint21 newInstance(@NonNull Context context, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { @@ -362,8 +366,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final HalResultController controller = new HalResultController(sensorProps.sensorId, context, handler, scheduler); - return new Fingerprint21(context, sensorProps, scheduler, handler, lockoutResetDispatcher, - controller); + return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler, + lockoutResetDispatcher, controller); } @Override @@ -557,8 +561,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - @FingerprintManager.EnrollReason int enrollReason, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + @FingerprintManager.EnrollReason int enrollReason) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -570,13 +573,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { - fingerprintStateCallback.onClientStarted(clientMonitor); + mFingerprintStateCallback.onClientStarted(clientMonitor); } @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { - fingerprintStateCallback.onClientFinished(clientMonitor, success); + mFingerprintStateCallback.onClientFinished(clientMonitor, success); if (success) { // Update authenticatorIds scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(), @@ -597,10 +600,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, - int statsClient, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + int statsClient) { final long id = mRequestCounter.incrementAndGet(); - mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -609,7 +610,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mLazyDaemon, token, id, listener, userId, opPackageName, mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric, statsClient); - mScheduler.scheduleClientMonitor(client, fingerprintStateCallback); + mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); return id; @@ -619,8 +620,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + boolean allowBackgroundAuthentication) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -631,7 +631,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, isStrongBiometric, statsClient, mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, allowBackgroundAuthentication, mSensorProperties); - mScheduler.scheduleClientMonitor(client, fingerprintStateCallback); + mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); } @@ -639,13 +639,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, boolean restricted, int statsClient, - boolean allowBackgroundAuthentication, - @NonNull FingerprintStateCallback fingerprintStateCallback) { + boolean allowBackgroundAuthentication) { final long id = mRequestCounter.incrementAndGet(); scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener, - opPackageName, id, restricted, statsClient, allowBackgroundAuthentication, - fingerprintStateCallback); + opPackageName, id, restricted, statsClient, allowBackgroundAuthentication); return id; } @@ -672,7 +670,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId, userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), mSensorProperties.sensorId, mAuthenticatorIds); - mScheduler.scheduleClientMonitor(client); + mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); } @@ -689,7 +687,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider 0 /* fingerprintId */, userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), mSensorProperties.sensorId, mAuthenticatorIds); - mScheduler.scheduleClientMonitor(client); + mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); } @@ -711,7 +709,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void scheduleInternalCleanup(int sensorId, int userId, @Nullable BaseClientMonitor.Callback callback) { - scheduleInternalCleanup(userId, callback); + scheduleInternalCleanup(userId, new BaseClientMonitor.CompositeCallback(callback, + mFingerprintStateCallback)); } @Override @@ -919,9 +918,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull String opPackageName) { return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback, - fingerprintStateCallback, this, mHalResultController); + mFingerprintStateCallback, this, mHalResultController); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 24ce8676235a7..79ad8e1a5c705 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -26,6 +26,7 @@ import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.FingerprintStateListener; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Handler; import android.os.IBinder; @@ -42,6 +43,7 @@ import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import java.util.ArrayList; @@ -270,6 +272,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage } public static Fingerprint21UdfpsMock newInstance(@NonNull Context context, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { @@ -280,8 +283,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher); final MockHalResultController controller = new MockHalResultController(sensorProps.sensorId, context, handler, scheduler); - return new Fingerprint21UdfpsMock(context, sensorProps, scheduler, handler, - lockoutResetDispatcher, controller); + return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler, + handler, lockoutResetDispatcher, controller); } private static abstract class FakeFingerRunnable implements Runnable { @@ -400,17 +403,19 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage // internal preemption logic is not run. mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token, operationId, user, cookie, listener, opPackageName, restricted, statsClient, - isKeyguard, null /* fingerprintStateCallback */); + isKeyguard); } } private Fingerprint21UdfpsMock(@NonNull Context context, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull TestableBiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull MockHalResultController controller) { - super(context, sensorProps, scheduler, handler, lockoutResetDispatcher, controller); + super(context, fingerprintStateCallback, sensorProps, scheduler, handler, + lockoutResetDispatcher, controller); mScheduler = scheduler; mScheduler.init(this); mHandler = handler; diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java new file mode 100644 index 0000000000000..09b5c5cac466a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@Presubmit +@SmallTest +public class CompositeCallbackTest { + + @Test + public void testNullCallback() { + BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class); + BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class); + BaseClientMonitor.Callback callback3 = null; + + BaseClientMonitor.CompositeCallback callback = new BaseClientMonitor.CompositeCallback( + callback1, callback2, callback3); + + BaseClientMonitor clientMonitor = mock(BaseClientMonitor.class); + + callback.onClientStarted(clientMonitor); + verify(callback1).onClientStarted(eq(clientMonitor)); + verify(callback2).onClientStarted(eq(clientMonitor)); + + callback.onClientFinished(clientMonitor, true /* success */); + verify(callback1).onClientFinished(eq(clientMonitor), eq(true)); + verify(callback2).onClientFinished(eq(clientMonitor), eq(true)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java new file mode 100644 index 0000000000000..38e8dfa452621 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.fingerprint.FingerprintStateListener; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.EnrollClient; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@Presubmit +@SmallTest +public class FingerprintStateCallbackTest { + + private FingerprintStateCallback mCallback; + + @Mock + FingerprintStateListener mFingerprintStateListener; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + mCallback = new FingerprintStateCallback(); + mCallback.registerFingerprintStateListener(mFingerprintStateListener); + } + + @Test + public void testNoEnrollmentsToEnrollments_callbackNotified() { + testEnrollmentCallback(true /* changed */, true /* isNowEnrolled */, + true /* expectCallback */, true /* expectedCallbackValue */); + } + + @Test + public void testEnrollmentsToNoEnrollments_callbackNotified() { + testEnrollmentCallback(true /* changed */, false /* isNowEnrolled */, + true /* expectCallback */, false /* expectedCallbackValue */); + } + + @Test + public void testEnrollmentsToEnrollments_callbackNotNotified() { + testEnrollmentCallback(false /* changed */, true /* isNowEnrolled */, + false /* expectCallback */, false /* expectedCallbackValue */); + } + + private void testEnrollmentCallback(boolean changed, boolean isNowEnrolled, + boolean expectCallback, boolean expectedCallbackValue) { + EnrollClient client = mock(EnrollClient.class); + + final int userId = 10; + final int sensorId = 100; + + when(client.hasEnrollmentStateChanged()).thenReturn(changed); + when(client.hasEnrollments()).thenReturn(isNowEnrolled); + when(client.getTargetUserId()).thenReturn(userId); + when(client.getSensorId()).thenReturn(sensorId); + + mCallback.onClientFinished(client, true /* success */); + if (expectCallback) { + verify(mFingerprintStateListener).onEnrollmentsChanged(eq(userId), eq(sensorId), + eq(expectedCallbackValue)); + } else { + verify(mFingerprintStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(), + anyBoolean()); + } + } + + @Test + public void testAuthentication_enrollmentCallbackNeverNotified() { + AuthenticationClient client = mock(AuthenticationClient.class); + mCallback.onClientFinished(client, true /* success */); + verify(mFingerprintStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(), + anyBoolean()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index 35c37efa16b41..b51918e24b13e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import org.junit.Before; @@ -58,6 +59,8 @@ public class FingerprintProviderTest { private UserManager mUserManager; @Mock private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; + @Mock + private FingerprintStateCallback mFingerprintStateCallback; private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; @@ -87,8 +90,8 @@ public class FingerprintProviderTest { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); - mFingerprintProvider = new TestableFingerprintProvider(mContext, mSensorProps, TAG, - mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + mFingerprintProvider = new TestableFingerprintProvider(mContext, mFingerprintStateCallback, + mSensorProps, TAG, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } @SuppressWarnings("rawtypes") @@ -133,11 +136,12 @@ public class FingerprintProviderTest { private static class TestableFingerprintProvider extends FingerprintProvider { public TestableFingerprintProvider(@NonNull Context context, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - super(context, props, halInstanceName, lockoutResetDispatcher, + super(context, fingerprintStateCallback, props, halInstanceName, lockoutResetDispatcher, gestureAvailabilityDispatcher); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java index 0a0dcc97272b9..f6b92097a9fbf 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java @@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import org.junit.Before; import org.junit.Test; @@ -67,6 +68,8 @@ public class Fingerprint21Test { Fingerprint21.HalResultController mHalResultController; @Mock private BiometricScheduler mScheduler; + @Mock + private FingerprintStateCallback mFingerprintStateCallback; private LockoutResetDispatcher mLockoutResetDispatcher; private Fingerprint21 mFingerprint21; @@ -96,8 +99,9 @@ public class Fingerprint21Test { componentInfo, FingerprintSensorProperties.TYPE_UNKNOWN, resetLockoutRequiresHardwareAuthToken); - mFingerprint21 = new TestableFingerprint21(mContext, sensorProps, mScheduler, - new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, mHalResultController); + mFingerprint21 = new TestableFingerprint21(mContext, mFingerprintStateCallback, sensorProps, + mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, + mHalResultController); } @Test @@ -118,11 +122,13 @@ public class Fingerprint21Test { private static class TestableFingerprint21 extends Fingerprint21 { TestableFingerprint21(@NonNull Context context, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull BiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull HalResultController controller) { - super(context, sensorProps, scheduler, handler, lockoutResetDispatcher, controller); + super(context, fingerprintStateCallback, sensorProps, scheduler, handler, + lockoutResetDispatcher, controller); } @Override