Files
app_Settings/tests/unit/src/com/android/settings/biometrics/ParentalConsentHelperTest.java
Kevin Chyn 156db33a56 Disable combined controller only if all modalities require consent
Additionally, ensure that consent is only requested for modalities
that have not been previously consented to. We retrieve the consent
requirement (which come from DPM) in onActivityResult instead of
onCreate, since the signal may not be ready immediately.

Fixes: 196060286
Fixes: 204592495
Test: make -j56 RunSettingsRoboTests ROBOTEST_FILTER=CombinedBiometricStatusPreferenceControllerTest

Change-Id: I984e61f28ffbf957c16cac4ea84f40b6ad7d8ae9
2021-11-16 12:32:56 -08:00

225 lines
8.5 KiB
Java

/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_MODALITY;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Intent;
import android.hardware.biometrics.BiometricAuthenticator;
import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.biometrics.face.FaceEnrollParentalConsent;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollParentalConsent;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
public class ParentalConsentHelperTest {
private static final int REQUEST_CODE = 12;
@Rule
public final MockitoRule mMocks = MockitoJUnit.rule();
@Mock
private Activity mRootActivity;
@Mock
private Intent mRootActivityIntent;
@Captor
ArgumentCaptor<Intent> mLastStarted;
@Before
public void setup() {
when(mRootActivity.getIntent()).thenAnswer(invocation -> mRootActivityIntent);
when(mRootActivityIntent.getBundleExtra(any())).thenAnswer(invocation -> null);
when(mRootActivityIntent.getStringExtra(any())).thenAnswer(invocation -> null);
when(mRootActivityIntent.getBooleanExtra(any(), anyBoolean()))
.thenAnswer(invocation -> invocation.getArguments()[1]);
}
@Test
public void testLaunchNext_face_and_fingerprint_all_consent() {
testLaunchNext(
true /* requireFace */, true /* grantFace */,
true /* requireFingerprint */, true /* grantFace */,
90 /* gkpw */);
}
@Test
public void testLaunchNext_nothing_to_consent() {
testLaunchNext(
false /* requireFace */, false /* grantFace */,
false /* requireFingerprint */, false /* grantFace */,
80 /* gkpw */);
}
@Test
public void testLaunchNext_face_and_fingerprint_no_consent() {
testLaunchNext(
true /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, false /* grantFace */,
70 /* gkpw */);
}
@Test
public void testLaunchNext_face_and_fingerprint_only_face_consent() {
testLaunchNext(
true /* requireFace */, true /* grantFace */,
true /* requireFingerprint */, false /* grantFace */,
60 /* gkpw */);
}
@Test
public void testLaunchNext_face_and_fingerprint_only_fingerprint_consent() {
testLaunchNext(
true /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, true /* grantFace */,
50 /* gkpw */);
}
@Test
public void testLaunchNext_face_with_consent() {
testLaunchNext(
true /* requireFace */, true /* grantFace */,
false /* requireFingerprint */, false /* grantFace */,
40 /* gkpw */);
}
@Test
public void testLaunchNext_face_without_consent() {
testLaunchNext(
true /* requireFace */, false /* grantFace */,
false /* requireFingerprint */, false /* grantFace */,
30 /* gkpw */);
}
@Test
public void testLaunchNext_fingerprint_with_consent() {
testLaunchNext(
false /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, true /* grantFace */,
20 /* gkpw */);
}
@Test
public void testLaunchNext_fingerprint_without_consent() {
testLaunchNext(
false /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, false /* grantFace */,
10 /* gkpw */);
}
private void testLaunchNext(
boolean requireFace, boolean grantFace,
boolean requireFingerprint, boolean grantFingerprint,
long gkpw) {
final List<Pair<String, Boolean>> expectedLaunches = new ArrayList<>();
if (requireFace) {
expectedLaunches.add(new Pair(FaceEnrollParentalConsent.class.getName(), grantFace));
}
if (requireFingerprint) {
expectedLaunches.add(
new Pair(FingerprintEnrollParentalConsent.class.getName(), grantFingerprint));
}
// initial consent status
final ParentalConsentHelper helper = new ParentalConsentHelper(gkpw);
helper.setConsentRequirement(requireFace, requireFingerprint);
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
.isFalse();
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
.isFalse();
// check expected launches
for (int i = 0; i <= expectedLaunches.size(); i++) {
final Pair<String, Boolean> expected = i > 0 ? expectedLaunches.get(i - 1) : null;
final boolean launchedNext = i == 0
? helper.launchNext(mRootActivity, REQUEST_CODE)
: helper.launchNext(mRootActivity, REQUEST_CODE,
expected.second ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED,
getResultIntent(getStartedModality(expected.first)));
assertThat(launchedNext).isEqualTo(i < expectedLaunches.size());
}
verify(mRootActivity, times(expectedLaunches.size()))
.startActivityForResult(mLastStarted.capture(), eq(REQUEST_CODE));
assertThat(mLastStarted.getAllValues()
.stream().map(i -> i.getComponent().getClassName()).collect(Collectors.toList()))
.containsExactlyElementsIn(
expectedLaunches.stream().map(i -> i.first).collect(Collectors.toList()))
.inOrder();
if (!expectedLaunches.isEmpty()) {
assertThat(mLastStarted.getAllValues()
.stream().map(BiometricUtils::getGatekeeperPasswordHandle).distinct()
.collect(Collectors.toList()))
.containsExactly(gkpw);
}
// final consent status
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
.isEqualTo(requireFace && grantFace);
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
.isEqualTo(requireFingerprint && grantFingerprint);
}
private static Intent getResultIntent(@BiometricAuthenticator.Modality int modality) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_KEY_MODALITY, modality);
return intent;
}
@BiometricAuthenticator.Modality
private static int getStartedModality(String name) {
if (name.equals(FaceEnrollParentalConsent.class.getName())) {
return TYPE_FACE;
}
if (name.equals(FingerprintEnrollParentalConsent.class.getName())) {
return TYPE_FINGERPRINT;
}
return TYPE_NONE;
}
}