1 /* 2 * Copyright (C) 2021 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.hardware.biometrics.BiometricConstants; 23 import android.os.Handler; 24 import android.os.IBinder; 25 import android.os.RemoteException; 26 import android.util.Slog; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.util.ArrayUtils; 30 import com.android.modules.expresslog.Counter; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.Arrays; 35 36 37 /** 38 * Contains all the necessary information for a HAL operation. 39 */ 40 public class BiometricSchedulerOperation { 41 protected static final String TAG = "BiometricSchedulerOperation"; 42 43 /** 44 * The operation is added to the list of pending operations and waiting for its turn. 45 */ 46 protected static final int STATE_WAITING_IN_QUEUE = 0; 47 48 /** 49 * The operation is added to the list of pending operations, but a subsequent operation 50 * has been added. This state only applies to interruptable operations. When this 51 * operation reaches the head of the queue, it will send ERROR_CANCELED and finish. 52 */ 53 protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1; 54 55 /** 56 * The operation has reached the front of the queue and has started. 57 */ 58 protected static final int STATE_STARTED = 2; 59 60 /** 61 * The operation was started, but is now canceling. Operations should wait for the HAL to 62 * acknowledge that the operation was canceled, at which point it finishes. 63 */ 64 protected static final int STATE_STARTED_CANCELING = 3; 65 66 /** 67 * The operation has reached the head of the queue but is waiting for BiometricService 68 * to acknowledge and start the operation. 69 */ 70 protected static final int STATE_WAITING_FOR_COOKIE = 4; 71 72 /** 73 * The {@link ClientMonitorCallback} has been invoked and the client is finished. 74 */ 75 protected static final int STATE_FINISHED = 5; 76 77 @IntDef({STATE_WAITING_IN_QUEUE, 78 STATE_WAITING_IN_QUEUE_CANCELING, 79 STATE_STARTED, 80 STATE_STARTED_CANCELING, 81 STATE_WAITING_FOR_COOKIE, 82 STATE_FINISHED}) 83 @Retention(RetentionPolicy.SOURCE) 84 protected @interface OperationState {} 85 86 private static final int CANCEL_WATCHDOG_DELAY_MS = 3000; 87 88 @NonNull 89 private final BaseClientMonitor mClientMonitor; 90 @Nullable 91 private final ClientMonitorCallback mClientCallback; 92 @Nullable 93 private ClientMonitorCallback mOnStartCallback; 94 @OperationState 95 private int mState; 96 @VisibleForTesting 97 @NonNull 98 final Runnable mCancelWatchdog; 99 100 @VisibleForTesting BiometricSchedulerOperation( @onNull BaseClientMonitor clientMonitor, @Nullable ClientMonitorCallback callback )101 BiometricSchedulerOperation( 102 @NonNull BaseClientMonitor clientMonitor, 103 @Nullable ClientMonitorCallback callback 104 ) { 105 this(clientMonitor, callback, STATE_WAITING_IN_QUEUE); 106 } 107 BiometricSchedulerOperation( @onNull BaseClientMonitor clientMonitor, @Nullable ClientMonitorCallback callback, @OperationState int state )108 protected BiometricSchedulerOperation( 109 @NonNull BaseClientMonitor clientMonitor, 110 @Nullable ClientMonitorCallback callback, 111 @OperationState int state 112 ) { 113 mClientMonitor = clientMonitor; 114 mClientCallback = callback; 115 mState = state; 116 mCancelWatchdog = () -> { 117 if (!isFinished()) { 118 Slog.e(TAG, "[Watchdog Triggered]: " + this); 119 try { 120 mClientMonitor.getListener().onError(mClientMonitor.getSensorId(), 121 mClientMonitor.getCookie(), BiometricConstants.BIOMETRIC_ERROR_CANCELED, 122 0 /* vendorCode */); 123 } catch (RemoteException e) { 124 Slog.e(TAG, "Remote exception when trying to send error in cancel " 125 + "watchdog."); 126 } 127 getWrappedCallback(mOnStartCallback) 128 .onClientFinished(mClientMonitor, false /* success */); 129 } 130 }; 131 } 132 133 /** 134 * Zero if this operation is ready to start or has already started. A non-zero cookie 135 * is returned if the operation has not started and is waiting on 136 * {@link android.hardware.biometrics.IBiometricService#onReadyForAuthentication(int)}. 137 * 138 * @return cookie or 0 if ready/started 139 */ isReadyToStart(@onNull ClientMonitorCallback callback)140 public int isReadyToStart(@NonNull ClientMonitorCallback callback) { 141 if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) { 142 final int cookie = mClientMonitor.getCookie(); 143 if (cookie != 0) { 144 mState = STATE_WAITING_FOR_COOKIE; 145 mClientMonitor.waitForCookie(getWrappedCallback(callback)); 146 } 147 return cookie; 148 } 149 150 return 0; 151 } 152 153 /** 154 * Start this operation without waiting for a cookie 155 * (i.e. {@link #isReadyToStart(ClientMonitorCallback)} returns zero} 156 * 157 * @param callback lifecycle callback 158 * @return if this operation started 159 */ start(@onNull ClientMonitorCallback callback)160 public boolean start(@NonNull ClientMonitorCallback callback) { 161 if (errorWhenNoneOf("start", 162 STATE_WAITING_IN_QUEUE, 163 STATE_WAITING_FOR_COOKIE, 164 STATE_WAITING_IN_QUEUE_CANCELING)) { 165 return hasOperationAlreadyStarted(); 166 } 167 168 if (mClientMonitor.getCookie() != 0) { 169 String err = "operation requires cookie"; 170 Counter.logIncrement("biometric.value_biometric_scheduler_operation_state_error_count"); 171 Slog.e(TAG, err); 172 } 173 174 return doStart(callback); 175 } 176 177 /** 178 * Start this operation after receiving the given cookie. 179 * 180 * @param callback lifecycle callback 181 * @param cookie cookie indicting the operation should begin 182 * @return if this operation started 183 */ startWithCookie(@onNull ClientMonitorCallback callback, int cookie)184 public boolean startWithCookie(@NonNull ClientMonitorCallback callback, int cookie) { 185 if (mClientMonitor.getCookie() != cookie) { 186 Slog.e(TAG, "Mismatched cookie for operation: " + this + ", received: " + cookie); 187 return false; 188 } 189 190 if (errorWhenNoneOf("start", 191 STATE_WAITING_IN_QUEUE, 192 STATE_WAITING_FOR_COOKIE, 193 STATE_WAITING_IN_QUEUE_CANCELING)) { 194 return hasOperationAlreadyStarted(); 195 } 196 197 return doStart(callback); 198 } 199 doStart(@onNull ClientMonitorCallback callback)200 private boolean doStart(@NonNull ClientMonitorCallback callback) { 201 mOnStartCallback = callback; 202 final ClientMonitorCallback cb = getWrappedCallback(callback); 203 204 if (mState == STATE_WAITING_IN_QUEUE_CANCELING) { 205 Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this); 206 207 cb.onClientFinished(mClientMonitor, true /* success */); 208 if (mClientMonitor instanceof ErrorConsumer) { 209 final ErrorConsumer errorConsumer = (ErrorConsumer) mClientMonitor; 210 errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, 211 0 /* vendorCode */); 212 } else { 213 Slog.w(TAG, "monitor cancelled but does not implement ErrorConsumer"); 214 } 215 216 return false; 217 } 218 219 if (isUnstartableHalOperation()) { 220 Slog.v(TAG, "unable to start: " + this); 221 ((HalClientMonitor<?>) mClientMonitor).unableToStart(); 222 cb.onClientFinished(mClientMonitor, false /* success */); 223 return false; 224 } 225 226 mState = STATE_STARTED; 227 mClientMonitor.start(cb); 228 229 Slog.v(TAG, "started: " + this); 230 return true; 231 } 232 hasOperationAlreadyStarted()233 private boolean hasOperationAlreadyStarted() { 234 return mState == STATE_STARTED; 235 } 236 237 /** 238 * Abort a pending operation. 239 * 240 * This is similar to cancel but the operation must not have been started. It will 241 * immediately abort the operation and notify the client that it has finished unsuccessfully. 242 */ abort()243 public void abort() { 244 if (errorWhenNoneOf("abort", 245 STATE_WAITING_IN_QUEUE, 246 STATE_WAITING_FOR_COOKIE, 247 STATE_WAITING_IN_QUEUE_CANCELING)) { 248 return; 249 } 250 251 if (isHalOperation()) { 252 ((HalClientMonitor<?>) mClientMonitor).unableToStart(); 253 } 254 getWrappedCallback().onClientFinished(mClientMonitor, false /* success */); 255 256 Slog.v(TAG, "Aborted: " + this); 257 } 258 259 /** Flags this operation as canceled, if possible, but does not cancel it until started. */ markCanceling()260 public boolean markCanceling() { 261 if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) { 262 mState = STATE_WAITING_IN_QUEUE_CANCELING; 263 return true; 264 } 265 return false; 266 } 267 markCancelingForWatchdog()268 @VisibleForTesting void markCancelingForWatchdog() { 269 mState = STATE_WAITING_IN_QUEUE_CANCELING; 270 } 271 272 /** 273 * Cancel the operation now. 274 * 275 * @param handler handler to use for the cancellation watchdog 276 * @param callback lifecycle callback (only used if this operation hasn't started, otherwise 277 * the callback used from {@link #start(ClientMonitorCallback)} is used) 278 */ cancel(@onNull Handler handler, @NonNull ClientMonitorCallback callback)279 public void cancel(@NonNull Handler handler, @NonNull ClientMonitorCallback callback) { 280 if (errorWhenOneOf("cancel", STATE_FINISHED)) { 281 return; 282 } 283 284 final int currentState = mState; 285 if (currentState == STATE_STARTED_CANCELING) { 286 Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this); 287 return; 288 } 289 290 mState = STATE_STARTED_CANCELING; 291 if (currentState == STATE_WAITING_IN_QUEUE 292 || currentState == STATE_WAITING_IN_QUEUE_CANCELING 293 || currentState == STATE_WAITING_FOR_COOKIE) { 294 Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor); 295 mClientMonitor.cancelWithoutStarting(getWrappedCallback(callback)); 296 } else { 297 Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor); 298 mClientMonitor.cancel(); 299 } 300 301 // forcibly finish this client if the HAL does not acknowledge within the timeout 302 handler.postDelayed(mCancelWatchdog, CANCEL_WATCHDOG_DELAY_MS); 303 } 304 305 @NonNull getWrappedCallback()306 private ClientMonitorCallback getWrappedCallback() { 307 return getWrappedCallback(null); 308 } 309 310 @NonNull getWrappedCallback( @ullable ClientMonitorCallback callback)311 private ClientMonitorCallback getWrappedCallback( 312 @Nullable ClientMonitorCallback callback) { 313 final ClientMonitorCallback destroyCallback = new ClientMonitorCallback() { 314 @Override 315 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, 316 boolean success) { 317 Slog.d(TAG, "[Finished / destroy]: " + clientMonitor); 318 mClientMonitor.destroy(); 319 mState = STATE_FINISHED; 320 } 321 }; 322 return new ClientMonitorCompositeCallback(destroyCallback, callback, mClientCallback); 323 } 324 325 /** {@link BaseClientMonitor#getSensorId()}. */ getSensorId()326 public int getSensorId() { 327 return mClientMonitor.getSensorId(); 328 } 329 330 /** {@link BaseClientMonitor#getProtoEnum()}. */ getProtoEnum()331 public int getProtoEnum() { 332 return mClientMonitor.getProtoEnum(); 333 } 334 335 /** {@link BaseClientMonitor#getTargetUserId()}. */ getTargetUserId()336 public int getTargetUserId() { 337 return mClientMonitor.getTargetUserId(); 338 } 339 340 /** If the given clientMonitor is the same as the one in the constructor. */ isFor(@onNull BaseClientMonitor clientMonitor)341 public boolean isFor(@NonNull BaseClientMonitor clientMonitor) { 342 return mClientMonitor == clientMonitor; 343 } 344 345 /** If this operation is interruptable. */ isInterruptable()346 public boolean isInterruptable() { 347 return mClientMonitor.isInterruptable(); 348 } 349 isHalOperation()350 private boolean isHalOperation() { 351 return mClientMonitor instanceof HalClientMonitor<?>; 352 } 353 isUnstartableHalOperation()354 private boolean isUnstartableHalOperation() { 355 if (isHalOperation()) { 356 final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor; 357 if (client.getFreshDaemon() == null) { 358 return true; 359 } 360 } 361 return false; 362 } 363 364 /** If this operation is an enrollment. */ isEnrollOperation()365 public boolean isEnrollOperation() { 366 return mClientMonitor instanceof EnrollClient; 367 } 368 369 /** If this operation is authentication. */ isAuthenticateOperation()370 public boolean isAuthenticateOperation() { 371 return mClientMonitor instanceof AuthenticationClient; 372 } 373 374 /** If this operation is authentication or detection. */ isAuthenticationOrDetectionOperation()375 public boolean isAuthenticationOrDetectionOperation() { 376 final boolean isAuthentication = mClientMonitor instanceof AuthenticationConsumer; 377 final boolean isDetection = mClientMonitor instanceof DetectionConsumer; 378 return isAuthentication || isDetection; 379 } 380 381 /** If this operation is {@link StartUserClient}. */ isStartUserOperation()382 public boolean isStartUserOperation() { 383 return mClientMonitor instanceof StartUserClient<?, ?>; 384 } 385 386 /** If this operation performs acquisition {@link AcquisitionClient}. */ isAcquisitionOperation()387 public boolean isAcquisitionOperation() { 388 return mClientMonitor instanceof AcquisitionClient; 389 } 390 391 /** 392 * If this operation matches the original requestId. 393 * 394 * By default, monitors are not associated with a request id to retain the original 395 * behavior (i.e. if no requestId is explicitly set then assume it matches) 396 * 397 * @param requestId a unique id {@link BaseClientMonitor#setRequestId(long)}. 398 */ isMatchingRequestId(long requestId)399 public boolean isMatchingRequestId(long requestId) { 400 return !mClientMonitor.hasRequestId() 401 || mClientMonitor.getRequestId() == requestId; 402 } 403 404 /** If the token matches */ isMatchingToken(@ullable IBinder token)405 public boolean isMatchingToken(@Nullable IBinder token) { 406 return mClientMonitor.getToken() == token; 407 } 408 409 /** If this operation has started. */ isStarted()410 public boolean isStarted() { 411 return mState == STATE_STARTED; 412 } 413 414 /** If this operation is cancelling but has not yet completed. */ isCanceling()415 public boolean isCanceling() { 416 return mState == STATE_STARTED_CANCELING; 417 } 418 419 /** If this operation has finished and completed its lifecycle. */ isFinished()420 public boolean isFinished() { 421 return mState == STATE_FINISHED; 422 } 423 424 /** If {@link #markCanceling()} was called but the operation hasn't been canceled. */ isMarkedCanceling()425 public boolean isMarkedCanceling() { 426 return mState == STATE_WAITING_IN_QUEUE_CANCELING; 427 } 428 429 /** 430 * The monitor passed to the constructor. 431 * @deprecated avoid using and move to encapsulate within the operation 432 */ 433 @Deprecated getClientMonitor()434 public BaseClientMonitor getClientMonitor() { 435 return mClientMonitor; 436 } 437 errorWhenOneOf(String op, @OperationState int... states)438 private boolean errorWhenOneOf(String op, @OperationState int... states) { 439 final boolean isError = ArrayUtils.contains(states, mState); 440 if (isError) { 441 Counter.logIncrement( 442 "biometric.value_biometric_scheduler_operation_state_error_count"); 443 final String err = op + ": mState must not be " + mState; 444 Slog.e(TAG, err); 445 } 446 return isError; 447 } 448 errorWhenNoneOf(String op, @OperationState int... states)449 private boolean errorWhenNoneOf(String op, @OperationState int... states) { 450 final boolean isError = !ArrayUtils.contains(states, mState); 451 if (isError) { 452 Counter.logIncrement( 453 "biometric.value_biometric_scheduler_operation_state_error_count"); 454 final String err = op + ": mState=" + mState + " must be one of " 455 + Arrays.toString(states); 456 Slog.e(TAG, err); 457 } 458 return isError; 459 } 460 461 @Override toString()462 public String toString() { 463 return mClientMonitor + ", State: " + mState; 464 } 465 } 466