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.server.biometrics.sensors; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.ActivityManager; 23 import android.app.ActivityTaskManager; 24 import android.app.TaskStackListener; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.hardware.biometrics.BiometricAuthenticator; 29 import android.hardware.biometrics.BiometricConstants; 30 import android.hardware.biometrics.BiometricManager; 31 import android.hardware.biometrics.BiometricOverlayConstants; 32 import android.hardware.biometrics.BiometricsProtoEnums; 33 import android.os.IBinder; 34 import android.os.RemoteException; 35 import android.os.SystemClock; 36 import android.security.KeyStore; 37 import android.util.EventLog; 38 import android.util.Slog; 39 40 import com.android.server.biometrics.BiometricsProto; 41 import com.android.server.biometrics.Utils; 42 43 import java.util.ArrayList; 44 import java.util.List; 45 46 /** 47 * A class to keep track of the authentication state for a given client. 48 */ 49 public abstract class AuthenticationClient<T> extends AcquisitionClient<T> 50 implements AuthenticationConsumer { 51 52 private static final String TAG = "Biometrics/AuthenticationClient"; 53 54 // New, has not started yet 55 public static final int STATE_NEW = 0; 56 // Framework/HAL have started this operation 57 public static final int STATE_STARTED = 1; 58 // Operation is started, but requires some user action to start (such as finger lift & re-touch) 59 public static final int STATE_STARTED_PAUSED = 2; 60 // Same as above, except auth was attempted (rejected, timed out, etc). 61 public static final int STATE_STARTED_PAUSED_ATTEMPTED = 3; 62 // Done, errored, canceled, etc. HAL/framework are not running this sensor anymore. 63 public static final int STATE_STOPPED = 4; 64 65 @IntDef({STATE_NEW, 66 STATE_STARTED, 67 STATE_STARTED_PAUSED, 68 STATE_STARTED_PAUSED_ATTEMPTED, 69 STATE_STOPPED}) 70 @interface State {} 71 72 private final boolean mIsStrongBiometric; 73 private final boolean mRequireConfirmation; 74 private final ActivityTaskManager mActivityTaskManager; 75 private final BiometricManager mBiometricManager; 76 @Nullable private final TaskStackListener mTaskStackListener; 77 private final LockoutTracker mLockoutTracker; 78 private final boolean mIsRestricted; 79 private final boolean mAllowBackgroundAuthentication; 80 private final boolean mIsKeyguardBypassEnabled; 81 82 protected final long mOperationId; 83 84 private long mStartTimeMs; 85 86 private boolean mAuthAttempted; 87 88 // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update 89 // the state. We should think of a way to improve this in the future. 90 protected @State int mState = STATE_NEW; 91 92 /** 93 * Handles lifecycle, e.g. {@link BiometricScheduler}, 94 * {@link com.android.server.biometrics.sensors.BaseClientMonitor.Callback} after authentication 95 * results are known. Note that this happens asynchronously from (but shortly after) 96 * {@link #onAuthenticated(BiometricAuthenticator.Identifier, boolean, ArrayList)} and allows 97 * {@link CoexCoordinator} a chance to invoke/delay this event. 98 * @param authenticated 99 */ handleLifecycleAfterAuth(boolean authenticated)100 protected abstract void handleLifecycleAfterAuth(boolean authenticated); 101 102 /** 103 * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched. 104 * etc) 105 */ wasUserDetected()106 public abstract boolean wasUserDetected(); 107 AuthenticationClient(@onNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication, boolean shouldVibrate, boolean isKeyguardBypassEnabled)108 public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, 109 @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, 110 int targetUserId, long operationId, boolean restricted, @NonNull String owner, 111 int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, 112 int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener, 113 @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication, 114 boolean shouldVibrate, boolean isKeyguardBypassEnabled) { 115 super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId, 116 shouldVibrate, statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, 117 statsClient); 118 mIsStrongBiometric = isStrongBiometric; 119 mOperationId = operationId; 120 mRequireConfirmation = requireConfirmation; 121 mActivityTaskManager = ActivityTaskManager.getInstance(); 122 mBiometricManager = context.getSystemService(BiometricManager.class); 123 mTaskStackListener = taskStackListener; 124 mLockoutTracker = lockoutTracker; 125 mIsRestricted = restricted; 126 mAllowBackgroundAuthentication = allowBackgroundAuthentication; 127 mIsKeyguardBypassEnabled = isKeyguardBypassEnabled; 128 } 129 handleFailedAttempt(int userId)130 public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) { 131 final @LockoutTracker.LockoutMode int lockoutMode = 132 mLockoutTracker.getLockoutModeForUser(userId); 133 final PerformanceTracker performanceTracker = 134 PerformanceTracker.getInstanceForSensorId(getSensorId()); 135 136 if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) { 137 performanceTracker.incrementPermanentLockoutForUser(userId); 138 } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) { 139 performanceTracker.incrementTimedLockoutForUser(userId); 140 } 141 142 return lockoutMode; 143 } 144 getStartTimeMs()145 protected long getStartTimeMs() { 146 return mStartTimeMs; 147 } 148 149 @Override binderDied()150 public void binderDied() { 151 final boolean clearListener = !isBiometricPrompt(); 152 binderDiedInternal(clearListener); 153 } 154 isBiometricPrompt()155 public boolean isBiometricPrompt() { 156 return getCookie() != 0; 157 } 158 getOperationId()159 public long getOperationId() { 160 return mOperationId; 161 } 162 isRestricted()163 public boolean isRestricted() { 164 return mIsRestricted; 165 } 166 isKeyguard()167 public boolean isKeyguard() { 168 return Utils.isKeyguard(getContext(), getOwnerString()); 169 } 170 isSettings()171 private boolean isSettings() { 172 return Utils.isSettings(getContext(), getOwnerString()); 173 } 174 175 @Override isCryptoOperation()176 protected boolean isCryptoOperation() { 177 return mOperationId != 0; 178 } 179 180 @Override onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> hardwareAuthToken)181 public void onAuthenticated(BiometricAuthenticator.Identifier identifier, 182 boolean authenticated, ArrayList<Byte> hardwareAuthToken) { 183 super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation, 184 getTargetUserId(), isBiometricPrompt()); 185 186 final ClientMonitorCallbackConverter listener = getListener(); 187 188 if (DEBUG) Slog.v(TAG, "onAuthenticated(" + authenticated + ")" 189 + ", ID:" + identifier.getBiometricId() 190 + ", Owner: " + getOwnerString() 191 + ", isBP: " + isBiometricPrompt() 192 + ", listener: " + listener 193 + ", requireConfirmation: " + mRequireConfirmation 194 + ", user: " + getTargetUserId() 195 + ", clientMonitor: " + toString()); 196 197 final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId()); 198 if (isCryptoOperation()) { 199 pm.incrementCryptoAuthForUser(getTargetUserId(), authenticated); 200 } else { 201 pm.incrementAuthForUser(getTargetUserId(), authenticated); 202 } 203 204 if (mAllowBackgroundAuthentication) { 205 Slog.w(TAG, "Allowing background authentication," 206 + " this is allowed only for platform or test invocations"); 207 } 208 209 // Ensure authentication only succeeds if the client activity is on top. 210 boolean isBackgroundAuth = false; 211 if (!mAllowBackgroundAuthentication && authenticated 212 && !Utils.isKeyguard(getContext(), getOwnerString()) 213 && !Utils.isSystem(getContext(), getOwnerString())) { 214 final List<ActivityManager.RunningTaskInfo> tasks = 215 mActivityTaskManager.getTasks(1); 216 if (tasks == null || tasks.isEmpty()) { 217 Slog.e(TAG, "No running tasks reported"); 218 isBackgroundAuth = true; 219 } else { 220 final ComponentName topActivity = tasks.get(0).topActivity; 221 if (topActivity == null) { 222 Slog.e(TAG, "Unable to get top activity"); 223 isBackgroundAuth = true; 224 } else { 225 final String topPackage = topActivity.getPackageName(); 226 if (!topPackage.contentEquals(getOwnerString())) { 227 Slog.e(TAG, "Background authentication detected, top: " + topPackage 228 + ", client: " + getOwnerString()); 229 isBackgroundAuth = true; 230 } 231 } 232 } 233 } 234 235 // Fail authentication if we can't confirm the client activity is on top. 236 if (isBackgroundAuth) { 237 Slog.e(TAG, "Failing possible background authentication"); 238 authenticated = false; 239 240 // SafetyNet logging for exploitation attempts of b/159249069. 241 final ApplicationInfo appInfo = getContext().getApplicationInfo(); 242 EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1, 243 "Attempted background authentication"); 244 } 245 246 if (authenticated) { 247 // SafetyNet logging for b/159249069 if constraint is violated. 248 if (isBackgroundAuth) { 249 final ApplicationInfo appInfo = getContext().getApplicationInfo(); 250 EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1, 251 "Successful background authentication!"); 252 } 253 254 markAlreadyDone(); 255 256 if (mTaskStackListener != null) { 257 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); 258 } 259 260 final byte[] byteToken = new byte[hardwareAuthToken.size()]; 261 for (int i = 0; i < hardwareAuthToken.size(); i++) { 262 byteToken[i] = hardwareAuthToken.get(i); 263 } 264 265 if (mIsStrongBiometric) { 266 mBiometricManager.resetLockoutTimeBound(getToken(), 267 getContext().getOpPackageName(), 268 getSensorId(), getTargetUserId(), byteToken); 269 } 270 271 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 272 coordinator.onAuthenticationSucceeded(SystemClock.uptimeMillis(), this, 273 new CoexCoordinator.Callback() { 274 @Override 275 public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { 276 if (addAuthTokenIfStrong && mIsStrongBiometric) { 277 final int result = KeyStore.getInstance().addAuthToken(byteToken); 278 Slog.d(TAG, "addAuthToken: " + result); 279 } else { 280 Slog.d(TAG, "Skipping addAuthToken"); 281 } 282 283 if (listener != null) { 284 try { 285 // Explicitly have if/else here to make it super obvious in case the 286 // code is touched in the future. 287 if (!mIsRestricted) { 288 listener.onAuthenticationSucceeded(getSensorId(), 289 identifier, 290 byteToken, 291 getTargetUserId(), 292 mIsStrongBiometric); 293 } else { 294 listener.onAuthenticationSucceeded(getSensorId(), 295 null /* identifier */, 296 byteToken, 297 getTargetUserId(), 298 mIsStrongBiometric); 299 } 300 } catch (RemoteException e) { 301 Slog.e(TAG, "Unable to notify listener", e); 302 } 303 } else { 304 Slog.w(TAG, "Client not listening"); 305 } 306 } 307 308 @Override 309 public void sendHapticFeedback() { 310 if (listener != null && mShouldVibrate) { 311 vibrateSuccess(); 312 } 313 } 314 315 @Override 316 public void handleLifecycleAfterAuth() { 317 AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */); 318 } 319 320 @Override 321 public void sendAuthenticationCanceled() { 322 sendCancelOnly(listener); 323 } 324 }); 325 } else { 326 // Allow system-defined limit of number of attempts before giving up 327 final @LockoutTracker.LockoutMode int lockoutMode = 328 handleFailedAttempt(getTargetUserId()); 329 if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { 330 markAlreadyDone(); 331 } 332 333 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 334 coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode, 335 new CoexCoordinator.Callback() { 336 @Override 337 public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { 338 if (listener != null) { 339 try { 340 listener.onAuthenticationFailed(getSensorId()); 341 } catch (RemoteException e) { 342 Slog.e(TAG, "Unable to notify listener", e); 343 } 344 } 345 } 346 347 @Override 348 public void sendHapticFeedback() { 349 if (listener != null && mShouldVibrate) { 350 vibrateError(); 351 } 352 } 353 354 @Override 355 public void handleLifecycleAfterAuth() { 356 AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */); 357 } 358 359 @Override 360 public void sendAuthenticationCanceled() { 361 sendCancelOnly(listener); 362 } 363 }); 364 } 365 } 366 367 /** 368 * Only call this method on interfaces where lockout does not come from onError, I.E. the 369 * old HIDL implementation. 370 */ onLockoutTimed(long durationMillis)371 protected void onLockoutTimed(long durationMillis) { 372 final ClientMonitorCallbackConverter listener = getListener(); 373 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 374 coordinator.onAuthenticationError(this, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, 375 new CoexCoordinator.ErrorCallback() { 376 @Override 377 public void sendHapticFeedback() { 378 if (listener != null && mShouldVibrate) { 379 vibrateError(); 380 } 381 } 382 }); 383 } 384 385 /** 386 * Only call this method on interfaces where lockout does not come from onError, I.E. the 387 * old HIDL implementation. 388 */ onLockoutPermanent()389 protected void onLockoutPermanent() { 390 final ClientMonitorCallbackConverter listener = getListener(); 391 final CoexCoordinator coordinator = CoexCoordinator.getInstance(); 392 coordinator.onAuthenticationError(this, 393 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT, 394 new CoexCoordinator.ErrorCallback() { 395 @Override 396 public void sendHapticFeedback() { 397 if (listener != null && mShouldVibrate) { 398 vibrateError(); 399 } 400 } 401 }); 402 } 403 sendCancelOnly(@ullable ClientMonitorCallbackConverter listener)404 private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) { 405 if (listener == null) { 406 Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null"); 407 return; 408 } 409 try { 410 listener.onError(getSensorId(), 411 getCookie(), 412 BiometricConstants.BIOMETRIC_ERROR_CANCELED, 413 0 /* vendorCode */); 414 } catch (RemoteException e) { 415 Slog.e(TAG, "Remote exception", e); 416 } 417 } 418 419 @Override onAcquired(int acquiredInfo, int vendorCode)420 public void onAcquired(int acquiredInfo, int vendorCode) { 421 super.onAcquired(acquiredInfo, vendorCode); 422 423 final @LockoutTracker.LockoutMode int lockoutMode = 424 mLockoutTracker.getLockoutModeForUser(getTargetUserId()); 425 if (lockoutMode == LockoutTracker.LOCKOUT_NONE) { 426 PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId()); 427 pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation()); 428 } 429 } 430 431 @Override onError(@iometricConstants.Errors int errorCode, int vendorCode)432 public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) { 433 super.onError(errorCode, vendorCode); 434 mState = STATE_STOPPED; 435 436 CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError); 437 } 438 439 /** 440 * Start authentication 441 */ 442 @Override start(@onNull Callback callback)443 public void start(@NonNull Callback callback) { 444 super.start(callback); 445 446 final @LockoutTracker.LockoutMode int lockoutMode = 447 mLockoutTracker.getLockoutModeForUser(getTargetUserId()); 448 if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { 449 Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing authentication"); 450 int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED 451 ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT 452 : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; 453 onError(errorCode, 0 /* vendorCode */); 454 return; 455 } 456 457 if (mTaskStackListener != null) { 458 mActivityTaskManager.registerTaskStackListener(mTaskStackListener); 459 } 460 461 Slog.d(TAG, "Requesting auth for " + getOwnerString()); 462 463 mStartTimeMs = System.currentTimeMillis(); 464 mAuthAttempted = true; 465 startHalOperation(); 466 } 467 468 @Override cancel()469 public void cancel() { 470 super.cancel(); 471 472 if (mTaskStackListener != null) { 473 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); 474 } 475 } 476 getState()477 public @State int getState() { 478 return mState; 479 } 480 481 /** 482 * @return true if the client supports bypass (e.g. passive auth such as face), and if it's 483 * enabled by the user. 484 */ isKeyguardBypassEnabled()485 public boolean isKeyguardBypassEnabled() { 486 return mIsKeyguardBypassEnabled; 487 } 488 489 @Override getProtoEnum()490 public int getProtoEnum() { 491 return BiometricsProto.CM_AUTHENTICATE; 492 } 493 494 @Override interruptsPrecedingClients()495 public boolean interruptsPrecedingClients() { 496 return true; 497 } 498 wasAuthAttempted()499 public boolean wasAuthAttempted() { 500 return mAuthAttempted; 501 } 502 getShowOverlayReason()503 protected int getShowOverlayReason() { 504 if (isKeyguard()) { 505 return BiometricOverlayConstants.REASON_AUTH_KEYGUARD; 506 } else if (isBiometricPrompt()) { 507 // BP reason always takes precedent over settings, since callers from within 508 // settings can always invoke BP. 509 return BiometricOverlayConstants.REASON_AUTH_BP; 510 } else if (isSettings()) { 511 // This is pretty much only for FingerprintManager#authenticate usage from 512 // FingerprintSettings. 513 return BiometricOverlayConstants.REASON_AUTH_SETTINGS; 514 } else { 515 return BiometricOverlayConstants.REASON_AUTH_OTHER; 516 } 517 } 518 } 519