1 /* 2 * Copyright (C) 2018 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.server.biometrics; 18 19 import android.content.Context; 20 import android.hardware.biometrics.BiometricAuthenticator; 21 import android.hardware.biometrics.BiometricConstants; 22 import android.hardware.biometrics.BiometricsProtoEnums; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.util.Slog; 26 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 30 /** 31 * A class to keep track of the enrollment state for a given client. 32 */ 33 public abstract class EnrollClient extends ClientMonitor { 34 private static final long MS_PER_SEC = 1000; 35 private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute 36 private final byte[] mCryptoToken; 37 private final BiometricUtils mBiometricUtils; 38 private final int[] mDisabledFeatures; 39 private long mEnrollmentStartTimeMs; 40 shouldVibrate()41 public abstract boolean shouldVibrate(); 42 EnrollClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils, final int[] disabledFeatures)43 public EnrollClient(Context context, Constants constants, 44 BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, 45 BiometricServiceBase.ServiceListener listener, int userId, int groupId, 46 byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils, 47 final int[] disabledFeatures) { 48 super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, 49 owner, 0 /* cookie */); 50 mBiometricUtils = utils; 51 mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length); 52 mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); 53 } 54 55 @Override statsAction()56 protected int statsAction() { 57 return BiometricsProtoEnums.ACTION_ENROLL; 58 } 59 60 @Override onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)61 public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, 62 int remaining) { 63 if (remaining == 0) { 64 mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier); 65 logOnEnrolled(getTargetUserId(), 66 System.currentTimeMillis() - mEnrollmentStartTimeMs, 67 true /* enrollSuccessful */); 68 } 69 notifyUserActivity(); 70 return sendEnrollResult(identifier, remaining); 71 } 72 73 /* 74 * @return true if we're done. 75 */ sendEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)76 private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier, 77 int remaining) { 78 if (shouldVibrate()) { 79 vibrateSuccess(); 80 } 81 mMetricsLogger.action(mConstants.actionBiometricEnroll()); 82 try { 83 final BiometricServiceBase.ServiceListener listener = getListener(); 84 if (listener != null) { 85 listener.onEnrollResult(identifier, remaining); 86 } 87 return remaining == 0; 88 } catch (RemoteException e) { 89 Slog.w(getLogTag(), "Failed to notify EnrollResult:", e); 90 return true; 91 } 92 } 93 94 @Override start()95 public int start() { 96 mEnrollmentStartTimeMs = System.currentTimeMillis(); 97 final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC); 98 try { 99 final ArrayList<Integer> disabledFeatures = new ArrayList<>(); 100 for (int i = 0; i < mDisabledFeatures.length; i++) { 101 disabledFeatures.add(mDisabledFeatures[i]); 102 } 103 104 final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout, 105 disabledFeatures); 106 if (result != 0) { 107 Slog.w(getLogTag(), "startEnroll failed, result=" + result); 108 mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result); 109 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 110 0 /* vendorCode */); 111 return result; 112 } 113 } catch (RemoteException e) { 114 Slog.e(getLogTag(), "startEnroll failed", e); 115 } 116 return 0; // success 117 } 118 119 @Override stop(boolean initiatedByClient)120 public int stop(boolean initiatedByClient) { 121 if (mAlreadyCancelled) { 122 Slog.w(getLogTag(), "stopEnroll: already cancelled!"); 123 return 0; 124 } 125 126 try { 127 final int result = getDaemonWrapper().cancel(); 128 if (result != 0) { 129 Slog.w(getLogTag(), "startEnrollCancel failed, result = " + result); 130 return result; 131 } 132 } catch (RemoteException e) { 133 Slog.e(getLogTag(), "stopEnrollment failed", e); 134 } 135 mAlreadyCancelled = true; 136 return 0; 137 } 138 139 @Override onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)140 public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) { 141 if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for enroll!"); 142 return true; // Invalid for EnrollClient 143 } 144 145 @Override onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining)146 public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, 147 int remaining) { 148 if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for enroll!"); 149 return true; // Invalid for EnrollClient 150 } 151 152 @Override onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token)153 public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, 154 boolean authenticated, ArrayList<Byte> token) { 155 if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enroll!"); 156 return true; // Invalid for EnrollClient 157 } 158 159 /** 160 * Called when we get notification from the biometric's HAL that an error has occurred with the 161 * current operation. Common to authenticate, enroll, enumerate and remove. 162 * @param error 163 * @return true if client should be removed 164 */ 165 @Override onError(long deviceId, int error, int vendorCode)166 public boolean onError(long deviceId, int error, int vendorCode) { 167 logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs, 168 false /* enrollSuccessful */); 169 return super.onError(deviceId, error, vendorCode); 170 } 171 172 } 173