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.security.KeyStore; 26 import android.util.Slog; 27 28 import java.util.ArrayList; 29 30 /** 31 * A class to keep track of the authentication state for a given client. 32 */ 33 public abstract class AuthenticationClient extends ClientMonitor { 34 private long mOpId; 35 handleFailedAttempt()36 public abstract int handleFailedAttempt(); resetFailedAttempts()37 public void resetFailedAttempts() {} 38 39 public static final int LOCKOUT_NONE = 0; 40 public static final int LOCKOUT_TIMED = 1; 41 public static final int LOCKOUT_PERMANENT = 2; 42 43 private final boolean mRequireConfirmation; 44 45 // We need to track this state since it's possible for applications to request for 46 // authentication while the device is already locked out. In that case, the client is created 47 // but not started yet. The user shouldn't receive the error haptics in this case. 48 private boolean mStarted; 49 50 /** 51 * This method is called when authentication starts. 52 */ onStart()53 public abstract void onStart(); 54 55 /** 56 * This method is called when a biometric is authenticated or authentication is stopped 57 * (cancelled by the user, or an error such as lockout has occurred). 58 */ onStop()59 public abstract void onStop(); 60 61 /** 62 * @return true if the framework should handle lockout. 63 */ shouldFrameworkHandleLockout()64 public abstract boolean shouldFrameworkHandleLockout(); 65 wasUserDetected()66 public abstract boolean wasUserDetected(); 67 AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation)68 public AuthenticationClient(Context context, Constants constants, 69 BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, 70 BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, 71 boolean restricted, String owner, int cookie, boolean requireConfirmation) { 72 super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId, 73 restricted, owner, cookie); 74 mOpId = opId; 75 mRequireConfirmation = requireConfirmation; 76 } 77 78 @Override binderDied()79 public void binderDied() { 80 super.binderDied(); 81 // When the binder dies, we should stop the client. This probably belongs in 82 // ClientMonitor's binderDied(), but testing all the cases would be tricky. 83 // AuthenticationClient is the most user-visible case. 84 stop(false /* initiatedByClient */); 85 } 86 87 @Override statsAction()88 protected int statsAction() { 89 return BiometricsProtoEnums.ACTION_AUTHENTICATE; 90 } 91 isBiometricPrompt()92 public boolean isBiometricPrompt() { 93 return getCookie() != 0; 94 } 95 getRequireConfirmation()96 public boolean getRequireConfirmation() { 97 return mRequireConfirmation; 98 } 99 100 @Override isCryptoOperation()101 protected boolean isCryptoOperation() { 102 return mOpId != 0; 103 } 104 105 @Override onError(long deviceId, int error, int vendorCode)106 public boolean onError(long deviceId, int error, int vendorCode) { 107 if (!shouldFrameworkHandleLockout()) { 108 switch (error) { 109 case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: 110 if (!wasUserDetected() && !isBiometricPrompt()) { 111 // No vibration if user was not detected on keyguard 112 break; 113 } 114 case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: 115 case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: 116 if (mStarted) { 117 vibrateError(); 118 } 119 break; 120 default: 121 break; 122 } 123 } 124 return super.onError(deviceId, error, vendorCode); 125 } 126 127 @Override onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token)128 public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, 129 boolean authenticated, ArrayList<Byte> token) { 130 super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation, 131 getTargetUserId(), isBiometricPrompt()); 132 133 final BiometricServiceBase.ServiceListener listener = getListener(); 134 135 mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated); 136 boolean result = false; 137 138 try { 139 if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + authenticated + ")" 140 + ", ID:" + identifier.getBiometricId() 141 + ", Owner: " + getOwnerString() 142 + ", isBP: " + isBiometricPrompt() 143 + ", listener: " + listener 144 + ", requireConfirmation: " + mRequireConfirmation 145 + ", user: " + getTargetUserId()); 146 147 if (authenticated) { 148 mAlreadyDone = true; 149 150 if (listener != null) { 151 vibrateSuccess(); 152 } 153 result = true; 154 if (shouldFrameworkHandleLockout()) { 155 resetFailedAttempts(); 156 } 157 onStop(); 158 159 final byte[] byteToken = new byte[token.size()]; 160 for (int i = 0; i < token.size(); i++) { 161 byteToken[i] = token.get(i); 162 } 163 if (isBiometricPrompt() && listener != null) { 164 // BiometricService will add the token to keystore 165 listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken); 166 } else if (!isBiometricPrompt() && listener != null) { 167 KeyStore.getInstance().addAuthToken(byteToken); 168 try { 169 // Explicitly have if/else here to make it super obvious in case the code is 170 // touched in the future. 171 if (!getIsRestricted()) { 172 listener.onAuthenticationSucceeded( 173 getHalDeviceId(), identifier, getTargetUserId()); 174 } else { 175 listener.onAuthenticationSucceeded( 176 getHalDeviceId(), null, getTargetUserId()); 177 } 178 } catch (RemoteException e) { 179 Slog.e(getLogTag(), "Remote exception", e); 180 } 181 } else { 182 // Client not listening 183 Slog.w(getLogTag(), "Client not listening"); 184 result = true; 185 } 186 } else { 187 if (listener != null) { 188 vibrateError(); 189 } 190 191 // Allow system-defined limit of number of attempts before giving up 192 final int lockoutMode = handleFailedAttempt(); 193 if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) { 194 Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode(" 195 + lockoutMode + ")"); 196 stop(false); 197 final int errorCode = lockoutMode == LOCKOUT_TIMED 198 ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT 199 : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; 200 onError(getHalDeviceId(), errorCode, 0 /* vendorCode */); 201 } else { 202 // Don't send onAuthenticationFailed if we're in lockout, it causes a 203 // janky UI on Keyguard/BiometricPrompt since "authentication failed" 204 // will show briefly and be replaced by "device locked out" message. 205 if (listener != null) { 206 if (isBiometricPrompt()) { 207 listener.onAuthenticationFailedInternal(getCookie(), 208 getRequireConfirmation()); 209 } else { 210 listener.onAuthenticationFailed(getHalDeviceId()); 211 } 212 } 213 } 214 result = lockoutMode != LOCKOUT_NONE; // in a lockout mode 215 } 216 } catch (RemoteException e) { 217 Slog.e(getLogTag(), "Remote exception", e); 218 result = true; 219 } 220 return result; 221 } 222 223 /** 224 * Start authentication 225 */ 226 @Override start()227 public int start() { 228 mStarted = true; 229 onStart(); 230 try { 231 final int result = getDaemonWrapper().authenticate(mOpId, getGroupId()); 232 if (result != 0) { 233 Slog.w(getLogTag(), "startAuthentication failed, result=" + result); 234 mMetricsLogger.histogram(mConstants.tagAuthStartError(), result); 235 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 236 0 /* vendorCode */); 237 return result; 238 } 239 if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating..."); 240 } catch (RemoteException e) { 241 Slog.e(getLogTag(), "startAuthentication failed", e); 242 return ERROR_ESRCH; 243 } 244 return 0; // success 245 } 246 247 @Override stop(boolean initiatedByClient)248 public int stop(boolean initiatedByClient) { 249 if (mAlreadyCancelled) { 250 Slog.w(getLogTag(), "stopAuthentication: already cancelled!"); 251 return 0; 252 } 253 254 mStarted = false; 255 256 onStop(); 257 258 try { 259 final int result = getDaemonWrapper().cancel(); 260 if (result != 0) { 261 Slog.w(getLogTag(), "stopAuthentication failed, result=" + result); 262 return result; 263 } 264 if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + 265 " is no longer authenticating"); 266 } catch (RemoteException e) { 267 Slog.e(getLogTag(), "stopAuthentication failed", e); 268 return ERROR_ESRCH; 269 } 270 271 mAlreadyCancelled = true; 272 return 0; // success 273 } 274 275 @Override onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)276 public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, 277 int remaining) { 278 if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for authenticate!"); 279 return true; // Invalid for Authenticate 280 } 281 282 @Override onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)283 public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) { 284 if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for authenticate!"); 285 return true; // Invalid for Authenticate 286 } 287 288 @Override onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining)289 public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, 290 int remaining) { 291 if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for authenticate!"); 292 return true; // Invalid for Authenticate 293 } 294 } 295