• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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