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.face.hidl; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.ActivityManager; 22 import android.app.SynchronousUserSwitchObserver; 23 import android.app.UserSwitchObserver; 24 import android.content.Context; 25 import android.content.pm.UserInfo; 26 import android.hardware.biometrics.BiometricConstants; 27 import android.hardware.biometrics.BiometricFaceConstants; 28 import android.hardware.biometrics.BiometricsProtoEnums; 29 import android.hardware.biometrics.ITestSession; 30 import android.hardware.biometrics.ITestSessionCallback; 31 import android.hardware.biometrics.face.V1_0.IBiometricsFace; 32 import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; 33 import android.hardware.face.Face; 34 import android.hardware.face.FaceAuthenticateOptions; 35 import android.hardware.face.FaceSensorPropertiesInternal; 36 import android.hardware.face.IFaceServiceReceiver; 37 import android.os.Binder; 38 import android.os.Build; 39 import android.os.Handler; 40 import android.os.IBinder; 41 import android.os.IHwBinder; 42 import android.os.Looper; 43 import android.os.NativeHandle; 44 import android.os.RemoteException; 45 import android.os.SystemProperties; 46 import android.os.UserHandle; 47 import android.os.UserManager; 48 import android.provider.Settings; 49 import android.util.Slog; 50 import android.util.proto.ProtoOutputStream; 51 import android.view.Surface; 52 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.internal.util.FrameworkStatsLog; 55 import com.android.server.biometrics.AuthenticationStatsCollector; 56 import com.android.server.biometrics.SensorServiceStateProto; 57 import com.android.server.biometrics.SensorStateProto; 58 import com.android.server.biometrics.UserStateProto; 59 import com.android.server.biometrics.Utils; 60 import com.android.server.biometrics.log.BiometricContext; 61 import com.android.server.biometrics.log.BiometricLogger; 62 import com.android.server.biometrics.sensors.AcquisitionClient; 63 import com.android.server.biometrics.sensors.AuthenticationConsumer; 64 import com.android.server.biometrics.sensors.BaseClientMonitor; 65 import com.android.server.biometrics.sensors.BiometricNotificationImpl; 66 import com.android.server.biometrics.sensors.BiometricScheduler; 67 import com.android.server.biometrics.sensors.BiometricStateCallback; 68 import com.android.server.biometrics.sensors.ClientMonitorCallback; 69 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; 70 import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback; 71 import com.android.server.biometrics.sensors.EnumerateConsumer; 72 import com.android.server.biometrics.sensors.ErrorConsumer; 73 import com.android.server.biometrics.sensors.LockoutResetDispatcher; 74 import com.android.server.biometrics.sensors.LockoutTracker; 75 import com.android.server.biometrics.sensors.PerformanceTracker; 76 import com.android.server.biometrics.sensors.RemovalConsumer; 77 import com.android.server.biometrics.sensors.face.FaceUtils; 78 import com.android.server.biometrics.sensors.face.LockoutHalImpl; 79 import com.android.server.biometrics.sensors.face.ServiceProvider; 80 import com.android.server.biometrics.sensors.face.UsageStats; 81 82 import org.json.JSONArray; 83 import org.json.JSONException; 84 import org.json.JSONObject; 85 86 import java.io.FileDescriptor; 87 import java.io.FileOutputStream; 88 import java.io.IOException; 89 import java.io.PrintWriter; 90 import java.time.Clock; 91 import java.util.ArrayList; 92 import java.util.Arrays; 93 import java.util.HashMap; 94 import java.util.List; 95 import java.util.Map; 96 import java.util.concurrent.atomic.AtomicLong; 97 import java.util.function.Supplier; 98 99 /** 100 * Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or its extended 101 * minor versions. 102 */ 103 public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { 104 105 private static final String TAG = "Face10"; 106 107 private static final int ENROLL_TIMEOUT_SEC = 75; 108 private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000; 109 private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS = 110 FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC * 1000; 111 @VisibleForTesting 112 public static Clock sSystemClock = Clock.systemUTC(); 113 114 private boolean mTestHalEnabled; 115 116 @NonNull private final FaceSensorPropertiesInternal mSensorProperties; 117 @NonNull private final BiometricStateCallback mBiometricStateCallback; 118 @NonNull private final Context mContext; 119 @NonNull private final BiometricScheduler mScheduler; 120 @NonNull private final Handler mHandler; 121 @NonNull private final Supplier<IBiometricsFace> mLazyDaemon; 122 @NonNull private final LockoutHalImpl mLockoutTracker; 123 @NonNull private final UsageStats mUsageStats; 124 @NonNull private final Map<Integer, Long> mAuthenticatorIds; 125 @Nullable private IBiometricsFace mDaemon; 126 @NonNull private final HalResultController mHalResultController; 127 @NonNull private final BiometricContext mBiometricContext; 128 @NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector; 129 // for requests that do not use biometric prompt 130 @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); 131 private int mCurrentUserId = UserHandle.USER_NULL; 132 private final int mSensorId; 133 private final List<Long> mGeneratedChallengeCount = new ArrayList<>(); 134 private FaceGenerateChallengeClient mGeneratedChallengeCache = null; 135 136 private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() { 137 @Override 138 public void onUserSwitching(int newUserId) { 139 scheduleInternalCleanup(newUserId, null /* callback */); 140 scheduleGetFeature(mSensorId, new Binder(), newUserId, 141 BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, 142 null, mContext.getOpPackageName()); 143 } 144 }; 145 146 public static class HalResultController extends IBiometricsFaceClientCallback.Stub { 147 /** 148 * Interface to sends results to the HalResultController's owner. 149 */ 150 public interface Callback { 151 /** 152 * Invoked when the HAL sends ERROR_HW_UNAVAILABLE. 153 */ onHardwareUnavailable()154 void onHardwareUnavailable(); 155 } 156 157 private final int mSensorId; 158 @NonNull private final Context mContext; 159 @NonNull private final Handler mHandler; 160 @NonNull private final BiometricScheduler mScheduler; 161 @Nullable private Callback mCallback; 162 @NonNull private final LockoutHalImpl mLockoutTracker; 163 @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; 164 165 HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, @NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker, @NonNull LockoutResetDispatcher lockoutResetDispatcher)166 HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, 167 @NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker, 168 @NonNull LockoutResetDispatcher lockoutResetDispatcher) { 169 mSensorId = sensorId; 170 mContext = context; 171 mHandler = handler; 172 mScheduler = scheduler; 173 mLockoutTracker = lockoutTracker; 174 mLockoutResetDispatcher = lockoutResetDispatcher; 175 } 176 setCallback(@ullable Callback callback)177 public void setCallback(@Nullable Callback callback) { 178 mCallback = callback; 179 } 180 181 @Override onEnrollResult(long deviceId, int faceId, int userId, int remaining)182 public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) { 183 mHandler.post(() -> { 184 final CharSequence name = FaceUtils.getLegacyInstance(mSensorId) 185 .getUniqueName(mContext, userId); 186 final Face face = new Face(name, faceId, deviceId); 187 188 final BaseClientMonitor client = mScheduler.getCurrentClient(); 189 if (!(client instanceof FaceEnrollClient)) { 190 Slog.e(TAG, "onEnrollResult for non-enroll client: " 191 + Utils.getClientName(client)); 192 return; 193 } 194 195 final FaceEnrollClient enrollClient = (FaceEnrollClient) client; 196 enrollClient.onEnrollResult(face, remaining); 197 }); 198 } 199 200 @Override onAuthenticated(long deviceId, int faceId, int userId, ArrayList<Byte> token)201 public void onAuthenticated(long deviceId, int faceId, int userId, 202 ArrayList<Byte> token) { 203 mHandler.post(() -> { 204 final BaseClientMonitor client = mScheduler.getCurrentClient(); 205 if (!(client instanceof AuthenticationConsumer)) { 206 Slog.e(TAG, "onAuthenticated for non-authentication consumer: " 207 + Utils.getClientName(client)); 208 return; 209 } 210 211 final AuthenticationConsumer authenticationConsumer = 212 (AuthenticationConsumer) client; 213 final boolean authenticated = faceId != 0; 214 final Face face = new Face("", faceId, deviceId); 215 authenticationConsumer.onAuthenticated(face, authenticated, token); 216 }); 217 } 218 219 @Override onAcquired(long deviceId, int userId, int acquiredInfo, int vendorCode)220 public void onAcquired(long deviceId, int userId, int acquiredInfo, 221 int vendorCode) { 222 mHandler.post(() -> { 223 final BaseClientMonitor client = mScheduler.getCurrentClient(); 224 if (!(client instanceof AcquisitionClient)) { 225 Slog.e(TAG, "onAcquired for non-acquire client: " 226 + Utils.getClientName(client)); 227 return; 228 } 229 230 final AcquisitionClient<?> acquisitionClient = 231 (AcquisitionClient<?>) client; 232 acquisitionClient.onAcquired(acquiredInfo, vendorCode); 233 }); 234 } 235 236 @Override onError(long deviceId, int userId, int error, int vendorCode)237 public void onError(long deviceId, int userId, int error, int vendorCode) { 238 mHandler.post(() -> { 239 final BaseClientMonitor client = mScheduler.getCurrentClient(); 240 Slog.d(TAG, "handleError" 241 + ", client: " + (client != null ? client.getOwnerString() : null) 242 + ", error: " + error 243 + ", vendorCode: " + vendorCode); 244 if (!(client instanceof ErrorConsumer)) { 245 Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName( 246 client)); 247 return; 248 } 249 250 final ErrorConsumer errorConsumer = (ErrorConsumer) client; 251 errorConsumer.onError(error, vendorCode); 252 253 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { 254 Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE"); 255 if (mCallback != null) { 256 mCallback.onHardwareUnavailable(); 257 } 258 } 259 }); 260 } 261 262 @Override onRemoved(long deviceId, ArrayList<Integer> removed, int userId)263 public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) { 264 mHandler.post(() -> { 265 final BaseClientMonitor client = mScheduler.getCurrentClient(); 266 if (!(client instanceof RemovalConsumer)) { 267 Slog.e(TAG, "onRemoved for non-removal consumer: " 268 + Utils.getClientName(client)); 269 return; 270 } 271 272 final RemovalConsumer removalConsumer = (RemovalConsumer) client; 273 274 if (!removed.isEmpty()) { 275 // Convert to old fingerprint-like behavior, where remove() receives 276 // one removal at a time. This way, remove can share some more common code. 277 for (int i = 0; i < removed.size(); i++) { 278 final int id = removed.get(i); 279 final Face face = new Face("", id, deviceId); 280 final int remaining = removed.size() - i - 1; 281 Slog.d(TAG, "Removed, faceId: " + id + ", remaining: " + remaining); 282 removalConsumer.onRemoved(face, remaining); 283 } 284 } else { 285 removalConsumer.onRemoved(null, 0 /* remaining */); 286 } 287 288 Settings.Secure.putIntForUser(mContext.getContentResolver(), 289 Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT); 290 }); 291 } 292 293 @Override onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)294 public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) { 295 mHandler.post(() -> { 296 final BaseClientMonitor client = mScheduler.getCurrentClient(); 297 if (!(client instanceof EnumerateConsumer)) { 298 Slog.e(TAG, "onEnumerate for non-enumerate consumer: " 299 + Utils.getClientName(client)); 300 return; 301 } 302 303 final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client; 304 305 if (!faceIds.isEmpty()) { 306 // Convert to old fingerprint-like behavior, where enumerate() receives one 307 // template at a time. This way, enumerate can share some more common code. 308 for (int i = 0; i < faceIds.size(); i++) { 309 final Face face = new Face("", faceIds.get(i), deviceId); 310 enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1); 311 } 312 } else { 313 // For face, the HIDL contract is to receive an empty list when there are no 314 // templates enrolled. Send a null identifier since we don't consume them 315 // anywhere, and send remaining == 0 so this code can be shared with Face@1.1 316 enumerateConsumer.onEnumerationResult(null /* identifier */, 0); 317 } 318 }); 319 } 320 321 @Override onLockoutChanged(long duration)322 public void onLockoutChanged(long duration) { 323 mHandler.post(() -> { 324 Slog.d(TAG, "onLockoutChanged: " + duration); 325 final @LockoutTracker.LockoutMode int lockoutMode; 326 if (duration == 0) { 327 lockoutMode = LockoutTracker.LOCKOUT_NONE; 328 } else if (duration == -1 || duration == Long.MAX_VALUE) { 329 lockoutMode = LockoutTracker.LOCKOUT_PERMANENT; 330 } else { 331 lockoutMode = LockoutTracker.LOCKOUT_TIMED; 332 } 333 334 mLockoutTracker.setCurrentUserLockoutMode(lockoutMode); 335 336 if (duration == 0) { 337 mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId); 338 } 339 }); 340 } 341 } 342 343 @VisibleForTesting Face10(@onNull Context context, @NonNull BiometricStateCallback biometricStateCallback, @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull Handler handler, @NonNull BiometricScheduler scheduler, @NonNull BiometricContext biometricContext)344 Face10(@NonNull Context context, 345 @NonNull BiometricStateCallback biometricStateCallback, 346 @NonNull FaceSensorPropertiesInternal sensorProps, 347 @NonNull LockoutResetDispatcher lockoutResetDispatcher, 348 @NonNull Handler handler, 349 @NonNull BiometricScheduler scheduler, 350 @NonNull BiometricContext biometricContext) { 351 mSensorProperties = sensorProps; 352 mContext = context; 353 mBiometricStateCallback = biometricStateCallback; 354 mSensorId = sensorProps.sensorId; 355 mScheduler = scheduler; 356 mHandler = handler; 357 mBiometricContext = biometricContext; 358 mUsageStats = new UsageStats(context); 359 mAuthenticatorIds = new HashMap<>(); 360 mLazyDaemon = Face10.this::getDaemon; 361 mLockoutTracker = new LockoutHalImpl(); 362 mHalResultController = new HalResultController(sensorProps.sensorId, context, mHandler, 363 mScheduler, mLockoutTracker, lockoutResetDispatcher); 364 mHalResultController.setCallback(() -> { 365 mDaemon = null; 366 mCurrentUserId = UserHandle.USER_NULL; 367 }); 368 369 mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, 370 BiometricsProtoEnums.MODALITY_FACE, new BiometricNotificationImpl()); 371 372 try { 373 ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); 374 } catch (RemoteException e) { 375 Slog.e(TAG, "Unable to register user switch observer"); 376 } 377 } 378 newInstance(@onNull Context context, @NonNull BiometricStateCallback biometricStateCallback, @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher)379 public static Face10 newInstance(@NonNull Context context, 380 @NonNull BiometricStateCallback biometricStateCallback, 381 @NonNull FaceSensorPropertiesInternal sensorProps, 382 @NonNull LockoutResetDispatcher lockoutResetDispatcher) { 383 final Handler handler = new Handler(Looper.getMainLooper()); 384 return new Face10(context, biometricStateCallback, sensorProps, lockoutResetDispatcher, 385 handler, new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, 386 null /* gestureAvailabilityTracker */), 387 BiometricContext.getInstance(context)); 388 } 389 390 @Override serviceDied(long cookie)391 public void serviceDied(long cookie) { 392 Slog.e(TAG, "HAL died"); 393 mHandler.post(() -> { 394 PerformanceTracker.getInstanceForSensorId(mSensorId) 395 .incrementHALDeathCount(); 396 mDaemon = null; 397 mCurrentUserId = UserHandle.USER_NULL; 398 399 final BaseClientMonitor client = mScheduler.getCurrentClient(); 400 if (client instanceof ErrorConsumer) { 401 Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client); 402 final ErrorConsumer errorConsumer = (ErrorConsumer) client; 403 errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 404 0 /* vendorCode */); 405 406 FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, 407 BiometricsProtoEnums.MODALITY_FACE, 408 BiometricsProtoEnums.ISSUE_HAL_DEATH, 409 -1 /* sensorId */); 410 } 411 412 mScheduler.recordCrashState(); 413 mScheduler.reset(); 414 }); 415 } 416 getDaemon()417 private synchronized IBiometricsFace getDaemon() { 418 if (mTestHalEnabled) { 419 final TestHal testHal = new TestHal(mContext, mSensorId); 420 testHal.setCallback(mHalResultController); 421 return testHal; 422 } 423 424 if (mDaemon != null) { 425 return mDaemon; 426 } 427 428 Slog.d(TAG, "Daemon was null, reconnecting, current operation: " 429 + mScheduler.getCurrentClient()); 430 431 try { 432 mDaemon = IBiometricsFace.getService(); 433 } catch (java.util.NoSuchElementException e) { 434 // Service doesn't exist or cannot be opened. 435 Slog.w(TAG, "NoSuchElementException", e); 436 } catch (RemoteException e) { 437 Slog.e(TAG, "Failed to get face HAL", e); 438 } 439 440 if (mDaemon == null) { 441 Slog.w(TAG, "Face HAL not available"); 442 return null; 443 } 444 445 mDaemon.asBinder().linkToDeath(this, 0 /* flags */); 446 447 // HAL ID for these HIDL versions are only used to determine if callbacks have been 448 // successfully set. 449 long halId = 0; 450 try { 451 halId = mDaemon.setCallback(mHalResultController).value; 452 } catch (RemoteException e) { 453 Slog.e(TAG, "Failed to set callback for face HAL", e); 454 mDaemon = null; 455 } 456 457 Slog.d(TAG, "Face HAL ready, HAL ID: " + halId); 458 if (halId != 0) { 459 scheduleLoadAuthenticatorIds(); 460 scheduleInternalCleanup(ActivityManager.getCurrentUser(), null /* callback */); 461 scheduleGetFeature(mSensorId, new Binder(), 462 ActivityManager.getCurrentUser(), 463 BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, null, 464 mContext.getOpPackageName()); 465 } else { 466 Slog.e(TAG, "Unable to set callback"); 467 mDaemon = null; 468 } 469 470 return mDaemon; 471 } 472 473 @Override containsSensor(int sensorId)474 public boolean containsSensor(int sensorId) { 475 return mSensorId == sensorId; 476 } 477 478 @Override 479 @NonNull getSensorProperties()480 public List<FaceSensorPropertiesInternal> getSensorProperties() { 481 final List<FaceSensorPropertiesInternal> properties = new ArrayList<>(); 482 properties.add(mSensorProperties); 483 return properties; 484 } 485 486 @NonNull 487 @Override getSensorProperties(int sensorId)488 public FaceSensorPropertiesInternal getSensorProperties(int sensorId) { 489 return mSensorProperties; 490 } 491 492 @Override 493 @NonNull getEnrolledFaces(int sensorId, int userId)494 public List<Face> getEnrolledFaces(int sensorId, int userId) { 495 return FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); 496 } 497 498 @Override hasEnrollments(int sensorId, int userId)499 public boolean hasEnrollments(int sensorId, int userId) { 500 return !getEnrolledFaces(sensorId, userId).isEmpty(); 501 } 502 503 @Override 504 @LockoutTracker.LockoutMode getLockoutModeForUser(int sensorId, int userId)505 public int getLockoutModeForUser(int sensorId, int userId) { 506 return mLockoutTracker.getLockoutModeForUser(userId); 507 } 508 509 @Override getAuthenticatorId(int sensorId, int userId)510 public long getAuthenticatorId(int sensorId, int userId) { 511 return mAuthenticatorIds.getOrDefault(userId, 0L); 512 } 513 514 @Override isHardwareDetected(int sensorId)515 public boolean isHardwareDetected(int sensorId) { 516 return getDaemon() != null; 517 } 518 isGeneratedChallengeCacheValid()519 private boolean isGeneratedChallengeCacheValid() { 520 return mGeneratedChallengeCache != null 521 && sSystemClock.millis() - mGeneratedChallengeCache.getCreatedAt() 522 < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS; 523 } 524 incrementChallengeCount()525 private void incrementChallengeCount() { 526 mGeneratedChallengeCount.add(0, sSystemClock.millis()); 527 } 528 decrementChallengeCount()529 private int decrementChallengeCount() { 530 final long now = sSystemClock.millis(); 531 // ignore values that are old in case generate/revoke calls are not matched 532 // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing 533 mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS); 534 if (!mGeneratedChallengeCount.isEmpty()) { 535 mGeneratedChallengeCount.remove(0); 536 } 537 return mGeneratedChallengeCount.size(); 538 } 539 540 /** 541 * {@link IBiometricsFace} only supports a single in-flight challenge but there are cases where 542 * two callers both need challenges (e.g. resetLockout right before enrollment). 543 */ 544 @Override scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName)545 public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, 546 @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { 547 mHandler.post(() -> { 548 incrementChallengeCount(); 549 550 if (isGeneratedChallengeCacheValid()) { 551 Slog.d(TAG, "Current challenge is cached and will be reused"); 552 mGeneratedChallengeCache.reuseResult(receiver); 553 return; 554 } 555 556 scheduleUpdateActiveUserWithoutHandler(userId); 557 558 final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext, 559 mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, 560 opPackageName, mSensorId, 561 createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, 562 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 563 mBiometricContext, sSystemClock.millis()); 564 mGeneratedChallengeCache = client; 565 mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { 566 @Override 567 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { 568 if (client != clientMonitor) { 569 Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client." 570 + " Expecting: " + client + ", received: " + clientMonitor); 571 } 572 } 573 }); 574 }); 575 } 576 577 @Override scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull String opPackageName, long challenge)578 public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token, 579 @NonNull String opPackageName, long challenge) { 580 mHandler.post(() -> { 581 final boolean shouldRevoke = decrementChallengeCount() == 0; 582 if (!shouldRevoke) { 583 Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: " 584 + mGeneratedChallengeCount); 585 return; 586 } 587 588 Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients"); 589 mGeneratedChallengeCache = null; 590 591 final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext, 592 mLazyDaemon, token, userId, opPackageName, mSensorId, 593 createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, 594 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 595 mBiometricContext); 596 mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { 597 @Override 598 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, 599 boolean success) { 600 if (client != clientMonitor) { 601 Slog.e(TAG, "scheduleRevokeChallenge, mismatched client." 602 + "Expecting: " + client + ", received: " + clientMonitor); 603 } 604 } 605 }); 606 }); 607 } 608 609 @Override scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, @NonNull int[] disabledFeatures, @Nullable Surface previewSurface, boolean debugConsent)610 public long scheduleEnroll(int sensorId, @NonNull IBinder token, 611 @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, 612 @NonNull String opPackageName, @NonNull int[] disabledFeatures, 613 @Nullable Surface previewSurface, boolean debugConsent) { 614 final long id = mRequestCounter.incrementAndGet(); 615 mHandler.post(() -> { 616 scheduleUpdateActiveUserWithoutHandler(userId); 617 618 final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, 619 new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, 620 opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, 621 ENROLL_TIMEOUT_SEC, previewSurface, mSensorId, 622 createLogger(BiometricsProtoEnums.ACTION_ENROLL, 623 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 624 mBiometricContext); 625 626 mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { 627 @Override 628 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { 629 mBiometricStateCallback.onClientStarted(clientMonitor); 630 } 631 632 @Override 633 public void onBiometricAction(int action) { 634 mBiometricStateCallback.onBiometricAction(action); 635 } 636 637 @Override 638 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, 639 boolean success) { 640 mBiometricStateCallback.onClientFinished(clientMonitor, success); 641 if (success) { 642 // Update authenticatorIds 643 scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId()); 644 } 645 } 646 }); 647 }); 648 return id; 649 } 650 651 @Override cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId)652 public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) { 653 mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId)); 654 } 655 656 @Override scheduleFaceDetect(@onNull IBinder token, @NonNull ClientMonitorCallbackConverter callback, @NonNull FaceAuthenticateOptions options, int statsClient)657 public long scheduleFaceDetect(@NonNull IBinder token, 658 @NonNull ClientMonitorCallbackConverter callback, 659 @NonNull FaceAuthenticateOptions options, int statsClient) { 660 throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you" 661 + "forget to check the supportsFaceDetection flag?"); 662 } 663 664 @Override cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId)665 public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) { 666 throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you" 667 + "forget to check the supportsFaceDetection flag?"); 668 } 669 670 @Override scheduleAuthenticate(@onNull IBinder token, long operationId, int cookie, @NonNull ClientMonitorCallbackConverter receiver, @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted, int statsClient, boolean allowBackgroundAuthentication)671 public void scheduleAuthenticate(@NonNull IBinder token, long operationId, 672 int cookie, @NonNull ClientMonitorCallbackConverter receiver, 673 @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted, 674 int statsClient, boolean allowBackgroundAuthentication) { 675 mHandler.post(() -> { 676 final int userId = options.getUserId(); 677 scheduleUpdateActiveUserWithoutHandler(userId); 678 679 final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId); 680 final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext, 681 mLazyDaemon, token, requestId, receiver, operationId, restricted, 682 options, cookie, false /* requireConfirmation */, 683 createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient, 684 mAuthenticationStatsCollector), 685 mBiometricContext, isStrongBiometric, mLockoutTracker, 686 mUsageStats, allowBackgroundAuthentication, 687 Utils.getCurrentStrength(mSensorId)); 688 mScheduler.scheduleClientMonitor(client); 689 }); 690 } 691 692 @Override scheduleAuthenticate(@onNull IBinder token, long operationId, int cookie, @NonNull ClientMonitorCallbackConverter receiver, @NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient, boolean allowBackgroundAuthentication)693 public long scheduleAuthenticate(@NonNull IBinder token, long operationId, 694 int cookie, @NonNull ClientMonitorCallbackConverter receiver, 695 @NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient, 696 boolean allowBackgroundAuthentication) { 697 final long id = mRequestCounter.incrementAndGet(); 698 699 scheduleAuthenticate(token, operationId, cookie, receiver, 700 options, id, restricted, statsClient, allowBackgroundAuthentication); 701 702 return id; 703 } 704 705 @Override cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId)706 public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) { 707 mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId)); 708 } 709 710 @Override scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName)711 public void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId, 712 @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { 713 mHandler.post(() -> { 714 scheduleUpdateActiveUserWithoutHandler(userId); 715 716 final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token, 717 new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName, 718 FaceUtils.getLegacyInstance(mSensorId), mSensorId, 719 createLogger(BiometricsProtoEnums.ACTION_REMOVE, 720 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 721 mBiometricContext, mAuthenticatorIds); 722 mScheduler.scheduleClientMonitor(client, mBiometricStateCallback); 723 }); 724 } 725 726 @Override scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName)727 public void scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId, 728 @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { 729 mHandler.post(() -> { 730 scheduleUpdateActiveUserWithoutHandler(userId); 731 732 // For IBiometricsFace@1.0, remove(0) means remove all enrollments 733 final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token, 734 new ClientMonitorCallbackConverter(receiver), 0 /* faceId */, userId, 735 opPackageName, 736 FaceUtils.getLegacyInstance(mSensorId), mSensorId, 737 createLogger(BiometricsProtoEnums.ACTION_REMOVE, 738 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 739 mBiometricContext, mAuthenticatorIds); 740 mScheduler.scheduleClientMonitor(client, mBiometricStateCallback); 741 }); 742 } 743 744 @Override scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken)745 public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) { 746 mHandler.post(() -> { 747 if (getEnrolledFaces(sensorId, userId).isEmpty()) { 748 Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId); 749 return; 750 } 751 752 scheduleUpdateActiveUserWithoutHandler(userId); 753 754 final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext, 755 mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, 756 createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, 757 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 758 mBiometricContext, hardwareAuthToken); 759 mScheduler.scheduleClientMonitor(client); 760 }); 761 } 762 763 @Override scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature, boolean enabled, @NonNull byte[] hardwareAuthToken, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName)764 public void scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature, 765 boolean enabled, @NonNull byte[] hardwareAuthToken, 766 @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { 767 mHandler.post(() -> { 768 final List<Face> faces = getEnrolledFaces(sensorId, userId); 769 if (faces.isEmpty()) { 770 Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId); 771 return; 772 } 773 774 scheduleUpdateActiveUserWithoutHandler(userId); 775 776 final int faceId = faces.get(0).getBiometricId(); 777 final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, 778 mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, 779 opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext), 780 mBiometricContext, 781 feature, enabled, hardwareAuthToken, faceId); 782 mScheduler.scheduleClientMonitor(client); 783 }); 784 } 785 786 @Override scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature, @Nullable ClientMonitorCallbackConverter listener, @NonNull String opPackageName)787 public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature, 788 @Nullable ClientMonitorCallbackConverter listener, @NonNull String opPackageName) { 789 mHandler.post(() -> { 790 final List<Face> faces = getEnrolledFaces(sensorId, userId); 791 if (faces.isEmpty()) { 792 Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId); 793 return; 794 } 795 796 scheduleUpdateActiveUserWithoutHandler(userId); 797 798 final int faceId = faces.get(0).getBiometricId(); 799 final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon, 800 token, listener, userId, opPackageName, mSensorId, 801 BiometricLogger.ofUnknown(mContext), mBiometricContext, 802 feature, faceId); 803 mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { 804 @Override 805 public void onClientFinished( 806 @NonNull BaseClientMonitor clientMonitor, boolean success) { 807 if (success && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) { 808 final int settingsValue = client.getValue() ? 1 : 0; 809 Slog.d(TAG, "Updating attention value for user: " + userId 810 + " to value: " + settingsValue); 811 Settings.Secure.putIntForUser(mContext.getContentResolver(), 812 Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 813 settingsValue, userId); 814 } 815 } 816 }); 817 }); 818 } 819 scheduleInternalCleanup(int userId, @Nullable ClientMonitorCallback callback)820 private void scheduleInternalCleanup(int userId, 821 @Nullable ClientMonitorCallback callback) { 822 mHandler.post(() -> { 823 scheduleUpdateActiveUserWithoutHandler(userId); 824 825 final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, 826 mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, 827 createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, 828 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 829 mBiometricContext, 830 FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); 831 mScheduler.scheduleClientMonitor(client, new ClientMonitorCompositeCallback(callback, 832 mBiometricStateCallback)); 833 }); 834 } 835 836 @Override scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback)837 public void scheduleInternalCleanup(int sensorId, int userId, 838 @Nullable ClientMonitorCallback callback) { 839 scheduleInternalCleanup(userId, mBiometricStateCallback); 840 } 841 842 @Override scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments)843 public void scheduleInternalCleanup(int sensorId, int userId, 844 @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) { 845 scheduleInternalCleanup(userId, callback); 846 } 847 848 @Override startPreparedClient(int sensorId, int cookie)849 public void startPreparedClient(int sensorId, int cookie) { 850 mHandler.post(() -> { 851 mScheduler.startPreparedClient(cookie); 852 }); 853 } 854 855 @Override dumpProtoState(int sensorId, ProtoOutputStream proto, boolean clearSchedulerBuffer)856 public void dumpProtoState(int sensorId, ProtoOutputStream proto, 857 boolean clearSchedulerBuffer) { 858 final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); 859 860 proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); 861 proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE); 862 proto.write(SensorStateProto.CURRENT_STRENGTH, 863 Utils.getCurrentStrength(mSensorProperties.sensorId)); 864 proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer)); 865 866 for (UserInfo user : UserManager.get(mContext).getUsers()) { 867 final int userId = user.getUserHandle().getIdentifier(); 868 869 final long userToken = proto.start(SensorStateProto.USER_STATES); 870 proto.write(UserStateProto.USER_ID, userId); 871 proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getLegacyInstance(mSensorId) 872 .getBiometricsForUser(mContext, userId).size()); 873 proto.end(userToken); 874 } 875 876 proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN, 877 mSensorProperties.resetLockoutRequiresHardwareAuthToken); 878 proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE, 879 mSensorProperties.resetLockoutRequiresChallenge); 880 881 proto.end(sensorToken); 882 } 883 884 @Override dumpProtoMetrics(int sensorId, FileDescriptor fd)885 public void dumpProtoMetrics(int sensorId, FileDescriptor fd) { 886 } 887 888 @Override dumpInternal(int sensorId, PrintWriter pw)889 public void dumpInternal(int sensorId, PrintWriter pw) { 890 PerformanceTracker performanceTracker = 891 PerformanceTracker.getInstanceForSensorId(mSensorId); 892 893 JSONObject dump = new JSONObject(); 894 try { 895 dump.put("service", TAG); 896 897 JSONArray sets = new JSONArray(); 898 for (UserInfo user : UserManager.get(mContext).getUsers()) { 899 final int userId = user.getUserHandle().getIdentifier(); 900 final int c = FaceUtils.getLegacyInstance(mSensorId) 901 .getBiometricsForUser(mContext, userId).size(); 902 JSONObject set = new JSONObject(); 903 set.put("id", userId); 904 set.put("count", c); 905 set.put("accept", performanceTracker.getAcceptForUser(userId)); 906 set.put("reject", performanceTracker.getRejectForUser(userId)); 907 set.put("acquire", performanceTracker.getAcquireForUser(userId)); 908 set.put("lockout", performanceTracker.getTimedLockoutForUser(userId)); 909 set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId)); 910 // cryptoStats measures statistics about secure face transactions 911 // (e.g. to unlock password storage, make secure purchases, etc.) 912 set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId)); 913 set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId)); 914 set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId)); 915 sets.put(set); 916 } 917 918 dump.put("prints", sets); 919 } catch (JSONException e) { 920 Slog.e(TAG, "dump formatting failure", e); 921 } 922 pw.println(dump); 923 pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); 924 925 mScheduler.dump(pw); 926 mUsageStats.print(pw); 927 } 928 scheduleLoadAuthenticatorIds()929 private void scheduleLoadAuthenticatorIds() { 930 // Note that this can be performed on the scheduler (as opposed to being done immediately 931 // when the HAL is (re)loaded, since 932 // 1) If this is truly the first time it's being performed (e.g. system has just started), 933 // this will be run very early and way before any applications need to generate keys. 934 // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has 935 // just been reloaded), the framework already has a cache of the authenticatorIds. This 936 // is safe because authenticatorIds only change when A) new template has been enrolled, 937 // or B) all templates are removed. 938 mHandler.post(() -> { 939 for (UserInfo user : UserManager.get(mContext).getAliveUsers()) { 940 final int targetUserId = user.id; 941 if (!mAuthenticatorIds.containsKey(targetUserId)) { 942 scheduleUpdateActiveUserWithoutHandler(targetUserId); 943 } 944 } 945 }); 946 } 947 948 /** 949 * Schedules the {@link FaceUpdateActiveUserClient} without posting the work onto the handler. 950 * Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser" 951 * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule 952 * this operation on the same lambda/runnable as those operations so that the ordering is 953 * correct. 954 */ scheduleUpdateActiveUserWithoutHandler(int targetUserId)955 private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) { 956 final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty(); 957 final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext, 958 mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, 959 createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, 960 BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), 961 mBiometricContext, hasEnrolled, mAuthenticatorIds); 962 mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { 963 @Override 964 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, 965 boolean success) { 966 if (success) { 967 mCurrentUserId = targetUserId; 968 } else { 969 Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId); 970 } 971 } 972 }); 973 } 974 createLogger(int statsAction, int statsClient, AuthenticationStatsCollector authenticationStatsCollector)975 private BiometricLogger createLogger(int statsAction, int statsClient, 976 AuthenticationStatsCollector authenticationStatsCollector) { 977 return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE, 978 statsAction, statsClient, authenticationStatsCollector); 979 } 980 981 /** 982 * Sends a debug message to the HAL with the provided FileDescriptor and arguments. 983 */ dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args)984 public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) { 985 // WARNING: CDD restricts image data from leaving TEE unencrypted on 986 // production devices: 987 // [C-1-10] MUST not allow unencrypted access to identifiable biometric 988 // data or any data derived from it (such as embeddings) to the 989 // Application Processor outside the context of the TEE. 990 // As such, this API should only be enabled for testing purposes on 991 // engineering and userdebug builds. All modules in the software stack 992 // MUST enforce final build products do NOT have this functionality. 993 // Additionally, the following check MUST NOT be removed. 994 if (!(Build.IS_ENG || Build.IS_USERDEBUG)) { 995 return; 996 } 997 998 // Additionally, this flag allows turning off face for a device 999 // (either permanently through the build or on an individual device). 1000 if (SystemProperties.getBoolean("ro.face.disable_debug_data", false) 1001 || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) { 1002 return; 1003 } 1004 1005 // The debug method takes two file descriptors. The first is for text 1006 // output, which we will drop. The second is for binary data, which 1007 // will be the protobuf data. 1008 final IBiometricsFace daemon = getDaemon(); 1009 if (daemon != null) { 1010 FileOutputStream devnull = null; 1011 try { 1012 devnull = new FileOutputStream("/dev/null"); 1013 final NativeHandle handle = new NativeHandle( 1014 new FileDescriptor[]{devnull.getFD(), fd}, 1015 new int[0], false); 1016 daemon.debug(handle, new ArrayList<String>(Arrays.asList(args))); 1017 } catch (IOException | RemoteException ex) { 1018 Slog.d(TAG, "error while reading face debugging data", ex); 1019 } finally { 1020 if (devnull != null) { 1021 try { 1022 devnull.close(); 1023 } catch (IOException ex) { 1024 } 1025 } 1026 } 1027 } 1028 } 1029 setTestHalEnabled(boolean enabled)1030 void setTestHalEnabled(boolean enabled) { 1031 mTestHalEnabled = enabled; 1032 } 1033 1034 @NonNull 1035 @Override createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName)1036 public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, 1037 @NonNull String opPackageName) { 1038 return new BiometricTestSessionImpl(mContext, mSensorId, callback, 1039 this, mHalResultController); 1040 } 1041 } 1042