Add timeout to all ALS logging.

There are cases where the logger is not being disabled at the end of an operation, possibly due to a scheduler lifecycle bug. The timeout is a safeguard to ensure disable is always called after ALS logging is enabled.

Fix: 233656407
Test: atest BiometricLoggerTest ALSProbeTest
Test: manual (auth on lock scren & verify logs)
Test: manual (kill hal and verify watch dog: adb shell ps | grep biometrics.face-service | awk '{print $2}' | xargs adb shell kill -9)

Change-Id: I27ee27a046ed97912ec4b675feecf882f550a0a2
This commit is contained in:
Joe Bolinger
2022-08-25 19:37:59 +00:00
parent 638d45938c
commit 0f01549d1e
14 changed files with 396 additions and 119 deletions

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2022 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.log;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import java.util.concurrent.TimeUnit;
/** Probe for ambient light. */
final class ALSProbe implements Probe {
private static final String TAG = "ALSProbe";
@Nullable
private final SensorManager mSensorManager;
@Nullable
private final Sensor mLightSensor;
@NonNull
private final Handler mTimer;
@DurationMillisLong
private long mMaxSubscriptionTime = -1;
private boolean mEnabled = false;
private boolean mDestroyed = false;
private volatile float mLastAmbientLux = -1;
private final SensorEventListener mLightSensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
mLastAmbientLux = event.values[0];
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Not used.
}
};
/**
* Create a probe with a 1-minute max sampling time.
*
* @param sensorManager Sensor manager
*/
ALSProbe(@NonNull SensorManager sensorManager) {
this(sensorManager, new Handler(Looper.getMainLooper()),
TimeUnit.MINUTES.toMillis(1));
}
/**
* Create a probe with a given max sampling time.
*
* Note: The max time is a workaround for potential scheduler bugs where
* {@link BaseClientMonitor#destroy()} is not called due to an abnormal lifecycle. Clients
* should ensure that {@link #disable()} and {@link #destroy()} are called appropriately and
* avoid relying on this timeout to unsubscribe from the sensor when it is not needed.
*
* @param sensorManager Sensor manager
* @param handler Timeout handler
* @param maxTime The max amount of time to subscribe to events. If this time is exceeded
* {@link #disable()} will be called and no sampling will occur until {@link
* #enable()} is called again.
*/
@VisibleForTesting
ALSProbe(@Nullable SensorManager sensorManager, @NonNull Handler handler,
@DurationMillisLong long maxTime) {
mSensorManager = sensorManager;
mLightSensor = sensorManager != null
? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null;
mTimer = handler;
mMaxSubscriptionTime = maxTime;
if (mSensorManager == null || mLightSensor == null) {
Slog.w(TAG, "No sensor - probe disabled");
mDestroyed = true;
}
}
@Override
public synchronized void enable() {
if (!mDestroyed) {
enableLightSensorLoggingLocked();
}
}
@Override
public synchronized void disable() {
if (!mDestroyed) {
disableLightSensorLoggingLocked();
}
}
@Override
public synchronized void destroy() {
disable();
mDestroyed = true;
}
/** The most recent lux reading. */
public float getCurrentLux() {
return mLastAmbientLux;
}
private void enableLightSensorLoggingLocked() {
if (!mEnabled) {
mEnabled = true;
mLastAmbientLux = -1;
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
SensorManager.SENSOR_DELAY_NORMAL);
Slog.v(TAG, "Enable ALS: " + mLightSensorListener.hashCode());
}
resetTimerLocked(true /* start */);
}
private void disableLightSensorLoggingLocked() {
resetTimerLocked(false /* start */);
if (mEnabled) {
mEnabled = false;
mLastAmbientLux = -1;
mSensorManager.unregisterListener(mLightSensorListener);
Slog.v(TAG, "Disable ALS: " + mLightSensorListener.hashCode());
}
}
private void resetTimerLocked(boolean start) {
mTimer.removeCallbacksAndMessages(this /* token */);
if (start && mMaxSubscriptionTime > 0) {
mTimer.postDelayed(this::onTimeout, this /* token */, mMaxSubscriptionTime);
}
}
private void onTimeout() {
Slog.e(TAG, "Max time exceeded for ALS logger - disabling: "
+ mLightSensorListener.hashCode());
disable();
}
}

View File

