blob: cde6f4b1dbf006673efab2e1866109faae960514 [file] [log] [blame]
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001/**
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
17package android.hardware.face;
18
19import static android.Manifest.permission.INTERACT_ACROSS_USERS;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070020import static android.Manifest.permission.MANAGE_BIOMETRIC;
21import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020022
23import android.annotation.NonNull;
24import android.annotation.Nullable;
25import android.annotation.RequiresPermission;
26import android.annotation.SystemService;
27import android.app.ActivityManager;
28import android.content.Context;
Kevin Chyna56dff72018-06-19 18:41:12 -070029import android.hardware.biometrics.BiometricAuthenticator;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070030import android.hardware.biometrics.BiometricConstants;
Kevin Chyna56dff72018-06-19 18:41:12 -070031import android.hardware.biometrics.BiometricFaceConstants;
Ilya Matyukhinef410e32020-02-04 13:39:48 -080032import android.hardware.biometrics.BiometricNativeHandleUtils;
Kevin Chyna56dff72018-06-19 18:41:12 -070033import android.hardware.biometrics.CryptoObject;
Ilya Matyukhinef410e32020-02-04 13:39:48 -080034import android.hardware.biometrics.IBiometricNativeHandle;
Kevin Chyna56dff72018-06-19 18:41:12 -070035import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020036import android.os.Binder;
37import android.os.CancellationSignal;
38import android.os.CancellationSignal.OnCancelListener;
39import android.os.Handler;
40import android.os.IBinder;
41import android.os.IRemoteCallback;
42import android.os.Looper;
Ilya Matyukhinef410e32020-02-04 13:39:48 -080043import android.os.NativeHandle;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020044import android.os.PowerManager;
45import android.os.RemoteException;
Ianfd4021c2019-04-05 13:22:59 -070046import android.os.Trace;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020047import android.os.UserHandle;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020048import android.util.Log;
49import android.util.Slog;
50
51import com.android.internal.R;
Kevin Chyne62749a2019-04-02 19:33:56 -070052import com.android.internal.os.SomeArgs;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020053
Kevin Chyna56dff72018-06-19 18:41:12 -070054import java.util.List;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020055
56/**
57 * A class that coordinates access to the face authentication hardware.
58 * @hide
59 */
60@SystemService(Context.FACE_SERVICE)
Kevin Chyna24e9fd2018-08-27 12:39:17 -070061public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
Kevin Chyna56dff72018-06-19 18:41:12 -070062
Gilad Brettercb51b8b2018-03-22 17:04:51 +020063 private static final String TAG = "FaceManager";
64 private static final boolean DEBUG = true;
65 private static final int MSG_ENROLL_RESULT = 100;
66 private static final int MSG_ACQUIRED = 101;
67 private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
68 private static final int MSG_AUTHENTICATION_FAILED = 103;
69 private static final int MSG_ERROR = 104;
70 private static final int MSG_REMOVED = 105;
Kevin Chyne62749a2019-04-02 19:33:56 -070071 private static final int MSG_GET_FEATURE_COMPLETED = 106;
72 private static final int MSG_SET_FEATURE_COMPLETED = 107;
Kevin Chyna56dff72018-06-19 18:41:12 -070073
Gilad Brettercb51b8b2018-03-22 17:04:51 +020074 private IFaceService mService;
Kevin Chyna56dff72018-06-19 18:41:12 -070075 private final Context mContext;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020076 private IBinder mToken = new Binder();
Kevin Chyna24e9fd2018-08-27 12:39:17 -070077 private AuthenticationCallback mAuthenticationCallback;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020078 private EnrollmentCallback mEnrollmentCallback;
79 private RemovalCallback mRemovalCallback;
Kevin Chyne62749a2019-04-02 19:33:56 -070080 private SetFeatureCallback mSetFeatureCallback;
81 private GetFeatureCallback mGetFeatureCallback;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020082 private CryptoObject mCryptoObject;
83 private Face mRemovalFace;
84 private Handler mHandler;
Kevin Chyna56dff72018-06-19 18:41:12 -070085
Gilad Brettercb51b8b2018-03-22 17:04:51 +020086 private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
87
88 @Override // binder call
89 public void onEnrollResult(long deviceId, int faceId, int remaining) {
90 mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
91 new Face(null, faceId, deviceId)).sendToTarget();
92 }
93
94 @Override // binder call
95 public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
96 mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
97 }
98
99 @Override // binder call
Kevin Chyn8d2694a2019-04-11 18:30:40 -0700100 public void onAuthenticationSucceeded(long deviceId, Face face, int userId) {
101 mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, face).sendToTarget();
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200102 }
103
104 @Override // binder call
105 public void onAuthenticationFailed(long deviceId) {
106 mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
107 }
108
109 @Override // binder call
110 public void onError(long deviceId, int error, int vendorCode) {
111 mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
112 }
113
114 @Override // binder call
115 public void onRemoved(long deviceId, int faceId, int remaining) {
116 mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
117 new Face(null, faceId, deviceId)).sendToTarget();
118 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800119
120 @Override
121 public void onEnumerated(long deviceId, int faceId, int remaining) {
122 // TODO: Finish. Low priority since it's not used.
123 }
Kevin Chyne62749a2019-04-02 19:33:56 -0700124
125 @Override
126 public void onFeatureSet(boolean success, int feature) {
127 mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget();
128 }
129
130 @Override
131 public void onFeatureGet(boolean success, int feature, boolean value) {
132 SomeArgs args = SomeArgs.obtain();
133 args.arg1 = success;
134 args.argi1 = feature;
135 args.arg2 = value;
136 mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
137 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200138 };
139
140 /**
141 * @hide
142 */
143 public FaceManager(Context context, IFaceService service) {
144 mContext = context;
145 mService = service;
146 if (mService == null) {
147 Slog.v(TAG, "FaceAuthenticationManagerService was null");
148 }
149 mHandler = new MyHandler(context);
150 }
151
152 /**
153 * Request authentication of a crypto object. This call operates the face recognition hardware
154 * and starts capturing images. It terminates when
155 * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
156 * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
157 * which point the object is no longer valid. The operation can be canceled by using the
158 * provided cancel object.
159 *
160 * @param crypto object associated with the call or null if none required.
161 * @param cancel an object that can be used to cancel authentication
162 * @param flags optional flags; should be 0
163 * @param callback an object to receive authentication events
164 * @param handler an optional handler to handle callback events
165 * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
166 * by
167 * <a href="{@docRoot}training/articles/keystore.html">Android
168 * Keystore facility</a>.
169 * @throws IllegalStateException if the crypto primitive is not initialized.
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200170 * @hide
171 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700172 @RequiresPermission(USE_BIOMETRIC_INTERNAL)
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200173 public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
Kevin Chyna56dff72018-06-19 18:41:12 -0700174 int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
Kevin Chyn8d2694a2019-04-11 18:30:40 -0700175 authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
176 }
177
178 /**
179 * Use the provided handler thread for events.
180 */
181 private void useHandler(Handler handler) {
182 if (handler != null) {
183 mHandler = new MyHandler(handler.getLooper());
184 } else if (mHandler.getLooper() != mContext.getMainLooper()) {
185 mHandler = new MyHandler(mContext.getMainLooper());
186 }
187 }
188
189 /**
190 * Request authentication of a crypto object. This call operates the face recognition hardware
191 * and starts capturing images. It terminates when
192 * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
193 * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
194 * which point the object is no longer valid. The operation can be canceled by using the
195 * provided cancel object.
196 *
197 * @param crypto object associated with the call or null if none required.
198 * @param cancel an object that can be used to cancel authentication
199 * @param flags optional flags; should be 0
200 * @param callback an object to receive authentication events
201 * @param handler an optional handler to handle callback events
202 * @param userId userId to authenticate for
203 * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
204 * by
205 * <a href="{@docRoot}training/articles/keystore.html">Android
206 * Keystore facility</a>.
207 * @throws IllegalStateException if the crypto primitive is not initialized.
208 * @hide
209 */
210 public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
211 int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler,
212 int userId) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200213 if (callback == null) {
214 throw new IllegalArgumentException("Must supply an authentication callback");
215 }
216
217 if (cancel != null) {
218 if (cancel.isCanceled()) {
219 Log.w(TAG, "authentication already canceled");
220 return;
221 } else {
222 cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
223 }
224 }
225
226 if (mService != null) {
227 try {
228 useHandler(handler);
229 mAuthenticationCallback = callback;
230 mCryptoObject = crypto;
231 long sessionId = crypto != null ? crypto.getOpId() : 0;
Ianfd4021c2019-04-05 13:22:59 -0700232 Trace.beginSection("FaceManager#authenticate");
Kevin Chyn8d2694a2019-04-11 18:30:40 -0700233 mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
Kevin Chyn747e29b2019-01-11 17:01:53 -0800234 flags, mContext.getOpPackageName());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200235 } catch (RemoteException e) {
236 Log.w(TAG, "Remote exception while authenticating: ", e);
237 if (callback != null) {
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700238 // Though this may not be a hardware issue, it will cause apps to give up or
239 // try again later.
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200240 callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700241 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
Kevin Chyn8d2694a2019-04-11 18:30:40 -0700242 0 /* vendorCode */));
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200243 }
Ianfd4021c2019-04-05 13:22:59 -0700244 } finally {
245 Trace.endSection();
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200246 }
247 }
248 }
249
250 /**
Ilya Matyukhinef410e32020-02-04 13:39:48 -0800251 * Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback,
252 * int[], NativeHandle)} with {@code windowId} set to null.
253 *
254 * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[],
255 * NativeHandle)
256 */
257 @RequiresPermission(MANAGE_BIOMETRIC)
258 public void enroll(int userId, byte[] token, CancellationSignal cancel,
259 EnrollmentCallback callback, int[] disabledFeatures) {
260 enroll(userId, token, cancel, callback, disabledFeatures, null /* windowId */);
261 }
262
263 /**
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200264 * Request face authentication enrollment. This call operates the face authentication hardware
265 * and starts capturing images. Progress will be indicated by callbacks to the
266 * {@link EnrollmentCallback} object. It terminates when
267 * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
268 * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
269 * which point the object is no longer valid. The operation can be canceled by using the
270 * provided cancel object.
271 *
272 * @param token a unique token provided by a recent creation or verification of device
273 * credentials (e.g. pin, pattern or password).
274 * @param cancel an object that can be used to cancel enrollment
275 * @param flags optional flags
276 * @param userId the user to whom this face will belong to
277 * @param callback an object to receive enrollment events
Ilya Matyukhinef410e32020-02-04 13:39:48 -0800278 * @param windowId optional ID of a camera preview window for a single-camera device. Must be
279 * null if not used.
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200280 * @hide
281 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700282 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chyn593e6262019-06-28 13:24:44 -0700283 public void enroll(int userId, byte[] token, CancellationSignal cancel,
Ilya Matyukhinef410e32020-02-04 13:39:48 -0800284 EnrollmentCallback callback, int[] disabledFeatures, @Nullable NativeHandle windowId) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200285 if (callback == null) {
286 throw new IllegalArgumentException("Must supply an enrollment callback");
287 }
288
289 if (cancel != null) {
290 if (cancel.isCanceled()) {
291 Log.w(TAG, "enrollment already canceled");
292 return;
293 } else {
294 cancel.setOnCancelListener(new OnEnrollCancelListener());
295 }
296 }
297
298 if (mService != null) {
Ilya Matyukhinef410e32020-02-04 13:39:48 -0800299 IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200300 try {
301 mEnrollmentCallback = callback;
Ianfd4021c2019-04-05 13:22:59 -0700302 Trace.beginSection("FaceManager#enroll");
Kevin Chyn593e6262019-06-28 13:24:44 -0700303 mService.enroll(userId, mToken, token, mServiceReceiver,
Ilya Matyukhinef410e32020-02-04 13:39:48 -0800304 mContext.getOpPackageName(), disabledFeatures, handle);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200305 } catch (RemoteException e) {
306 Log.w(TAG, "Remote exception in enroll: ", e);
Ilya Matyukhinef410e32020-02-04 13:39:48 -0800307 // Though this may not be a hardware issue, it will cause apps to give up or
308 // try again later.
309 callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
310 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700311 0 /* vendorCode */));
Ilya Matyukhinef410e32020-02-04 13:39:48 -0800312 } finally {
313 Trace.endSection();
314 BiometricNativeHandleUtils.close(handle);
315 }
316 }
317 }
318
319 /**
320 * Request face authentication enrollment for a remote client, for example Android Auto.
321 * This call operates the face authentication hardware and starts capturing images.
322 * Progress will be indicated by callbacks to the
323 * {@link EnrollmentCallback} object. It terminates when
324 * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
325 * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
326 * which point the object is no longer valid. The operation can be canceled by using the
327 * provided cancel object.
328 *
329 * @param token a unique token provided by a recent creation or verification of device
330 * credentials (e.g. pin, pattern or password).
331 * @param cancel an object that can be used to cancel enrollment
332 * @param userId the user to whom this face will belong to
333 * @param callback an object to receive enrollment events
334 * @hide
335 */
336 @RequiresPermission(MANAGE_BIOMETRIC)
337 public void enrollRemotely(int userId, byte[] token, CancellationSignal cancel,
338 EnrollmentCallback callback, int[] disabledFeatures) {
339 if (callback == null) {
340 throw new IllegalArgumentException("Must supply an enrollment callback");
341 }
342
343 if (cancel != null) {
344 if (cancel.isCanceled()) {
345 Log.w(TAG, "enrollRemotely is already canceled.");
346 return;
347 } else {
348 cancel.setOnCancelListener(new OnEnrollCancelListener());
349 }
350 }
351
352 if (mService != null) {
353 try {
354 mEnrollmentCallback = callback;
355 Trace.beginSection("FaceManager#enrollRemotely");
356 mService.enrollRemotely(userId, mToken, token, mServiceReceiver,
357 mContext.getOpPackageName(), disabledFeatures);
358 } catch (RemoteException e) {
359 Log.w(TAG, "Remote exception in enrollRemotely: ", e);
360 // Though this may not be a hardware issue, it will cause apps to give up or
361 // try again later.
362 callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
363 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
364 0 /* vendorCode */));
Ianfd4021c2019-04-05 13:22:59 -0700365 } finally {
366 Trace.endSection();
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200367 }
368 }
369 }
370
371 /**
Kevin Chynd79e24e2018-09-25 12:06:59 -0700372 * Requests an auth token to tie sensitive operations to the confirmation of
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200373 * existing device credentials (e.g. pin/pattern/password).
374 *
375 * @hide
376 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700377 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chynd79e24e2018-09-25 12:06:59 -0700378 public long generateChallenge() {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200379 long result = 0;
380 if (mService != null) {
381 try {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700382 result = mService.generateChallenge(mToken);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200383 } catch (RemoteException e) {
384 throw e.rethrowFromSystemServer();
385 }
386 }
387 return result;
388 }
389
390 /**
Kevin Chynd79e24e2018-09-25 12:06:59 -0700391 * Invalidates the current auth token.
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200392 *
393 * @hide
394 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700395 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chynd79e24e2018-09-25 12:06:59 -0700396 public int revokeChallenge() {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200397 int result = 0;
398 if (mService != null) {
399 try {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700400 result = mService.revokeChallenge(mToken);
401 } catch (RemoteException e) {
402 throw e.rethrowFromSystemServer();
403 }
404 }
405 return result;
406 }
407
408 /**
409 * @hide
410 */
411 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chyn593e6262019-06-28 13:24:44 -0700412 public void setFeature(int userId, int feature, boolean enabled, byte[] token,
Kevin Chyne62749a2019-04-02 19:33:56 -0700413 SetFeatureCallback callback) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700414 if (mService != null) {
415 try {
Kevin Chyne62749a2019-04-02 19:33:56 -0700416 mSetFeatureCallback = callback;
Kevin Chyn593e6262019-06-28 13:24:44 -0700417 mService.setFeature(userId, feature, enabled, token, mServiceReceiver,
418 mContext.getOpPackageName());
Kevin Chynd79e24e2018-09-25 12:06:59 -0700419 } catch (RemoteException e) {
420 throw e.rethrowFromSystemServer();
421 }
422 }
423 }
424
425 /**
426 * @hide
427 */
428 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chyn593e6262019-06-28 13:24:44 -0700429 public void getFeature(int userId, int feature, GetFeatureCallback callback) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700430 if (mService != null) {
431 try {
Kevin Chyne62749a2019-04-02 19:33:56 -0700432 mGetFeatureCallback = callback;
Kevin Chyn593e6262019-06-28 13:24:44 -0700433 mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200434 } catch (RemoteException e) {
435 throw e.rethrowFromSystemServer();
436 }
437 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200438 }
439
440 /**
Kevin Chyn57f119b2018-10-25 12:03:41 -0700441 * Pokes the the driver to have it start looking for faces again.
442 * @hide
443 */
444 @RequiresPermission(MANAGE_BIOMETRIC)
445 public void userActivity() {
446 if (mService != null) {
447 try {
448 mService.userActivity();
449 } catch (RemoteException e) {
450 throw e.rethrowFromSystemServer();
451 }
452 }
453 }
454
455 /**
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200456 * Sets the active user. This is meant to be used to select the current profile for enrollment
457 * to allow separate enrolled faces for a work profile
458 *
459 * @hide
460 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700461 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chynbf830a32018-10-07 15:58:46 -0700462 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200463 public void setActiveUser(int userId) {
464 if (mService != null) {
465 try {
466 mService.setActiveUser(userId);
467 } catch (RemoteException e) {
468 throw e.rethrowFromSystemServer();
469 }
470 }
471 }
472
473 /**
474 * Remove given face template from face hardware and/or protected storage.
475 *
476 * @param face the face item to remove
477 * @param userId the user who this face belongs to
478 * @param callback an optional callback to verify that face templates have been
479 * successfully removed. May be null if no callback is required.
480 * @hide
481 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700482 @RequiresPermission(MANAGE_BIOMETRIC)
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200483 public void remove(Face face, int userId, RemovalCallback callback) {
484 if (mService != null) {
485 try {
486 mRemovalCallback = callback;
487 mRemovalFace = face;
Kevin Chyn593e6262019-06-28 13:24:44 -0700488 mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver,
489 mContext.getOpPackageName());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200490 } catch (RemoteException e) {
491 Log.w(TAG, "Remote exception in remove: ", e);
492 if (callback != null) {
493 callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700494 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
495 0 /* vendorCode */));
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200496 }
497 }
498 }
499 }
500
501 /**
502 * Obtain the enrolled face template.
503 *
504 * @return the current face item
505 * @hide
506 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700507 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chyna56dff72018-06-19 18:41:12 -0700508 public List<Face> getEnrolledFaces(int userId) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200509 if (mService != null) {
510 try {
Kevin Chyna56dff72018-06-19 18:41:12 -0700511 return mService.getEnrolledFaces(userId, mContext.getOpPackageName());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200512 } catch (RemoteException e) {
513 throw e.rethrowFromSystemServer();
514 }
515 }
516 return null;
517 }
518
519 /**
520 * Obtain the enrolled face template.
521 *
522 * @return the current face item
523 * @hide
524 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700525 @RequiresPermission(MANAGE_BIOMETRIC)
Kevin Chyna56dff72018-06-19 18:41:12 -0700526 public List<Face> getEnrolledFaces() {
527 return getEnrolledFaces(UserHandle.myUserId());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200528 }
529
530 /**
531 * Determine if there is a face enrolled.
532 *
533 * @return true if a face is enrolled, false otherwise
534 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700535 @RequiresPermission(USE_BIOMETRIC_INTERNAL)
536 @Override
537 public boolean hasEnrolledTemplates() {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200538 if (mService != null) {
539 try {
Kevin Chyna56dff72018-06-19 18:41:12 -0700540 return mService.hasEnrolledFaces(
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200541 UserHandle.myUserId(), mContext.getOpPackageName());
542 } catch (RemoteException e) {
543 throw e.rethrowFromSystemServer();
544 }
545 }
546 return false;
547 }
548
549 /**
550 * @hide
551 */
552 @RequiresPermission(allOf = {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700553 USE_BIOMETRIC_INTERNAL,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200554 INTERACT_ACROSS_USERS})
Kevin Chyn75dbb832018-10-05 17:32:57 -0700555 @Override
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700556 public boolean hasEnrolledTemplates(int userId) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200557 if (mService != null) {
558 try {
Kevin Chyna56dff72018-06-19 18:41:12 -0700559 return mService.hasEnrolledFaces(userId, mContext.getOpPackageName());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200560 } catch (RemoteException e) {
561 throw e.rethrowFromSystemServer();
562 }
563 }
564 return false;
565 }
566
567 /**
568 * Determine if face authentication sensor hardware is present and functional.
569 *
570 * @return true if hardware is present and functional, false otherwise.
571 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700572 @RequiresPermission(USE_BIOMETRIC_INTERNAL)
573 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200574 public boolean isHardwareDetected() {
575 if (mService != null) {
576 try {
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700577 return mService.isHardwareDetected(mContext.getOpPackageName());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200578 } catch (RemoteException e) {
579 throw e.rethrowFromSystemServer();
580 }
581 } else {
582 Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
583 }
584 return false;
585 }
586
587 /**
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200588 * @hide
589 */
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700590 @RequiresPermission(USE_BIOMETRIC_INTERNAL)
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200591 public void addLockoutResetCallback(final LockoutResetCallback callback) {
592 if (mService != null) {
593 try {
594 final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
595 mService.addLockoutResetCallback(
Kevin Chyna56dff72018-06-19 18:41:12 -0700596 new IBiometricServiceLockoutResetCallback.Stub() {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200597
598 @Override
599 public void onLockoutReset(long deviceId,
600 IRemoteCallback serverCallback)
601 throws RemoteException {
602 try {
603 final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
604 PowerManager.PARTIAL_WAKE_LOCK,
605 "faceLockoutResetCallback");
606 wakeLock.acquire();
607 mHandler.post(() -> {
608 try {
609 callback.onLockoutReset();
610 } finally {
611 wakeLock.release();
612 }
613 });
614 } finally {
615 serverCallback.sendResult(null /* data */);
616 }
617 }
618 });
619 } catch (RemoteException e) {
620 throw e.rethrowFromSystemServer();
621 }
622 } else {
623 Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
624 }
625 }
626
627 private int getCurrentUserId() {
628 try {
629 return ActivityManager.getService().getCurrentUser().id;
630 } catch (RemoteException e) {
631 throw e.rethrowFromSystemServer();
632 }
633 }
634
635 private void cancelEnrollment() {
636 if (mService != null) {
637 try {
638 mService.cancelEnrollment(mToken);
639 } catch (RemoteException e) {
640 throw e.rethrowFromSystemServer();
641 }
642 }
643 }
644
645 private void cancelAuthentication(CryptoObject cryptoObject) {
646 if (mService != null) {
647 try {
648 mService.cancelAuthentication(mToken, mContext.getOpPackageName());
649 } catch (RemoteException e) {
650 throw e.rethrowFromSystemServer();
651 }
652 }
653 }
654
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700655 /**
656 * @hide
657 */
658 public static String getErrorString(Context context, int errMsg, int vendorCode) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200659 switch (errMsg) {
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700660 case FACE_ERROR_HW_UNAVAILABLE:
661 return context.getString(
662 com.android.internal.R.string.face_error_hw_not_available);
Kevin Chyn68823b02019-02-06 08:59:16 -0800663 case FACE_ERROR_UNABLE_TO_PROCESS:
664 return context.getString(
665 com.android.internal.R.string.face_error_unable_to_process);
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700666 case FACE_ERROR_TIMEOUT:
667 return context.getString(com.android.internal.R.string.face_error_timeout);
Kevin Chyn68823b02019-02-06 08:59:16 -0800668 case FACE_ERROR_NO_SPACE:
669 return context.getString(com.android.internal.R.string.face_error_no_space);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200670 case FACE_ERROR_CANCELED:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700671 return context.getString(com.android.internal.R.string.face_error_canceled);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200672 case FACE_ERROR_LOCKOUT:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700673 return context.getString(com.android.internal.R.string.face_error_lockout);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200674 case FACE_ERROR_LOCKOUT_PERMANENT:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700675 return context.getString(
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200676 com.android.internal.R.string.face_error_lockout_permanent);
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700677 case FACE_ERROR_USER_CANCELED:
678 return context.getString(com.android.internal.R.string.face_error_user_canceled);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200679 case FACE_ERROR_NOT_ENROLLED:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700680 return context.getString(com.android.internal.R.string.face_error_not_enrolled);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200681 case FACE_ERROR_HW_NOT_PRESENT:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700682 return context.getString(com.android.internal.R.string.face_error_hw_not_present);
Kevin Chyneab9fe12020-02-11 14:39:02 -0800683 case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
684 return context.getString(
685 com.android.internal.R.string.face_error_security_update_required);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200686 case FACE_ERROR_VENDOR: {
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700687 String[] msgArray = context.getResources().getStringArray(
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200688 com.android.internal.R.array.face_error_vendor);
689 if (vendorCode < msgArray.length) {
690 return msgArray[vendorCode];
691 }
692 }
693 }
694 Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
Kevin Chyn29692c52019-06-13 16:28:44 -0700695 return "";
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200696 }
697
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700698 /**
699 * @hide
700 */
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700701 public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200702 switch (acquireInfo) {
703 case FACE_ACQUIRED_GOOD:
704 return null;
705 case FACE_ACQUIRED_INSUFFICIENT:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700706 return context.getString(R.string.face_acquired_insufficient);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200707 case FACE_ACQUIRED_TOO_BRIGHT:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700708 return context.getString(R.string.face_acquired_too_bright);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200709 case FACE_ACQUIRED_TOO_DARK:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700710 return context.getString(R.string.face_acquired_too_dark);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200711 case FACE_ACQUIRED_TOO_CLOSE:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700712 return context.getString(R.string.face_acquired_too_close);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200713 case FACE_ACQUIRED_TOO_FAR:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700714 return context.getString(R.string.face_acquired_too_far);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200715 case FACE_ACQUIRED_TOO_HIGH:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700716 return context.getString(R.string.face_acquired_too_high);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200717 case FACE_ACQUIRED_TOO_LOW:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700718 return context.getString(R.string.face_acquired_too_low);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200719 case FACE_ACQUIRED_TOO_RIGHT:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700720 return context.getString(R.string.face_acquired_too_right);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200721 case FACE_ACQUIRED_TOO_LEFT:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700722 return context.getString(R.string.face_acquired_too_left);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200723 case FACE_ACQUIRED_POOR_GAZE:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700724 return context.getString(R.string.face_acquired_poor_gaze);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200725 case FACE_ACQUIRED_NOT_DETECTED:
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700726 return context.getString(R.string.face_acquired_not_detected);
Kevin Chyn68823b02019-02-06 08:59:16 -0800727 case FACE_ACQUIRED_TOO_MUCH_MOTION:
728 return context.getString(R.string.face_acquired_too_much_motion);
729 case FACE_ACQUIRED_RECALIBRATE:
730 return context.getString(R.string.face_acquired_recalibrate);
731 case FACE_ACQUIRED_TOO_DIFFERENT:
732 return context.getString(R.string.face_acquired_too_different);
733 case FACE_ACQUIRED_TOO_SIMILAR:
734 return context.getString(R.string.face_acquired_too_similar);
735 case FACE_ACQUIRED_PAN_TOO_EXTREME:
736 return context.getString(R.string.face_acquired_pan_too_extreme);
737 case FACE_ACQUIRED_TILT_TOO_EXTREME:
738 return context.getString(R.string.face_acquired_tilt_too_extreme);
739 case FACE_ACQUIRED_ROLL_TOO_EXTREME:
740 return context.getString(R.string.face_acquired_roll_too_extreme);
741 case FACE_ACQUIRED_FACE_OBSCURED:
742 return context.getString(R.string.face_acquired_obscured);
743 case FACE_ACQUIRED_START:
744 return null;
Kevin Chyn7e7b5092019-03-18 16:59:56 -0700745 case FACE_ACQUIRED_SENSOR_DIRTY:
746 return context.getString(R.string.face_acquired_sensor_dirty);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200747 case FACE_ACQUIRED_VENDOR: {
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700748 String[] msgArray = context.getResources().getStringArray(
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200749 R.array.face_acquired_vendor);
750 if (vendorCode < msgArray.length) {
751 return msgArray[vendorCode];
752 }
753 }
754 }
755 Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
756 return null;
757 }
758
759 /**
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700760 * Used so BiometricPrompt can map the face ones onto existing public constants.
761 * @hide
762 */
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700763 public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700764 switch (acquireInfo) {
765 case FACE_ACQUIRED_GOOD:
766 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
767 case FACE_ACQUIRED_INSUFFICIENT:
768 case FACE_ACQUIRED_TOO_BRIGHT:
769 case FACE_ACQUIRED_TOO_DARK:
770 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
771 case FACE_ACQUIRED_TOO_CLOSE:
772 case FACE_ACQUIRED_TOO_FAR:
773 case FACE_ACQUIRED_TOO_HIGH:
774 case FACE_ACQUIRED_TOO_LOW:
775 case FACE_ACQUIRED_TOO_RIGHT:
776 case FACE_ACQUIRED_TOO_LEFT:
777 return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL;
778 case FACE_ACQUIRED_POOR_GAZE:
779 case FACE_ACQUIRED_NOT_DETECTED:
780 case FACE_ACQUIRED_TOO_MUCH_MOTION:
781 case FACE_ACQUIRED_RECALIBRATE:
782 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
783 case FACE_ACQUIRED_VENDOR:
784 return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode;
785 default:
786 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
787 }
788 }
789
790 /**
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200791 * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
792 * CancellationSignal, int, AuthenticationCallback, Handler)}.
793 */
794 public static class AuthenticationResult {
795 private Face mFace;
796 private CryptoObject mCryptoObject;
797 private int mUserId;
798
799 /**
800 * Authentication result
801 *
802 * @param crypto the crypto object
803 * @param face the recognized face data, if allowed.
804 * @hide
805 */
806 public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
807 mCryptoObject = crypto;
808 mFace = face;
809 mUserId = userId;
810 }
811
812 /**
813 * Obtain the crypto object associated with this transaction
814 *
815 * @return crypto object provided to {@link FaceManager#authenticate
816 * (CryptoObject,
817 * CancellationSignal, int, AuthenticationCallback, Handler)}.
818 */
819 public CryptoObject getCryptoObject() {
820 return mCryptoObject;
821 }
822
823 /**
824 * Obtain the Face associated with this operation. Applications are strongly
825 * discouraged from associating specific faces with specific applications or operations.
826 *
827 * @hide
828 */
829 public Face getFace() {
830 return mFace;
831 }
832
833 /**
834 * Obtain the userId for which this face was authenticated.
835 *
836 * @hide
837 */
838 public int getUserId() {
839 return mUserId;
840 }
841 }
842
843 /**
844 * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
845 * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
846 * FaceManager#authenticate(CryptoObject, CancellationSignal,
847 * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
848 * to face events.
849 */
Kevin Chyna56dff72018-06-19 18:41:12 -0700850 public abstract static class AuthenticationCallback
851 extends BiometricAuthenticator.AuthenticationCallback {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200852
853 /**
854 * Called when an unrecoverable error has been encountered and the operation is complete.
855 * No further callbacks will be made on this object.
856 *
857 * @param errorCode An integer identifying the error message
858 * @param errString A human-readable error string that can be shown in UI
859 */
860 public void onAuthenticationError(int errorCode, CharSequence errString) {
861 }
862
863 /**
864 * Called when a recoverable error has been encountered during authentication. The help
865 * string is provided to give the user guidance for what went wrong, such as
866 * "Sensor dirty, please clean it."
867 *
868 * @param helpCode An integer identifying the error message
869 * @param helpString A human-readable string that can be shown in UI
870 */
871 public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
872 }
873
874 /**
875 * Called when a face is recognized.
876 *
877 * @param result An object containing authentication-related data
878 */
879 public void onAuthenticationSucceeded(AuthenticationResult result) {
880 }
881
882 /**
883 * Called when a face is detected but not recognized.
884 */
885 public void onAuthenticationFailed() {
886 }
887
888 /**
889 * Called when a face image has been acquired, but wasn't processed yet.
890 *
891 * @param acquireInfo one of FACE_ACQUIRED_* constants
892 * @hide
893 */
894 public void onAuthenticationAcquired(int acquireInfo) {
895 }
896 }
897
898 /**
899 * Callback structure provided to {@link FaceManager#enroll(long,
900 * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
901 * must provide an implementation of this to {@link FaceManager#enroll(long,
902 * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
903 *
904 * @hide
905 */
906 public abstract static class EnrollmentCallback {
907
908 /**
909 * Called when an unrecoverable error has been encountered and the operation is complete.
910 * No further callbacks will be made on this object.
911 *
912 * @param errMsgId An integer identifying the error message
913 * @param errString A human-readable error string that can be shown in UI
914 */
915 public void onEnrollmentError(int errMsgId, CharSequence errString) {
916 }
917
918 /**
919 * Called when a recoverable error has been encountered during enrollment. The help
920 * string is provided to give the user guidance for what went wrong, such as
921 * "Image too dark, uncover light source" or what they need to do next, such as
922 * "Rotate face up / down."
923 *
924 * @param helpMsgId An integer identifying the error message
925 * @param helpString A human-readable string that can be shown in UI
926 */
927 public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
928 }
929
930 /**
931 * Called as each enrollment step progresses. Enrollment is considered complete when
932 * remaining reaches 0. This function will not be called if enrollment fails. See
933 * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
934 *
935 * @param remaining The number of remaining steps
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200936 */
Kevin Chyn8277f002018-07-02 15:14:44 -0700937 public void onEnrollmentProgress(int remaining) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200938 }
939 }
940
941 /**
942 * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
943 * may
944 * optionally provide an implementation of this to
945 * {@link #remove(Face, int, RemovalCallback)} for listening to face template
946 * removal events.
947 *
948 * @hide
949 */
950 public abstract static class RemovalCallback {
951
952 /**
953 * Called when the given face can't be removed.
954 *
955 * @param face The face that the call attempted to remove
956 * @param errMsgId An associated error message id
957 * @param errString An error message indicating why the face id can't be removed
958 */
959 public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
960 }
961
962 /**
963 * Called when a given face is successfully removed.
964 *
965 * @param face The face template that was removed.
966 */
Kevin Chyn7e88d1132018-06-29 12:55:22 -0700967 public void onRemovalSucceeded(Face face, int remaining) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200968 }
969 }
970
971 /**
972 * @hide
973 */
974 public abstract static class LockoutResetCallback {
975
976 /**
977 * Called when lockout period expired and clients are allowed to listen for face
978 * authentication
979 * again.
980 */
981 public void onLockoutReset() {
982 }
983 }
984
Kevin Chyne62749a2019-04-02 19:33:56 -0700985 /**
986 * @hide
987 */
988 public abstract static class SetFeatureCallback {
989 public abstract void onCompleted(boolean success, int feature);
990 }
991
992 /**
993 * @hide
994 */
995 public abstract static class GetFeatureCallback {
996 public abstract void onCompleted(boolean success, int feature, boolean value);
997 }
998
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200999 private class OnEnrollCancelListener implements OnCancelListener {
1000 @Override
1001 public void onCancel() {
1002 cancelEnrollment();
1003 }
1004 }
1005
1006 private class OnAuthenticationCancelListener implements OnCancelListener {
1007 private CryptoObject mCrypto;
1008
1009 OnAuthenticationCancelListener(CryptoObject crypto) {
1010 mCrypto = crypto;
1011 }
1012
1013 @Override
1014 public void onCancel() {
1015 cancelAuthentication(mCrypto);
1016 }
1017 }
1018
1019 private class MyHandler extends Handler {
1020 private MyHandler(Context context) {
1021 super(context.getMainLooper());
1022 }
1023
1024 private MyHandler(Looper looper) {
1025 super(looper);
1026 }
1027
1028 @Override
1029 public void handleMessage(android.os.Message msg) {
Ianfd4021c2019-04-05 13:22:59 -07001030 Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what));
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001031 switch (msg.what) {
1032 case MSG_ENROLL_RESULT:
Kevin Chyn8277f002018-07-02 15:14:44 -07001033 sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001034 break;
1035 case MSG_ACQUIRED:
1036 sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
1037 msg.arg2 /* vendorCode */);
1038 break;
1039 case MSG_AUTHENTICATION_SUCCEEDED:
1040 sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
1041 break;
1042 case MSG_AUTHENTICATION_FAILED:
1043 sendAuthenticatedFailed();
1044 break;
1045 case MSG_ERROR:
1046 sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
1047 msg.arg2 /* vendorCode */);
1048 break;
1049 case MSG_REMOVED:
Kevin Chyn7e88d1132018-06-29 12:55:22 -07001050 sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001051 break;
Kevin Chyne62749a2019-04-02 19:33:56 -07001052 case MSG_SET_FEATURE_COMPLETED:
1053 sendSetFeatureCompleted((boolean) msg.obj /* success */,
1054 msg.arg1 /* feature */);
1055 break;
1056 case MSG_GET_FEATURE_COMPLETED:
1057 SomeArgs args = (SomeArgs) msg.obj;
1058 sendGetFeatureCompleted((boolean) args.arg1 /* success */,
1059 args.argi1 /* feature */,
1060 (boolean) args.arg2 /* value */);
1061 args.recycle();
1062 break;
1063 default:
1064 Log.w(TAG, "Unknown message: " + msg.what);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001065 }
Ianfd4021c2019-04-05 13:22:59 -07001066 Trace.endSection();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001067 }
Kevin Chyne62749a2019-04-02 19:33:56 -07001068 }
1069
1070 private void sendSetFeatureCompleted(boolean success, int feature) {
1071 if (mSetFeatureCallback == null) {
1072 return;
1073 }
1074 mSetFeatureCallback.onCompleted(success, feature);
1075 }
1076
1077 private void sendGetFeatureCompleted(boolean success, int feature, boolean value) {
1078 if (mGetFeatureCallback == null) {
1079 return;
1080 }
1081 mGetFeatureCallback.onCompleted(success, feature, value);
1082 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001083
Kevin Chyn7e88d1132018-06-29 12:55:22 -07001084 private void sendRemovedResult(Face face, int remaining) {
Kevin Chyna56dff72018-06-19 18:41:12 -07001085 if (mRemovalCallback == null) {
1086 return;
1087 }
1088 if (face == null) {
1089 Log.e(TAG, "Received MSG_REMOVED, but face is null");
1090 return;
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001091 }
Kevin Chyn7e88d1132018-06-29 12:55:22 -07001092 mRemovalCallback.onRemovalSucceeded(face, remaining);
Kevin Chyna56dff72018-06-19 18:41:12 -07001093 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001094
Kevin Chyna56dff72018-06-19 18:41:12 -07001095 private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
1096 // emulate HAL 2.1 behavior and send real errMsgId
1097 final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
1098 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
1099 if (mEnrollmentCallback != null) {
1100 mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001101 getErrorString(mContext, errMsgId, vendorCode));
Kevin Chyna56dff72018-06-19 18:41:12 -07001102 } else if (mAuthenticationCallback != null) {
1103 mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001104 getErrorString(mContext, errMsgId, vendorCode));
Kevin Chyna56dff72018-06-19 18:41:12 -07001105 } else if (mRemovalCallback != null) {
1106 mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001107 getErrorString(mContext, errMsgId, vendorCode));
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001108 }
Kevin Chyna56dff72018-06-19 18:41:12 -07001109 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001110
Kevin Chyn8277f002018-07-02 15:14:44 -07001111 private void sendEnrollResult(Face face, int remaining) {
Kevin Chyna56dff72018-06-19 18:41:12 -07001112 if (mEnrollmentCallback != null) {
Kevin Chyn8277f002018-07-02 15:14:44 -07001113 mEnrollmentCallback.onEnrollmentProgress(remaining);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001114 }
Kevin Chyna56dff72018-06-19 18:41:12 -07001115 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001116
Kevin Chyna56dff72018-06-19 18:41:12 -07001117 private void sendAuthenticatedSucceeded(Face face, int userId) {
1118 if (mAuthenticationCallback != null) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001119 final AuthenticationResult result =
1120 new AuthenticationResult(mCryptoObject, face, userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001121 mAuthenticationCallback.onAuthenticationSucceeded(result);
1122 }
1123 }
1124
1125 private void sendAuthenticatedFailed() {
1126 if (mAuthenticationCallback != null) {
1127 mAuthenticationCallback.onAuthenticationFailed();
1128 }
1129 }
1130
1131 private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
1132 if (mAuthenticationCallback != null) {
1133 mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
1134 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001135 final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
Kevin Chyna56dff72018-06-19 18:41:12 -07001136 final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
1137 ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
1138 if (mEnrollmentCallback != null) {
1139 mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
Kevin Chynffef7142018-12-27 16:28:48 -08001140 } else if (mAuthenticationCallback != null && msg != null) {
Kevin Chyna56dff72018-06-19 18:41:12 -07001141 mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001142 }
1143 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001144}