1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.biometrics.face; 18 19 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 20 import static android.Manifest.permission.MANAGE_BIOMETRIC; 21 import static android.Manifest.permission.RESET_FACE_LOCKOUT; 22 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; 23 24 import android.app.ActivityManager; 25 import android.app.AppOpsManager; 26 import android.app.Notification; 27 import android.app.NotificationChannel; 28 import android.app.NotificationManager; 29 import android.app.PendingIntent; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.pm.UserInfo; 33 import android.hardware.biometrics.BiometricAuthenticator; 34 import android.hardware.biometrics.BiometricConstants; 35 import android.hardware.biometrics.BiometricsProtoEnums; 36 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; 37 import android.hardware.biometrics.IBiometricServiceReceiverInternal; 38 import android.hardware.biometrics.face.V1_0.IBiometricsFace; 39 import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; 40 import android.hardware.biometrics.face.V1_0.OptionalBool; 41 import android.hardware.biometrics.face.V1_0.Status; 42 import android.hardware.face.Face; 43 import android.hardware.face.FaceManager; 44 import android.hardware.face.IFaceService; 45 import android.hardware.face.IFaceServiceReceiver; 46 import android.os.Binder; 47 import android.os.Build; 48 import android.os.Environment; 49 import android.os.IBinder; 50 import android.os.NativeHandle; 51 import android.os.RemoteException; 52 import android.os.SELinux; 53 import android.os.SystemProperties; 54 import android.os.UserHandle; 55 import android.os.UserManager; 56 import android.util.Slog; 57 import android.util.proto.ProtoOutputStream; 58 59 import com.android.internal.R; 60 import com.android.internal.annotations.GuardedBy; 61 import com.android.internal.logging.MetricsLogger; 62 import com.android.internal.util.DumpUtils; 63 import com.android.server.SystemServerInitThreadPool; 64 import com.android.server.biometrics.AuthenticationClient; 65 import com.android.server.biometrics.BiometricServiceBase; 66 import com.android.server.biometrics.BiometricUtils; 67 import com.android.server.biometrics.Constants; 68 import com.android.server.biometrics.EnumerateClient; 69 import com.android.server.biometrics.RemovalClient; 70 71 import org.json.JSONArray; 72 import org.json.JSONException; 73 import org.json.JSONObject; 74 75 import java.io.File; 76 import java.io.FileDescriptor; 77 import java.io.FileOutputStream; 78 import java.io.IOException; 79 import java.io.PrintWriter; 80 import java.util.ArrayList; 81 import java.util.Arrays; 82 import java.util.List; 83 84 /** 85 * A service to manage multiple clients that want to access the face HAL API. 86 * The service is responsible for maintaining a list of clients and dispatching all 87 * face-related events. 88 * 89 * @hide 90 */ 91 public class FaceService extends BiometricServiceBase { 92 93 protected static final String TAG = "FaceService"; 94 private static final boolean DEBUG = true; 95 private static final String FACE_DATA_DIR = "facedata"; 96 private static final String ACTION_LOCKOUT_RESET = 97 "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET"; 98 private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes 99 100 private final class FaceAuthClient extends AuthenticationClientImpl { 101 private int mLastAcquire; 102 FaceAuthClient(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation)103 public FaceAuthClient(Context context, 104 DaemonWrapper daemon, long halDeviceId, IBinder token, 105 ServiceListener listener, int targetUserId, int groupId, long opId, 106 boolean restricted, String owner, int cookie, boolean requireConfirmation) { 107 super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId, 108 restricted, owner, cookie, requireConfirmation); 109 } 110 111 @Override statsModality()112 protected int statsModality() { 113 return FaceService.this.statsModality(); 114 } 115 116 @Override shouldFrameworkHandleLockout()117 public boolean shouldFrameworkHandleLockout() { 118 return false; 119 } 120 121 @Override wasUserDetected()122 public boolean wasUserDetected() { 123 return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED; 124 } 125 126 @Override onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token)127 public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, 128 boolean authenticated, ArrayList<Byte> token) { 129 final boolean result = super.onAuthenticated(identifier, authenticated, token); 130 131 // For face, the authentication lifecycle ends either when 132 // 1) Authenticated == true 133 // 2) Error occurred 134 // 3) Authenticated == false 135 // Fingerprint currently does not end when the third condition is met which is a bug, 136 // but let's leave it as-is for now. 137 return result || !authenticated; 138 } 139 140 @Override getAcquireIgnorelist()141 public int[] getAcquireIgnorelist() { 142 if (isBiometricPrompt()) { 143 return mBiometricPromptIgnoreList; 144 } else { 145 // Keyguard 146 return mKeyguardIgnoreList; 147 } 148 } 149 150 @Override getAcquireVendorIgnorelist()151 public int[] getAcquireVendorIgnorelist() { 152 if (isBiometricPrompt()) { 153 return mBiometricPromptIgnoreListVendor; 154 } else { 155 // Keyguard 156 return mKeyguardIgnoreListVendor; 157 } 158 } 159 160 @Override onAcquired(int acquireInfo, int vendorCode)161 public boolean onAcquired(int acquireInfo, int vendorCode) { 162 163 mLastAcquire = acquireInfo; 164 165 if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) { 166 final String name = 167 getContext().getString(R.string.face_recalibrate_notification_name); 168 final String title = 169 getContext().getString(R.string.face_recalibrate_notification_title); 170 final String content = 171 getContext().getString(R.string.face_recalibrate_notification_content); 172 173 final Intent intent = new Intent("android.settings.FACE_SETTINGS"); 174 intent.setPackage("com.android.settings"); 175 176 final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(), 177 0 /* requestCode */, intent, 0 /* flags */, null /* options */, 178 UserHandle.CURRENT); 179 180 final String id = "FaceService"; 181 182 NotificationManager nm = 183 getContext().getSystemService(NotificationManager.class); 184 NotificationChannel channel = new NotificationChannel(id, name, 185 NotificationManager.IMPORTANCE_HIGH); 186 Notification notification = new Notification.Builder(getContext(), id) 187 .setSmallIcon(R.drawable.ic_lock) 188 .setContentTitle(title) 189 .setContentText(content) 190 .setSubText(name) 191 .setOnlyAlertOnce(true) 192 .setLocalOnly(true) 193 .setAutoCancel(true) 194 .setCategory(Notification.CATEGORY_SYSTEM) 195 .setContentIntent(pendingIntent) 196 .build(); 197 198 nm.createNotificationChannel(channel); 199 nm.notifyAsUser(null /* tag */, 0 /* id */, notification, UserHandle.CURRENT); 200 } 201 202 return super.onAcquired(acquireInfo, vendorCode); 203 } 204 } 205 206 /** 207 * Receives the incoming binder calls from FaceManager. 208 */ 209 private final class FaceServiceWrapper extends IFaceService.Stub { 210 211 /** 212 * The following methods contain common code which is shared in biometrics/common. 213 */ 214 215 @Override // Binder call generateChallenge(IBinder token)216 public long generateChallenge(IBinder token) { 217 checkPermission(MANAGE_BIOMETRIC); 218 return startGenerateChallenge(token); 219 } 220 221 @Override // Binder call revokeChallenge(IBinder token)222 public int revokeChallenge(IBinder token) { 223 checkPermission(MANAGE_BIOMETRIC); 224 return startRevokeChallenge(token); 225 } 226 227 @Override // Binder call enroll(final IBinder token, final byte[] cryptoToken, final IFaceServiceReceiver receiver, final String opPackageName, final int[] disabledFeatures)228 public void enroll(final IBinder token, final byte[] cryptoToken, 229 final IFaceServiceReceiver receiver, final String opPackageName, 230 final int[] disabledFeatures) { 231 checkPermission(MANAGE_BIOMETRIC); 232 233 final boolean restricted = isRestricted(); 234 final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, 235 mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 236 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) { 237 238 @Override 239 public int[] getAcquireIgnorelist() { 240 return mEnrollIgnoreList; 241 } 242 243 @Override 244 public int[] getAcquireVendorIgnorelist() { 245 return mEnrollIgnoreListVendor; 246 } 247 248 @Override 249 public boolean shouldVibrate() { 250 return false; 251 } 252 253 @Override 254 protected int statsModality() { 255 return FaceService.this.statsModality(); 256 } 257 }; 258 259 enrollInternal(client, mCurrentUserId); 260 } 261 262 @Override // Binder call cancelEnrollment(final IBinder token)263 public void cancelEnrollment(final IBinder token) { 264 checkPermission(MANAGE_BIOMETRIC); 265 cancelEnrollmentInternal(token); 266 } 267 268 @Override // Binder call authenticate(final IBinder token, final long opId, int userId, final IFaceServiceReceiver receiver, final int flags, final String opPackageName)269 public void authenticate(final IBinder token, final long opId, int userId, 270 final IFaceServiceReceiver receiver, final int flags, 271 final String opPackageName) { 272 checkPermission(USE_BIOMETRIC_INTERNAL); 273 updateActiveGroup(userId, opPackageName); 274 final boolean restricted = isRestricted(); 275 final AuthenticationClientImpl client = new FaceAuthClient(getContext(), 276 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), 277 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, 278 0 /* cookie */, false /* requireConfirmation */); 279 authenticateInternal(client, opId, opPackageName); 280 } 281 282 @Override // Binder call prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId, int groupId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)283 public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId, 284 int groupId, IBiometricServiceReceiverInternal wrapperReceiver, 285 String opPackageName, int cookie, int callingUid, int callingPid, 286 int callingUserId) { 287 checkPermission(USE_BIOMETRIC_INTERNAL); 288 updateActiveGroup(groupId, opPackageName); 289 final boolean restricted = true; // BiometricPrompt is always restricted 290 final AuthenticationClientImpl client = new FaceAuthClient(getContext(), 291 mDaemonWrapper, mHalDeviceId, token, 292 new BiometricPromptServiceListenerImpl(wrapperReceiver), 293 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie, 294 requireConfirmation); 295 authenticateInternal(client, opId, opPackageName, callingUid, callingPid, 296 callingUserId); 297 } 298 299 @Override // Binder call startPreparedClient(int cookie)300 public void startPreparedClient(int cookie) { 301 checkPermission(MANAGE_BIOMETRIC); 302 startCurrentClient(cookie); 303 } 304 305 @Override // Binder call cancelAuthentication(final IBinder token, final String opPackageName)306 public void cancelAuthentication(final IBinder token, final String opPackageName) { 307 checkPermission(USE_BIOMETRIC_INTERNAL); 308 cancelAuthenticationInternal(token, opPackageName); 309 } 310 311 @Override // Binder call cancelAuthenticationFromService(final IBinder token, final String opPackageName, int callingUid, int callingPid, int callingUserId, boolean fromClient)312 public void cancelAuthenticationFromService(final IBinder token, final String opPackageName, 313 int callingUid, int callingPid, int callingUserId, boolean fromClient) { 314 checkPermission(USE_BIOMETRIC_INTERNAL); 315 cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, 316 callingUserId, fromClient); 317 } 318 319 @Override // Binder call setActiveUser(final int userId)320 public void setActiveUser(final int userId) { 321 checkPermission(MANAGE_BIOMETRIC); 322 setActiveUserInternal(userId); 323 } 324 325 @Override // Binder call remove(final IBinder token, final int faceId, final int userId, final IFaceServiceReceiver receiver)326 public void remove(final IBinder token, final int faceId, final int userId, 327 final IFaceServiceReceiver receiver) { 328 checkPermission(MANAGE_BIOMETRIC); 329 330 if (token == null) { 331 Slog.w(TAG, "remove(): token is null"); 332 return; 333 } 334 335 final boolean restricted = isRestricted(); 336 final RemovalClient client = new RemovalClient(getContext(), getConstants(), 337 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 338 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) { 339 @Override 340 protected int statsModality() { 341 return FaceService.this.statsModality(); 342 } 343 }; 344 removeInternal(client); 345 } 346 347 @Override enumerate(final IBinder token, final int userId, final IFaceServiceReceiver receiver)348 public void enumerate(final IBinder token, final int userId, 349 final IFaceServiceReceiver receiver) { 350 checkPermission(MANAGE_BIOMETRIC); 351 352 final boolean restricted = isRestricted(); 353 final EnumerateClient client = new EnumerateClient(getContext(), getConstants(), 354 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, 355 userId, restricted, getContext().getOpPackageName()) { 356 @Override 357 protected int statsModality() { 358 return FaceService.this.statsModality(); 359 } 360 }; 361 enumerateInternal(client); 362 } 363 364 @Override addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)365 public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback) 366 throws RemoteException { 367 checkPermission(USE_BIOMETRIC_INTERNAL); 368 FaceService.super.addLockoutResetCallback(callback); 369 } 370 371 @Override // Binder call dump(FileDescriptor fd, PrintWriter pw, String[] args)372 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 373 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { 374 return; 375 } 376 377 final long ident = Binder.clearCallingIdentity(); 378 try { 379 if (args.length > 1 && "--hal".equals(args[0])) { 380 dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass())); 381 } else if (args.length > 0 && "--proto".equals(args[0])) { 382 dumpProto(fd); 383 } else { 384 dumpInternal(pw); 385 } 386 } finally { 387 Binder.restoreCallingIdentity(ident); 388 } 389 } 390 391 /** 392 * The following methods don't use any common code from BiometricService 393 */ 394 395 // TODO: refactor out common code here 396 @Override // Binder call isHardwareDetected(long deviceId, String opPackageName)397 public boolean isHardwareDetected(long deviceId, String opPackageName) { 398 checkPermission(USE_BIOMETRIC_INTERNAL); 399 if (!canUseBiometric(opPackageName, false /* foregroundOnly */, 400 Binder.getCallingUid(), Binder.getCallingPid(), 401 UserHandle.getCallingUserId())) { 402 return false; 403 } 404 405 final long token = Binder.clearCallingIdentity(); 406 try { 407 IBiometricsFace daemon = getFaceDaemon(); 408 return daemon != null && mHalDeviceId != 0; 409 } finally { 410 Binder.restoreCallingIdentity(token); 411 } 412 } 413 414 @Override // Binder call rename(final int faceId, final String name)415 public void rename(final int faceId, final String name) { 416 checkPermission(MANAGE_BIOMETRIC); 417 if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) { 418 return; 419 } 420 mHandler.post(new Runnable() { 421 @Override 422 public void run() { 423 getBiometricUtils().renameBiometricForUser(getContext(), mCurrentUserId, 424 faceId, name); 425 } 426 }); 427 } 428 429 @Override // Binder call getEnrolledFaces(int userId, String opPackageName)430 public List<Face> getEnrolledFaces(int userId, String opPackageName) { 431 checkPermission(MANAGE_BIOMETRIC); 432 if (!canUseBiometric(opPackageName, false /* foregroundOnly */, 433 Binder.getCallingUid(), Binder.getCallingPid(), 434 UserHandle.getCallingUserId())) { 435 return null; 436 } 437 438 return FaceService.this.getEnrolledTemplates(userId); 439 } 440 441 @Override // Binder call hasEnrolledFaces(int userId, String opPackageName)442 public boolean hasEnrolledFaces(int userId, String opPackageName) { 443 checkPermission(USE_BIOMETRIC_INTERNAL); 444 if (!canUseBiometric(opPackageName, false /* foregroundOnly */, 445 Binder.getCallingUid(), Binder.getCallingPid(), 446 UserHandle.getCallingUserId())) { 447 return false; 448 } 449 450 return FaceService.this.hasEnrolledBiometrics(userId); 451 } 452 453 @Override // Binder call getAuthenticatorId(String opPackageName)454 public long getAuthenticatorId(String opPackageName) { 455 // In this method, we're not checking whether the caller is permitted to use face 456 // API because current authenticator ID is leaked (in a more contrived way) via Android 457 // Keystore (android.security.keystore package): the user of that API can create a key 458 // which requires face authentication for its use, and then query the key's 459 // characteristics (hidden API) which returns, among other things, face 460 // authenticator ID which was active at key creation time. 461 // 462 // Reason: The part of Android Keystore which runs inside an app's process invokes this 463 // method in certain cases. Those cases are not always where the developer demonstrates 464 // explicit intent to use face functionality. Thus, to avoiding throwing an 465 // unexpected SecurityException this method does not check whether its caller is 466 // permitted to use face API. 467 // 468 // The permission check should be restored once Android Keystore no longer invokes this 469 // method from inside app processes. 470 471 return FaceService.this.getAuthenticatorId(opPackageName); 472 } 473 474 @Override // Binder call resetLockout(byte[] token)475 public void resetLockout(byte[] token) { 476 checkPermission(MANAGE_BIOMETRIC); 477 478 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) { 479 Slog.w(TAG, "Ignoring lockout reset, no templates enrolled"); 480 return; 481 } 482 483 try { 484 mDaemonWrapper.resetLockout(token); 485 } catch (RemoteException e) { 486 Slog.e(getTag(), "Unable to reset lockout", e); 487 } 488 } 489 490 @Override setFeature(int feature, boolean enabled, final byte[] token, IFaceServiceReceiver receiver)491 public void setFeature(int feature, boolean enabled, final byte[] token, 492 IFaceServiceReceiver receiver) { 493 checkPermission(MANAGE_BIOMETRIC); 494 495 mHandler.post(() -> { 496 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) { 497 Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature); 498 return; 499 } 500 501 final ArrayList<Byte> byteToken = new ArrayList<>(); 502 for (int i = 0; i < token.length; i++) { 503 byteToken.add(token[i]); 504 } 505 506 // TODO: Support multiple faces 507 final int faceId = getFirstTemplateForUser(mCurrentUserId); 508 509 if (mDaemon != null) { 510 try { 511 final int result = mDaemon.setFeature(feature, enabled, byteToken, faceId); 512 receiver.onFeatureSet(result == Status.OK, feature); 513 } catch (RemoteException e) { 514 Slog.e(getTag(), "Unable to set feature: " + feature 515 + " to enabled:" + enabled, e); 516 } 517 } 518 }); 519 520 } 521 522 @Override getFeature(int feature, IFaceServiceReceiver receiver)523 public void getFeature(int feature, IFaceServiceReceiver receiver) { 524 checkPermission(MANAGE_BIOMETRIC); 525 526 mHandler.post(() -> { 527 // This should ideally return tri-state, but the user isn't shown settings unless 528 // they are enrolled so it's fine for now. 529 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) { 530 Slog.e(TAG, "No enrolled biometrics while getting feature: " + feature); 531 return; 532 } 533 534 // TODO: Support multiple faces 535 final int faceId = getFirstTemplateForUser(mCurrentUserId); 536 537 if (mDaemon != null) { 538 try { 539 OptionalBool result = mDaemon.getFeature(feature, faceId); 540 receiver.onFeatureGet(result.status == Status.OK, feature, result.value); 541 } catch (RemoteException e) { 542 Slog.e(getTag(), "Unable to getRequireAttention", e); 543 } 544 } 545 }); 546 547 } 548 549 @Override userActivity()550 public void userActivity() { 551 checkPermission(MANAGE_BIOMETRIC); 552 553 if (mDaemon != null) { 554 try { 555 mDaemon.userActivity(); 556 } catch (RemoteException e) { 557 Slog.e(getTag(), "Unable to send userActivity", e); 558 } 559 } 560 } 561 562 // TODO: Support multiple faces getFirstTemplateForUser(int user)563 private int getFirstTemplateForUser(int user) { 564 final List<Face> faces = FaceService.this.getEnrolledTemplates(user); 565 if (!faces.isEmpty()) { 566 return faces.get(0).getBiometricId(); 567 } 568 return 0; 569 } 570 } 571 572 /** 573 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to 574 * BiometricPrompt. 575 */ 576 private class BiometricPromptServiceListenerImpl extends BiometricServiceListener { BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver)577 BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) { 578 super(wrapperReceiver); 579 } 580 581 @Override onAcquired(long deviceId, int acquiredInfo, int vendorCode)582 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) 583 throws RemoteException { 584 /** 585 * Map the acquired codes onto existing {@link BiometricConstants} acquired codes. 586 */ 587 if (getWrapperReceiver() != null) { 588 getWrapperReceiver().onAcquired( 589 FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode), 590 FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode)); 591 } 592 } 593 594 @Override onError(long deviceId, int error, int vendorCode, int cookie)595 public void onError(long deviceId, int error, int vendorCode, int cookie) 596 throws RemoteException { 597 if (getWrapperReceiver() != null) { 598 getWrapperReceiver().onError(cookie, error, 599 FaceManager.getErrorString(getContext(), error, vendorCode)); 600 } 601 } 602 } 603 604 /** 605 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to 606 * the FaceManager. 607 */ 608 private class ServiceListenerImpl implements ServiceListener { 609 private IFaceServiceReceiver mFaceServiceReceiver; 610 ServiceListenerImpl(IFaceServiceReceiver receiver)611 public ServiceListenerImpl(IFaceServiceReceiver receiver) { 612 mFaceServiceReceiver = receiver; 613 } 614 615 @Override onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)616 public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) 617 throws RemoteException { 618 if (mFaceServiceReceiver != null) { 619 mFaceServiceReceiver.onEnrollResult(identifier.getDeviceId(), 620 identifier.getBiometricId(), 621 remaining); 622 } 623 } 624 625 @Override onAcquired(long deviceId, int acquiredInfo, int vendorCode)626 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) 627 throws RemoteException { 628 if (mFaceServiceReceiver != null) { 629 mFaceServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode); 630 } 631 } 632 633 @Override onAuthenticationSucceeded(long deviceId, BiometricAuthenticator.Identifier biometric, int userId)634 public void onAuthenticationSucceeded(long deviceId, 635 BiometricAuthenticator.Identifier biometric, int userId) 636 throws RemoteException { 637 if (mFaceServiceReceiver != null) { 638 if (biometric == null || biometric instanceof Face) { 639 mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face) biometric, 640 userId); 641 } else { 642 Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric"); 643 } 644 } 645 } 646 647 @Override onAuthenticationFailed(long deviceId)648 public void onAuthenticationFailed(long deviceId) throws RemoteException { 649 if (mFaceServiceReceiver != null) { 650 mFaceServiceReceiver.onAuthenticationFailed(deviceId); 651 } 652 } 653 654 @Override onError(long deviceId, int error, int vendorCode, int cookie)655 public void onError(long deviceId, int error, int vendorCode, int cookie) 656 throws RemoteException { 657 if (mFaceServiceReceiver != null) { 658 mFaceServiceReceiver.onError(deviceId, error, vendorCode); 659 } 660 } 661 662 @Override onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)663 public void onRemoved(BiometricAuthenticator.Identifier identifier, 664 int remaining) throws RemoteException { 665 if (mFaceServiceReceiver != null) { 666 mFaceServiceReceiver.onRemoved(identifier.getDeviceId(), 667 identifier.getBiometricId(), remaining); 668 } 669 } 670 671 @Override onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)672 public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining) 673 throws RemoteException { 674 if (mFaceServiceReceiver != null) { 675 mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(), 676 identifier.getBiometricId(), remaining); 677 } 678 } 679 } 680 681 private final FaceConstants mFaceConstants = new FaceConstants(); 682 683 @GuardedBy("this") 684 private IBiometricsFace mDaemon; 685 // One of the AuthenticationClient constants 686 private int mCurrentUserLockoutMode; 687 688 private int[] mBiometricPromptIgnoreList; 689 private int[] mBiometricPromptIgnoreListVendor; 690 private int[] mKeyguardIgnoreList; 691 private int[] mKeyguardIgnoreListVendor; 692 private int[] mEnrollIgnoreList; 693 private int[] mEnrollIgnoreListVendor; 694 695 /** 696 * Receives callbacks from the HAL. 697 */ 698 private IBiometricsFaceClientCallback mDaemonCallback = 699 new IBiometricsFaceClientCallback.Stub() { 700 @Override 701 public void onEnrollResult(final long deviceId, int faceId, int userId, 702 int remaining) { 703 mHandler.post(() -> { 704 final Face face = new Face(getBiometricUtils() 705 .getUniqueName(getContext(), userId), faceId, deviceId); 706 FaceService.super.handleEnrollResult(face, remaining); 707 }); 708 } 709 710 @Override 711 public void onAcquired(final long deviceId, final int userId, 712 final int acquiredInfo, 713 final int vendorCode) { 714 mHandler.post(() -> { 715 FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode); 716 }); 717 } 718 719 @Override 720 public void onAuthenticated(final long deviceId, final int faceId, final int userId, 721 ArrayList<Byte> token) { 722 mHandler.post(() -> { 723 Face face = new Face("", faceId, deviceId); 724 FaceService.super.handleAuthenticated(face, token); 725 }); 726 } 727 728 @Override 729 public void onError(final long deviceId, final int userId, final int error, 730 final int vendorCode) { 731 mHandler.post(() -> { 732 FaceService.super.handleError(deviceId, error, vendorCode); 733 734 // TODO: this chunk of code should be common to all biometric services 735 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { 736 // If we get HW_UNAVAILABLE, try to connect again later... 737 Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client."); 738 synchronized (this) { 739 mDaemon = null; 740 mHalDeviceId = 0; 741 mCurrentUserId = UserHandle.USER_NULL; 742 } 743 } 744 }); 745 } 746 747 @Override 748 public void onRemoved(final long deviceId, ArrayList<Integer> faceIds, final int userId) { 749 mHandler.post(() -> { 750 if (!faceIds.isEmpty()) { 751 for (int i = 0; i < faceIds.size(); i++) { 752 final Face face = new Face("", faceIds.get(i), deviceId); 753 // Convert to old behavior 754 FaceService.super.handleRemoved(face, faceIds.size() - i - 1); 755 } 756 } else { 757 final Face face = new Face("", 0 /* identifier */, deviceId); 758 FaceService.super.handleRemoved(face, 0 /* remaining */); 759 } 760 761 }); 762 } 763 764 @Override 765 public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) 766 throws RemoteException { 767 mHandler.post(() -> { 768 if (!faceIds.isEmpty()) { 769 for (int i = 0; i < faceIds.size(); i++) { 770 final Face face = new Face("", faceIds.get(i), deviceId); 771 // Convert to old old behavior 772 FaceService.super.handleEnumerate(face, faceIds.size() - i - 1); 773 } 774 } else { 775 // For face, the HIDL contract is to receive an empty list when there are no 776 // templates enrolled. Send a null identifier since we don't consume them 777 // anywhere, and send remaining == 0 to plumb this with existing common code. 778 FaceService.super.handleEnumerate(null /* identifier */, 0); 779 } 780 }); 781 } 782 783 @Override 784 public void onLockoutChanged(long duration) { 785 Slog.d(TAG, "onLockoutChanged: " + duration); 786 if (duration == 0) { 787 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE; 788 } else if (duration == Long.MAX_VALUE) { 789 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT; 790 } else { 791 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED; 792 } 793 794 mHandler.post(() -> { 795 if (duration == 0) { 796 notifyLockoutResetMonitors(); 797 } 798 }); 799 } 800 }; 801 802 /** 803 * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they 804 * can be shared between the multiple biometric services. 805 */ 806 private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() { 807 @Override 808 public int authenticate(long operationId, int groupId) throws RemoteException { 809 IBiometricsFace daemon = getFaceDaemon(); 810 if (daemon == null) { 811 Slog.w(TAG, "authenticate(): no face HAL!"); 812 return ERROR_ESRCH; 813 } 814 return daemon.authenticate(operationId); 815 } 816 817 @Override 818 public int cancel() throws RemoteException { 819 IBiometricsFace daemon = getFaceDaemon(); 820 if (daemon == null) { 821 Slog.w(TAG, "cancel(): no face HAL!"); 822 return ERROR_ESRCH; 823 } 824 return daemon.cancel(); 825 } 826 827 @Override 828 public int remove(int groupId, int biometricId) throws RemoteException { 829 IBiometricsFace daemon = getFaceDaemon(); 830 if (daemon == null) { 831 Slog.w(TAG, "remove(): no face HAL!"); 832 return ERROR_ESRCH; 833 } 834 return daemon.remove(biometricId); 835 } 836 837 @Override 838 public int enumerate() throws RemoteException { 839 IBiometricsFace daemon = getFaceDaemon(); 840 if (daemon == null) { 841 Slog.w(TAG, "enumerate(): no face HAL!"); 842 return ERROR_ESRCH; 843 } 844 return daemon.enumerate(); 845 } 846 847 @Override 848 public int enroll(byte[] cryptoToken, int groupId, int timeout, 849 ArrayList<Integer> disabledFeatures) throws RemoteException { 850 IBiometricsFace daemon = getFaceDaemon(); 851 if (daemon == null) { 852 Slog.w(TAG, "enroll(): no face HAL!"); 853 return ERROR_ESRCH; 854 } 855 final ArrayList<Byte> token = new ArrayList<>(); 856 for (int i = 0; i < cryptoToken.length; i++) { 857 token.add(cryptoToken[i]); 858 } 859 return daemon.enroll(token, timeout, disabledFeatures); 860 } 861 862 @Override 863 public void resetLockout(byte[] cryptoToken) throws RemoteException { 864 IBiometricsFace daemon = getFaceDaemon(); 865 if (daemon == null) { 866 Slog.w(TAG, "resetLockout(): no face HAL!"); 867 return; 868 } 869 final ArrayList<Byte> token = new ArrayList<>(); 870 for (int i = 0; i < cryptoToken.length; i++) { 871 token.add(cryptoToken[i]); 872 } 873 daemon.resetLockout(token); 874 } 875 }; 876 877 FaceService(Context context)878 public FaceService(Context context) { 879 super(context); 880 881 mBiometricPromptIgnoreList = getContext().getResources() 882 .getIntArray(R.array.config_face_acquire_biometricprompt_ignorelist); 883 mBiometricPromptIgnoreListVendor = getContext().getResources() 884 .getIntArray(R.array.config_face_acquire_vendor_biometricprompt_ignorelist); 885 mKeyguardIgnoreList = getContext().getResources() 886 .getIntArray(R.array.config_face_acquire_keyguard_ignorelist); 887 mKeyguardIgnoreListVendor = getContext().getResources() 888 .getIntArray(R.array.config_face_acquire_vendor_keyguard_ignorelist); 889 mEnrollIgnoreList = getContext().getResources() 890 .getIntArray(R.array.config_face_acquire_enroll_ignorelist); 891 mEnrollIgnoreListVendor = getContext().getResources() 892 .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); 893 } 894 895 @Override onStart()896 public void onStart() { 897 super.onStart(); 898 publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper()); 899 SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart"); 900 } 901 902 @Override getTag()903 public String getTag() { 904 return TAG; 905 } 906 907 @Override getDaemonWrapper()908 protected DaemonWrapper getDaemonWrapper() { 909 return mDaemonWrapper; 910 } 911 912 @Override getBiometricUtils()913 protected BiometricUtils getBiometricUtils() { 914 return FaceUtils.getInstance(); 915 } 916 917 @Override getConstants()918 protected Constants getConstants() { 919 return mFaceConstants; 920 } 921 922 @Override hasReachedEnrollmentLimit(int userId)923 protected boolean hasReachedEnrollmentLimit(int userId) { 924 final int limit = getContext().getResources().getInteger( 925 com.android.internal.R.integer.config_faceMaxTemplatesPerUser); 926 final int enrolled = FaceService.this.getEnrolledTemplates(userId).size(); 927 if (enrolled >= limit) { 928 Slog.w(TAG, "Too many faces registered, user: " + userId); 929 return true; 930 } 931 return false; 932 } 933 934 @Override serviceDied(long cookie)935 public void serviceDied(long cookie) { 936 super.serviceDied(cookie); 937 mDaemon = null; 938 939 mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate 940 } 941 942 @Override updateActiveGroup(int userId, String clientPackage)943 protected void updateActiveGroup(int userId, String clientPackage) { 944 IBiometricsFace daemon = getFaceDaemon(); 945 946 if (daemon != null) { 947 try { 948 userId = getUserOrWorkProfileId(clientPackage, userId); 949 if (userId != mCurrentUserId) { 950 final File baseDir = Environment.getDataVendorDeDirectory(userId); 951 final File faceDir = new File(baseDir, FACE_DATA_DIR); 952 if (!faceDir.exists()) { 953 if (!faceDir.mkdir()) { 954 Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath()); 955 return; 956 } 957 // Calling mkdir() from this process will create a directory with our 958 // permissions (inherited from the containing dir). This command fixes 959 // the label. 960 if (!SELinux.restorecon(faceDir)) { 961 Slog.w(TAG, "Restorecons failed. Directory will have wrong label."); 962 return; 963 } 964 } 965 966 daemon.setActiveUser(userId, faceDir.getAbsolutePath()); 967 mCurrentUserId = userId; 968 } 969 mAuthenticatorIds.put(userId, 970 hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L); 971 } catch (RemoteException e) { 972 Slog.e(TAG, "Failed to setActiveUser():", e); 973 } 974 } 975 } 976 977 @Override getLockoutResetIntent()978 protected String getLockoutResetIntent() { 979 return ACTION_LOCKOUT_RESET; 980 } 981 982 @Override getLockoutBroadcastPermission()983 protected String getLockoutBroadcastPermission() { 984 return RESET_FACE_LOCKOUT; 985 } 986 987 @Override getHalDeviceId()988 protected long getHalDeviceId() { 989 return mHalDeviceId; 990 } 991 992 @Override handleUserSwitching(int userId)993 protected void handleUserSwitching(int userId) { 994 super.handleUserSwitching(userId); 995 // Will be updated when we get the callback from HAL 996 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE; 997 } 998 999 @Override hasEnrolledBiometrics(int userId)1000 protected boolean hasEnrolledBiometrics(int userId) { 1001 if (userId != UserHandle.getCallingUserId()) { 1002 checkPermission(INTERACT_ACROSS_USERS); 1003 } 1004 return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0; 1005 } 1006 1007 @Override getManageBiometricPermission()1008 protected String getManageBiometricPermission() { 1009 return MANAGE_BIOMETRIC; 1010 } 1011 1012 @Override checkUseBiometricPermission()1013 protected void checkUseBiometricPermission() { 1014 // noop for Face. The permission checks are all done on the incoming binder call. 1015 } 1016 1017 @Override checkAppOps(int uid, String opPackageName)1018 protected boolean checkAppOps(int uid, String opPackageName) { 1019 return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName) 1020 == AppOpsManager.MODE_ALLOWED; 1021 } 1022 1023 @Override getEnrolledTemplates(int userId)1024 protected List<Face> getEnrolledTemplates(int userId) { 1025 return getBiometricUtils().getBiometricsForUser(getContext(), userId); 1026 } 1027 1028 @Override notifyClientActiveCallbacks(boolean isActive)1029 protected void notifyClientActiveCallbacks(boolean isActive) { 1030 // noop for Face. 1031 } 1032 1033 @Override statsModality()1034 protected int statsModality() { 1035 return BiometricsProtoEnums.MODALITY_FACE; 1036 } 1037 1038 @Override getLockoutMode()1039 protected int getLockoutMode() { 1040 return mCurrentUserLockoutMode; 1041 } 1042 1043 /** Gets the face daemon */ getFaceDaemon()1044 private synchronized IBiometricsFace getFaceDaemon() { 1045 if (mDaemon == null) { 1046 Slog.v(TAG, "mDaemon was null, reconnect to face"); 1047 try { 1048 mDaemon = IBiometricsFace.getService(); 1049 } catch (java.util.NoSuchElementException e) { 1050 // Service doesn't exist or cannot be opened. Logged below. 1051 } catch (RemoteException e) { 1052 Slog.e(TAG, "Failed to get biometric interface", e); 1053 } 1054 if (mDaemon == null) { 1055 Slog.w(TAG, "face HIDL not available"); 1056 return null; 1057 } 1058 1059 mDaemon.asBinder().linkToDeath(this, 0); 1060 1061 try { 1062 mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value; 1063 } catch (RemoteException e) { 1064 Slog.e(TAG, "Failed to open face HAL", e); 1065 mDaemon = null; // try again later! 1066 } 1067 1068 if (DEBUG) Slog.v(TAG, "Face HAL id: " + mHalDeviceId); 1069 if (mHalDeviceId != 0) { 1070 loadAuthenticatorIds(); 1071 updateActiveGroup(ActivityManager.getCurrentUser(), null); 1072 doTemplateCleanupForUser(ActivityManager.getCurrentUser()); 1073 } else { 1074 Slog.w(TAG, "Failed to open Face HAL!"); 1075 MetricsLogger.count(getContext(), "faced_openhal_error", 1); 1076 mDaemon = null; 1077 } 1078 } 1079 return mDaemon; 1080 } 1081 startGenerateChallenge(IBinder token)1082 private long startGenerateChallenge(IBinder token) { 1083 IBiometricsFace daemon = getFaceDaemon(); 1084 if (daemon == null) { 1085 Slog.w(TAG, "startGenerateChallenge: no face HAL!"); 1086 return 0; 1087 } 1088 try { 1089 return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value; 1090 } catch (RemoteException e) { 1091 Slog.e(TAG, "startGenerateChallenge failed", e); 1092 } 1093 return 0; 1094 } 1095 startRevokeChallenge(IBinder token)1096 private int startRevokeChallenge(IBinder token) { 1097 IBiometricsFace daemon = getFaceDaemon(); 1098 if (daemon == null) { 1099 Slog.w(TAG, "startRevokeChallenge: no face HAL!"); 1100 return 0; 1101 } 1102 try { 1103 return daemon.revokeChallenge(); 1104 } catch (RemoteException e) { 1105 Slog.e(TAG, "startRevokeChallenge failed", e); 1106 } 1107 return 0; 1108 } 1109 dumpInternal(PrintWriter pw)1110 private void dumpInternal(PrintWriter pw) { 1111 JSONObject dump = new JSONObject(); 1112 try { 1113 dump.put("service", "Face Manager"); 1114 1115 JSONArray sets = new JSONArray(); 1116 for (UserInfo user : UserManager.get(getContext()).getUsers()) { 1117 final int userId = user.getUserHandle().getIdentifier(); 1118 final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size(); 1119 PerformanceStats stats = mPerformanceMap.get(userId); 1120 PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId); 1121 JSONObject set = new JSONObject(); 1122 set.put("id", userId); 1123 set.put("count", N); 1124 set.put("accept", (stats != null) ? stats.accept : 0); 1125 set.put("reject", (stats != null) ? stats.reject : 0); 1126 set.put("acquire", (stats != null) ? stats.acquire : 0); 1127 set.put("lockout", (stats != null) ? stats.lockout : 0); 1128 set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0); 1129 // cryptoStats measures statistics about secure face transactions 1130 // (e.g. to unlock password storage, make secure purchases, etc.) 1131 set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0); 1132 set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0); 1133 set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0); 1134 set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0); 1135 set.put("permanentLockoutCrypto", 1136 (cryptoStats != null) ? cryptoStats.permanentLockout : 0); 1137 sets.put(set); 1138 } 1139 1140 dump.put("prints", sets); 1141 } catch (JSONException e) { 1142 Slog.e(TAG, "dump formatting failure", e); 1143 } 1144 pw.println(dump); 1145 pw.println("HAL Deaths: " + mHALDeathCount); 1146 mHALDeathCount = 0; 1147 } 1148 dumpProto(FileDescriptor fd)1149 private void dumpProto(FileDescriptor fd) { 1150 final ProtoOutputStream proto = new ProtoOutputStream(fd); 1151 for (UserInfo user : UserManager.get(getContext()).getUsers()) { 1152 final int userId = user.getUserHandle().getIdentifier(); 1153 1154 final long userToken = proto.start(FaceServiceDumpProto.USERS); 1155 1156 proto.write(FaceUserStatsProto.USER_ID, userId); 1157 proto.write(FaceUserStatsProto.NUM_FACES, 1158 getBiometricUtils().getBiometricsForUser(getContext(), userId).size()); 1159 1160 // Normal face authentications (e.g. lockscreen) 1161 final PerformanceStats normal = mPerformanceMap.get(userId); 1162 if (normal != null) { 1163 final long countsToken = proto.start(FaceUserStatsProto.NORMAL); 1164 proto.write(FaceActionStatsProto.ACCEPT, normal.accept); 1165 proto.write(FaceActionStatsProto.REJECT, normal.reject); 1166 proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire); 1167 proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout); 1168 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout); 1169 proto.end(countsToken); 1170 } 1171 1172 // Statistics about secure face transactions (e.g. to unlock password 1173 // storage, make secure purchases, etc.) 1174 final PerformanceStats crypto = mCryptoPerformanceMap.get(userId); 1175 if (crypto != null) { 1176 final long countsToken = proto.start(FaceUserStatsProto.CRYPTO); 1177 proto.write(FaceActionStatsProto.ACCEPT, crypto.accept); 1178 proto.write(FaceActionStatsProto.REJECT, crypto.reject); 1179 proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire); 1180 proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout); 1181 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout); 1182 proto.end(countsToken); 1183 } 1184 1185 proto.end(userToken); 1186 } 1187 proto.flush(); 1188 mPerformanceMap.clear(); 1189 mCryptoPerformanceMap.clear(); 1190 } 1191 dumpHal(FileDescriptor fd, String[] args)1192 private void dumpHal(FileDescriptor fd, String[] args) { 1193 // WARNING: CDD restricts image data from leaving TEE unencrypted on 1194 // production devices: 1195 // [C-1-10] MUST not allow unencrypted access to identifiable biometric 1196 // data or any data derived from it (such as embeddings) to the 1197 // Application Processor outside the context of the TEE. 1198 // As such, this API should only be enabled for testing purposes on 1199 // engineering and userdebug builds. All modules in the software stack 1200 // MUST enforce final build products do NOT have this functionality. 1201 // Additionally, the following check MUST NOT be removed. 1202 if (!(Build.IS_ENG || Build.IS_USERDEBUG)) { 1203 return; 1204 } 1205 1206 // Additionally, this flag allows turning off face for a device 1207 // (either permanently through the build or on an individual device). 1208 if (SystemProperties.getBoolean("ro.face.disable_debug_data", false) 1209 || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) { 1210 return; 1211 } 1212 1213 // The debug method takes two file descriptors. The first is for text 1214 // output, which we will drop. The second is for binary data, which 1215 // will be the protobuf data. 1216 final IBiometricsFace daemon = getFaceDaemon(); 1217 if (daemon != null) { 1218 FileOutputStream devnull = null; 1219 try { 1220 devnull = new FileOutputStream("/dev/null"); 1221 final NativeHandle handle = new NativeHandle( 1222 new FileDescriptor[] { devnull.getFD(), fd }, 1223 new int[0], false); 1224 daemon.debug(handle, new ArrayList<String>(Arrays.asList(args))); 1225 } catch (IOException | RemoteException ex) { 1226 Slog.d(TAG, "error while reading face debugging data", ex); 1227 } finally { 1228 if (devnull != null) { 1229 try { 1230 devnull.close(); 1231 } catch (IOException ex) { 1232 } 1233 } 1234 } 1235 } 1236 } 1237 } 1238