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