@@ -17,21 +17,15 @@
package com.android.server.biometrics.log;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.Utils;
@@ -43,61 +37,16 @@ public class BiometricLogger {
public static final String TAG = "BiometricLogger";
public static final boolean DEBUG = false;
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static int sAlsCounter;
private final int mStatsModality;
private final int mStatsAction;
private final int mStatsClient;
private final BiometricFrameworkStatsLogger mSink;
@NonNull private final SensorManager mSensorManager;
@NonNull private final ALSProbe mALSProbe;
private long mFirstAcquireTimeMs;
private boolean mLightSensorEnabled = false;
private boolean mShouldLogMetrics = true;
private class ALSProbe implements Probe {
private boolean mDestroyed = false;
@Override
public synchronized void enable() {
if (!mDestroyed) {
setLightSensorLoggingEnabled(getAmbientLightSensor(mSensorManager));
}
}
@Override
public synchronized void disable() {
if (!mDestroyed) {
setLightSensorLoggingEnabled(null);
}
}
@Override
public synchronized void destroy() {
disable();
mDestroyed = true;
}
}
// report only the most recent value
// consider com.android.server.display.utils.AmbientFilter or similar if need arises
private volatile float mLastAmbientLux = 0;
private final SensorEventListener mLightSensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
mLastAmbientLux = event.values[0];
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Not used.
}
};
/** Get a new logger with all unknown fields (for operations that do not require logs). */
public static BiometricLogger ofUnknown(@NonNull Context context) {
return new BiometricLogger(context, BiometricsProtoEnums.MODALITY_UNKNOWN,
@@ -105,6 +54,11 @@ public class BiometricLogger {
}
/**
* Creates a new logger for an instance of a biometric operation.
*
* Do not reuse across operations. Instead, create a new one or use
* {@link #swapAction(Context, int)}.
*
* @param context system_server context
* @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants.
* @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants.
@@ -125,7 +79,7 @@ public class BiometricLogger {
mStatsAction = statsAction;
mStatsClient = statsClient;
mSink = logSink;
mSensorManager = sensorManager;
mALSProbe = new ALSProbe(sensorManager);
}
/** Creates a new logger with the action replaced with the new action. */
@@ -136,6 +90,7 @@ public class BiometricLogger {
/** Disable logging metrics and only log critical events, such as system health issues. */
public void disableMetrics() {
mShouldLogMetrics = false;
mALSProbe.destroy();
}
/** {@link BiometricsProtoEnums} CLIENT_* constants */
@@ -265,7 +220,7 @@ public class BiometricLogger {
+ ", RequireConfirmation: " + requireConfirmation
+ ", State: " + authState
+ ", Latency: " + latency
+ ", Lux: " + mLastAmbientLux);
+ ", Lux: " + mALSProbe.getCurrentLux());
} else {
Slog.v(TAG, "Authentication latency: " + latency);
}
@@ -276,7 +231,7 @@ public class BiometricLogger {
mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
latency, authState, requireConfirmation, targetUserId, mLastAmbientLux);
latency, authState, requireConfirmation, targetUserId, mALSProbe.getCurrentLux());
}
/** Log enrollment outcome. */
@@ -290,7 +245,7 @@ public class BiometricLogger {
+ ", User: " + targetUserId
+ ", Client: " + mStatsClient
+ ", Latency: " + latency
+ ", Lux: " + mLastAmbientLux
+ ", Lux: " + mALSProbe.getCurrentLux()
+ ", Success: " + enrollSuccessful);
} else {
Slog.v(TAG, "Enroll latency: " + latency);
@@ -301,7 +256,7 @@ public class BiometricLogger {
}
mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
targetUserId, latency, enrollSuccessful, mLastAmbientLux);
targetUserId, latency, enrollSuccessful, mALSProbe.getCurrentLux());
}
/** Report unexpected enrollment reported by the HAL. */
@@ -323,7 +278,9 @@ public class BiometricLogger {
}
/**
* Get a callback to start/stop ALS capture when a client runs.
* Get a callback to start/stop ALS capture when the client runs. Do not create
* multiple callbacks since there is at most one light sensor (they will all share
* a single probe sampling from that sensor).
*
* If the probe should not run for the entire operation, do not set startWithClient and
* start/stop the problem when needed.
@@ -331,53 +288,7 @@ public class BiometricLogger {
* @param startWithClient if probe should start automatically when the operation starts.
*/
@NonNull
public CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
return new CallbackWithProbe<>(new ALSProbe(), startWithClient);
}
/** The sensor to use for ALS logging. */
@Nullable
protected Sensor getAmbientLightSensor(@NonNull SensorManager sensorManager) {
return mShouldLogMetrics ? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null;
}
private void setLightSensorLoggingEnabled(@Nullable Sensor lightSensor) {
if (DEBUG) {
Slog.v(TAG, "capturing ambient light using: "
+ (lightSensor != null ? lightSensor : "[disabled]"));
}
if (lightSensor != null) {
if (!mLightSensorEnabled) {
mLightSensorEnabled = true;
mLastAmbientLux = 0;
int localAlsCounter;
synchronized (sLock) {
localAlsCounter = sAlsCounter++;
}
if (localAlsCounter == 0) {
mSensorManager.registerListener(mLightSensorListener, lightSensor,
SensorManager.SENSOR_DELAY_NORMAL);
} else {
Slog.e(TAG, "Ignoring request to subscribe to ALSProbe due to non-zero ALS"
+ " counter: " + localAlsCounter);
Slog.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
} else {
mLightSensorEnabled = false;
mLastAmbientLux = 0;
mSensorManager.unregisterListener(mLightSensorListener);
int localAlsCounter;
synchronized (sLock) {
localAlsCounter = --sAlsCounter;
}
if (localAlsCounter != 0) {
Slog.e(TAG, "Non-zero ALS counter after unsubscribing from ALSProbe: "
+ localAlsCounter);
Slog.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
public CallbackWithProbe<Probe> getAmbientLightProbe(boolean startWithClient) {
return new CallbackWithProbe<>(mALSProbe, startWithClient);
}
}

View File

@@ -131,7 +131,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override

View File

@@ -115,7 +115,7 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(mPreviewHandleDeleterCallback,
getLogger().createALSCallback(true /* startWithClient */), callback);
getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override

View File

@@ -101,7 +101,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override

View File

@@ -75,7 +75,7 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override

View File

@@ -129,7 +129,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache = lockoutCache;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
mHandler = handler;
mWaitForAuthKeyguard =

View File

@@ -94,7 +94,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {

View File

@@ -86,7 +86,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */);
}
@Override

View File

@@ -84,7 +84,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return new ClientMonitorCompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
getLogger().getAmbientLightProbe(true /* startWithClient */), callback);
}
@Override

View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) 2022 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.log;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.input.InputSensorInfo;
import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@Presubmit
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class ALSProbeTest {
private static final long TIMEOUT_MS = 1000;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@Mock
private SensorManager mSensorManager;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
private TestableLooper mLooper;
private Sensor mLightSensor = new Sensor(
new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
"", "", 0, 0, 0));
private ALSProbe mProbe;
@Before
public void setup() {
mLooper = TestableLooper.get(this);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mLightSensor);
mProbe = new ALSProbe(mSensorManager, new Handler(mLooper.getLooper()), TIMEOUT_MS - 1);
reset(mSensorManager);
}
@Test
public void testEnable() {
final float value = 2.0f;
mProbe.enable();
verify(mSensorManager).registerListener(
mSensorEventListenerCaptor.capture(), any(), anyInt());
mSensorEventListenerCaptor.getValue().onSensorChanged(
new SensorEvent(mLightSensor, 1, 1, new float[]{4.0f}));
mSensorEventListenerCaptor.getValue().onSensorChanged(
new SensorEvent(mLightSensor, 1, 2, new float[]{value}));
assertThat(mProbe.getCurrentLux()).isEqualTo(value);
}
@Test
public void testEnableOnlyOnce() {
mProbe.enable();
mProbe.enable();
verify(mSensorManager).registerListener(any(), any(), anyInt());
verifyNoMoreInteractions(mSensorManager);
}
@Test
public void testDisable() {
mProbe.enable();
verify(mSensorManager).registerListener(
mSensorEventListenerCaptor.capture(), any(), anyInt());
mProbe.disable();
verify(mSensorManager).unregisterListener(eq(mSensorEventListenerCaptor.getValue()));
verifyNoMoreInteractions(mSensorManager);
}
@Test
public void testDestroy() {
mProbe.destroy();
mProbe.enable();
verify(mSensorManager, never()).registerListener(any(), any(), anyInt());
verifyNoMoreInteractions(mSensorManager);
}
@Test
public void testDisabledReportsNegativeValue() {
assertThat(mProbe.getCurrentLux()).isLessThan(0f);
mProbe.enable();
verify(mSensorManager).registerListener(
mSensorEventListenerCaptor.capture(), any(), anyInt());
mSensorEventListenerCaptor.getValue().onSensorChanged(
new SensorEvent(mLightSensor, 1, 1, new float[]{4.0f}));
mProbe.disable();
assertThat(mProbe.getCurrentLux()).isLessThan(0f);
}
@Test
public void testWatchDog() {
mProbe.enable();
verify(mSensorManager).registerListener(
mSensorEventListenerCaptor.capture(), any(), anyInt());
mSensorEventListenerCaptor.getValue().onSensorChanged(
new SensorEvent(mLightSensor, 1, 1, new float[]{4.0f}));
moveTimeBy(TIMEOUT_MS);
verify(mSensorManager).unregisterListener(eq(mSensorEventListenerCaptor.getValue()));
verifyNoMoreInteractions(mSensorManager);
assertThat(mProbe.getCurrentLux()).isLessThan(0f);
}
@Test
public void testEnableExtendsWatchDog() {
mProbe.enable();
verify(mSensorManager).registerListener(any(), any(), anyInt());
moveTimeBy(TIMEOUT_MS / 2);
verify(mSensorManager, never()).unregisterListener(any(SensorEventListener.class));
mProbe.enable();
moveTimeBy(TIMEOUT_MS);
verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
verifyNoMoreInteractions(mSensorManager);
assertThat(mProbe.getCurrentLux()).isLessThan(0f);
}
private void moveTimeBy(long millis) {
mLooper.moveTimeForward(millis);
mLooper.processAllMessages();
}
}

