diff options
32 files changed, 1120 insertions, 292 deletions
diff --git a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl index 2e8e763b0ee4..18979a9f78eb 100644 --- a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl +++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl @@ -17,7 +17,7 @@ package android.hardware.biometrics; /** * A secondary communication channel from AuthController back to BiometricService for - * events that are not associated with an autentication session. See + * events that are not associated with an authentication session. See * {@link IBiometricSysuiReceiver} for events associated with a session. * * @hide @@ -27,4 +27,16 @@ oneway interface IBiometricContextListener { // These may be called while the device is still transitioning to the new state // (i.e. about to become awake or enter doze) void onDozeChanged(boolean isDozing, boolean isAwake); + + @VintfStability + @Backing(type="int") + enum FoldState { + UNKNOWN = 0, + HALF_OPENED = 1, + FULLY_OPENED = 2, + FULLY_CLOSED = 3, + } + + // Called when the fold state of the device changes. + void onFoldChanged(FoldState FoldState); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index d03578531a99..a0f3ecb0634b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -20,9 +20,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -74,6 +71,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.CoreStartable; import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor; +import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -81,7 +79,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.data.repository.BiometricType; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -121,7 +118,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, private final Context mContext; private final Execution mExecution; private final CommandQueue mCommandQueue; - private final StatusBarStateController mStatusBarStateController; private final ActivityTaskManager mActivityTaskManager; @Nullable private final FingerprintManager mFingerprintManager; @Nullable private final FaceManager mFaceManager; @@ -131,6 +127,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, // TODO: these should be migrated out once ready @NonNull private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor; @NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider; + @NonNull private final LogContextInteractor mLogContextInteractor; private final Display mDisplay; private float mScaleFactor = 1f; @@ -153,7 +150,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, @Nullable private UdfpsController mUdfpsController; @Nullable private IUdfpsRefreshRateRequestCallback mUdfpsRefreshRateRequestCallback; @Nullable private SideFpsController mSideFpsController; - @Nullable private IBiometricContextListener mBiometricContextListener; @Nullable private UdfpsLogger mUdfpsLogger; @VisibleForTesting IBiometricSysuiReceiver mReceiver; @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener; @@ -728,7 +724,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull UdfpsLogger udfpsLogger, - @NonNull StatusBarStateController statusBarStateController, + @NonNull LogContextInteractor logContextInteractor, @NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor, @NonNull Provider<CredentialViewModel> credentialViewModelProvider, @NonNull InteractionJankMonitor jankMonitor, @@ -756,6 +752,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, mFaceEnrolledForUser = new SparseBooleanArray(); mVibratorHelper = vibrator; + mLogContextInteractor = logContextInteractor; mBiometricPromptInteractor = biometricPromptInteractor; mCredentialViewModelProvider = credentialViewModelProvider; @@ -770,25 +767,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, }); mWakefulnessLifecycle = wakefulnessLifecycle; - mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() { - @Override - public void onFinishedWakingUp() { - notifyDozeChanged(mStatusBarStateController.isDozing(), WAKEFULNESS_AWAKE); - } - - @Override - public void onStartedGoingToSleep() { - notifyDozeChanged(mStatusBarStateController.isDozing(), WAKEFULNESS_GOING_TO_SLEEP); - } - }); - - mStatusBarStateController = statusBarStateController; - mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() { - @Override - public void onDozingChanged(boolean isDozing) { - notifyDozeChanged(isDozing, wakefulnessLifecycle.getWakefulness()); - } - }); mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null; int[] faceAuthLocation = context.getResources().getIntArray( @@ -894,21 +872,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, @Override public void setBiometricContextListener(IBiometricContextListener listener) { - mBiometricContextListener = listener; - notifyDozeChanged(mStatusBarStateController.isDozing(), - mWakefulnessLifecycle.getWakefulness()); - } - - private void notifyDozeChanged(boolean isDozing, - @WakefulnessLifecycle.Wakefulness int wakefullness) { - if (mBiometricContextListener != null) { - try { - final boolean isAwake = wakefullness == WAKEFULNESS_AWAKE; - mBiometricContextListener.onDozeChanged(isDozing, isAwake); - } catch (RemoteException e) { - Log.w(TAG, "failed to notify initial doze state"); - } - } + mLogContextInteractor.addBiometricContextListener(listener); } /** @@ -1274,7 +1238,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricMultiSensorMode int multiSensorConfig, - @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt index 7c0c3b710e66..6f8efba2e84f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt @@ -20,6 +20,8 @@ import com.android.systemui.biometrics.data.repository.PromptRepository import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl import com.android.systemui.biometrics.domain.interactor.CredentialInteractor import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl +import com.android.systemui.biometrics.domain.interactor.LogContextInteractor +import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.concurrency.ThreadFactory import dagger.Binds @@ -40,6 +42,10 @@ interface BiometricsModule { @SysUISingleton fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor + @Binds + @SysUISingleton + fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor + companion object { /** Background [Executor] for HAL related operations. */ @Provides diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt new file mode 100644 index 000000000000..3b53eff9dfc5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt @@ -0,0 +1,169 @@ +/* + * 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.systemui.biometrics.domain.interactor + +import android.hardware.biometrics.IBiometricContextListener +import android.util.Log +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN +import com.android.systemui.unfold.updates.FoldStateProvider +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.launch + +/** + * Aggregates UI/device state that is not directly related to biometrics, but is often useful for + * logging or optimization purposes (fold state, screen state, etc.) + */ +interface LogContextInteractor { + + /** If the device is dozing. */ + val isDozing: Flow<Boolean> + + /** If the device is currently awake with the screen on. */ + val isAwake: Flow<Boolean> + + /** Current device fold state, defined as [IBiometricContextListener.FoldState]. */ + val foldState: Flow<Int> + + /** + * Add a permanent context listener. + * + * Use this method for registering remote context listeners. Use the properties exposed via this + * class directly within SysUI. + */ + fun addBiometricContextListener(listener: IBiometricContextListener): Job +} + +@SysUISingleton +class LogContextInteractorImpl +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val statusBarStateController: StatusBarStateController, + private val wakefulnessLifecycle: WakefulnessLifecycle, + private val foldProvider: FoldStateProvider, +) : LogContextInteractor { + + init { + foldProvider.start() + } + + override val isDozing = + conflatedCallbackFlow { + val callback = + object : StatusBarStateController.StateListener { + override fun onDozingChanged(isDozing: Boolean) { + trySendWithFailureLogging(isDozing, TAG) + } + } + + statusBarStateController.addCallback(callback) + trySendWithFailureLogging(statusBarStateController.isDozing, TAG) + awaitClose { statusBarStateController.removeCallback(callback) } + } + .distinctUntilChanged() + + override val isAwake = + conflatedCallbackFlow { + val callback = + object : WakefulnessLifecycle.Observer { + override fun onFinishedWakingUp() { + trySendWithFailureLogging(true, TAG) + } + + override fun onStartedGoingToSleep() { + trySendWithFailureLogging(false, TAG) + } + } + + wakefulnessLifecycle.addObserver(callback) + trySendWithFailureLogging(wakefulnessLifecycle.isAwake, TAG) + awaitClose { wakefulnessLifecycle.removeObserver(callback) } + } + .distinctUntilChanged() + + override val foldState: Flow<Int> = + conflatedCallbackFlow { + val callback = + object : FoldStateProvider.FoldUpdatesListener { + override fun onHingeAngleUpdate(angle: Float) {} + + override fun onFoldUpdate(@FoldStateProvider.FoldUpdate update: Int) { + val loggedState = + when (update) { + FOLD_UPDATE_FINISH_HALF_OPEN -> + IBiometricContextListener.FoldState.HALF_OPENED + FOLD_UPDATE_FINISH_FULL_OPEN -> + IBiometricContextListener.FoldState.FULLY_OPENED + FOLD_UPDATE_FINISH_CLOSED -> + IBiometricContextListener.FoldState.FULLY_CLOSED + else -> null + } + if (loggedState != null) { + trySendWithFailureLogging(loggedState, TAG) + } + } + } + + foldProvider.addCallback(callback) + trySendWithFailureLogging(IBiometricContextListener.FoldState.UNKNOWN, TAG) + awaitClose { foldProvider.removeCallback(callback) } + } + .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1) + + override fun addBiometricContextListener(listener: IBiometricContextListener): Job { + return applicationScope.launch { + combine(isDozing, isAwake) { doze, awake -> doze to awake } + .onEach { (doze, awake) -> listener.onDozeChanged(doze, awake) } + .catch { t -> Log.w(TAG, "failed to notify new doze state", t) } + .launchIn(this) + + foldState + .onEach { state -> listener.onFoldChanged(state) } + .catch { t -> Log.w(TAG, "failed to notify new fold state", t) } + .launchIn(this) + + listener.asBinder().linkToDeath({ cancel() }, 0) + } + } + + companion object { + private const val TAG = "ContextRepositoryImpl" + } +} + +private val WakefulnessLifecycle.isAwake: Boolean + get() = wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE 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 30dc9c911876..67b293f44cf4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -20,8 +20,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; - import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; @@ -35,12 +33,11 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -90,9 +87,9 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor; +import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -108,7 +105,6 @@ import org.junit.runner.RunWith; import org.mockito.AdditionalMatchers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -117,8 +113,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; -import javax.inject.Provider; - @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest @@ -162,7 +156,7 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private LockPatternUtils mLockPatternUtils; @Mock - private StatusBarStateController mStatusBarStateController; + private LogContextInteractor mLogContextInteractor; @Mock private UdfpsLogger mUdfpsLogger; @Mock @@ -178,10 +172,6 @@ public class AuthControllerTest extends SysuiTestCase { private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; @Captor private ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor; - @Captor - private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; - @Captor - private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefullnessObserverCaptor; private TestableContext mContextSpy; private Execution mExecution; @@ -253,10 +243,7 @@ public class AuthControllerTest extends SysuiTestCase { when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps); when(mVibratorHelper.hasVibrator()).thenReturn(true); - mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue, - mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager, - () -> mUdfpsController, () -> mSideFpsController, mStatusBarStateController, - mVibratorHelper); + mAuthController = new TestableAuthController(mContextSpy); mAuthController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( @@ -264,11 +251,6 @@ public class AuthControllerTest extends SysuiTestCase { verify(mFaceManager).addAuthenticatorsRegisteredCallback( mFaceAuthenticatorsRegisteredCaptor.capture()); - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); - verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); - verify(mWakefulnessLifecycle).addObserver(mWakefullnessObserverCaptor.capture()); - mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps); mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps); @@ -286,10 +268,7 @@ public class AuthControllerTest extends SysuiTestCase { reset(mFaceManager); // This test requires an uninitialized AuthController. - AuthController authController = new TestableAuthController(mContextSpy, mExecution, - mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, - mFaceManager, () -> mUdfpsController, () -> mSideFpsController, - mStatusBarStateController, mVibratorHelper); + AuthController authController = new TestableAuthController(mContextSpy); authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( @@ -316,10 +295,7 @@ public class AuthControllerTest extends SysuiTestCase { reset(mFaceManager); // This test requires an uninitialized AuthController. - AuthController authController = new TestableAuthController(mContextSpy, mExecution, - mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, - mFaceManager, () -> mUdfpsController, () -> mSideFpsController, - mStatusBarStateController, mVibratorHelper); + AuthController authController = new TestableAuthController(mContextSpy); authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( @@ -809,37 +785,9 @@ public class AuthControllerTest extends SysuiTestCase { } @Test - public void testForwardsDozeEvents() throws RemoteException { - when(mStatusBarStateController.isDozing()).thenReturn(true); - when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); - mAuthController.setBiometricContextListener(mContextListener); - - mStatusBarStateListenerCaptor.getValue().onDozingChanged(true); - mStatusBarStateListenerCaptor.getValue().onDozingChanged(false); - - InOrder order = inOrder(mContextListener); - order.verify(mContextListener, times(2)).onDozeChanged(eq(true), eq(true)); - order.verify(mContextListener).onDozeChanged(eq(false), eq(true)); - order.verifyNoMoreInteractions(); - } - - @Test - public void testForwardsWakeEvents() throws RemoteException { - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); + public void testSubscribesToLogContext() { mAuthController.setBiometricContextListener(mContextListener); - - mWakefullnessObserverCaptor.getValue().onStartedGoingToSleep(); - mWakefullnessObserverCaptor.getValue().onFinishedGoingToSleep(); - mWakefullnessObserverCaptor.getValue().onStartedWakingUp(); - mWakefullnessObserverCaptor.getValue().onFinishedWakingUp(); - mWakefullnessObserverCaptor.getValue().onPostFinishedWakingUp(); - - InOrder order = inOrder(mContextListener); - order.verify(mContextListener).onDozeChanged(eq(false), eq(true)); - order.verify(mContextListener).onDozeChanged(eq(false), eq(false)); - order.verify(mContextListener).onDozeChanged(eq(false), eq(true)); - order.verifyNoMoreInteractions(); + verify(mLogContextInteractor).addBiometricContextListener(same(mContextListener)); } @Test @@ -1001,23 +949,13 @@ public class AuthControllerTest extends SysuiTestCase { private int mBuildCount = 0; private PromptInfo mLastBiometricPromptInfo; - TestableAuthController(Context context, - Execution execution, - CommandQueue commandQueue, - ActivityTaskManager activityTaskManager, - WindowManager windowManager, - FingerprintManager fingerprintManager, - FaceManager faceManager, - Provider<UdfpsController> udfpsControllerFactory, - Provider<SideFpsController> sidefpsControllerFactory, - StatusBarStateController statusBarStateController, - VibratorHelper vibratorHelper) { - super(context, execution, commandQueue, activityTaskManager, windowManager, - fingerprintManager, faceManager, udfpsControllerFactory, - sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, - mUserManager, mLockPatternUtils, mUdfpsLogger, statusBarStateController, + TestableAuthController(Context context) { + super(context, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, + mFingerprintManager, mFaceManager, () -> mUdfpsController, + () -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle, + mUserManager, mLockPatternUtils, mUdfpsLogger, mLogContextInteractor, () -> mBiometricPromptCredentialInteractor, () -> mCredentialViewModel, - mInteractionJankMonitor, mHandler, mBackgroundExecutor, vibratorHelper); + mInteractionJankMonitor, mHandler, mBackgroundExecutor, mVibratorHelper); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt new file mode 100644 index 000000000000..3eb82a7d69bb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt @@ -0,0 +1,198 @@ +package com.android.systemui.biometrics.domain.interactor + +import android.hardware.biometrics.IBiometricContextListener +import android.hardware.biometrics.IBiometricContextListener.FoldState +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN +import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING +import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING +import com.android.systemui.unfold.updates.FoldStateProvider +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class LogContextInteractorImplTest : SysuiTestCase() { + + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + private val testScope = TestScope() + + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle + @Mock private lateinit var foldProvider: FoldStateProvider + + private lateinit var interactor: LogContextInteractorImpl + + @Before + fun setup() { + interactor = + LogContextInteractorImpl( + testScope.backgroundScope, + statusBarStateController, + wakefulnessLifecycle, + foldProvider + ) + } + + @Test + fun isDozingChanges() = + testScope.runTest { + whenever(statusBarStateController.isDozing).thenReturn(true) + + val isDozing = collectLastValue(interactor.isDozing) + runCurrent() + val listener = statusBarStateController.captureListener() + + assertThat(isDozing()).isTrue() + + listener.onDozingChanged(true) + listener.onDozingChanged(true) + listener.onDozingChanged(false) + + assertThat(isDozing()).isFalse() + } + + @Test + fun isAwakeChanges() = + testScope.runTest { + whenever(wakefulnessLifecycle.wakefulness) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE) + + val isAwake = collectLastValue(interactor.isAwake) + runCurrent() + val listener = wakefulnessLifecycle.captureObserver() + + assertThat(isAwake()).isTrue() + + listener.onStartedGoingToSleep() + listener.onFinishedGoingToSleep() + listener.onStartedWakingUp() + + assertThat(isAwake()).isFalse() + + listener.onFinishedWakingUp() + listener.onPostFinishedWakingUp() + + assertThat(isAwake()).isTrue() + } + + @Test + fun foldStateChanges() = + testScope.runTest { + val foldState = collectLastValue(interactor.foldState) + runCurrent() + val listener = foldProvider.captureListener() + + listener.onFoldUpdate(FOLD_UPDATE_START_OPENING) + assertThat(foldState()).isEqualTo(FoldState.UNKNOWN) + + listener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) + assertThat(foldState()).isEqualTo(FoldState.HALF_OPENED) + + listener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) + assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED) + + listener.onFoldUpdate(FOLD_UPDATE_START_CLOSING) + assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED) + + listener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) + assertThat(foldState()).isEqualTo(FoldState.FULLY_CLOSED) + } + + @Test + fun contextSubscriberChanges() = + testScope.runTest { + whenever(statusBarStateController.isDozing).thenReturn(false) + whenever(wakefulnessLifecycle.wakefulness) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE) + runCurrent() + + val foldListener = foldProvider.captureListener() + foldListener.onFoldUpdate(FOLD_UPDATE_START_CLOSING) + foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) + + var dozing: Boolean? = null + var awake: Boolean? = null + var folded: Int? = null + val job = + interactor.addBiometricContextListener( + object : IBiometricContextListener.Stub() { + override fun onDozeChanged(isDozing: Boolean, isAwake: Boolean) { + dozing = isDozing + awake = isAwake + } + + override fun onFoldChanged(foldState: Int) { + folded = foldState + } + } + ) + runCurrent() + + val statusBarStateListener = statusBarStateController.captureListener() + val wakefullnessObserver = wakefulnessLifecycle.captureObserver() + + assertThat(dozing).isFalse() + assertThat(awake).isTrue() + assertThat(folded).isEqualTo(FoldState.FULLY_CLOSED) + + statusBarStateListener.onDozingChanged(true) + wakefullnessObserver.onStartedGoingToSleep() + foldListener.onFoldUpdate(FOLD_UPDATE_START_OPENING) + foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) + wakefullnessObserver.onFinishedGoingToSleep() + runCurrent() + + assertThat(dozing).isTrue() + assertThat(awake).isFalse() + assertThat(folded).isEqualTo(FoldState.HALF_OPENED) + + job.cancel() + + // stale updates should be ignored + statusBarStateListener.onDozingChanged(false) + wakefullnessObserver.onFinishedWakingUp() + foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) + runCurrent() + + assertThat(dozing).isTrue() + assertThat(awake).isFalse() + assertThat(folded).isEqualTo(FoldState.HALF_OPENED) + } +} + +private fun StatusBarStateController.captureListener() = + withArgCaptor<StatusBarStateController.StateListener> { + verify(this@captureListener).addCallback(capture()) + } + +private fun WakefulnessLifecycle.captureObserver() = + withArgCaptor<WakefulnessLifecycle.Observer> { + verify(this@captureObserver).addObserver(capture()) + } + +private fun FoldStateProvider.captureListener() = + withArgCaptor<FoldStateProvider.FoldUpdatesListener> { + verify(this@captureListener).addCallback(capture()) + } diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 41ca13f5d5f5..7c8e6df4acdc 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -49,7 +49,6 @@ import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; -import android.hardware.biometrics.common.OperationContext; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -61,7 +60,9 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricFrameworkStatsLogger; +import com.android.server.biometrics.log.OperationContextExt; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -106,6 +107,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } private final Context mContext; + @NonNull private final BiometricContext mBiometricContext; private final IStatusBarService mStatusBarService; @VisibleForTesting final IBiometricSysuiReceiver mSysuiReceiver; private final KeyStore mKeyStore; @@ -148,6 +150,7 @@ public final class AuthSession implements IBinder.DeathRecipient { private long mAuthenticatedTimeMs; AuthSession(@NonNull Context context, + @NonNull BiometricContext biometricContext, @NonNull IStatusBarService statusBarService, @NonNull IBiometricSysuiReceiver sysuiReceiver, @NonNull KeyStore keystore, @@ -166,6 +169,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) { Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo); mContext = context; + mBiometricContext = biometricContext; mStatusBarService = statusBarService; mSysuiReceiver = sysuiReceiver; mKeyStore = keystore; @@ -694,10 +698,8 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } - final OperationContext operationContext = new OperationContext(); - operationContext.isCrypto = isCrypto(); BiometricFrameworkStatsLogger.getInstance().authenticate( - operationContext, + mBiometricContext.updateContext(new OperationContextExt(), isCrypto()), statsModality(), BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, @@ -726,10 +728,8 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } // Auth canceled - final OperationContext operationContext = new OperationContext(); - operationContext.isCrypto = isCrypto(); BiometricFrameworkStatsLogger.getInstance().error( - operationContext, + mBiometricContext.updateContext(new OperationContextExt(), isCrypto()), statsModality(), BiometricsProtoEnums.ACTION_AUTHENTICATE, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index d97195383778..c8d43e4c40c6 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -1314,7 +1314,7 @@ public class BiometricService extends SystemService { } final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId); - mAuthSession = new AuthSession(getContext(), mStatusBarService, + mAuthSession = new AuthSession(getContext(), mBiometricContext, mStatusBarService, createSysuiReceiver(requestId), mKeyStore, mRandom, createClientDeathReceiver(requestId), preAuthInfo, token, requestId, operationId, userId, createBiometricSensorReceiver(requestId), receiver, diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java index be04364dcff3..9199acb0db82 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.common.OperationContext; +import android.view.Surface; import com.android.server.biometrics.sensors.AuthSessionCoordinator; @@ -38,14 +39,14 @@ public interface BiometricContext { } /** Update the given context with the most recent values and return it. */ - OperationContext updateContext(@NonNull OperationContext operationContext, + OperationContextExt updateContext(@NonNull OperationContextExt operationContext, boolean isCryptoOperation); /** The session id for keyguard entry, if active, or null. */ - @Nullable Integer getKeyguardEntrySessionId(); + @Nullable BiometricContextSessionInfo getKeyguardEntrySessionInfo(); /** The session id for biometric prompt usage, if active, or null. */ - @Nullable Integer getBiometricPromptSessionId(); + @Nullable BiometricContextSessionInfo getBiometricPromptSessionInfo(); /** If the display is in AOD. */ boolean isAod(); @@ -53,16 +54,35 @@ public interface BiometricContext { /** If the device is awake or is becoming awake. */ boolean isAwake(); + /** If the display is on. */ + boolean isDisplayOn(); + + /** Current dock state from {@link android.content.Intent#EXTRA_DOCK_STATE}. */ + int getDockedState(); + + /** + * Current fold state from + * {@link android.hardware.biometrics.IBiometricContextListener.FoldState}. + */ + int getFoldState(); + + /** Current device display rotation. */ + @Surface.Rotation + int getCurrentRotation(); + /** * Subscribe to context changes. * + * Note that this method only notifies for properties that are visible to the HAL. + * * @param context context that will be modified when changed * @param consumer callback when the context is modified */ - void subscribe(@NonNull OperationContext context, @NonNull Consumer<OperationContext> consumer); + void subscribe(@NonNull OperationContextExt context, + @NonNull Consumer<OperationContext> consumer); /** Unsubscribe from context changes. */ - void unsubscribe(@NonNull OperationContext context); + void unsubscribe(@NonNull OperationContextExt context); /** Obtains an AuthSessionCoordinator. */ AuthSessionCoordinator getAuthSessionCoordinator(); diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java index d456736e7e8b..b63e8e31f73f 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java @@ -19,10 +19,12 @@ package com.android.server.biometrics.log; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.StatusBarManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.common.OperationContext; -import android.hardware.biometrics.common.OperationReason; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.RemoteException; @@ -30,6 +32,8 @@ import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.UserHandle; import android.util.Slog; +import android.view.Display; +import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; @@ -42,7 +46,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; /** - * A default provider for {@link BiometricContext}. + * A default provider for {@link BiometricContext} that aggregates device state from SysUI + * and packages it into an {@link android.hardware.biometrics.common.OperationContext} that can + * be propagated to the HAL. */ public final class BiometricContextProvider implements BiometricContext { @@ -57,7 +63,8 @@ public final class BiometricContextProvider implements BiometricContext { synchronized (BiometricContextProvider.class) { if (sInstance == null) { try { - sInstance = new BiometricContextProvider( + sInstance = new BiometricContextProvider(context, + (WindowManager) context.getSystemService(Context.WINDOW_SERVICE), new AmbientDisplayConfiguration(context), IStatusBarService.Stub.asInterface(ServiceManager.getServiceOrThrow( Context.STATUS_BAR_SERVICE)), null /* handler */, @@ -71,24 +78,47 @@ public final class BiometricContextProvider implements BiometricContext { } @NonNull - private final Map<OperationContext, Consumer<OperationContext>> mSubscribers = + private final Map<OperationContextExt, Consumer<OperationContext>> mSubscribers = new ConcurrentHashMap<>(); @Nullable - private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>(); + private final Map<Integer, BiometricContextSessionInfo> mSession = new ConcurrentHashMap<>(); private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; private final AuthSessionCoordinator mAuthSessionCoordinator; + private final WindowManager mWindowManager; + @Nullable private final Handler mHandler; private boolean mIsAod = false; private boolean mIsAwake = false; + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN; @VisibleForTesting - public BiometricContextProvider( + final BroadcastReceiver mDockStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + // no need to notify, not sent to HAL + } + }; + + @VisibleForTesting + public BiometricContextProvider(@NonNull Context context, + @NonNull WindowManager windowManager, @NonNull AmbientDisplayConfiguration ambientDisplayConfiguration, @NonNull IStatusBarService service, @Nullable Handler handler, - AuthSessionCoordinator authSessionCoordinator) { + @NonNull AuthSessionCoordinator authSessionCoordinator) { + mWindowManager = windowManager; mAmbientDisplayConfiguration = ambientDisplayConfiguration; mAuthSessionCoordinator = authSessionCoordinator; + mHandler = handler; + + subscribeBiometricContextListener(service); + subscribeDockState(context); + } + + private void subscribeBiometricContextListener(@NonNull IStatusBarService service) { try { service.setBiometicContextListener(new IBiometricContextListener.Stub() { @Override @@ -102,12 +132,10 @@ public final class BiometricContextProvider implements BiometricContext { } } - private void notifyChanged() { - if (handler != null) { - handler.post(() -> notifySubscribers()); - } else { - notifySubscribers(); - } + @Override + public void onFoldChanged(int foldState) { + mFoldState = foldState; + // no need to notify, not sent to HAL } private boolean isAodEnabled() { @@ -117,13 +145,13 @@ public final class BiometricContextProvider implements BiometricContext { service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() { @Override public void onSessionStarted(int sessionType, InstanceId instance) { - mSession.put(sessionType, instance); + mSession.put(sessionType, new BiometricContextSessionInfo(instance)); } @Override public void onSessionEnded(int sessionType, InstanceId instance) { - final InstanceId id = mSession.remove(sessionType); - if (id != null && instance != null && id.getId() != instance.getId()) { + final BiometricContextSessionInfo info = mSession.remove(sessionType); + if (info != null && instance != null && info.getId() != instance.getId()) { Slog.w(TAG, "session id mismatch"); } } @@ -133,46 +161,28 @@ public final class BiometricContextProvider implements BiometricContext { } } + private void subscribeDockState(@NonNull Context context) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DOCK_EVENT); + context.registerReceiver(mDockStateReceiver, filter); + } + @Override - public OperationContext updateContext(@NonNull OperationContext operationContext, + public OperationContextExt updateContext(@NonNull OperationContextExt operationContext, boolean isCryptoOperation) { - operationContext.isAod = isAod(); - operationContext.isCrypto = isCryptoOperation; - setFirstSessionId(operationContext); - return operationContext; - } - - private void setFirstSessionId(@NonNull OperationContext operationContext) { - Integer sessionId = getKeyguardEntrySessionId(); - if (sessionId != null) { - operationContext.id = sessionId; - operationContext.reason = OperationReason.KEYGUARD; - return; - } - - sessionId = getBiometricPromptSessionId(); - if (sessionId != null) { - operationContext.id = sessionId; - operationContext.reason = OperationReason.BIOMETRIC_PROMPT; - return; - } - - operationContext.id = 0; - operationContext.reason = OperationReason.UNKNOWN; + return operationContext.update(this); } @Nullable @Override - public Integer getKeyguardEntrySessionId() { - final InstanceId id = mSession.get(StatusBarManager.SESSION_KEYGUARD); - return id != null ? id.getId() : null; + public BiometricContextSessionInfo getKeyguardEntrySessionInfo() { + return mSession.get(StatusBarManager.SESSION_KEYGUARD); } @Nullable @Override - public Integer getBiometricPromptSessionId() { - final InstanceId id = mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT); - return id != null ? id.getId() : null; + public BiometricContextSessionInfo getBiometricPromptSessionInfo() { + return mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT); } @Override @@ -186,13 +196,33 @@ public final class BiometricContextProvider implements BiometricContext { } @Override - public void subscribe(@NonNull OperationContext context, + public boolean isDisplayOn() { + return mWindowManager.getDefaultDisplay().getState() == Display.STATE_ON; + } + + @Override + public int getDockedState() { + return mDockState; + } + + @Override + public int getFoldState() { + return mFoldState; + } + + @Override + public int getCurrentRotation() { + return mWindowManager.getDefaultDisplay().getRotation(); + } + + @Override + public void subscribe(@NonNull OperationContextExt context, @NonNull Consumer<OperationContext> consumer) { mSubscribers.put(context, consumer); } @Override - public void unsubscribe(@NonNull OperationContext context) { + public void unsubscribe(@NonNull OperationContextExt context) { mSubscribers.remove(context); } @@ -201,10 +231,28 @@ public final class BiometricContextProvider implements BiometricContext { return mAuthSessionCoordinator; } + private void notifyChanged() { + if (mHandler != null) { + mHandler.post(this::notifySubscribers); + } else { + notifySubscribers(); + } + } + private void notifySubscribers() { mSubscribers.forEach((context, consumer) -> { - context.isAod = isAod(); - consumer.accept(context); + consumer.accept(context.update(this).toAidlContext()); }); } + + @Override + public String toString() { + return "[keyguard session: " + getKeyguardEntrySessionInfo() + ", " + + "bp session: " + getBiometricPromptSessionInfo() + ", " + + "isAod: " + isAod() + ", " + + "isAwake: " + isAwake() + ", " + + "isDisplayOn: " + isDisplayOn() + ", " + + "dock: " + getDockedState() + ", " + + "rotation: " + getCurrentRotation() + "]"; + } } diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextSessionInfo.java b/services/core/java/com/android/server/biometrics/log/BiometricContextSessionInfo.java new file mode 100644 index 000000000000..70f589766c7c --- /dev/null +++ b/services/core/java/com/android/server/biometrics/log/BiometricContextSessionInfo.java @@ -0,0 +1,59 @@ +/* + * 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.NonNull; + +import com.android.internal.logging.InstanceId; + +import java.util.concurrent.atomic.AtomicInteger; + +/** State for an authentication session {@see com.android.internal.statusbar.ISessionListener}. */ +class BiometricContextSessionInfo { + private final InstanceId mId; + private final AtomicInteger mOrder = new AtomicInteger(0); + + /** Wrap a session id with the initial state. */ + BiometricContextSessionInfo(@NonNull InstanceId id) { + mId = id; + } + + /** Get the session id. */ + public int getId() { + return mId.getId(); + } + + /** Gets the current order counter for the session. */ + public int getOrder() { + return mOrder.get(); + } + + /** + * Gets the current order counter for the session and increment the counter. + * + * This should be called by the framework after processing any logged events, + * such as success / failure, to preserve the order each event was processed in. + */ + public int getOrderAndIncrement() { + return mOrder.getAndIncrement(); + } + + @Override + public String toString() { + return "[sid: " + mId.getId() + "]"; + } +} diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java index 27a70c51f667..82444f0cc299 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java @@ -17,9 +17,10 @@ package com.android.server.biometrics.log; import android.hardware.biometrics.BiometricsProtoEnums; -import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.common.OperationReason; import android.util.Slog; +import android.view.Surface; import com.android.internal.util.FrameworkStatsLog; @@ -41,32 +42,38 @@ public class BiometricFrameworkStatsLogger { } /** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */ - public void acquired(OperationContext operationContext, + public void acquired(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, int acquiredInfo, int vendorCode, int targetUserId) { FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED, statsModality, targetUserId, - operationContext.isCrypto, + operationContext.isCrypto(), statsAction, statsClient, acquiredInfo, vendorCode, isDebug, -1 /* sensorId */, - operationContext.id, - sessionType(operationContext.reason), - operationContext.isAod); + operationContext.getId(), + sessionType(operationContext.getReason()), + operationContext.isAod(), + operationContext.isDisplayOn(), + operationContext.getDockState(), + orientationType(operationContext.getOrientation()), + foldType(operationContext.getFoldState()), + operationContext.getOrderAndIncrement(), + BiometricsProtoEnums.WAKE_REASON_UNKNOWN); } /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */ - public void authenticate(OperationContext operationContext, + public void authenticate(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int authState, boolean requireConfirmation, int targetUserId, float ambientLightLux) { FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED, statsModality, targetUserId, - operationContext.isCrypto, + operationContext.isCrypto(), statsClient, requireConfirmation, authState, @@ -74,13 +81,19 @@ public class BiometricFrameworkStatsLogger { isDebug, -1 /* sensorId */, ambientLightLux, - operationContext.id, - sessionType(operationContext.reason), - operationContext.isAod); + operationContext.getId(), + sessionType(operationContext.getReason()), + operationContext.isAod(), + operationContext.isDisplayOn(), + operationContext.getDockState(), + orientationType(operationContext.getOrientation()), + foldType(operationContext.getFoldState()), + operationContext.getOrderAndIncrement(), + BiometricsProtoEnums.WAKE_REASON_UNKNOWN); } /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */ - public void authenticate(OperationContext operationContext, + public void authenticate(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int authState, boolean requireConfirmation, int targetUserId, ALSProbe alsProbe) { alsProbe.awaitNextLux((ambientLightLux) -> { @@ -102,13 +115,13 @@ public class BiometricFrameworkStatsLogger { } /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */ - public void error(OperationContext operationContext, + public void error(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int error, int vendorCode, int targetUserId) { FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED, statsModality, targetUserId, - operationContext.isCrypto, + operationContext.isCrypto(), statsAction, statsClient, error, @@ -116,9 +129,15 @@ public class BiometricFrameworkStatsLogger { isDebug, sanitizeLatency(latency), -1 /* sensorId */, - operationContext.id, - sessionType(operationContext.reason), - operationContext.isAod); + operationContext.getId(), + sessionType(operationContext.getReason()), + operationContext.isAod(), + operationContext.isDisplayOn(), + operationContext.getDockState(), + orientationType(operationContext.getOrientation()), + foldType(operationContext.getFoldState()), + operationContext.getOrderAndIncrement(), + BiometricsProtoEnums.WAKE_REASON_UNKNOWN); } /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */ @@ -154,4 +173,30 @@ public class BiometricFrameworkStatsLogger { } return BiometricsProtoEnums.SESSION_TYPE_UNKNOWN; } + + private static int orientationType(@Surface.Rotation int rotation) { + switch (rotation) { + case Surface.ROTATION_0: + return BiometricsProtoEnums.ORIENTATION_0; + case Surface.ROTATION_90: + return BiometricsProtoEnums.ORIENTATION_90; + case Surface.ROTATION_180: + return BiometricsProtoEnums.ORIENTATION_180; + case Surface.ROTATION_270: + return BiometricsProtoEnums.ORIENTATION_270; + } + return BiometricsProtoEnums.ORIENTATION_UNKNOWN; + } + + private static int foldType(int foldType) { + switch (foldType) { + case IBiometricContextListener.FoldState.FULLY_CLOSED: + return BiometricsProtoEnums.FOLD_CLOSED; + case IBiometricContextListener.FoldState.FULLY_OPENED: + return BiometricsProtoEnums.FOLD_OPEN; + case IBiometricContextListener.FoldState.HALF_OPENED: + return BiometricsProtoEnums.FOLD_HALF_OPEN; + } + return BiometricsProtoEnums.FOLD_UNKNOWN; + } } 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 55fe854e1404..c76a2e38aabc 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java @@ -21,7 +21,6 @@ import android.content.Context; 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.Slog; @@ -118,7 +117,7 @@ public class BiometricLogger { } /** Log an acquisition event. */ - public void logOnAcquired(Context context, OperationContext operationContext, + public void logOnAcquired(Context context, OperationContextExt operationContext, int acquiredInfo, int vendorCode, int targetUserId) { if (!mShouldLogMetrics) { return; @@ -139,7 +138,7 @@ public class BiometricLogger { if (DEBUG) { Slog.v(TAG, "Acquired! Modality: " + mStatsModality + ", User: " + targetUserId - + ", IsCrypto: " + operationContext.isCrypto + + ", IsCrypto: " + operationContext.isCrypto() + ", Action: " + mStatsAction + ", Client: " + mStatsClient + ", AcquiredInfo: " + acquiredInfo @@ -150,13 +149,14 @@ public class BiometricLogger { return; } - mSink.acquired(operationContext, mStatsModality, mStatsAction, mStatsClient, + mSink.acquired(operationContext, + mStatsModality, mStatsAction, mStatsClient, Utils.isDebugEnabled(context, targetUserId), acquiredInfo, vendorCode, targetUserId); } /** Log an error during an operation. */ - public void logOnError(Context context, OperationContext operationContext, + public void logOnError(Context context, OperationContextExt operationContext, int error, int vendorCode, int targetUserId) { if (!mShouldLogMetrics) { return; @@ -168,7 +168,7 @@ public class BiometricLogger { if (DEBUG) { Slog.v(TAG, "Error! Modality: " + mStatsModality + ", User: " + targetUserId - + ", IsCrypto: " + operationContext.isCrypto + + ", IsCrypto: " + operationContext.isCrypto() + ", Action: " + mStatsAction + ", Client: " + mStatsClient + ", Error: " + error @@ -182,15 +182,16 @@ public class BiometricLogger { return; } - mSink.error(operationContext, mStatsModality, mStatsAction, mStatsClient, + mSink.error(operationContext, + mStatsModality, mStatsAction, mStatsClient, Utils.isDebugEnabled(context, targetUserId), latency, error, vendorCode, targetUserId); } /** Log authentication attempt. */ - public void logOnAuthenticated(Context context, OperationContext operationContext, - boolean authenticated, boolean requireConfirmation, - int targetUserId, boolean isBiometricPrompt) { + public void logOnAuthenticated(Context context, OperationContextExt operationContext, + boolean authenticated, boolean requireConfirmation, int targetUserId, + boolean isBiometricPrompt) { if (!mShouldLogMetrics) { return; } @@ -215,7 +216,7 @@ public class BiometricLogger { if (DEBUG) { Slog.v(TAG, "Authenticated! Modality: " + mStatsModality + ", User: " + targetUserId - + ", IsCrypto: " + operationContext.isCrypto + + ", IsCrypto: " + operationContext.isCrypto() + ", Client: " + mStatsClient + ", RequireConfirmation: " + requireConfirmation + ", State: " + authState @@ -229,7 +230,8 @@ public class BiometricLogger { return; } - mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient, + mSink.authenticate(operationContext, + mStatsModality, mStatsAction, mStatsClient, Utils.isDebugEnabled(context, targetUserId), latency, authState, requireConfirmation, targetUserId, mALSProbe); } diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java new file mode 100644 index 000000000000..42be95b7377a --- /dev/null +++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java @@ -0,0 +1,138 @@ +/* + * 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.NonNull; +import android.annotation.Nullable; +import android.content.Intent; +import android.hardware.biometrics.IBiometricContextListener; +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.common.OperationReason; +import android.view.Surface; + +/** + * Wrapper around {@link OperationContext} to include properties that are not + * shared with the HAL. + * + * When useful, these properties should move to the wrapped object for use by HAL in + * future releases. + */ +public class OperationContextExt { + + @NonNull private final OperationContext mAidlContext; + @Nullable private BiometricContextSessionInfo mSessionInfo; + private boolean mIsDisplayOn = false; + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + @Surface.Rotation private int mOrientation = Surface.ROTATION_0; + private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN; + + /** Create a new empty context. */ + public OperationContextExt() { + this(new OperationContext()); + } + + /** Create a wrapped context. */ + public OperationContextExt(@NonNull OperationContext context) { + mAidlContext = context; + } + + /** Gets the subset of the context that can be shared with the HAL. */ + @NonNull + public OperationContext toAidlContext() { + return mAidlContext; + } + + /** {@link OperationContext#id}. */ + public int getId() { + return mAidlContext.id; + } + + /** Gets the current order counter for the session and increment the counter. */ + public int getOrderAndIncrement() { + final BiometricContextSessionInfo info = mSessionInfo; + return info != null ? info.getOrderAndIncrement() : -1; + } + + /** {@link OperationContext#reason}. */ + public byte getReason() { + return mAidlContext.reason; + } + + /** If the screen is currently on. */ + public boolean isDisplayOn() { + return mIsDisplayOn; + } + + /** {@link OperationContext#isAod}. */ + public boolean isAod() { + return mAidlContext.isAod; + } + + /** {@link OperationContext#isCrypto}. */ + public boolean isCrypto() { + return mAidlContext.isCrypto; + } + + /** The dock state when this event occurred {@see Intent.EXTRA_DOCK_STATE_UNDOCKED}. */ + public int getDockState() { + return mDockState; + } + + /** The fold state of the device when this event occurred. */ + public int getFoldState() { + return mFoldState; + } + + /** The orientation of the device when this event occurred. */ + @Surface.Rotation + public int getOrientation() { + return mOrientation; + } + + /** Update this object with the latest values from the given context. */ + OperationContextExt update(@NonNull BiometricContext biometricContext) { + mAidlContext.isAod = biometricContext.isAod(); + setFirstSessionId(biometricContext); + + mIsDisplayOn = biometricContext.isDisplayOn(); + mDockState = biometricContext.getDockedState(); + mFoldState = biometricContext.getFoldState(); + mOrientation = biometricContext.getCurrentRotation(); + + return this; + } + + private void setFirstSessionId(@NonNull BiometricContext biometricContext) { + mSessionInfo = biometricContext.getKeyguardEntrySessionInfo(); + if (mSessionInfo != null) { + mAidlContext.id = mSessionInfo.getId(); + mAidlContext.reason = OperationReason.KEYGUARD; + return; + } + + mSessionInfo = biometricContext.getBiometricPromptSessionInfo(); + if (mSessionInfo != null) { + mAidlContext.id = mSessionInfo.getId(); + mAidlContext.reason = OperationReason.BIOMETRIC_PROMPT; + return; + } + + // no session + mAidlContext.id = 0; + mAidlContext.reason = OperationReason.UNKNOWN; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java index a6e89115400d..d0a4807d937e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java @@ -19,11 +19,11 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.common.OperationContext; import android.os.IBinder; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.OperationContextExt; import java.util.function.Supplier; @@ -37,7 +37,7 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { protected final Supplier<T> mLazyDaemon; @NonNull - private final OperationContext mOperationContext = new OperationContext(); + private final OperationContextExt mOperationContext = new OperationContextExt(); /** * @param context system_server context @@ -85,7 +85,7 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { unsubscribeBiometricContext(); } - protected OperationContext getOperationContext() { + protected OperationContextExt getOperationContext() { return getBiometricContext().updateContext(mOperationContext, isCryptoOperation()); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index b1cb257b2443..d8b825d2c0e5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -164,7 +164,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> if (session.hasContextMethods()) { return session.getSession().authenticateWithContext( - mOperationId, getOperationContext()); + mOperationId, getOperationContext().toAidlContext()); } else { return session.getSession().authenticate(mOperationId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java index ded18100e458..506b2bc8d9db 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java @@ -115,7 +115,8 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { - return session.getSession().detectInteractionWithContext(getOperationContext()); + return session.getSession().detectInteractionWithContext( + getOperationContext().toAidlContext()); } else { return session.getSession().detectInteraction(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index 5d62cde01a6f..792b52e7d658 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -199,7 +199,8 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> { if (session.hasContextMethods()) { return session.getSession().enrollWithContext( - hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, getOperationContext()); + hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, + getOperationContext().toAidlContext()); } else { return session.getSession().enroll(hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle); 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 index 11f45176dc45..9669950e236e 100644 --- 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 @@ -29,7 +29,6 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; @@ -47,6 +46,7 @@ import com.android.internal.R; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; @@ -333,7 +333,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> private ICancellationSignal doAuthenticate() throws RemoteException { final AidlSession session = getFreshDaemon(); - final OperationContext opContext = getOperationContext(); + final OperationContextExt opContext = getOperationContext(); getBiometricContext().subscribe(opContext, ctx -> { if (session.hasContextMethods()) { try { @@ -356,7 +356,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> } if (session.hasContextMethods()) { - return session.getSession().authenticateWithContext(mOperationId, opContext); + return session.getSession().authenticateWithContext( + mOperationId, opContext.toAidlContext()); } else { return session.getSession().authenticate(mOperationId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 52822347e96f..f6911ea29837 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -102,7 +102,8 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { - return session.getSession().detectInteractionWithContext(getOperationContext()); + return session.getSession().detectInteractionWithContext( + getOperationContext().toAidlContext()); } else { return session.getSession().detectInteraction(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index fa54983b31dc..2ac8b433b3af 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -24,7 +24,6 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; @@ -42,6 +41,7 @@ import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; @@ -185,9 +185,9 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken); if (session.hasContextMethods()) { - final OperationContext opContext = getOperationContext(); + final OperationContextExt opContext = getOperationContext(); final ICancellationSignal cancel = session.getSession().enrollWithContext( - hat, opContext); + hat, opContext.toAidlContext()); getBiometricContext().subscribe(opContext, ctx -> { try { session.getSession().onContextChanged(ctx); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index e95924ad7109..4cdca268fc4b 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -62,6 +62,7 @@ import android.security.KeyStore; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; +import com.android.server.biometrics.log.BiometricContext; import org.junit.Before; import org.junit.Test; @@ -81,6 +82,7 @@ public class AuthSessionTest { private static final long TEST_REQUEST_ID = 22; @Mock private Context mContext; + @Mock private BiometricContext mBiometricContext; @Mock private ITrustManager mTrustManager; @Mock private DevicePolicyManager mDevicePolicyManager; @Mock private BiometricService.SettingObserver mSettingObserver; @@ -102,6 +104,8 @@ public class AuthSessionTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mClientReceiver.asBinder()).thenReturn(mock(Binder.class)); + when(mBiometricContext.updateContext(any(), anyBoolean())) + .thenAnswer(invocation -> invocation.getArgument(0)); mRandom = new Random(); mToken = new Binder(); mSensors = new ArrayList<>(); @@ -437,9 +441,9 @@ public class AuthSessionTest { final PreAuthInfo preAuthInfo = createPreAuthInfo(sensors, userId, promptInfo, checkDevicePolicyManager); - return new AuthSession(mContext, mStatusBarService, mSysuiReceiver, mKeyStore, - mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, operationId, userId, - mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo, + return new AuthSession(mContext, mBiometricContext, mStatusBarService, mSysuiReceiver, + mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, + operationId, userId, mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo, false /* debugEnabled */, mFingerprintSensorProps); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 0d6d1a2e93f9..7d6110e82139 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -19,6 +19,7 @@ package com.android.server.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT; +import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED; @@ -65,12 +66,16 @@ import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.AmbientDisplayConfiguration; +import android.hardware.display.DisplayManagerGlobal; import android.hardware.fingerprint.FingerprintManager; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.security.KeyStore; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.WindowManager; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -134,6 +139,8 @@ public class BiometricServiceTest { @Mock DevicePolicyManager mDevicePolicyManager; @Mock + private WindowManager mWindowManager; + @Mock private IStatusBarService mStatusBarService; @Mock private ISessionListener mSessionListener; @@ -174,9 +181,13 @@ public class BiometricServiceTest { when(mResources.getString(R.string.biometric_error_user_canceled)) .thenReturn(ERROR_USER_CANCELED); + when(mWindowManager.getDefaultDisplay()).thenReturn( + new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, + new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS)); when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); - mBiometricContextProvider = new BiometricContextProvider(mAmbientDisplayConfiguration, - mStatusBarService, null /* handler */, mAuthSessionCoordinator); + mBiometricContextProvider = new BiometricContextProvider(mContext, mWindowManager, + mAmbientDisplayConfiguration, mStatusBarService, null /* handler */, + mAuthSessionCoordinator); when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider); final String[] config = { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java index 58f338c7a5bb..2ccdda81b755 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.log; +import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; @@ -27,14 +29,22 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.StatusBarManager; +import android.content.Intent; import android.hardware.biometrics.IBiometricContextListener; +import android.hardware.biometrics.IBiometricContextListener.FoldState; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; import android.hardware.display.AmbientDisplayConfiguration; +import android.hardware.display.DisplayManagerGlobal; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.WindowManager; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.logging.InstanceId; import com.android.internal.statusbar.ISessionListener; @@ -58,6 +68,9 @@ public class BiometricContextProviderTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getContext()); @Mock private IStatusBarService mStatusBarService; @@ -65,16 +78,22 @@ public class BiometricContextProviderTest { private ISessionListener mSessionListener; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration; + @Mock + private WindowManager mWindowManager; - private OperationContext mOpContext = new OperationContext(); + private OperationContextExt mOpContext = new OperationContextExt(); private IBiometricContextListener mListener; private BiometricContextProvider mProvider; @Before public void setup() throws RemoteException { when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); - mProvider = new BiometricContextProvider(mAmbientDisplayConfiguration, mStatusBarService, - null /* handler */, null /* authSessionCoordinator */); + when(mWindowManager.getDefaultDisplay()).thenReturn( + new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, + new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS)); + mProvider = new BiometricContextProvider(mContext, mWindowManager, + mAmbientDisplayConfiguration, mStatusBarService, null /* handler */, + null /* authSessionCoordinator */); ArgumentCaptor<IBiometricContextListener> captor = ArgumentCaptor.forClass(IBiometricContextListener.class); verify(mStatusBarService).setBiometicContextListener(captor.capture()); @@ -112,11 +131,37 @@ public class BiometricContextProviderTest { } @Test + public void testGetDockedState() { + final List<Integer> states = List.of(Intent.EXTRA_DOCK_STATE_DESK, + Intent.EXTRA_DOCK_STATE_CAR, Intent.EXTRA_DOCK_STATE_UNDOCKED); + + for (int state : states) { + final Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_DOCK_STATE, state); + mProvider.mDockStateReceiver.onReceive(mContext, intent); + + assertThat(mProvider.getDockedState()).isEqualTo(state); + } + } + + @Test + public void testGetFoldState() throws RemoteException { + final List<Integer> states = List.of(FoldState.FULLY_CLOSED, FoldState.FULLY_OPENED, + FoldState.UNKNOWN, FoldState.HALF_OPENED); + + for (int state : states) { + mListener.onFoldChanged(state); + + assertThat(mProvider.getFoldState()).isEqualTo(state); + } + } + + @Test public void testSubscribesToAod() throws RemoteException { final List<Boolean> actual = new ArrayList<>(); mProvider.subscribe(mOpContext, ctx -> { - assertThat(ctx).isSameInstanceAs(mOpContext); + assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext()); assertThat(mProvider.isAod()).isEqualTo(ctx.isAod); assertThat(mProvider.isAwake()).isFalse(); actual.add(ctx.isAod); @@ -134,7 +179,7 @@ public class BiometricContextProviderTest { final List<Boolean> actual = new ArrayList<>(); mProvider.subscribe(mOpContext, ctx -> { - assertThat(ctx).isSameInstanceAs(mOpContext); + assertThat(ctx).isSameInstanceAs(mOpContext.toAidlContext()); assertThat(ctx.isAod).isFalse(); assertThat(mProvider.isAod()).isFalse(); actual.add(mProvider.isAwake()); @@ -162,7 +207,7 @@ public class BiometricContextProviderTest { mListener.onDozeChanged(true /* isDozing */, false /* isAwake */); verify(emptyConsumer, never()).accept(any()); - verify(nonEmptyConsumer).accept(same(mOpContext)); + verify(nonEmptyConsumer).accept(same(mOpContext.toAidlContext())); } @Test @@ -170,45 +215,47 @@ public class BiometricContextProviderTest { final int keyguardSessionId = 10; final int bpSessionId = 20; - assertThat(mProvider.getBiometricPromptSessionId()).isNull(); - assertThat(mProvider.getKeyguardEntrySessionId()).isNull(); + assertThat(mProvider.getBiometricPromptSessionInfo()).isNull(); + assertThat(mProvider.getKeyguardEntrySessionInfo()).isNull(); mSessionListener.onSessionStarted(StatusBarManager.SESSION_KEYGUARD, InstanceId.fakeInstanceId(keyguardSessionId)); - assertThat(mProvider.getBiometricPromptSessionId()).isNull(); - assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId); + assertThat(mProvider.getBiometricPromptSessionInfo()).isNull(); + assertThat(mProvider.getKeyguardEntrySessionInfo().getId()).isEqualTo(keyguardSessionId); mSessionListener.onSessionStarted(StatusBarManager.SESSION_BIOMETRIC_PROMPT, InstanceId.fakeInstanceId(bpSessionId)); - assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId); - assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId); + assertThat(mProvider.getBiometricPromptSessionInfo().getId()).isEqualTo(bpSessionId); + assertThat(mProvider.getKeyguardEntrySessionInfo().getId()).isEqualTo(keyguardSessionId); mSessionListener.onSessionEnded(StatusBarManager.SESSION_KEYGUARD, InstanceId.fakeInstanceId(keyguardSessionId)); - assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId); - assertThat(mProvider.getKeyguardEntrySessionId()).isNull(); + assertThat(mProvider.getBiometricPromptSessionInfo().getId()).isEqualTo(bpSessionId); + assertThat(mProvider.getKeyguardEntrySessionInfo()).isNull(); mSessionListener.onSessionEnded(StatusBarManager.SESSION_BIOMETRIC_PROMPT, InstanceId.fakeInstanceId(bpSessionId)); - assertThat(mProvider.getBiometricPromptSessionId()).isNull(); - assertThat(mProvider.getKeyguardEntrySessionId()).isNull(); + assertThat(mProvider.getBiometricPromptSessionInfo()).isNull(); + assertThat(mProvider.getKeyguardEntrySessionInfo()).isNull(); } @Test public void testUpdate() throws RemoteException { mListener.onDozeChanged(false /* isDozing */, false /* isAwake */); - OperationContext context = mProvider.updateContext(mOpContext, false /* crypto */); + + OperationContextExt context = mProvider.updateContext(mOpContext, false /* crypto */); + OperationContext aidlContext = context.toAidlContext(); // default state when nothing has been set assertThat(context).isSameInstanceAs(mOpContext); - assertThat(mOpContext.id).isEqualTo(0); - assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN); - assertThat(mOpContext.isAod).isEqualTo(false); - assertThat(mOpContext.isCrypto).isEqualTo(false); + assertThat(aidlContext.id).isEqualTo(0); + assertThat(aidlContext.reason).isEqualTo(OperationReason.UNKNOWN); + assertThat(aidlContext.isAod).isEqualTo(false); + assertThat(aidlContext.isCrypto).isEqualTo(false); for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT, StatusBarManager.SESSION_KEYGUARD)) { @@ -218,21 +265,23 @@ public class BiometricContextProviderTest { mListener.onDozeChanged(aod /* isDozing */, false /* isAwake */); mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id)); context = mProvider.updateContext(mOpContext, false /* crypto */); + aidlContext = context.toAidlContext(); assertThat(context).isSameInstanceAs(mOpContext); - assertThat(mOpContext.id).isEqualTo(id); - assertThat(mOpContext.reason).isEqualTo(reason(type)); - assertThat(mOpContext.isAod).isEqualTo(aod); - assertThat(mOpContext.isCrypto).isEqualTo(false); + assertThat(aidlContext.id).isEqualTo(id); + assertThat(aidlContext.reason).isEqualTo(reason(type)); + assertThat(aidlContext.isAod).isEqualTo(aod); + assertThat(aidlContext.isCrypto).isEqualTo(false); mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id)); } context = mProvider.updateContext(mOpContext, false /* crypto */); + aidlContext = context.toAidlContext(); assertThat(context).isSameInstanceAs(mOpContext); - assertThat(mOpContext.id).isEqualTo(0); - assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN); - assertThat(mOpContext.isAod).isEqualTo(false); - assertThat(mOpContext.isCrypto).isEqualTo(false); + assertThat(aidlContext.id).isEqualTo(0); + assertThat(aidlContext.reason).isEqualTo(OperationReason.UNKNOWN); + assertThat(aidlContext.isAod).isEqualTo(false); + assertThat(aidlContext.isCrypto).isEqualTo(false); } private static byte reason(int type) { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java index 88a9646cac8a..81dcf4838b07 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java @@ -32,7 +32,6 @@ import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.biometrics.BiometricsProtoEnums; -import android.hardware.biometrics.common.OperationContext; import android.hardware.input.InputSensorInfo; import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; @@ -70,12 +69,12 @@ public class BiometricLoggerTest { @Mock private BaseClientMonitor mClient; - private OperationContext mOpContext; + private OperationContextExt mOpContext; private BiometricLogger mLogger; @Before public void setUp() { - mOpContext = new OperationContext(); + mOpContext = new OperationContextExt(); mContext.addMockSystemService(SensorManager.class, mSensorManager); when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn( new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java new file mode 100644 index 000000000000..c7962c82471a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java @@ -0,0 +1,137 @@ +/* + * 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 android.content.Intent; +import android.hardware.biometrics.IBiometricContextListener; +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.common.OperationReason; +import android.platform.test.annotations.Presubmit; +import android.view.Surface; + +import static org.mockito.Mockito.when; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.InstanceId; + +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@Presubmit +@SmallTest +public class OperationContextExtTest { + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private BiometricContext mBiometricContext; + + @Test + public void hasAidlContext() { + OperationContextExt context = new OperationContextExt(); + assertThat(context.toAidlContext()).isNotNull(); + + final OperationContext aidlContext = newAidlContext(); + + context = new OperationContextExt(aidlContext); + assertThat(context.toAidlContext()).isSameInstanceAs(aidlContext); + + final int id = 5; + final byte reason = OperationReason.UNKNOWN; + aidlContext.id = id; + aidlContext.isAod = true; + aidlContext.isCrypto = true; + aidlContext.reason = reason; + + assertThat(context.getId()).isEqualTo(id); + assertThat(context.isAod()).isTrue(); + assertThat(context.isCrypto()).isTrue(); + assertThat(context.getReason()).isEqualTo(reason); + } + + @Test + public void hasNoOrderWithoutSession() { + OperationContextExt context = new OperationContextExt(); + assertThat(context.getOrderAndIncrement()).isEqualTo(-1); + assertThat(context.getOrderAndIncrement()).isEqualTo(-1); + } + + @Test + public void updatesFromSourceForKeyguard() { + final BiometricContextSessionInfo info = + new BiometricContextSessionInfo(InstanceId.fakeInstanceId(9)); + when(mBiometricContext.getKeyguardEntrySessionInfo()).thenReturn(info); + updatesFromSource(info, OperationReason.KEYGUARD); + } + + @Test + public void updatesFromSourceForBiometricPrompt() { + final BiometricContextSessionInfo info = + new BiometricContextSessionInfo(InstanceId.fakeInstanceId(9)); + when(mBiometricContext.getBiometricPromptSessionInfo()).thenReturn(info); + updatesFromSource(info, OperationReason.BIOMETRIC_PROMPT); + } + + @Test + public void updatesFromSourceWithoutSession() { + updatesFromSource(null, OperationReason.UNKNOWN); + } + + private void updatesFromSource(BiometricContextSessionInfo sessionInfo, int sessionType) { + final int rotation = Surface.ROTATION_270; + final int foldState = IBiometricContextListener.FoldState.HALF_OPENED; + final int dockState = Intent.EXTRA_DOCK_STATE_CAR; + + when(mBiometricContext.getCurrentRotation()).thenReturn(rotation); + when(mBiometricContext.getFoldState()).thenReturn(foldState); + when(mBiometricContext.getDockedState()).thenReturn(dockState); + when(mBiometricContext.isDisplayOn()).thenReturn(true); + + final OperationContextExt context = new OperationContextExt(newAidlContext()); + + assertThat(context.update(mBiometricContext)).isSameInstanceAs(context); + + if (sessionInfo != null) { + assertThat(context.getId()).isEqualTo(sessionInfo.getId()); + final int order = context.getOrderAndIncrement(); + assertThat(context.getOrderAndIncrement()).isEqualTo(order + 1); + } else { + assertThat(context.getId()).isEqualTo(0); + } + assertThat(context.getReason()).isEqualTo(sessionType); + assertThat(context.getDockState()).isEqualTo(dockState); + assertThat(context.getFoldState()).isEqualTo(foldState); + assertThat(context.getOrientation()).isEqualTo(rotation); + assertThat(context.isDisplayOn()).isTrue(); + } + + private static OperationContext newAidlContext() { + final OperationContext aidlContext = new OperationContext(); + aidlContext.id = -1; + aidlContext.isAod = false; + aidlContext.isCrypto = false; + aidlContext.reason = 0; + return aidlContext; + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java index d10d7d433e7a..139910ba8926 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -31,7 +31,6 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.ComponentName; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; import android.os.IBinder; @@ -44,6 +43,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -96,7 +96,7 @@ public class FaceAuthenticationClientTest { @Mock private AuthSessionCoordinator mAuthSessionCoordinator; @Captor - private ArgumentCaptor<OperationContext> mOperationContextCaptor; + private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -126,7 +126,7 @@ public class FaceAuthenticationClientTest { order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); order.verify(mHal).authenticateWithContext( - eq(OP_ID), same(mOperationContextCaptor.getValue())); + eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext())); verify(mHal, never()).authenticate(anyLong()); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java index 25135c6670db..e0fdb8cfc74e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.face.ISession; import android.os.IBinder; import android.os.RemoteException; @@ -36,6 +35,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -74,7 +74,7 @@ public class FaceDetectClientTest { @Mock private Sensor.HalSessionCallback mHalSessionCallback; @Captor - private ArgumentCaptor<OperationContext> mOperationContextCaptor; + private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -102,7 +102,8 @@ public class FaceDetectClientTest { InOrder order = inOrder(mHal, mBiometricContext); order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); - order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue())); + order.verify(mHal).detectInteractionWithContext( + same(mOperationContextCaptor.getValue().toAidlContext())); verify(mHal, never()).detectInteraction(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java index 38e048bc1ba7..d75aca1ce480 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; import android.os.IBinder; @@ -38,6 +37,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -80,7 +80,7 @@ public class FaceEnrollClientTest { @Mock private Sensor.HalSessionCallback mHalSessionCallback; @Captor - private ArgumentCaptor<OperationContext> mOperationContextCaptor; + private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -109,7 +109,7 @@ public class FaceEnrollClientTest { order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); order.verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), - same(mOperationContextCaptor.getValue())); + same(mOperationContextCaptor.getValue().toAidlContext())); verify(mHal, never()).enroll(any(), anyByte(), any(), any()); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index 22c53d349c5f..20beed0f6e82 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyFloat; @@ -57,6 +59,7 @@ import com.android.internal.R; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -131,7 +134,7 @@ public class FingerprintAuthenticationClientTest { @Mock private Clock mClock; @Captor - private ArgumentCaptor<OperationContext> mOperationContextCaptor; + private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; private final TestLooper mLooper = new TestLooper(); @@ -164,7 +167,7 @@ public class FingerprintAuthenticationClientTest { order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); order.verify(mHal).authenticateWithContext( - eq(OP_ID), same(mOperationContextCaptor.getValue())); + eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext())); verify(mHal, never()).authenticate(anyLong()); } @@ -236,9 +239,14 @@ public class FingerprintAuthenticationClientTest { final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); - verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); - OperationContext opContext = mOperationContextCaptor.getValue(); - verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture()); + OperationContext opContext = captor.getValue(); + verify(mBiometricContext).subscribe( + mOperationContextCaptor.capture(), mContextInjector.capture()); + assertThat(mOperationContextCaptor.getValue().toAidlContext()) + .isSameInstanceAs(opContext); mContextInjector.getValue().accept(opContext); verify(mLuxProbe, never()).enable(); @@ -282,9 +290,14 @@ public class FingerprintAuthenticationClientTest { final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); - verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); - OperationContext opContext = mOperationContextCaptor.getValue(); - verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture()); + OperationContext opContext = captor.getValue(); + verify(mBiometricContext).subscribe( + mOperationContextCaptor.capture(), mContextInjector.capture()); + assertThat(opContext).isSameInstanceAs( + mOperationContextCaptor.getValue().toAidlContext()); mContextInjector.getValue().accept(opContext); verify(mLuxProbe, never()).enable(); @@ -310,16 +323,21 @@ public class FingerprintAuthenticationClientTest { final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); - verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); - OperationContext opContext = mOperationContextCaptor.getValue(); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture()); + OperationContext opContext = captor.getValue(); // fake an update to the context - verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture()); + verify(mBiometricContext).subscribe( + mOperationContextCaptor.capture(), mContextInjector.capture()); + assertThat(opContext).isSameInstanceAs( + mOperationContextCaptor.getValue().toAidlContext()); mContextInjector.getValue().accept(opContext); verify(mHal).onContextChanged(eq(opContext)); client.stopHalOperation(); - verify(mBiometricContext).unsubscribe(same(opContext)); + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); } @Test diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java index 4579fc15f661..2dbd8f69d694 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; @@ -37,6 +36,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -77,7 +77,7 @@ public class FingerprintDetectClientTest { @Mock private Sensor.HalSessionCallback mHalSessionCallback; @Captor - private ArgumentCaptor<OperationContext> mOperationContextCaptor; + private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -107,7 +107,8 @@ public class FingerprintDetectClientTest { InOrder order = inOrder(mHal, mBiometricContext); order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); - order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue())); + order.verify(mHal).detectInteractionWithContext( + same(mOperationContextCaptor.getValue().toAidlContext())); verify(mHal, never()).detectInteraction(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java index c3d478311423..26524d7df7c3 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java @@ -48,6 +48,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; +import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -107,7 +108,7 @@ public class FingerprintEnrollClientTest { @Mock private Probe mLuxProbe; @Captor - private ArgumentCaptor<OperationContext> mOperationContextCaptor; + private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor private ArgumentCaptor<PointerContext> mPointerContextCaptor; @Captor @@ -143,7 +144,8 @@ public class FingerprintEnrollClientTest { InOrder order = inOrder(mHal, mBiometricContext); order.verify(mBiometricContext).updateContext( mOperationContextCaptor.capture(), anyBoolean()); - order.verify(mHal).enrollWithContext(any(), same(mOperationContextCaptor.getValue())); + order.verify(mHal).enrollWithContext(any(), + same(mOperationContextCaptor.getValue().toAidlContext())); verify(mHal, never()).enroll(any()); } @@ -234,16 +236,20 @@ public class FingerprintEnrollClientTest { final FingerprintEnrollClient client = createClient(); client.start(mCallback); - verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture()); - OperationContext opContext = mOperationContextCaptor.getValue(); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + verify(mHal).enrollWithContext(any(), captor.capture()); + OperationContext opContext = captor.getValue(); // fake an update to the context - verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture()); - mContextInjector.getValue().accept(opContext); + verify(mBiometricContext).subscribe( + mOperationContextCaptor.capture(), mContextInjector.capture()); + mContextInjector.getValue().accept( + mOperationContextCaptor.getValue().toAidlContext()); verify(mHal).onContextChanged(eq(opContext)); client.stopHalOperation(); - verify(mBiometricContext).unsubscribe(same(opContext)); + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); } @Test |