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 android.server.biometrics; 18 19 import android.app.Activity; 20 import android.hardware.biometrics.BiometricPrompt; 21 import android.os.Bundle; 22 import android.server.wm.TestJournalProvider; 23 import android.util.Log; 24 25 import androidx.annotation.NonNull; 26 27 import java.util.ArrayList; 28 29 public class BiometricCallbackHelper extends BiometricPrompt.AuthenticationCallback { 30 31 private static final String TAG = "BiometricCallbackHelper"; 32 public static final String KEY = "key_auth_callback"; 33 34 public static class State { 35 private static final String KEY_ERRORS_RECEIVED = "key_errors_received"; 36 private static final String KEY_ACQUIRED_RECEIVED = "key_acquired_received"; 37 private static final String KEY_NUM_ACCEPTED = "key_num_accepted"; 38 private static final String KEY_NUM_REJECTED = "key_num_rejected"; 39 private static final String KEY_NEGATIVE_BUTTON_PRESSED = "key_negative_button_pressed"; 40 41 public final ArrayList<Integer> mErrorsReceived; 42 public final ArrayList<Integer> mAcquiredReceived; 43 public int mNumAuthAccepted; 44 public int mNumAuthRejected; 45 public boolean mNegativeButtonPressed; 46 State()47 public State() { 48 mErrorsReceived = new ArrayList<>(); 49 mAcquiredReceived = new ArrayList<>(); 50 } 51 toBundle()52 public Bundle toBundle() { 53 final Bundle bundle = new Bundle(); 54 bundle.putIntegerArrayList(KEY_ERRORS_RECEIVED, mErrorsReceived); 55 bundle.putIntegerArrayList(KEY_ACQUIRED_RECEIVED, mAcquiredReceived); 56 bundle.putInt(KEY_NUM_ACCEPTED, mNumAuthAccepted); 57 bundle.putInt(KEY_NUM_REJECTED, mNumAuthRejected); 58 bundle.putBoolean(KEY_NEGATIVE_BUTTON_PRESSED, mNegativeButtonPressed); 59 return bundle; 60 } 61 State(ArrayList<Integer> errorsReceived, ArrayList<Integer> acquiredReceived, int numAuthAccepted, int numAuthRejected, boolean negativeButtonPressed)62 private State(ArrayList<Integer> errorsReceived, ArrayList<Integer> acquiredReceived, 63 int numAuthAccepted, int numAuthRejected, boolean negativeButtonPressed) { 64 mErrorsReceived = errorsReceived; 65 mAcquiredReceived = acquiredReceived; 66 mNumAuthAccepted = numAuthAccepted; 67 mNumAuthRejected = numAuthRejected; 68 mNegativeButtonPressed = negativeButtonPressed; 69 } 70 fromBundle(@onNull Bundle bundle)71 public static BiometricCallbackHelper.State fromBundle(@NonNull Bundle bundle) { 72 return new BiometricCallbackHelper.State( 73 bundle.getIntegerArrayList(KEY_ERRORS_RECEIVED), 74 bundle.getIntegerArrayList(KEY_ACQUIRED_RECEIVED), 75 bundle.getInt(KEY_NUM_ACCEPTED), 76 bundle.getInt(KEY_NUM_REJECTED), 77 bundle.getBoolean(KEY_NEGATIVE_BUTTON_PRESSED)); 78 } 79 80 @Override toString()81 public String toString() { 82 final StringBuilder sb = new StringBuilder(); 83 sb.append("Accept: ").append(mNumAuthAccepted) 84 .append(", Reject: ").append(mNumAuthRejected) 85 .append(", Acquired Count: " ).append(mAcquiredReceived.size()) 86 .append(", Errors Count: ").append(mErrorsReceived.size()) 87 .append(", Negative pressed: ").append(mNegativeButtonPressed); 88 return sb.toString(); 89 } 90 } 91 92 private final Activity mActivity; 93 private final State mState; 94 95 @Override onAuthenticationError(int errorCode, CharSequence errString)96 public void onAuthenticationError(int errorCode, CharSequence errString) { 97 Log.d(TAG, "onAuthenticationError: " + errorCode + ", " + errString); 98 mState.mErrorsReceived.add(errorCode); 99 updateJournal(); 100 } 101 102 @Override onAuthenticationHelp(int helpCode, CharSequence helpString)103 public void onAuthenticationHelp(int helpCode, CharSequence helpString) { 104 Log.d(TAG, "onAuthenticationHelp: " + helpCode + ", " + helpString); 105 mState.mAcquiredReceived.add(helpCode); 106 updateJournal(); 107 } 108 109 @Override onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result)110 public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) { 111 Log.d(TAG, "onAuthenticationSucceeded"); 112 mState.mNumAuthAccepted++; 113 updateJournal(); 114 } 115 116 @Override onAuthenticationFailed()117 public void onAuthenticationFailed() { 118 Log.d(TAG, "onAuthenticationFailed"); 119 mState.mNumAuthRejected++; 120 updateJournal(); 121 } 122 onNegativeButtonPressed()123 void onNegativeButtonPressed() { 124 Log.d(TAG, "onNegativeButtonPressed"); 125 mState.mNegativeButtonPressed = true; 126 updateJournal(); 127 } 128 BiometricCallbackHelper(@onNull Activity activity)129 public BiometricCallbackHelper(@NonNull Activity activity) { 130 mActivity = activity; 131 mState = new BiometricCallbackHelper.State(); 132 133 // Update with empty state. It's faster than waiting/retrying for null on CTS-side. 134 updateJournal(); 135 } 136 updateJournal()137 private void updateJournal() { 138 TestJournalProvider.putExtras(mActivity, 139 bundle -> bundle.putBundle(KEY, mState.toBundle())); 140 } 141 } 142