View File

@@ -232,7 +232,7 @@ public class BiometricLoggerTest {
public void testALSCallback() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
mLogger.createALSCallback(true /* startWithClient */);
mLogger.getAmbientLightProbe(true /* startWithClient */);
callback.onClientStarted(mClient);
verify(mSensorManager).registerListener(any(), any(), anyInt());
@@ -241,11 +241,38 @@ public class BiometricLoggerTest {
verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
}
@Test
public void testALSCallbackWhenLogsDisabled() {
mLogger = createLogger();
mLogger.disableMetrics();
final CallbackWithProbe<Probe> callback =
mLogger.getAmbientLightProbe(true /* startWithClient */);
callback.onClientStarted(mClient);
verify(mSensorManager, never()).registerListener(any(), any(), anyInt());
callback.onClientFinished(mClient, true /* success */);
verify(mSensorManager, never()).unregisterListener(any(SensorEventListener.class));
}
@Test
public void testALSCallbackWhenDisabledAfterStarting() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
mLogger.getAmbientLightProbe(true /* startWithClient */);
callback.onClientStarted(mClient);
verify(mSensorManager).registerListener(any(), any(), anyInt());
mLogger.disableMetrics();
verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
}
@Test
public void testALSCallbackDoesNotStart() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
mLogger.createALSCallback(false /* startWithClient */);
mLogger.getAmbientLightProbe(false /* startWithClient */);
callback.onClientStarted(mClient);
callback.onClientFinished(mClient, true /* success */);
@@ -256,7 +283,7 @@ public class BiometricLoggerTest {
public void testALSCallbackDestroyed() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
mLogger.createALSCallback(true /* startWithClient */);
mLogger.getAmbientLightProbe(true /* startWithClient */);
callback.onClientStarted(mClient);
callback.onClientFinished(mClient, false /* success */);

View File

@@ -138,7 +138,7 @@ public class FingerprintAuthenticationClientTest {
@Before
public void setup() {
mContext.addMockSystemService(BiometricManager.class, mBiometricManager);
when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
when(mBiometricLogger.getAmbientLightProbe(anyBoolean())).thenAnswer(i ->
new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
i -> i.getArgument(0));

View File

@@ -119,7 +119,7 @@ public class FingerprintEnrollClientTest {
@Before
public void setup() {
when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
when(mBiometricLogger.getAmbientLightProbe(anyBoolean())).thenAnswer(i ->
new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
i -> i.getArgument(0));