diff options
7 files changed, 231 insertions, 21 deletions
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsBroadcastReceiver.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsBroadcastReceiver.java new file mode 100644 index 000000000000..832d73fd5d2d --- /dev/null +++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsBroadcastReceiver.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 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; + +import android.annotation.NonNull; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.biometrics.BiometricAuthenticator; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.server.biometrics.sensors.BiometricNotificationImpl; + +import java.util.function.Consumer; + +/** + * Receives broadcast to initialize AuthenticationStatsCollector. + */ +public class AuthenticationStatsBroadcastReceiver extends BroadcastReceiver { + + private static final String TAG = "AuthenticationStatsBroadcastReceiver"; + + @NonNull + private final Consumer<AuthenticationStatsCollector> mCollectorConsumer; + @BiometricAuthenticator.Modality + private final int mModality; + + public AuthenticationStatsBroadcastReceiver(@NonNull Context context, + @BiometricAuthenticator.Modality int modality, + @NonNull Consumer<AuthenticationStatsCollector> callback) { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); + context.registerReceiver(this, intentFilter); + + mCollectorConsumer = callback; + mModality = modality; + } + + @Override + public void onReceive(@NonNull Context context, @NonNull Intent intent) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + + if (userId != UserHandle.USER_NULL + && Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { + Slog.d(TAG, "Received: " + intent.getAction()); + + mCollectorConsumer.accept( + new AuthenticationStatsCollector(context, mModality, + new BiometricNotificationImpl())); + + context.unregisterReceiver(this); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java index 87037af11a84..dbef7178efd0 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.log; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.SensorManager; import android.hardware.biometrics.BiometricConstants; @@ -42,7 +43,7 @@ public class BiometricLogger { private final int mStatsAction; private final int mStatsClient; private final BiometricFrameworkStatsLogger mSink; - @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector; + @Nullable private final AuthenticationStatsCollector mAuthenticationStatsCollector; @NonNull private final ALSProbe mALSProbe; private long mFirstAcquireTimeMs; @@ -68,7 +69,7 @@ public class BiometricLogger { */ public BiometricLogger( @NonNull Context context, int statsModality, int statsAction, int statsClient, - AuthenticationStatsCollector authenticationStatsCollector) { + @Nullable AuthenticationStatsCollector authenticationStatsCollector) { this(statsModality, statsAction, statsClient, BiometricFrameworkStatsLogger.getInstance(), authenticationStatsCollector, @@ -79,7 +80,7 @@ public class BiometricLogger { BiometricLogger( int statsModality, int statsAction, int statsClient, BiometricFrameworkStatsLogger logSink, - @NonNull AuthenticationStatsCollector statsCollector, + @Nullable AuthenticationStatsCollector statsCollector, SensorManager sensorManager) { mStatsModality = statsModality; mStatsAction = statsAction; @@ -206,7 +207,9 @@ public class BiometricLogger { return; } - mAuthenticationStatsCollector.authenticate(targetUserId, authenticated); + if (mAuthenticationStatsCollector != null) { + mAuthenticationStatsCollector.authenticate(targetUserId, authenticated); + } int authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN; if (!authenticated) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 28f0a4dadbd5..cc3118cc3433 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -49,6 +49,7 @@ import android.util.proto.ProtoOutputStream; import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; @@ -56,7 +57,6 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; -import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -114,8 +114,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private final BiometricContext mBiometricContext; @NonNull private final AuthSessionCoordinator mAuthSessionCoordinator; - @NonNull - private final AuthenticationStatsCollector mAuthenticationStatsCollector; + @Nullable + private AuthenticationStatsCollector mAuthenticationStatsCollector; @Nullable private IFace mDaemon; @@ -177,8 +177,14 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; - mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FACE, new BiometricNotificationImpl()); + AuthenticationStatsBroadcastReceiver mBroadcastReceiver = + new AuthenticationStatsBroadcastReceiver( + mContext, + BiometricsProtoEnums.MODALITY_FACE, + (AuthenticationStatsCollector collector) -> { + Slog.d(getTag(), "Initializing AuthenticationStatsCollector"); + mAuthenticationStatsCollector = collector; + }); for (SensorProps prop : props) { final int sensorId = prop.commonProps.sensorId; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 808626120c1e..1499317478aa 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -52,6 +52,7 @@ import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; @@ -62,7 +63,6 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; -import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -125,7 +125,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Nullable private IBiometricsFace mDaemon; @NonNull private final HalResultController mHalResultController; @NonNull private final BiometricContext mBiometricContext; - @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector; + @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); private int mCurrentUserId = UserHandle.USER_NULL; @@ -366,8 +366,14 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mCurrentUserId = UserHandle.USER_NULL; }); - mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FACE, new BiometricNotificationImpl()); + AuthenticationStatsBroadcastReceiver mBroadcastReceiver = + new AuthenticationStatsBroadcastReceiver( + mContext, + BiometricsProtoEnums.MODALITY_FACE, + (AuthenticationStatsCollector collector) -> { + Slog.d(TAG, "Initializing AuthenticationStatsCollector"); + mAuthenticationStatsCollector = collector; + }); try { ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); 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 5f4b89439fd0..f74b45cbdb0e 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 @@ -57,6 +57,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; @@ -64,7 +65,6 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; -import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -124,7 +124,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Nullable private IUdfpsOverlayController mUdfpsOverlayController; @Nullable private ISidefpsController mSidefpsController; private AuthSessionCoordinator mAuthSessionCoordinator; - @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector; + @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; private final class BiometricTaskStackListener extends TaskStackListener { @Override @@ -184,8 +184,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; - mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FINGERPRINT, new BiometricNotificationImpl()); + AuthenticationStatsBroadcastReceiver mBroadcastReceiver = + new AuthenticationStatsBroadcastReceiver( + mContext, + BiometricsProtoEnums.MODALITY_FINGERPRINT, + (AuthenticationStatsCollector collector) -> { + Slog.d(getTag(), "Initializing AuthenticationStatsCollector"); + mAuthenticationStatsCollector = collector; + }); final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context); 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 d0b71fcf2dbb..a655f3601a07 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 @@ -52,6 +52,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; @@ -66,7 +67,6 @@ import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; -import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -125,7 +125,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Nullable private IUdfpsOverlayController mUdfpsOverlayController; @Nullable private ISidefpsController mSidefpsController; @NonNull private final BiometricContext mBiometricContext; - @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector; + @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); private int mCurrentUserId = UserHandle.USER_NULL; @@ -354,8 +354,14 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mCurrentUserId = UserHandle.USER_NULL; }); - mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FINGERPRINT, new BiometricNotificationImpl()); + AuthenticationStatsBroadcastReceiver mBroadcastReceiver = + new AuthenticationStatsBroadcastReceiver( + mContext, + BiometricsProtoEnums.MODALITY_FINGERPRINT, + (AuthenticationStatsCollector collector) -> { + Slog.d(TAG, "Initializing AuthenticationStatsCollector"); + mAuthenticationStatsCollector = collector; + }); try { ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsBroadcastReceiverTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsBroadcastReceiverTest.java new file mode 100644 index 000000000000..f4e5faaf2103 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsBroadcastReceiverTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023 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; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static java.util.Collections.emptySet; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.os.UserHandle; + +import com.android.internal.R; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.io.File; +import java.util.function.Consumer; + +public class AuthenticationStatsBroadcastReceiverTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + private AuthenticationStatsBroadcastReceiver mBroadcastReceiver; + private static final float FRR_THRESHOLD = 0.2f; + private static final int USER_ID_1 = 1; + + @Mock + Context mContext; + @Mock + private Resources mResources; + @Mock + private SharedPreferences mSharedPreferences; + @Mock + private SharedPreferences.Editor mEditor; + @Mock + Intent mIntent; + @Mock + Consumer<AuthenticationStatsCollector> mCallback; + + @Captor + private ArgumentCaptor<AuthenticationStatsCollector> mArgumentCaptor; + + @Before + public void setUp() { + + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getFraction(eq(R.fraction.config_biometricNotificationFrrThreshold), + anyInt(), anyInt())).thenReturn(FRR_THRESHOLD); + when(mContext.getSharedPreferences(any(File.class), anyInt())) + .thenReturn(mSharedPreferences); + when(mSharedPreferences.getStringSet(anyString(), anySet())).thenReturn(emptySet()); + when(mSharedPreferences.edit()).thenReturn(mEditor); + when(mEditor.putFloat(anyString(), anyFloat())).thenReturn(mEditor); + when(mEditor.putStringSet(anyString(), anySet())).thenReturn(mEditor); + + when(mIntent.getAction()).thenReturn(Intent.ACTION_USER_UNLOCKED); + when(mIntent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)) + .thenReturn(USER_ID_1); + + mBroadcastReceiver = new AuthenticationStatsBroadcastReceiver(mContext, + BiometricsProtoEnums.MODALITY_FINGERPRINT, mCallback); + } + + @Test + public void testRegisterReceiver() { + verify(mContext).registerReceiver(eq(mBroadcastReceiver), any()); + } + + @Test + public void testOnReceive_shouldInitializeAuthenticationStatsCollector() { + mBroadcastReceiver.onReceive(mContext, mIntent); + + // Verify AuthenticationStatsCollector is initialized + verify(mCallback).accept(mArgumentCaptor.capture()); + assertThat(mArgumentCaptor.getValue()).isNotNull(); + + // Verify receiver is unregistered after receiving the broadcast + verify(mContext).unregisterReceiver(mBroadcastReceiver); + } +} |