From 06f04c041cf65b19cbf736852996760b1284124c Mon Sep 17 00:00:00 2001 From: Ilya Matyukhin Date: Thu, 15 Apr 2021 19:23:47 -0700 Subject: [PATCH] Notify AuthController when fingerprint providers are ready This change introduces a callback that AuthController can provide to FingerprintService to get notified when all of the fingerprint providers are available. This fixes the bug where UDFPS overlay is not available if SystemUI starts before the fingerprint HAL, because SystemUI would query the fingerprint providers on startup. Bug: 184677066 Test: atest AuthControllerTest Test: on device Change-Id: I165d67bdda09ea10dd5aac539c4155e6b158227d --- .../fingerprint/FingerprintManager.java | 25 ++++++ ...printAuthenticatorsRegisteredCallback.aidl | 35 ++++++++ .../fingerprint/IFingerprintService.aidl | 5 ++ .../systemui/biometrics/AuthController.java | 58 ++++++++----- .../biometrics/AuthControllerTest.java | 11 ++- .../fingerprint/FingerprintService.java | 87 +++++++++++++++++-- 6 files changed, 190 insertions(+), 31 deletions(-) create mode 100644 core/java/android/hardware/fingerprint/IFingerprintAuthenticatorsRegisteredCallback.aidl diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 88d5ba8be8aba..cc1aeeb92685a 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1012,6 +1012,31 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing return sensorProps.sensorType == TYPE_POWER_BUTTON; } + /** + * Adds a callback that gets called when the service registers all of the fingerprint + * authenticators (HALs). + * + * If the fingerprint authenticators are already registered when the callback is added, the + * callback is invoked immediately. + * + * The callback is automatically removed after it's invoked. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void addAuthenticatorsRegisteredCallback( + IFingerprintAuthenticatorsRegisteredCallback callback) { + if (mService != null) { + try { + mService.addAuthenticatorsRegisteredCallback(callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "addProvidersAvailableCallback(): Service not connected!"); + } + } + /** * @hide */ diff --git a/core/java/android/hardware/fingerprint/IFingerprintAuthenticatorsRegisteredCallback.aidl b/core/java/android/hardware/fingerprint/IFingerprintAuthenticatorsRegisteredCallback.aidl new file mode 100644 index 0000000000000..5a2c9311eaf92 --- /dev/null +++ b/core/java/android/hardware/fingerprint/IFingerprintAuthenticatorsRegisteredCallback.aidl @@ -0,0 +1,35 @@ +/* + * 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 android.hardware.fingerprint; + +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import java.util.List; + +/** + * Callback to notify FingerprintManager that FingerprintService has registered all of the + * fingerprint authenticators (HALs). + * See {@link android.hardware.fingerprint.IFingerprintService#registerAuthenticators}. + * + * @hide + */ +oneway interface IFingerprintAuthenticatorsRegisteredCallback { + /** + * Notifies FingerprintManager that all of the fingerprint authenticators have been registered. + * + * @param sensors A consolidated list of sensor properties for all of the authenticators. + */ + void onAllAuthenticatorsRegistered(in List sensors); +} diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 0e3cdd6b650a5..833747f19a0c0 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -21,6 +21,7 @@ import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; import android.hardware.fingerprint.IFingerprintClientActiveCallback; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IFingerprintStateListener; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -149,6 +150,10 @@ interface IFingerprintService { // hidlSensors must be non-null and empty. See AuthService.java void registerAuthenticators(in List hidlSensors); + // Adds a callback which gets called when the service registers all of the fingerprint + // authenticators. The callback is automatically removed after it's invoked. + void addAuthenticatorsRegisteredCallback(IFingerprintAuthenticatorsRegisteredCallback callback); + // Notifies about a finger touching the sensor area. void onPointerDown(int sensorId, int x, int y, float minor, float major); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 3d86034093423..179b077a3714c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -20,6 +20,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricManager.Authenticators; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -39,6 +40,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.IFingerprintAuthenticatorsRegisteredCallback; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -57,6 +59,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.inject.Inject; @@ -73,16 +76,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private static final String TAG = "AuthController"; private static final boolean DEBUG = true; + private final Handler mHandler = new Handler(Looper.getMainLooper()); private final CommandQueue mCommandQueue; private final StatusBarStateController mStatusBarStateController; private final ActivityTaskManager mActivityTaskManager; @Nullable private final FingerprintManager mFingerprintManager; @Nullable private final FaceManager mFaceManager; private final Provider mUdfpsControllerFactory; - - @Nullable private final List mFpProps; - @Nullable private final List mFaceProps; - @Nullable private final List mUdfpsProps; @Nullable private final PointF mFaceAuthSensorLocation; // TODO: These should just be saved from onSaveState @@ -90,7 +90,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @VisibleForTesting AuthDialog mCurrentDialog; - private Handler mHandler = new Handler(Looper.getMainLooper()); private WindowManager mWindowManager; @Nullable private UdfpsController mUdfpsController; @@ -98,6 +97,9 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, TaskStackListener mTaskStackListener; @VisibleForTesting IBiometricSysuiReceiver mReceiver; + @Nullable private final List mFaceProps; + @Nullable private List mFpProps; + @Nullable private List mUdfpsProps; private class BiometricTaskStackListener extends TaskStackListener { @Override @@ -106,8 +108,31 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } } - @VisibleForTesting - final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @NonNull + private final IFingerprintAuthenticatorsRegisteredCallback + mFingerprintAuthenticatorsRegisteredCallback = + new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override public void onAllAuthenticatorsRegistered( + List sensors) { + if (DEBUG) { + Log.d(TAG, "onFingerprintProvidersAvailable | sensors: " + Arrays.toString( + sensors.toArray())); + } + mFpProps = sensors; + List udfpsProps = new ArrayList<>(); + for (FingerprintSensorPropertiesInternal props : mFpProps) { + if (props.isAnyUdfpsType()) { + udfpsProps.add(props); + } + } + mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null; + if (mUdfpsProps != null) { + mUdfpsController = mUdfpsControllerFactory.get(); + } + } + }; + + @VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mCurrentDialog != null @@ -348,19 +373,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mFaceManager = faceManager; mUdfpsControllerFactory = udfpsControllerFactory; - mFpProps = mFingerprintManager != null ? mFingerprintManager.getSensorPropertiesInternal() - : null; mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null; - List udfpsProps = new ArrayList<>(); - if (mFpProps != null) { - for (FingerprintSensorPropertiesInternal props : mFpProps) { - if (props.isAnyUdfpsType()) { - udfpsProps.add(props); - } - } - } - mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null; int[] faceAuthLocation = context.getResources().getIntArray( com.android.systemui.R.array.config_face_auth_props); if (faceAuthLocation == null || faceAuthLocation.length < 2) { @@ -383,9 +397,9 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCommandQueue.addCallback(this); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected() - && mUdfpsProps != null) { - mUdfpsController = mUdfpsControllerFactory.get(); + if (mFingerprintManager != null) { + mFingerprintManager.addAuthenticatorsRegisteredCallback( + mFingerprintAuthenticatorsRegisteredCallback); } mTaskStackListener = new BiometricTaskStackListener(); @@ -527,7 +541,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, return mFaceManager.hasEnrolledTemplates(userId); } - /** + /** * Whether the passed userId has enrolled UDFPS. */ public boolean isUdfpsEnrolled(int userId) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 3fe8cee8d7397..1b464a9b971b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -50,7 +50,9 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.os.Bundle; +import android.os.RemoteException; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; @@ -66,6 +68,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.AdditionalMatchers; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -100,12 +103,13 @@ public class AuthControllerTest extends SysuiTestCase { private FaceManager mFaceManager; @Mock private UdfpsController mUdfpsController; + @Captor + ArgumentCaptor mAuthenticatorsRegisteredCaptor; private TestableAuthController mAuthController; - @Before - public void setup() { + public void setup() throws RemoteException { MockitoAnnotations.initMocks(this); TestableContext context = spy(mContext); @@ -148,6 +152,9 @@ public class AuthControllerTest extends SysuiTestCase { () -> mUdfpsController); mAuthController.start(); + verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( + mAuthenticatorsRegisteredCaptor.capture()); + mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props); } // Callback tests 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 2b0637fe335a0..39b7a74caa52b 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 @@ -50,6 +50,7 @@ import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintServiceReceiver; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.IFingerprintClientActiveCallback; import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IFingerprintServiceReceiver; @@ -61,6 +62,7 @@ import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -71,6 +73,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.ServiceThread; @@ -99,14 +102,22 @@ public class FingerprintService extends SystemService { protected static final String TAG = "FingerprintService"; + private final Object mLock = new Object(); private final AppOpsManager mAppOps; private final LockoutResetDispatcher mLockoutResetDispatcher; private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; private final LockPatternUtils mLockPatternUtils; private final FingerprintServiceWrapper mServiceWrapper; - @NonNull private List mServiceProviders; + @NonNull private final List mServiceProviders; @NonNull private final FingerprintStateCallback mFingerprintStateCallback; + @GuardedBy("mLock") + @NonNull private final RemoteCallbackList + mAuthenticatorsRegisteredCallbacks; + + @GuardedBy("mLock") + @NonNull private final List mSensorProps; + /** * Registers FingerprintStateListener in list stored by FingerprintService * @param listener new FingerprintStateListener being added @@ -744,7 +755,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void resetLockout(IBinder token, int sensorId, int userId, - @Nullable byte [] hardwareAuthToken, String opPackageName) { + @Nullable byte[] hardwareAuthToken, String opPackageName) { Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -856,9 +867,39 @@ public class FingerprintService extends SystemService { } } } + + synchronized (mLock) { + for (ServiceProvider provider : mServiceProviders) { + mSensorProps.addAll(provider.getSensorProperties()); + } + } + + broadcastAllAuthenticatorsRegistered(); }); } + @Override + public void addAuthenticatorsRegisteredCallback( + IFingerprintAuthenticatorsRegisteredCallback callback) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + if (callback == null) { + Slog.e(TAG, "addAuthenticatorsRegisteredCallback, callback is null"); + return; + } + + final boolean registered; + final boolean hasSensorProps; + synchronized (mLock) { + registered = mAuthenticatorsRegisteredCallbacks.register(callback); + hasSensorProps = !mSensorProps.isEmpty(); + } + if (registered && hasSensorProps) { + broadcastAllAuthenticatorsRegistered(); + } else if (!registered) { + Slog.e(TAG, "addAuthenticatorsRegisteredCallback failed to register callback"); + } + } + @Override public void onPointerDown(int sensorId, int x, int y, float minor, float major) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); @@ -870,6 +911,7 @@ public class FingerprintService extends SystemService { } provider.onPointerDown(sensorId, x, y, minor, major); } + @Override public void onPointerUp(int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); @@ -906,6 +948,40 @@ public class FingerprintService extends SystemService { mLockPatternUtils = new LockPatternUtils(context); mServiceProviders = new ArrayList<>(); mFingerprintStateCallback = new FingerprintStateCallback(); + mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>(); + mSensorProps = new ArrayList<>(); + } + + // Notifies the callbacks that all of the authenticators have been registered and removes the + // invoked callbacks from the callback list. + private void broadcastAllAuthenticatorsRegistered() { + // Make a local copy of the data so it can be used outside of the synchronized block when + // making Binder calls. + final List callbacks = new ArrayList<>(); + final List props; + synchronized (mLock) { + if (!mSensorProps.isEmpty()) { + props = new ArrayList<>(mSensorProps); + } else { + Slog.e(TAG, "mSensorProps is empty"); + return; + } + final int n = mAuthenticatorsRegisteredCallbacks.beginBroadcast(); + for (int i = 0; i < n; ++i) { + final IFingerprintAuthenticatorsRegisteredCallback cb = + mAuthenticatorsRegisteredCallbacks.getBroadcastItem(i); + callbacks.add(cb); + mAuthenticatorsRegisteredCallbacks.unregister(cb); + } + mAuthenticatorsRegisteredCallbacks.finishBroadcast(); + } + for (IFingerprintAuthenticatorsRegisteredCallback cb : callbacks) { + try { + cb.onAllAuthenticatorsRegistered(props); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAllAuthenticatorsRegistered", e); + } + } } @Override @@ -950,12 +1026,9 @@ public class FingerprintService extends SystemService { @NonNull private List getSensorProperties() { - final List properties = new ArrayList<>(); - - for (ServiceProvider provider : mServiceProviders) { - properties.addAll(provider.getSensorProperties()); + synchronized (mLock) { + return mSensorProps; } - return properties; } @NonNull