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.content.Context; 23 import android.hardware.biometrics.BiometricConstants; 24 import android.hardware.biometrics.IBiometricService; 25 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.RemoteException; 30 import android.os.ServiceManager; 31 import android.util.Slog; 32 import android.util.proto.ProtoOutputStream; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.server.biometrics.BiometricSchedulerProto; 36 import com.android.server.biometrics.BiometricsProto; 37 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; 38 39 import java.io.PrintWriter; 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 import java.text.SimpleDateFormat; 43 import java.util.ArrayDeque; 44 import java.util.ArrayList; 45 import java.util.Date; 46 import java.util.Deque; 47 import java.util.List; 48 import java.util.Locale; 49 50 /** 51 * A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor} 52 * operations, without caring about its implementation details. Operations may perform zero or more 53 * interactions with the HAL before finishing. 54 * 55 * We currently assume (and require) that each biometric sensor have its own instance of a 56 * {@link BiometricScheduler}. See {@link CoexCoordinator}. 57 */ 58 public class BiometricScheduler { 59 60 private static final String BASE_TAG = "BiometricScheduler"; 61 // Number of recent operations to keep in our logs for dumpsys 62 protected static final int LOG_NUM_RECENT_OPERATIONS = 50; 63 64 /** 65 * Unknown sensor type. This should never be used, and is a sign that something is wrong during 66 * initialization. 67 */ 68 public static final int SENSOR_TYPE_UNKNOWN = 0; 69 70 /** 71 * Face authentication. 72 */ 73 public static final int SENSOR_TYPE_FACE = 1; 74 75 /** 76 * Any UDFPS type. See {@link FingerprintSensorPropertiesInternal#isAnyUdfpsType()}. 77 */ 78 public static final int SENSOR_TYPE_UDFPS = 2; 79 80 /** 81 * Any other fingerprint sensor. We can add additional definitions in the future when necessary. 82 */ 83 public static final int SENSOR_TYPE_FP_OTHER = 3; 84 85 @IntDef({SENSOR_TYPE_UNKNOWN, SENSOR_TYPE_FACE, SENSOR_TYPE_UDFPS, SENSOR_TYPE_FP_OTHER}) 86 @Retention(RetentionPolicy.SOURCE) 87 public @interface SensorType {} 88 sensorTypeFromFingerprintProperties( @onNull FingerprintSensorPropertiesInternal props)89 public static @SensorType int sensorTypeFromFingerprintProperties( 90 @NonNull FingerprintSensorPropertiesInternal props) { 91 if (props.isAnyUdfpsType()) { 92 return SENSOR_TYPE_UDFPS; 93 } 94 95 return SENSOR_TYPE_FP_OTHER; 96 } 97 sensorTypeToString(@ensorType int sensorType)98 public static String sensorTypeToString(@SensorType int sensorType) { 99 switch (sensorType) { 100 case SENSOR_TYPE_UNKNOWN: 101 return "Unknown"; 102 case SENSOR_TYPE_FACE: 103 return "Face"; 104 case SENSOR_TYPE_UDFPS: 105 return "Udfps"; 106 case SENSOR_TYPE_FP_OTHER: 107 return "OtherFp"; 108 default: 109 return "UnknownUnknown"; 110 } 111 } 112 113 /** 114 * Contains all the necessary information for a HAL operation. 115 */ 116 @VisibleForTesting 117 static final class Operation { 118 119 /** 120 * The operation is added to the list of pending operations and waiting for its turn. 121 */ 122 static final int STATE_WAITING_IN_QUEUE = 0; 123 124 /** 125 * The operation is added to the list of pending operations, but a subsequent operation 126 * has been added. This state only applies to {@link Interruptable} operations. When this 127 * operation reaches the head of the queue, it will send ERROR_CANCELED and finish. 128 */ 129 static final int STATE_WAITING_IN_QUEUE_CANCELING = 1; 130 131 /** 132 * The operation has reached the front of the queue and has started. 133 */ 134 static final int STATE_STARTED = 2; 135 136 /** 137 * The operation was started, but is now canceling. Operations should wait for the HAL to 138 * acknowledge that the operation was canceled, at which point it finishes. 139 */ 140 static final int STATE_STARTED_CANCELING = 3; 141 142 /** 143 * The operation has reached the head of the queue but is waiting for BiometricService 144 * to acknowledge and start the operation. 145 */ 146 static final int STATE_WAITING_FOR_COOKIE = 4; 147 148 /** 149 * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished. 150 */ 151 static final int STATE_FINISHED = 5; 152 153 @IntDef({STATE_WAITING_IN_QUEUE, 154 STATE_WAITING_IN_QUEUE_CANCELING, 155 STATE_STARTED, 156 STATE_STARTED_CANCELING, 157 STATE_WAITING_FOR_COOKIE, 158 STATE_FINISHED}) 159 @Retention(RetentionPolicy.SOURCE) 160 @interface OperationState {} 161 162 @NonNull final BaseClientMonitor mClientMonitor; 163 @Nullable final BaseClientMonitor.Callback mClientCallback; 164 @OperationState int mState; 165 Operation( @onNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback callback )166 Operation( 167 @NonNull BaseClientMonitor clientMonitor, 168 @Nullable BaseClientMonitor.Callback callback 169 ) { 170 this(clientMonitor, callback, STATE_WAITING_IN_QUEUE); 171 } 172 Operation( @onNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback callback, @OperationState int state )173 protected Operation( 174 @NonNull BaseClientMonitor clientMonitor, 175 @Nullable BaseClientMonitor.Callback callback, 176 @OperationState int state 177 ) { 178 mClientMonitor = clientMonitor; 179 mClientCallback = callback; 180 mState = state; 181 } 182 isHalOperation()183 public boolean isHalOperation() { 184 return mClientMonitor instanceof HalClientMonitor<?>; 185 } 186 187 /** 188 * @return true if the operation requires the HAL, and the HAL is null. 189 */ isUnstartableHalOperation()190 public boolean isUnstartableHalOperation() { 191 if (isHalOperation()) { 192 final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor; 193 if (client.getFreshDaemon() == null) { 194 return true; 195 } 196 } 197 return false; 198 } 199 200 @Override toString()201 public String toString() { 202 return mClientMonitor + ", State: " + mState; 203 } 204 } 205 206 /** 207 * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will 208 * kill the current operation and forcibly start the next. 209 */ 210 private static final class CancellationWatchdog implements Runnable { 211 static final int DELAY_MS = 3000; 212 213 final String tag; 214 final Operation operation; CancellationWatchdog(String tag, Operation operation)215 CancellationWatchdog(String tag, Operation operation) { 216 this.tag = tag; 217 this.operation = operation; 218 } 219 220 @Override run()221 public void run() { 222 if (operation.mState != Operation.STATE_FINISHED) { 223 Slog.e(tag, "[Watchdog Triggered]: " + operation); 224 operation.mClientMonitor.mCallback 225 .onClientFinished(operation.mClientMonitor, false /* success */); 226 } 227 } 228 } 229 230 private static final class CrashState { 231 static final int NUM_ENTRIES = 10; 232 final String timestamp; 233 final String currentOperation; 234 final List<String> pendingOperations; 235 CrashState(String timestamp, String currentOperation, List<String> pendingOperations)236 CrashState(String timestamp, String currentOperation, List<String> pendingOperations) { 237 this.timestamp = timestamp; 238 this.currentOperation = currentOperation; 239 this.pendingOperations = pendingOperations; 240 } 241 242 @Override toString()243 public String toString() { 244 final StringBuilder sb = new StringBuilder(); 245 sb.append(timestamp).append(": "); 246 sb.append("Current Operation: {").append(currentOperation).append("}"); 247 sb.append(", Pending Operations(").append(pendingOperations.size()).append(")"); 248 249 if (!pendingOperations.isEmpty()) { 250 sb.append(": "); 251 } 252 for (int i = 0; i < pendingOperations.size(); i++) { 253 sb.append(pendingOperations.get(i)); 254 if (i < pendingOperations.size() - 1) { 255 sb.append(", "); 256 } 257 } 258 return sb.toString(); 259 } 260 } 261 262 @NonNull protected final String mBiometricTag; 263 private final @SensorType int mSensorType; 264 @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; 265 @NonNull private final IBiometricService mBiometricService; 266 @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper()); 267 @NonNull private final InternalCallback mInternalCallback; 268 @VisibleForTesting @NonNull final Deque<Operation> mPendingOperations; 269 @VisibleForTesting @Nullable Operation mCurrentOperation; 270 @NonNull private final ArrayDeque<CrashState> mCrashStates; 271 272 private int mTotalOperationsHandled; 273 private final int mRecentOperationsLimit; 274 @NonNull private final List<Integer> mRecentOperations; 275 @NonNull private final CoexCoordinator mCoexCoordinator; 276 277 // Internal callback, notified when an operation is complete. Notifies the requester 278 // that the operation is complete, before performing internal scheduler work (such as 279 // starting the next client). 280 public class InternalCallback implements BaseClientMonitor.Callback { 281 @Override onClientStarted(@onNull BaseClientMonitor clientMonitor)282 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { 283 Slog.d(getTag(), "[Started] " + clientMonitor); 284 285 if (clientMonitor instanceof AuthenticationClient) { 286 mCoexCoordinator.addAuthenticationClient(mSensorType, 287 (AuthenticationClient<?>) clientMonitor); 288 } 289 290 if (mCurrentOperation.mClientCallback != null) { 291 mCurrentOperation.mClientCallback.onClientStarted(clientMonitor); 292 } 293 } 294 295 @Override onClientFinished(@onNull BaseClientMonitor clientMonitor, boolean success)296 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { 297 mHandler.post(() -> { 298 if (mCurrentOperation == null) { 299 Slog.e(getTag(), "[Finishing] " + clientMonitor 300 + " but current operation is null, success: " + success 301 + ", possible lifecycle bug in clientMonitor implementation?"); 302 return; 303 } 304 305 if (clientMonitor != mCurrentOperation.mClientMonitor) { 306 Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match" 307 + " current: " + mCurrentOperation.mClientMonitor); 308 return; 309 } 310 311 Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success); 312 if (clientMonitor instanceof AuthenticationClient) { 313 mCoexCoordinator.removeAuthenticationClient(mSensorType, 314 (AuthenticationClient<?>) clientMonitor); 315 } 316 317 mCurrentOperation.mState = Operation.STATE_FINISHED; 318 319 if (mCurrentOperation.mClientCallback != null) { 320 mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success); 321 } 322 323 if (mGestureAvailabilityDispatcher != null) { 324 mGestureAvailabilityDispatcher.markSensorActive( 325 mCurrentOperation.mClientMonitor.getSensorId(), false /* active */); 326 } 327 328 if (mRecentOperations.size() >= mRecentOperationsLimit) { 329 mRecentOperations.remove(0); 330 } 331 mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum()); 332 mCurrentOperation = null; 333 mTotalOperationsHandled++; 334 startNextOperationIfIdle(); 335 }); 336 } 337 } 338 339 @VisibleForTesting BiometricScheduler(@onNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull IBiometricService biometricService, int recentOperationsLimit, @NonNull CoexCoordinator coexCoordinator)340 BiometricScheduler(@NonNull String tag, @SensorType int sensorType, 341 @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, 342 @NonNull IBiometricService biometricService, int recentOperationsLimit, 343 @NonNull CoexCoordinator coexCoordinator) { 344 mBiometricTag = tag; 345 mSensorType = sensorType; 346 mInternalCallback = new InternalCallback(); 347 mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; 348 mPendingOperations = new ArrayDeque<>(); 349 mBiometricService = biometricService; 350 mCrashStates = new ArrayDeque<>(); 351 mRecentOperationsLimit = recentOperationsLimit; 352 mRecentOperations = new ArrayList<>(); 353 mCoexCoordinator = coexCoordinator; 354 } 355 356 /** 357 * Creates a new scheduler. 358 * @param tag for the specific instance of the scheduler. Should be unique. 359 * @param sensorType the sensorType that this scheduler is handling. 360 * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures 361 * (such as fingerprint swipe). 362 */ BiometricScheduler(@onNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher)363 public BiometricScheduler(@NonNull String tag, 364 @SensorType int sensorType, 365 @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { 366 this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( 367 ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS, 368 CoexCoordinator.getInstance()); 369 } 370 371 /** 372 * @return A reference to the internal callback that should be invoked whenever the scheduler 373 * needs to (e.g. client started, client finished). 374 */ getInternalCallback()375 @NonNull protected InternalCallback getInternalCallback() { 376 return mInternalCallback; 377 } 378 getTag()379 protected String getTag() { 380 return BASE_TAG + "/" + mBiometricTag; 381 } 382 startNextOperationIfIdle()383 protected void startNextOperationIfIdle() { 384 if (mCurrentOperation != null) { 385 Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation); 386 return; 387 } 388 if (mPendingOperations.isEmpty()) { 389 Slog.d(getTag(), "No operations, returning to idle"); 390 return; 391 } 392 393 mCurrentOperation = mPendingOperations.poll(); 394 final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor; 395 Slog.d(getTag(), "[Polled] " + mCurrentOperation); 396 397 // If the operation at the front of the queue has been marked for cancellation, send 398 // ERROR_CANCELED. No need to start this client. 399 if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) { 400 Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation); 401 if (!(currentClient instanceof Interruptable)) { 402 throw new IllegalStateException("Mis-implemented client or scheduler, " 403 + "trying to cancel non-interruptable operation: " + mCurrentOperation); 404 } 405 406 final Interruptable interruptable = (Interruptable) currentClient; 407 interruptable.cancelWithoutStarting(getInternalCallback()); 408 // Now we wait for the client to send its FinishCallback, which kicks off the next 409 // operation. 410 return; 411 } 412 413 if (mGestureAvailabilityDispatcher != null 414 && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) { 415 mGestureAvailabilityDispatcher.markSensorActive( 416 mCurrentOperation.mClientMonitor.getSensorId(), 417 true /* active */); 418 } 419 420 // Not all operations start immediately. BiometricPrompt waits for its operation 421 // to arrive at the head of the queue, before pinging it to start. 422 final boolean shouldStartNow = currentClient.getCookie() == 0; 423 if (shouldStartNow) { 424 if (mCurrentOperation.isUnstartableHalOperation()) { 425 final HalClientMonitor<?> halClientMonitor = 426 (HalClientMonitor<?>) mCurrentOperation.mClientMonitor; 427 // Note down current length of queue 428 final int pendingOperationsLength = mPendingOperations.size(); 429 final Operation lastOperation = mPendingOperations.peekLast(); 430 Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation 431 + ". Last pending operation: " + lastOperation); 432 433 // For current operations, 1) unableToStart, which notifies the caller-side, then 434 // 2) notify operation's callback, to notify applicable system service that the 435 // operation failed. 436 halClientMonitor.unableToStart(); 437 if (mCurrentOperation.mClientCallback != null) { 438 mCurrentOperation.mClientCallback.onClientFinished( 439 mCurrentOperation.mClientMonitor, false /* success */); 440 } 441 442 // Then for each operation currently in the pending queue at the time of this 443 // failure, do the same as above. Otherwise, it's possible that something like 444 // setActiveUser fails, but then authenticate (for the wrong user) is invoked. 445 for (int i = 0; i < pendingOperationsLength; i++) { 446 final Operation operation = mPendingOperations.pollFirst(); 447 if (operation == null) { 448 Slog.e(getTag(), "Null operation, index: " + i 449 + ", expected length: " + pendingOperationsLength); 450 break; 451 } 452 if (operation.isHalOperation()) { 453 ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart(); 454 } 455 if (operation.mClientCallback != null) { 456 operation.mClientCallback.onClientFinished(operation.mClientMonitor, 457 false /* success */); 458 } 459 Slog.w(getTag(), "[Aborted Operation] " + operation); 460 } 461 462 // It's possible that during cleanup a new set of operations came in. We can try to 463 // run these. A single request from the manager layer to the service layer may 464 // actually be multiple operations (i.e. updateActiveUser + authenticate). 465 mCurrentOperation = null; 466 startNextOperationIfIdle(); 467 } else { 468 Slog.d(getTag(), "[Starting] " + mCurrentOperation); 469 currentClient.start(getInternalCallback()); 470 mCurrentOperation.mState = Operation.STATE_STARTED; 471 } 472 } else { 473 try { 474 mBiometricService.onReadyForAuthentication(currentClient.getCookie()); 475 } catch (RemoteException e) { 476 Slog.e(getTag(), "Remote exception when contacting BiometricService", e); 477 } 478 Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation); 479 mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE; 480 } 481 } 482 483 /** 484 * Starts the {@link #mCurrentOperation} if 485 * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and 486 * 2) its cookie matches this cookie 487 * 488 * This is currently only used by {@link com.android.server.biometrics.BiometricService}, which 489 * requests sensors to prepare for authentication with a cookie. Once sensor(s) are ready (e.g. 490 * the BiometricService client becomes the current client in the scheduler), the cookie is 491 * returned to BiometricService. Once BiometricService decides that authentication can start, 492 * it invokes this code path. 493 * 494 * @param cookie of the operation to be started 495 */ startPreparedClient(int cookie)496 public void startPreparedClient(int cookie) { 497 if (mCurrentOperation == null) { 498 Slog.e(getTag(), "Current operation is null"); 499 return; 500 } 501 if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) { 502 if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) { 503 Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: " 504 + mCurrentOperation); 505 // This should trigger the internal onClientFinished callback, which clears the 506 // operation and starts the next one. 507 final ErrorConsumer errorConsumer = 508 (ErrorConsumer) mCurrentOperation.mClientMonitor; 509 errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, 510 0 /* vendorCode */); 511 return; 512 } else { 513 Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation 514 + ", expected STATE_WAITING_FOR_COOKIE"); 515 return; 516 } 517 } 518 if (mCurrentOperation.mClientMonitor.getCookie() != cookie) { 519 Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation 520 + ", received: " + cookie); 521 return; 522 } 523 524 if (mCurrentOperation.isUnstartableHalOperation()) { 525 Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation); 526 // This is BiometricPrompt trying to auth but something's wrong with the HAL. 527 final HalClientMonitor<?> halClientMonitor = 528 (HalClientMonitor<?>) mCurrentOperation.mClientMonitor; 529 halClientMonitor.unableToStart(); 530 if (mCurrentOperation.mClientCallback != null) { 531 mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor, 532 false /* success */); 533 } 534 mCurrentOperation = null; 535 startNextOperationIfIdle(); 536 } else { 537 Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation); 538 mCurrentOperation.mState = Operation.STATE_STARTED; 539 mCurrentOperation.mClientMonitor.start(getInternalCallback()); 540 } 541 } 542 543 /** 544 * Adds a {@link BaseClientMonitor} to the pending queue 545 * 546 * @param clientMonitor operation to be scheduled 547 */ scheduleClientMonitor(@onNull BaseClientMonitor clientMonitor)548 public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor) { 549 scheduleClientMonitor(clientMonitor, null /* clientFinishCallback */); 550 } 551 552 /** 553 * Adds a {@link BaseClientMonitor} to the pending queue 554 * 555 * @param clientMonitor operation to be scheduled 556 * @param clientCallback optional callback, invoked when the client state changes. 557 */ scheduleClientMonitor(@onNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback clientCallback)558 public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor, 559 @Nullable BaseClientMonitor.Callback clientCallback) { 560 // If the incoming operation should interrupt preceding clients, mark any interruptable 561 // pending clients as canceling. Once they reach the head of the queue, the scheduler will 562 // send ERROR_CANCELED and skip the operation. 563 if (clientMonitor.interruptsPrecedingClients()) { 564 for (Operation operation : mPendingOperations) { 565 if (operation.mClientMonitor instanceof Interruptable 566 && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) { 567 Slog.d(getTag(), "New client incoming, marking pending client as canceling: " 568 + operation.mClientMonitor); 569 operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; 570 } 571 } 572 } 573 574 mPendingOperations.add(new Operation(clientMonitor, clientCallback)); 575 Slog.d(getTag(), "[Added] " + clientMonitor 576 + ", new queue size: " + mPendingOperations.size()); 577 578 // If the new operation should interrupt preceding clients, and if the current operation is 579 // cancellable, start the cancellation process. 580 if (clientMonitor.interruptsPrecedingClients() 581 && mCurrentOperation != null 582 && mCurrentOperation.mClientMonitor instanceof Interruptable 583 && mCurrentOperation.mState == Operation.STATE_STARTED) { 584 Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation); 585 cancelInternal(mCurrentOperation); 586 } 587 588 startNextOperationIfIdle(); 589 } 590 cancelInternal(Operation operation)591 private void cancelInternal(Operation operation) { 592 if (operation != mCurrentOperation) { 593 Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation); 594 return; 595 } 596 if (!(operation.mClientMonitor instanceof Interruptable)) { 597 Slog.w(getTag(), "Operation not interruptable: " + operation); 598 return; 599 } 600 if (operation.mState == Operation.STATE_STARTED_CANCELING) { 601 Slog.w(getTag(), "Cancel already invoked for operation: " + operation); 602 return; 603 } 604 if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) { 605 Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation); 606 // We can set it to null immediately, since the HAL was never notified to start. 607 mCurrentOperation = null; 608 startNextOperationIfIdle(); 609 return; 610 } 611 Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor); 612 final Interruptable interruptable = (Interruptable) operation.mClientMonitor; 613 interruptable.cancel(); 614 operation.mState = Operation.STATE_STARTED_CANCELING; 615 616 // Add a watchdog. If the HAL does not acknowledge within the timeout, we will 617 // forcibly finish this client. 618 mHandler.postDelayed(new CancellationWatchdog(getTag(), operation), 619 CancellationWatchdog.DELAY_MS); 620 } 621 622 /** 623 * Requests to cancel enrollment. 624 * @param token from the caller, should match the token passed in when requesting enrollment 625 */ cancelEnrollment(IBinder token)626 public void cancelEnrollment(IBinder token) { 627 if (mCurrentOperation == null) { 628 Slog.e(getTag(), "Unable to cancel enrollment, null operation"); 629 return; 630 } 631 final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient; 632 final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token; 633 if (!isEnrolling || !tokenMatches) { 634 Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling 635 + " tokenMatches: " + tokenMatches); 636 return; 637 } 638 639 cancelInternal(mCurrentOperation); 640 } 641 642 /** 643 * Requests to cancel authentication or detection. 644 * @param token from the caller, should match the token passed in when requesting authentication 645 */ cancelAuthenticationOrDetection(IBinder token)646 public void cancelAuthenticationOrDetection(IBinder token) { 647 if (mCurrentOperation == null) { 648 Slog.e(getTag(), "Unable to cancel authentication, null operation"); 649 return; 650 } 651 final boolean isCorrectClient = isAuthenticationOrDetectionOperation(mCurrentOperation); 652 final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token; 653 654 Slog.d(getTag(), "cancelAuthenticationOrDetection, isCorrectClient: " + isCorrectClient 655 + ", tokenMatches: " + tokenMatches); 656 657 if (isCorrectClient && tokenMatches) { 658 Slog.d(getTag(), "Cancelling: " + mCurrentOperation); 659 cancelInternal(mCurrentOperation); 660 } else if (!isCorrectClient) { 661 // Look through the current queue for all authentication clients for the specified 662 // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking 663 // all of them, instead of just the first one, since the API surface currently doesn't 664 // allow us to distinguish between multiple authentication requests from the same 665 // process. However, this generally does not happen anyway, and would be a class of 666 // bugs on its own. 667 for (Operation operation : mPendingOperations) { 668 if (isAuthenticationOrDetectionOperation(operation) 669 && operation.mClientMonitor.getToken() == token) { 670 Slog.d(getTag(), "Marking " + operation 671 + " as STATE_WAITING_IN_QUEUE_CANCELING"); 672 operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; 673 } 674 } 675 } 676 } 677 isAuthenticationOrDetectionOperation(@onNull Operation operation)678 private boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) { 679 final boolean isAuthentication = operation.mClientMonitor 680 instanceof AuthenticationConsumer; 681 final boolean isDetection = operation.mClientMonitor instanceof DetectionConsumer; 682 return isAuthentication || isDetection; 683 } 684 685 /** 686 * @return the current operation 687 */ getCurrentClient()688 public BaseClientMonitor getCurrentClient() { 689 if (mCurrentOperation == null) { 690 return null; 691 } 692 return mCurrentOperation.mClientMonitor; 693 } 694 getCurrentPendingCount()695 public int getCurrentPendingCount() { 696 return mPendingOperations.size(); 697 } 698 recordCrashState()699 public void recordCrashState() { 700 if (mCrashStates.size() >= CrashState.NUM_ENTRIES) { 701 mCrashStates.removeFirst(); 702 } 703 final SimpleDateFormat dateFormat = 704 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); 705 final String timestamp = dateFormat.format(new Date(System.currentTimeMillis())); 706 final List<String> pendingOperations = new ArrayList<>(); 707 for (Operation operation : mPendingOperations) { 708 pendingOperations.add(operation.toString()); 709 } 710 711 final CrashState crashState = new CrashState(timestamp, 712 mCurrentOperation != null ? mCurrentOperation.toString() : null, 713 pendingOperations); 714 mCrashStates.add(crashState); 715 Slog.e(getTag(), "Recorded crash state: " + crashState.toString()); 716 } 717 dump(PrintWriter pw)718 public void dump(PrintWriter pw) { 719 pw.println("Dump of BiometricScheduler " + getTag()); 720 pw.println("Type: " + mSensorType); 721 pw.println("Current operation: " + mCurrentOperation); 722 pw.println("Pending operations: " + mPendingOperations.size()); 723 for (Operation operation : mPendingOperations) { 724 pw.println("Pending operation: " + operation); 725 } 726 for (CrashState crashState : mCrashStates) { 727 pw.println("Crash State " + crashState); 728 } 729 } 730 dumpProtoState(boolean clearSchedulerBuffer)731 public byte[] dumpProtoState(boolean clearSchedulerBuffer) { 732 final ProtoOutputStream proto = new ProtoOutputStream(); 733 proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null 734 ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE); 735 proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled); 736 737 if (!mRecentOperations.isEmpty()) { 738 for (int i = 0; i < mRecentOperations.size(); i++) { 739 proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, mRecentOperations.get(i)); 740 } 741 } else { 742 // TODO:(b/178828362) Unsure why protobuf has a problem decoding when an empty list 743 // is returned. So, let's just add a no-op for this case. 744 proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, BiometricsProto.CM_NONE); 745 } 746 proto.flush(); 747 748 if (clearSchedulerBuffer) { 749 mRecentOperations.clear(); 750 } 751 return proto.getBytes(); 752 } 753 754 /** 755 * Clears the scheduler of anything work-related. This should be used for example when the 756 * HAL dies. 757 */ reset()758 public void reset() { 759 mPendingOperations.clear(); 760 mCurrentOperation = null; 761 } 762 } 763