From f907d35aaff072767cb0793270dde4055790ab09 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Mon, 12 Oct 2020 13:36:02 -0700 Subject: [PATCH] 5/n: Add AuthenticationClient for IFingerprint 1) Adds FingerprintAuthenticationClient 2) Finishes plumbing for cancel enrollment Bug: 170497736 Test: Builds Change-Id: I4c53face501e16af5cf781c757c363e5dd30ae3c --- .../ClientMonitorCallbackConverter.java | 3 +- .../biometrics/sensors/LockoutConsumer.java | 25 +++ .../aidl/FingerprintAuthenticationClient.java | 152 ++++++++++++++++++ .../fingerprint/aidl/FingerprintProvider.java | 115 +++++++++++-- .../sensors/fingerprint/aidl/Sensor.java | 21 +++ .../fingerprint/hidl/Fingerprint21.java | 8 +- 6 files changed, 300 insertions(+), 24 deletions(-) create mode 100644 services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java create mode 100644 services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java index cb7db92ee132e..c87f62f944999 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java @@ -89,7 +89,8 @@ public final class ClientMonitorCallbackConverter { } } - void onError(int sensorId, int cookie, int error, int vendorCode) throws RemoteException { + public void onError(int sensorId, int cookie, int error, int vendorCode) + throws RemoteException { if (mSensorReceiver != null) { mSensorReceiver.onError(sensorId, cookie, error, vendorCode); } else if (mFaceServiceReceiver != null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java new file mode 100644 index 0000000000000..153bd4668a4f9 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * Interface that clients interested/eligible for lockout events should implement. + */ +public interface LockoutConsumer { + void onLockoutTimed(long durationMillis); + void onLockoutPermanent(); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java new file mode 100644 index 0000000000000..e923943bd3867 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -0,0 +1,152 @@ +/* + * 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.annotation.Nullable; +import android.app.TaskStackListener; +import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutConsumer; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.fingerprint.Udfps; +import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper; + +import java.util.ArrayList; + +/** + * Fingerprint-specific authentication client supporting the + * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface. + */ +public class FingerprintAuthenticationClient extends AuthenticationClient implements + Udfps, LockoutConsumer { + private static final String TAG = "FingerprintAuthenticationClient"; + + @NonNull private final LockoutCache mLockoutCache; + @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; + @Nullable private ICancellationSignal mCancellationSignal; + + public FingerprintAuthenticationClient(@NonNull Context context, + @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, + @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, + boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, + int sensorId, boolean isStrongBiometric, int statsClient, + @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache, + @Nullable IUdfpsOverlayController udfpsOverlayController) { + super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, + cookie, requireConfirmation, sensorId, isStrongBiometric, + BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener, + lockoutCache); + mLockoutCache = lockoutCache; + mUdfpsOverlayController = udfpsOverlayController; + } + + @Override + public void onAuthenticated(BiometricAuthenticator.Identifier identifier, + boolean authenticated, ArrayList token) { + super.onAuthenticated(identifier, authenticated, token); + + if (authenticated) { + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + mCallback.onClientFinished(this, true /* success */); + } + } + + @Override + protected void startHalOperation() { + UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + try { + mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + protected void stopHalOperation() { + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + public void onPointerDown(int x, int y, float minor, float major) { + try { + getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onPointerUp() { + try { + getFreshDaemon().onPointerUp(0 /* pointerId */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onLockoutTimed(long durationMillis) { + mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); + // Lockout metrics are logged as an error code. + final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; + logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId()); + + try { + getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onLockoutPermanent() { + mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT); + // Lockout metrics are logged as an error code. + final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT; + logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId()); + + try { + getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } +} 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 0b59086fb9071..5e6c30ecefc88 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 @@ -19,12 +19,14 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; +import android.app.TaskStackListener; import android.content.Context; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.Fingerprint; -import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -37,12 +39,15 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Surface; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; +import com.android.server.biometrics.sensors.fingerprint.Udfps; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -52,6 +57,7 @@ import java.util.List; /** * Provider for a single instance of the {@link IFingerprint} HAL. */ +@SuppressWarnings("deprecation") public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final Context mContext; @@ -60,9 +66,48 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final ClientMonitor.LazyDaemon mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; + @NonNull private final IActivityTaskManager mActivityTaskManager; + @NonNull private final BiometricTaskStackListener mTaskStackListener; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; + private final class BiometricTaskStackListener extends TaskStackListener { + @Override + public void onTaskStackChanged() { + mHandler.post(() -> { + for (int i = 0; i < mSensors.size(); i++) { + final ClientMonitor client = mSensors.get(i).getScheduler() + .getCurrentClient(); + if (!(client instanceof AuthenticationClient)) { + Slog.e(getTag(), "Task stack changed for client: " + client); + continue; + } + if (Utils.isKeyguard(mContext, client.getOwnerString())) { + continue; // Keyguard is always allowed + } + + try { + final List runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = + runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(getTag(), "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mSensors.get(i).getScheduler() + .cancelAuthentication(client.getToken()); + } + } + } catch (RemoteException e) { + Slog.e(getTag(), "Unable to get running tasks", e); + } + } + }); + } + } + public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { @@ -72,6 +117,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mLazyDaemon = this::getHalInstance; mHandler = new Handler(Looper.getMainLooper()); mLockoutResetDispatcher = lockoutResetDispatcher; + mActivityTaskManager = ActivityTaskManager.getService(); + mTaskStackListener = new BiometricTaskStackListener(); for (SensorProps prop : props) { final int sensorId = prop.commonProps.sensorId; @@ -135,7 +182,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback); } - private void scheduleCreateSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId, + private void createNewSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId, int userId) throws RemoteException { // Note that per IFingerprint createSession contract, this method will block until all // existing operations are canceled/finished. However, also note that this is fine, since @@ -178,7 +225,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi try { if (!mSensors.get(sensorId).hasSessionForUser(userId)) { - scheduleCreateSessionWithoutHandler(daemon, sensorId, userId); + createNewSessionWithoutHandler(daemon, sensorId, userId); } final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient( @@ -222,19 +269,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final IFingerprint daemon = getHalInstance(); if (daemon == null) { Slog.e(getTag(), "Null daemon during enroll, sensorId: " + sensorId); - - try { - receiver.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */); - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to send HW_UNAVAILABLE", e); - } + // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to + // this operation. We should not send the callback yet, since the scheduler may + // be processing something else. return; } try { if (!mSensors.get(sensorId).hasSessionForUser(userId)) { - scheduleCreateSessionWithoutHandler(daemon, sensorId, userId); + createNewSessionWithoutHandler(daemon, sensorId, userId); } final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties() @@ -262,7 +305,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void cancelEnrollment(int sensorId, @NonNull IBinder token) { - + mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token)); } @Override @@ -277,17 +320,43 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, boolean isKeyguard) { + mHandler.post(() -> { + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during authenticate, sensorId: " + sensorId); + // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to + // this operation. We should not send the callback yet, since the scheduler may + // be processing something else. + return; + } + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + + final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); + final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( + mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId, + operationId, restricted, opPackageName, cookie, + false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, + mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), + mUdfpsOverlayController); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling authenticate", e); + } + }); } @Override public void startPreparedClient(int sensorId, int cookie) { - + mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie)); } @Override public void cancelAuthentication(int sensorId, @NonNull IBinder token) { - + mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelAuthentication(token)); } @Override @@ -320,7 +389,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public int getLockoutModeForUser(int sensorId, int userId) { - return 0; + return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId); } @Override @@ -330,12 +399,24 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void onPointerDown(int sensorId, int x, int y, float minor, float major) { - + final ClientMonitor client = mSensors.get(sensorId).getScheduler().getCurrentClient(); + if (!(client instanceof Udfps)) { + Slog.e(getTag(), "onPointerDown received during client: " + client); + return; + } + final Udfps udfps = (Udfps) client; + udfps.onPointerDown(x, y, minor, major); } @Override public void onPointerUp(int sensorId) { - + final ClientMonitor client = mSensors.get(sensorId).getScheduler().getCurrentClient(); + if (!(client instanceof Udfps)) { + Slog.e(getTag(), "onPointerUp received during client: " + client); + return; + } + final Udfps udfps = (Udfps) client; + udfps.onPointerUp(); } @Override 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 2ed695d8821e6..dc90be8ea886c 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 @@ -37,6 +37,7 @@ import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.ClientMonitor; import com.android.server.biometrics.sensors.Interruptable; +import com.android.server.biometrics.sensors.LockoutConsumer; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; @@ -209,12 +210,32 @@ class Sensor { @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); + }); } @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 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 fedcfa576d354..da68bc8b79da7 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 @@ -594,16 +594,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void startPreparedClient(int sensorId, int cookie) { - mHandler.post(() -> { - mScheduler.startPreparedClient(cookie); - }); + mHandler.post(() -> mScheduler.startPreparedClient(cookie)); } @Override public void cancelAuthentication(int sensorId, @NonNull IBinder token) { - mHandler.post(() -> { - mScheduler.cancelAuthentication(token); - }); + mHandler.post(() -> mScheduler.cancelAuthentication(token)); } @Override