1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.biometrics; 18 19 import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; 20 import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL; 21 22 import static androidx.test.espresso.intent.Intents.intended; 23 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent; 24 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; 25 26 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS; 27 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE; 28 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT; 29 30 import static com.google.common.truth.Truth.assertThat; 31 32 import static org.junit.Assume.assumeTrue; 33 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.pm.PackageManager; 37 import android.hardware.biometrics.SensorProperties; 38 import android.hardware.face.FaceManager; 39 import android.hardware.face.FaceSensorPropertiesInternal; 40 import android.hardware.fingerprint.FingerprintManager; 41 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; 42 import android.os.UserHandle; 43 import android.provider.Settings; 44 45 import androidx.test.core.app.ActivityScenario; 46 import androidx.test.core.app.ApplicationProvider; 47 import androidx.test.espresso.intent.Intents; 48 import androidx.test.ext.junit.runners.AndroidJUnit4; 49 import androidx.test.filters.MediumTest; 50 51 import com.android.internal.widget.LockPatternChecker; 52 import com.android.internal.widget.LockPatternUtils; 53 import com.android.internal.widget.LockscreenCredential; 54 import com.android.settings.biometrics.face.FaceEnrollIntroduction; 55 import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction; 56 import com.android.settings.password.ChooseLockGeneric; 57 import com.android.settings.password.ChooseLockSettingsHelper; 58 import com.android.settings.password.ConfirmLockPassword; 59 import com.android.settings.testutils.AdbUtils; 60 61 import org.junit.After; 62 import org.junit.Before; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 66 import java.util.List; 67 68 @RunWith(AndroidJUnit4.class) 69 @MediumTest 70 public class BiometricEnrollActivityTest { 71 72 private static final String TAG = "BiometricEnrollActivityTest"; 73 private static final int ADB_TIMEOUT_MS = 5000; 74 private static final String TEST_PIN = "1234"; 75 76 private final Context mContext = ApplicationProvider.getApplicationContext(); 77 private boolean mHasFace; 78 private boolean mHasFingerprint; 79 private boolean mIsFaceStrong; 80 private boolean mIsFingerprintStrong; 81 82 @Before setup()83 public void setup() { 84 Intents.init(); 85 final PackageManager pm = mContext.getPackageManager(); 86 mHasFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); 87 mHasFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); 88 89 if (mHasFace) { 90 final FaceManager faceManager = mContext.getSystemService(FaceManager.class); 91 final List<FaceSensorPropertiesInternal> faceProperties = 92 faceManager.getSensorPropertiesInternal(); 93 if (!faceProperties.isEmpty()) { 94 final FaceSensorPropertiesInternal faceProp = faceProperties.get(0); 95 mIsFaceStrong = faceProp.sensorStrength == SensorProperties.STRENGTH_STRONG; 96 } 97 } 98 99 if (mHasFingerprint) { 100 final FingerprintManager fingerprintManager = mContext.getSystemService( 101 FingerprintManager.class); 102 final List<FingerprintSensorPropertiesInternal> fingerProperties = 103 fingerprintManager.getSensorPropertiesInternal(); 104 if (!fingerProperties.isEmpty()) { 105 final FingerprintSensorPropertiesInternal fingerProp = fingerProperties.get(0); 106 mIsFingerprintStrong = 107 fingerProp.sensorStrength == SensorProperties.STRENGTH_STRONG; 108 } 109 } 110 } 111 112 @After teardown()113 public void teardown() throws Exception { 114 Intents.release(); 115 AdbUtils.checkStringInAdbCommandOutput(TAG, "locksettings clear --old " + TEST_PIN, 116 "", "", ADB_TIMEOUT_MS); 117 } 118 119 @Test launchWithoutPin_setsPin()120 public void launchWithoutPin_setsPin() { 121 try (ActivityScenario<BiometricEnrollActivity> scenario = 122 ActivityScenario.launch(getIntent())) { 123 intended(hasComponent(ChooseLockGeneric.class.getName())); 124 if (mHasFace && mHasFingerprint) { 125 intended(hasExtra(EXTRA_KEY_FOR_BIOMETRICS, true)); 126 } else if (mHasFace) { 127 intended(hasExtra(EXTRA_KEY_FOR_FACE, true)); 128 } else if (mHasFingerprint) { 129 intended(hasExtra(EXTRA_KEY_FOR_FINGERPRINT, true)); 130 } 131 } 132 } 133 134 @Test launchWithPin_confirmsPin()135 public void launchWithPin_confirmsPin() throws Exception { 136 setPin(); 137 try (ActivityScenario<BiometricEnrollActivity> scenario = 138 ActivityScenario.launch(getIntent())) { 139 intended(hasComponent(ConfirmLockPassword.InternalActivity.class.getName())); 140 } 141 } 142 143 @Test launchWithPinAndPwHandle_confirmsPin()144 public void launchWithPinAndPwHandle_confirmsPin() throws Exception { 145 assumeTrue(mHasFace || mHasFingerprint); 146 147 setPin(); 148 final Intent intent = getIntent(true /* useInternal */); 149 LockPatternChecker.verifyCredential(new LockPatternUtils(mContext), 150 LockscreenCredential.createPin(TEST_PIN), UserHandle.myUserId(), 151 LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, (response, timeoutMs) -> { 152 assertThat(response.containsGatekeeperPasswordHandle()).isTrue(); 153 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 154 response.getGatekeeperPasswordHandle()); 155 }).get(); 156 157 try (ActivityScenario<BiometricEnrollActivity> scenario = 158 ActivityScenario.launch(intent)) { 159 intended(hasComponent(mHasFace && !mHasFingerprint 160 ? FaceEnrollIntroduction.class.getName() 161 : FingerprintEnrollIntroduction.class.getName())); 162 } 163 } 164 165 @Test launchWithStrongBiometricAllowed_doNotEnrollWeak()166 public void launchWithStrongBiometricAllowed_doNotEnrollWeak() throws Exception { 167 assumeTrue(mHasFace || mHasFingerprint); 168 169 // Allow only strong biometrics 170 Intent intent = getIntent(); 171 intent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG); 172 173 try (ActivityScenario<BiometricEnrollActivity> scenario = 174 ActivityScenario.launch(intent)) { 175 intended(hasComponent(ChooseLockGeneric.class.getName())); 176 if (mIsFaceStrong && mIsFingerprintStrong) { 177 intended(hasExtra(EXTRA_KEY_FOR_BIOMETRICS, true)); 178 } else if (mIsFaceStrong) { 179 intended(hasExtra(EXTRA_KEY_FOR_FACE, true)); 180 } else if (mIsFingerprintStrong) { 181 intended(hasExtra(EXTRA_KEY_FOR_FINGERPRINT, true)); 182 } 183 } 184 } 185 getIntent()186 private Intent getIntent() { 187 return getIntent(false /* useInternal */); 188 } 189 getIntent(boolean useInternal)190 private Intent getIntent(boolean useInternal) { 191 final Intent intent = new Intent(mContext, useInternal 192 ? BiometricEnrollActivity.InternalActivity.class : BiometricEnrollActivity.class); 193 intent.setAction(ACTION_BIOMETRIC_ENROLL); 194 return intent; 195 } 196 setPin()197 private static void setPin() throws Exception { 198 assertThat(AdbUtils.checkStringInAdbCommandOutput(TAG, "locksettings set-pin " + TEST_PIN, 199 "Pin set to ", "'" + TEST_PIN + "'", ADB_TIMEOUT_MS)).isTrue(); 200 } 201 } 202