blob: e6acc904d8112eef59b91c21f09d4a7868e95da7 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics.log;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.StatusBarManager;
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.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBarService;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@Presubmit
@SmallTest
public class BiometricContextProviderTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@Mock
private IStatusBarService mStatusBarService;
@Mock
private ISessionListener mSessionListener;
@Mock
private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private OperationContext mOpContext = new OperationContext();
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 */);
ArgumentCaptor<IBiometricContextListener> captor =
ArgumentCaptor.forClass(IBiometricContextListener.class);
verify(mStatusBarService).setBiometicContextListener(captor.capture());
mListener = captor.getValue();
ArgumentCaptor<ISessionListener> sessionCaptor =
ArgumentCaptor.forClass(ISessionListener.class);
verify(mStatusBarService).registerSessionListener(anyInt(), sessionCaptor.capture());
mSessionListener = sessionCaptor.getValue();
}
@Test
public void testIsAod() throws RemoteException {
mListener.onDozeChanged(true);
assertThat(mProvider.isAod()).isTrue();
mListener.onDozeChanged(false);
assertThat(mProvider.isAod()).isFalse();
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(false);
mListener.onDozeChanged(true);
assertThat(mProvider.isAod()).isFalse();
mListener.onDozeChanged(false);
assertThat(mProvider.isAod()).isFalse();
}
@Test
public void testSubscribesToAod() throws RemoteException {
final List<Boolean> expected = ImmutableList.of(true, false, true, true, false);
final List<Boolean> actual = new ArrayList<>();
mProvider.subscribe(mOpContext, ctx -> {
assertThat(ctx).isSameInstanceAs(mOpContext);
actual.add(ctx.isAod);
});
for (boolean v : expected) {
mListener.onDozeChanged(v);
}
assertThat(actual).containsExactlyElementsIn(expected).inOrder();
}
@Test
public void testUnsubscribes() throws RemoteException {
final Consumer<OperationContext> emptyConsumer = mock(Consumer.class);
mProvider.subscribe(mOpContext, emptyConsumer);
mProvider.unsubscribe(mOpContext);
mListener.onDozeChanged(true);
final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
mProvider.subscribe(mOpContext, nonEmptyConsumer);
mListener.onDozeChanged(false);
mProvider.unsubscribe(mOpContext);
mListener.onDozeChanged(true);
verify(emptyConsumer, never()).accept(any());
verify(nonEmptyConsumer).accept(same(mOpContext));
}
@Test
public void testSessionId() throws RemoteException {
final int keyguardSessionId = 10;
final int bpSessionId = 20;
assertThat(mProvider.getBiometricPromptSessionId()).isNull();
assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
mSessionListener.onSessionStarted(StatusBarManager.SESSION_KEYGUARD,
InstanceId.fakeInstanceId(keyguardSessionId));
assertThat(mProvider.getBiometricPromptSessionId()).isNull();
assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
mSessionListener.onSessionStarted(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
InstanceId.fakeInstanceId(bpSessionId));
assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
mSessionListener.onSessionEnded(StatusBarManager.SESSION_KEYGUARD,
InstanceId.fakeInstanceId(keyguardSessionId));
assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
mSessionListener.onSessionEnded(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
InstanceId.fakeInstanceId(bpSessionId));
assertThat(mProvider.getBiometricPromptSessionId()).isNull();
assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
}
@Test
public void testUpdate() throws RemoteException {
mListener.onDozeChanged(false);
OperationContext context = mProvider.updateContext(mOpContext, false /* crypto */);
// 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);
for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
StatusBarManager.SESSION_KEYGUARD)) {
final int id = 40 + type;
final boolean aod = (type & 1) == 0;
mListener.onDozeChanged(aod);
mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id));
context = mProvider.updateContext(mOpContext, false /* crypto */);
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);
mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id));
}
context = mProvider.updateContext(mOpContext, false /* crypto */);
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);
}
private static byte reason(int type) {
if (type == StatusBarManager.SESSION_BIOMETRIC_PROMPT) {
return OperationReason.BIOMETRIC_PROMPT;
}
if (type == StatusBarManager.SESSION_KEYGUARD) {
return OperationReason.KEYGUARD;
}
return OperationReason.UNKNOWN;
}
}