1 /*
2  * Copyright (C) 2020 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.cts.verifier.biometrics;
18 
19 import android.content.Intent;
20 import android.hardware.biometrics.BiometricManager;
21 import android.hardware.biometrics.BiometricManager.Authenticators;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.provider.Settings;
26 import android.util.Log;
27 import android.widget.Button;
28 import android.widget.Toast;
29 
30 import com.android.cts.verifier.PassFailButtons;
31 
32 import java.util.concurrent.Executor;
33 
34 /**
35  * Abstract base class for tests in this directory.
36  */
37 public abstract class AbstractBaseTest extends PassFailButtons.Activity {
38 
39     private static final int REQUEST_ENROLL_WHEN_NONE_ENROLLED = 1;
40 
getTag()41     abstract protected String getTag();
isOnPauseAllowed()42     abstract protected boolean isOnPauseAllowed();
43 
44     protected final Handler mHandler = new Handler(Looper.getMainLooper());
45     protected final Executor mExecutor = mHandler::post;
46 
47     protected boolean mCurrentlyEnrolling;
48 
49     BiometricManager mBiometricManager;
50 
51     @Override
onCreate(Bundle savedInstanceState)52     protected void onCreate(Bundle savedInstanceState) {
53         super.onCreate(savedInstanceState);
54         mBiometricManager = getSystemService(BiometricManager.class);
55     }
56 
57     @Override
onPause()58     protected void onPause() {
59         super.onPause();
60 
61         // Assume we only enable the pass button when all tests pass. There actually  isn't a way
62         // to easily do something like `this.isTestPassed()`
63         if (!getPassButton().isEnabled() && !isOnPauseAllowed()) {
64             showToastAndLog("This test must be completed without pausing the app");
65             // Do not allow the test to continue if it loses foreground. Testers must start over.
66             // 1) This is to avoid any potential change to the current enrollment / biometric state.
67             // 2) The authentication UI must not affect the caller's activity lifecycle.
68             finish();
69         }
70     }
71 
72     @Override
onActivityResult(int requestCode, int resultCode, Intent data)73     public void onActivityResult(int requestCode, int resultCode, Intent data) {
74         mCurrentlyEnrolling = false;
75 
76         if (requestCode == REQUEST_ENROLL_WHEN_NONE_ENROLLED) {
77             onBiometricEnrollFinished();
78         }
79     }
80 
showToastAndLog(String s)81     void showToastAndLog(String s) {
82         Log.d(getTag(), s);
83         Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
84     }
85 
showToastAndLog(String s, Exception e)86     void showToastAndLog(String s, Exception e) {
87         Log.d(getTag(), s, e);
88         Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
89     }
90 
onBiometricEnrollFinished()91     protected void onBiometricEnrollFinished() {
92     }
93 
checkAndEnroll(Button enrollButton, int requestedStrength)94     void checkAndEnroll(Button enrollButton, int requestedStrength) {
95         // Check that no biometrics (of any strength) are enrolled
96         int result = mBiometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK);
97         if (result == BiometricManager.BIOMETRIC_SUCCESS) {
98             showToastAndLog("Please ensure that all biometrics are removed before starting"
99                     + " this test");
100             return;
101         }
102 
103         result = mBiometricManager.canAuthenticate(requestedStrength);
104         if (result == BiometricManager.BIOMETRIC_SUCCESS) {
105             showToastAndLog("Please ensure that all biometrics are removed before starting"
106                     + " this test");
107         } else if (result == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
108             // Multi-sensor cases are more thoroughly tested in regular CTS (not CTS-V), this
109             // should be fine for the purposes of CTS-V.
110             showToastAndLog("This device does not have a sensor meeting the requested strength,"
111                     + " you may pass this test");
112             enrollButton.setEnabled(false);
113             getPassButton().setEnabled(true);
114         } else if (result == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
115             startBiometricEnroll(REQUEST_ENROLL_WHEN_NONE_ENROLLED, requestedStrength);
116         } else {
117             showToastAndLog("Unexpected result: " + result + ". Please ensure you have removed"
118                     + "all biometric enrollments.");
119         }
120     }
121 
startBiometricEnroll(int requestCode, int requestedStrength)122     private void startBiometricEnroll(int requestCode, int requestedStrength) {
123         mCurrentlyEnrolling = true;
124         final Intent enrollIntent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL);
125         enrollIntent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
126                 requestedStrength);
127 
128         startActivityForResult(enrollIntent, requestCode);
129     }
130 }
131