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