blob: c573bbb2e1f0e3988a6daab0487f2d433193ed25 [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
Kevin Chyn2ffadb32018-06-19 11:29:38 -070017package com.android.server.biometrics.face;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020018
19import static android.Manifest.permission.INTERACT_ACROSS_USERS;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070020import static android.Manifest.permission.MANAGE_BIOMETRIC;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020021import static android.Manifest.permission.RESET_FACE_LOCKOUT;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070022import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020023
24import android.app.ActivityManager;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020025import android.app.AppOpsManager;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020026import android.content.Context;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020027import android.content.pm.UserInfo;
Kevin Chyna56dff72018-06-19 18:41:12 -070028import android.hardware.biometrics.BiometricAuthenticator;
29import android.hardware.biometrics.BiometricConstants;
Kevin Chyn7782d142019-01-18 12:51:33 -080030import android.hardware.biometrics.BiometricsProtoEnums;
Kevin Chyna56dff72018-06-19 18:41:12 -070031import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
Kevin Chyn23289ef2018-11-28 16:32:36 -080032import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020033import android.hardware.biometrics.face.V1_0.IBiometricsFace;
34import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
Kevin Chynb95f1522019-03-04 16:45:15 -080035import android.hardware.biometrics.face.V1_0.OptionalBool;
Kevin Chynd79e24e2018-09-25 12:06:59 -070036import android.hardware.biometrics.face.V1_0.Status;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020037import android.hardware.face.Face;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070038import android.hardware.face.FaceManager;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020039import android.hardware.face.IFaceService;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020040import android.hardware.face.IFaceServiceReceiver;
41import android.os.Binder;
Joe Onoratobf955d22019-03-25 00:16:58 -070042import android.os.Build;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020043import android.os.Environment;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020044import android.os.IBinder;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020045import android.os.RemoteException;
46import android.os.SELinux;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020047import android.os.UserHandle;
48import android.os.UserManager;
Joe Onoratobf955d22019-03-25 00:16:58 -070049import android.service.restricted_image.RestrictedImageProto;
50import android.service.restricted_image.RestrictedImageSetProto;
Kevin Chyn1d6a2862019-04-02 16:20:21 -070051import android.service.restricted_image.RestrictedImagesDumpProto;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020052import android.util.Slog;
53import android.util.proto.ProtoOutputStream;
54
55import com.android.internal.annotations.GuardedBy;
56import com.android.internal.logging.MetricsLogger;
57import com.android.internal.util.DumpUtils;
58import com.android.server.SystemServerInitThreadPool;
Kevin Chyna38653c2019-02-11 17:46:21 -080059import com.android.server.biometrics.AuthenticationClient;
Kevin Chyn355c6bf2018-09-20 22:14:19 -070060import com.android.server.biometrics.BiometricServiceBase;
Kevin Chyn836f2cf2018-08-27 11:06:39 -070061import com.android.server.biometrics.BiometricUtils;
Kevin Chyn6737c572019-02-08 16:10:54 -080062import com.android.server.biometrics.EnumerateClient;
Kevin Chyn836f2cf2018-08-27 11:06:39 -070063import com.android.server.biometrics.Metrics;
Kevin Chyn6737c572019-02-08 16:10:54 -080064import com.android.server.biometrics.RemovalClient;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020065
66import org.json.JSONArray;
67import org.json.JSONException;
68import org.json.JSONObject;
69
70import java.io.File;
71import java.io.FileDescriptor;
72import java.io.PrintWriter;
73import java.util.ArrayList;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020074import java.util.List;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020075
76/**
77 * A service to manage multiple clients that want to access the face HAL API.
78 * The service is responsible for maintaining a list of clients and dispatching all
Kevin Chyn51676d22018-11-05 18:00:43 -080079 * face-related events.
Gilad Brettercb51b8b2018-03-22 17:04:51 +020080 *
81 * @hide
82 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -070083public class FaceService extends BiometricServiceBase {
Kevin Chyna56dff72018-06-19 18:41:12 -070084
85 protected static final String TAG = "FaceService";
86 private static final boolean DEBUG = true;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020087 private static final String FACE_DATA_DIR = "facedata";
Gilad Brettercb51b8b2018-03-22 17:04:51 +020088 private static final String ACTION_LOCKOUT_RESET =
Kevin Chyn2ffadb32018-06-19 11:29:38 -070089 "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
Kevin Chyne46a2162018-09-20 18:43:01 -070090 private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
Gilad Brettercb51b8b2018-03-22 17:04:51 +020091
Kevin Chyn8b7a0372018-09-17 15:06:05 -070092 private final class FaceAuthClient extends AuthenticationClientImpl {
93 public FaceAuthClient(Context context,
94 DaemonWrapper daemon, long halDeviceId, IBinder token,
95 ServiceListener listener, int targetUserId, int groupId, long opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -080096 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
Kevin Chyn8b7a0372018-09-17 15:06:05 -070097 super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -080098 restricted, owner, cookie, requireConfirmation);
Kevin Chyn8b7a0372018-09-17 15:06:05 -070099 }
Kevin Chyn7782d142019-01-18 12:51:33 -0800100
101 @Override
102 protected int statsModality() {
103 return FaceService.this.statsModality();
104 }
Kevin Chyna38653c2019-02-11 17:46:21 -0800105
106 @Override
107 public boolean shouldFrameworkHandleLockout() {
108 return false;
109 }
Kevin Chyn56d6b072019-02-13 18:39:01 -0800110
111 @Override
112 public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
113 boolean authenticated, ArrayList<Byte> token) {
114 final boolean result = super.onAuthenticated(identifier, authenticated, token);
115
116 // For face, the authentication lifecycle ends either when
117 // 1) Authenticated == true
118 // 2) Error occurred
119 // 3) Authenticated == false
120 // Fingerprint currently does not end when the third condition is met which is a bug,
121 // but let's leave it as-is for now.
122 return result || !authenticated;
123 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700124 }
125
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200126 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700127 * Receives the incoming binder calls from FaceManager.
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200128 */
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200129 private final class FaceServiceWrapper extends IFaceService.Stub {
Kevin Chyna56dff72018-06-19 18:41:12 -0700130
131 /**
132 * The following methods contain common code which is shared in biometrics/common.
133 */
Kevin Chyna38653c2019-02-11 17:46:21 -0800134
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200135 @Override // Binder call
Kevin Chynd79e24e2018-09-25 12:06:59 -0700136 public long generateChallenge(IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700137 checkPermission(MANAGE_BIOMETRIC);
Kevin Chynd79e24e2018-09-25 12:06:59 -0700138 return startGenerateChallenge(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200139 }
140
141 @Override // Binder call
Kevin Chynd79e24e2018-09-25 12:06:59 -0700142 public int revokeChallenge(IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700143 checkPermission(MANAGE_BIOMETRIC);
Kevin Chynd79e24e2018-09-25 12:06:59 -0700144 return startRevokeChallenge(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200145 }
146
147 @Override // Binder call
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800148 public void enroll(final IBinder token, final byte[] cryptoToken,
149 final IFaceServiceReceiver receiver, final String opPackageName,
150 final int[] disabledFeatures) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700151 checkPermission(MANAGE_BIOMETRIC);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200152
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200153 final boolean restricted = isRestricted();
Kevin Chyna56dff72018-06-19 18:41:12 -0700154 final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
155 mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
Kevin Chyn1429a312019-01-28 16:08:09 -0800156 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) {
157 @Override
158 public boolean shouldVibrate() {
159 return false;
160 }
Kevin Chyn7782d142019-01-18 12:51:33 -0800161
162 @Override
163 protected int statsModality() {
164 return FaceService.this.statsModality();
165 }
Kevin Chyn1429a312019-01-28 16:08:09 -0800166 };
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200167
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800168 enrollInternal(client, UserHandle.getCallingUserId());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200169 }
170
171 @Override // Binder call
172 public void cancelEnrollment(final IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700173 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700174 cancelEnrollmentInternal(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200175 }
176
177 @Override // Binder call
Kevin Chyn747e29b2019-01-11 17:01:53 -0800178 public void authenticate(final IBinder token, final long opId, int userId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700179 final IFaceServiceReceiver receiver, final int flags,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700180 final String opPackageName) {
181 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyn747e29b2019-01-11 17:01:53 -0800182 updateActiveGroup(userId, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200183 final boolean restricted = isRestricted();
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700184 final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
Kevin Chyna56dff72018-06-19 18:41:12 -0700185 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700186 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800187 0 /* cookie */, false /* requireConfirmation */);
Kevin Chyna56dff72018-06-19 18:41:12 -0700188 authenticateInternal(client, opId, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200189 }
190
191 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800192 public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
Kevin Chyn23289ef2018-11-28 16:32:36 -0800193 int groupId, IBiometricServiceReceiverInternal wrapperReceiver,
194 String opPackageName, int cookie, int callingUid, int callingPid,
195 int callingUserId) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700196 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyn41a80902019-02-06 08:12:15 -0800197 updateActiveGroup(groupId, opPackageName);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700198 final boolean restricted = true; // BiometricPrompt is always restricted
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700199 final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700200 mDaemonWrapper, mHalDeviceId, token,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800201 new BiometricPromptServiceListenerImpl(wrapperReceiver),
202 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
Kevin Chyn158fefb2019-01-03 18:59:05 -0800203 requireConfirmation);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700204 authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
205 callingUserId);
206 }
207
208 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800209 public void startPreparedClient(int cookie) {
210 checkPermission(MANAGE_BIOMETRIC);
211 startCurrentClient(cookie);
212 }
213
214 @Override // Binder call
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200215 public void cancelAuthentication(final IBinder token, final String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700216 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700217 cancelAuthenticationInternal(token, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200218 }
219
220 @Override // Binder call
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700221 public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
Kevin Chyne92cdae2018-11-21 16:35:04 -0800222 int callingUid, int callingPid, int callingUserId, boolean fromClient) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700223 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800224 cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
225 callingUserId, fromClient);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700226 }
227
228 @Override // Binder call
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200229 public void setActiveUser(final int userId) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700230 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700231 setActiveUserInternal(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200232 }
233
234 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700235 public void remove(final IBinder token, final int faceId, final int userId,
236 final IFaceServiceReceiver receiver) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700237 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700238
239 if (token == null) {
240 Slog.w(TAG, "remove(): token is null");
241 return;
242 }
243
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200244 final boolean restricted = isRestricted();
Kevin Chyn6737c572019-02-08 16:10:54 -0800245 final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
246 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
247 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
Kevin Chyn7782d142019-01-18 12:51:33 -0800248 @Override
249 protected int statsModality() {
250 return FaceService.this.statsModality();
251 }
252 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700253 removeInternal(client);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200254 }
255
Kevin Chyna56dff72018-06-19 18:41:12 -0700256 @Override
257 public void enumerate(final IBinder token, final int userId,
258 final IFaceServiceReceiver receiver) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700259 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700260
261 final boolean restricted = isRestricted();
Kevin Chyn6737c572019-02-08 16:10:54 -0800262 final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
263 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
264 userId, restricted, getContext().getOpPackageName()) {
Kevin Chyn7782d142019-01-18 12:51:33 -0800265 @Override
266 protected int statsModality() {
267 return FaceService.this.statsModality();
268 }
269 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700270 enumerateInternal(client);
271 }
272
273 @Override
274 public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
275 throws RemoteException {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700276 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700277 FaceService.super.addLockoutResetCallback(callback);
278 }
279
280 @Override // Binder call
281 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
282 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
283 return;
284 }
285
286 final long ident = Binder.clearCallingIdentity();
287 try {
Joe Onoratobf955d22019-03-25 00:16:58 -0700288 if (args.length == 1 && "--restricted_image".equals(args[0])) {
289 dumpRestrictedImage(fd);
290 } else if (args.length > 0 && "--proto".equals(args[0])) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700291 dumpProto(fd);
292 } else {
293 dumpInternal(pw);
294 }
295 } finally {
296 Binder.restoreCallingIdentity(ident);
297 }
298 }
299
300 /**
301 * The following methods don't use any common code from BiometricService
302 */
303
304 // TODO: refactor out common code here
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200305 @Override // Binder call
306 public boolean isHardwareDetected(long deviceId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700307 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700308 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200309 Binder.getCallingUid(), Binder.getCallingPid(),
310 UserHandle.getCallingUserId())) {
311 return false;
312 }
313
314 final long token = Binder.clearCallingIdentity();
315 try {
316 IBiometricsFace daemon = getFaceDaemon();
317 return daemon != null && mHalDeviceId != 0;
318 } finally {
319 Binder.restoreCallingIdentity(token);
320 }
321 }
322
323 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700324 public void rename(final int faceId, final String name) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700325 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700326 if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
327 return;
328 }
329 mHandler.post(new Runnable() {
330 @Override
331 public void run() {
332 getBiometricUtils().renameBiometricForUser(getContext(), mCurrentUserId,
333 faceId, name);
334 }
335 });
336 }
337
338 @Override // Binder call
339 public List<Face> getEnrolledFaces(int userId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700340 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700341 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200342 Binder.getCallingUid(), Binder.getCallingPid(),
343 UserHandle.getCallingUserId())) {
344 return null;
345 }
346
Kevin Chyn6737c572019-02-08 16:10:54 -0800347 return FaceService.this.getEnrolledTemplates(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200348 }
349
350 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700351 public boolean hasEnrolledFaces(int userId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700352 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700353 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200354 Binder.getCallingUid(), Binder.getCallingPid(),
355 UserHandle.getCallingUserId())) {
356 return false;
357 }
358
Kevin Chyna56dff72018-06-19 18:41:12 -0700359 return FaceService.this.hasEnrolledBiometrics(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200360 }
361
362 @Override // Binder call
363 public long getAuthenticatorId(String opPackageName) {
364 // In this method, we're not checking whether the caller is permitted to use face
365 // API because current authenticator ID is leaked (in a more contrived way) via Android
366 // Keystore (android.security.keystore package): the user of that API can create a key
367 // which requires face authentication for its use, and then query the key's
368 // characteristics (hidden API) which returns, among other things, face
369 // authenticator ID which was active at key creation time.
370 //
371 // Reason: The part of Android Keystore which runs inside an app's process invokes this
372 // method in certain cases. Those cases are not always where the developer demonstrates
373 // explicit intent to use face functionality. Thus, to avoiding throwing an
374 // unexpected SecurityException this method does not check whether its caller is
375 // permitted to use face API.
376 //
377 // The permission check should be restored once Android Keystore no longer invokes this
378 // method from inside app processes.
379
380 return FaceService.this.getAuthenticatorId(opPackageName);
381 }
382
383 @Override // Binder call
Kevin Chyna38653c2019-02-11 17:46:21 -0800384 public void resetLockout(byte[] token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700385 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyn1d6a2862019-04-02 16:20:21 -0700386
387 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
388 Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
389 return;
390 }
391
Kevin Chyna38653c2019-02-11 17:46:21 -0800392 try {
393 mDaemonWrapper.resetLockout(token);
394 } catch (RemoteException e) {
395 Slog.e(getTag(), "Unable to reset lockout", e);
396 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700397 }
Kevin Chynd79e24e2018-09-25 12:06:59 -0700398
399 @Override
Kevin Chyne62749a2019-04-02 19:33:56 -0700400 public void setFeature(int feature, boolean enabled, final byte[] token,
401 IFaceServiceReceiver receiver) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700402 checkPermission(MANAGE_BIOMETRIC);
403
Kevin Chyne62749a2019-04-02 19:33:56 -0700404 mHandler.post(() -> {
405 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
406 Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
407 return;
Kevin Chynb95f1522019-03-04 16:45:15 -0800408 }
Kevin Chyne62749a2019-04-02 19:33:56 -0700409
410 final ArrayList<Byte> byteToken = new ArrayList<>();
411 for (int i = 0; i < token.length; i++) {
412 byteToken.add(token[i]);
413 }
414
415 // TODO: Support multiple faces
416 final int faceId = getFirstTemplateForUser(mCurrentUserId);
417
418 if (mDaemon != null) {
419 try {
420 final int result = mDaemon.setFeature(feature, enabled, byteToken, faceId);
421 receiver.onFeatureSet(result == Status.OK, feature);
422 } catch (RemoteException e) {
423 Slog.e(getTag(), "Unable to set feature: " + feature
424 + " to enabled:" + enabled, e);
425 }
426 }
427 });
428
Kevin Chynd79e24e2018-09-25 12:06:59 -0700429 }
430
431 @Override
Kevin Chyne62749a2019-04-02 19:33:56 -0700432 public void getFeature(int feature, IFaceServiceReceiver receiver) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700433 checkPermission(MANAGE_BIOMETRIC);
434
Kevin Chyne62749a2019-04-02 19:33:56 -0700435 mHandler.post(() -> {
436 // This should ideally return tri-state, but the user isn't shown settings unless
437 // they are enrolled so it's fine for now.
438 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
439 Slog.e(TAG, "No enrolled biometrics while getting feature: " + feature);
440 return;
Kevin Chynb95f1522019-03-04 16:45:15 -0800441 }
Kevin Chyne62749a2019-04-02 19:33:56 -0700442
443 // TODO: Support multiple faces
444 final int faceId = getFirstTemplateForUser(mCurrentUserId);
445
446 if (mDaemon != null) {
447 try {
448 OptionalBool result = mDaemon.getFeature(feature, faceId);
449 receiver.onFeatureGet(result.status == Status.OK, feature, result.value);
450 } catch (RemoteException e) {
451 Slog.e(getTag(), "Unable to getRequireAttention", e);
452 }
453 }
454 });
455
Kevin Chynd79e24e2018-09-25 12:06:59 -0700456 }
Kevin Chyn57f119b2018-10-25 12:03:41 -0700457
458 @Override
459 public void userActivity() {
460 checkPermission(MANAGE_BIOMETRIC);
461
462 if (mDaemon != null) {
463 try {
464 mDaemon.userActivity();
465 } catch (RemoteException e) {
466 Slog.e(getTag(), "Unable to send userActivity", e);
467 }
468 }
469 }
Kevin Chynb95f1522019-03-04 16:45:15 -0800470
471 // TODO: Support multiple faces
472 private int getFirstTemplateForUser(int user) {
473 final List<Face> faces = FaceService.this.getEnrolledTemplates(user);
474 if (!faces.isEmpty()) {
475 return faces.get(0).getBiometricId();
476 }
477 return 0;
478 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700479 }
480
481 /**
482 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700483 * BiometricPrompt.
484 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800485 private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800486 BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800487 super(wrapperReceiver);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700488 }
489
490 @Override
491 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
492 throws RemoteException {
493 /**
494 * Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
495 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800496 if (getWrapperReceiver() != null) {
497 getWrapperReceiver().onAcquired(
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700498 FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
499 FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700500 }
501 }
502
503 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800504 public void onError(long deviceId, int error, int vendorCode, int cookie)
505 throws RemoteException {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800506 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800507 getWrapperReceiver().onError(cookie, error,
508 FaceManager.getErrorString(getContext(), error, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700509 }
510 }
511 }
512
513 /**
514 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna56dff72018-06-19 18:41:12 -0700515 * the FaceManager.
516 */
517 private class ServiceListenerImpl implements ServiceListener {
Kevin Chyna56dff72018-06-19 18:41:12 -0700518 private IFaceServiceReceiver mFaceServiceReceiver;
519
520 public ServiceListenerImpl(IFaceServiceReceiver receiver) {
521 mFaceServiceReceiver = receiver;
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200522 }
523
524 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700525 public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200526 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700527 if (mFaceServiceReceiver != null) {
528 mFaceServiceReceiver.onEnrollResult(identifier.getDeviceId(),
529 identifier.getBiometricId(),
530 remaining);
531 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200532 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700533
534 @Override
535 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
536 throws RemoteException {
537 if (mFaceServiceReceiver != null) {
538 mFaceServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
539 }
540 }
541
542 @Override
543 public void onAuthenticationSucceeded(long deviceId,
544 BiometricAuthenticator.Identifier biometric, int userId)
545 throws RemoteException {
546 if (mFaceServiceReceiver != null) {
Kevin Chyn628b7182018-11-13 12:00:48 -0800547 if (biometric == null || biometric instanceof Face) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700548 mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face)biometric);
549 } else {
550 Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
551 }
552 }
553 }
554
555 @Override
556 public void onAuthenticationFailed(long deviceId) throws RemoteException {
557 if (mFaceServiceReceiver != null) {
558 mFaceServiceReceiver.onAuthenticationFailed(deviceId);
559 }
560 }
561
562 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800563 public void onError(long deviceId, int error, int vendorCode, int cookie)
564 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700565 if (mFaceServiceReceiver != null) {
566 mFaceServiceReceiver.onError(deviceId, error, vendorCode);
567 }
568 }
569
570 @Override
571 public void onRemoved(BiometricAuthenticator.Identifier identifier,
572 int remaining) throws RemoteException {
573 if (mFaceServiceReceiver != null) {
574 mFaceServiceReceiver.onRemoved(identifier.getDeviceId(),
575 identifier.getBiometricId(), remaining);
576 }
577 }
578
579 @Override
580 public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
581 throws RemoteException {
582 if (mFaceServiceReceiver != null) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800583 mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(),
584 identifier.getBiometricId(), remaining);
Kevin Chyna56dff72018-06-19 18:41:12 -0700585 }
586 }
587 }
588
589 private final FaceMetrics mFaceMetrics = new FaceMetrics();
590
591 @GuardedBy("this")
592 private IBiometricsFace mDaemon;
Kevin Chyna38653c2019-02-11 17:46:21 -0800593 // One of the AuthenticationClient constants
594 private int mCurrentUserLockoutMode;
Kevin Chyna56dff72018-06-19 18:41:12 -0700595
596 /**
597 * Receives callbacks from the HAL.
598 */
599 private IBiometricsFaceClientCallback mDaemonCallback =
600 new IBiometricsFaceClientCallback.Stub() {
Kevin Chyn6737c572019-02-08 16:10:54 -0800601 @Override
602 public void onEnrollResult(final long deviceId, int faceId, int userId,
603 int remaining) {
604 mHandler.post(() -> {
605 final Face face = new Face(getBiometricUtils()
606 .getUniqueName(getContext(), userId), faceId, deviceId);
607 FaceService.super.handleEnrollResult(face, remaining);
608 });
609 }
610
611 @Override
612 public void onAcquired(final long deviceId, final int userId,
613 final int acquiredInfo,
614 final int vendorCode) {
615 mHandler.post(() -> {
616 FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
617 });
618 }
619
620 @Override
621 public void onAuthenticated(final long deviceId, final int faceId, final int userId,
622 ArrayList<Byte> token) {
623 mHandler.post(() -> {
624 Face face = new Face("", faceId, deviceId);
625 FaceService.super.handleAuthenticated(face, token);
626 });
627 }
628
629 @Override
630 public void onError(final long deviceId, final int userId, final int error,
631 final int vendorCode) {
632 mHandler.post(() -> {
633 FaceService.super.handleError(deviceId, error, vendorCode);
634
635 // TODO: this chunk of code should be common to all biometric services
636 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
637 // If we get HW_UNAVAILABLE, try to connect again later...
638 Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
639 synchronized (this) {
640 mDaemon = null;
641 mHalDeviceId = 0;
642 mCurrentUserId = UserHandle.USER_NULL;
643 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700644 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800645 });
646 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700647
Kevin Chyn6737c572019-02-08 16:10:54 -0800648 @Override
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700649 public void onRemoved(final long deviceId, ArrayList<Integer> faceIds, final int userId) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800650 mHandler.post(() -> {
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700651 if (!faceIds.isEmpty()) {
652 for (int i = 0; i < faceIds.size(); i++) {
653 final Face face = new Face("", faceIds.get(i), deviceId);
654 // Convert to old behavior
655 FaceService.super.handleRemoved(face, faceIds.size() - i - 1);
656 }
657 } else {
658 final Face face = new Face("", 0 /* identifier */, deviceId);
659 FaceService.super.handleRemoved(face, 0 /* remaining */);
660 }
661
Kevin Chyn6737c572019-02-08 16:10:54 -0800662 });
663 }
664
665 @Override
666 public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
667 throws RemoteException {
668 mHandler.post(() -> {
669 if (!faceIds.isEmpty()) {
670 for (int i = 0; i < faceIds.size(); i++) {
671 final Face face = new Face("", faceIds.get(i), deviceId);
672 // Convert to old old behavior
673 FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
674 }
675 } else {
676 // For face, the HIDL contract is to receive an empty list when there are no
677 // templates enrolled. Send a null identifier since we don't consume them
678 // anywhere, and send remaining == 0 to plumb this with existing common code.
679 FaceService.super.handleEnumerate(null /* identifier */, 0);
Kevin Chyna56dff72018-06-19 18:41:12 -0700680 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800681 });
682 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700683
Kevin Chyn6737c572019-02-08 16:10:54 -0800684 @Override
685 public void onLockoutChanged(long duration) {
Kevin Chyna38653c2019-02-11 17:46:21 -0800686 Slog.d(TAG, "onLockoutChanged: " + duration);
687 if (duration == 0) {
688 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
689 } else if (duration == Long.MAX_VALUE) {
690 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
691 } else {
692 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
693 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700694
Kevin Chyna38653c2019-02-11 17:46:21 -0800695 mHandler.post(() -> {
696 if (duration == 0) {
697 notifyLockoutResetMonitors();
698 }
699 });
Kevin Chyn6737c572019-02-08 16:10:54 -0800700 }
701 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700702
703 /**
704 * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
705 * can be shared between the multiple biometric services.
706 */
707 private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
708 @Override
709 public int authenticate(long operationId, int groupId) throws RemoteException {
710 IBiometricsFace daemon = getFaceDaemon();
711 if (daemon == null) {
712 Slog.w(TAG, "authenticate(): no face HAL!");
713 return ERROR_ESRCH;
714 }
715 return daemon.authenticate(operationId);
716 }
717
718 @Override
719 public int cancel() throws RemoteException {
720 IBiometricsFace daemon = getFaceDaemon();
721 if (daemon == null) {
722 Slog.w(TAG, "cancel(): no face HAL!");
723 return ERROR_ESRCH;
724 }
725 return daemon.cancel();
726 }
727
728 @Override
729 public int remove(int groupId, int biometricId) throws RemoteException {
730 IBiometricsFace daemon = getFaceDaemon();
731 if (daemon == null) {
732 Slog.w(TAG, "remove(): no face HAL!");
733 return ERROR_ESRCH;
734 }
735 return daemon.remove(biometricId);
736 }
737
738 @Override
739 public int enumerate() throws RemoteException {
740 IBiometricsFace daemon = getFaceDaemon();
741 if (daemon == null) {
742 Slog.w(TAG, "enumerate(): no face HAL!");
743 return ERROR_ESRCH;
744 }
745 return daemon.enumerate();
746 }
747
748 @Override
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800749 public int enroll(byte[] cryptoToken, int groupId, int timeout,
750 ArrayList<Integer> disabledFeatures) throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700751 IBiometricsFace daemon = getFaceDaemon();
752 if (daemon == null) {
753 Slog.w(TAG, "enroll(): no face HAL!");
754 return ERROR_ESRCH;
755 }
756 final ArrayList<Byte> token = new ArrayList<>();
757 for (int i = 0; i < cryptoToken.length; i++) {
758 token.add(cryptoToken[i]);
759 }
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800760 return daemon.enroll(token, timeout, disabledFeatures);
Kevin Chyna56dff72018-06-19 18:41:12 -0700761 }
Kevin Chyna38653c2019-02-11 17:46:21 -0800762
763 @Override
764 public void resetLockout(byte[] cryptoToken) throws RemoteException {
765 IBiometricsFace daemon = getFaceDaemon();
766 if (daemon == null) {
767 Slog.w(TAG, "resetLockout(): no face HAL!");
768 return;
769 }
770 final ArrayList<Byte> token = new ArrayList<>();
771 for (int i = 0; i < cryptoToken.length; i++) {
772 token.add(cryptoToken[i]);
773 }
774 daemon.resetLockout(token);
775 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700776 };
777
778
779 public FaceService(Context context) {
780 super(context);
Kevin Chyna56dff72018-06-19 18:41:12 -0700781 }
782
783 @Override
784 public void onStart() {
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -0700785 super.onStart();
Kevin Chyna56dff72018-06-19 18:41:12 -0700786 publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
787 SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
788 }
789
790 @Override
791 public String getTag() {
792 return TAG;
793 }
794
795 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800796 protected DaemonWrapper getDaemonWrapper() {
797 return mDaemonWrapper;
798 }
799
800 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700801 protected BiometricUtils getBiometricUtils() {
802 return FaceUtils.getInstance();
803 }
804
805 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700806 protected Metrics getMetrics() {
807 return mFaceMetrics;
808 }
809
810 @Override
811 protected boolean hasReachedEnrollmentLimit(int userId) {
812 final int limit = getContext().getResources().getInteger(
Kevin Chyn017e76e2018-06-27 18:35:06 -0700813 com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
Kevin Chyn6737c572019-02-08 16:10:54 -0800814 final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
Kevin Chyna56dff72018-06-19 18:41:12 -0700815 if (enrolled >= limit) {
816 Slog.w(TAG, "Too many faces registered");
817 return true;
818 }
819 return false;
820 }
821
822 @Override
Kevin Chyn9ba99912019-01-16 16:24:36 -0800823 public void serviceDied(long cookie) {
824 super.serviceDied(cookie);
825 mDaemon = null;
Jim Miller7b78b222019-02-07 16:47:38 -0800826
827 mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate
Kevin Chyn9ba99912019-01-16 16:24:36 -0800828 }
829
830 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700831 protected void updateActiveGroup(int userId, String clientPackage) {
832 IBiometricsFace daemon = getFaceDaemon();
833
834 if (daemon != null) {
835 try {
836 userId = getUserOrWorkProfileId(clientPackage, userId);
837 if (userId != mCurrentUserId) {
838 final File baseDir = Environment.getDataVendorDeDirectory(userId);
839 final File faceDir = new File(baseDir, FACE_DATA_DIR);
840 if (!faceDir.exists()) {
841 if (!faceDir.mkdir()) {
842 Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
843 return;
844 }
845 // Calling mkdir() from this process will create a directory with our
846 // permissions (inherited from the containing dir). This command fixes
847 // the label.
848 if (!SELinux.restorecon(faceDir)) {
849 Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
850 return;
851 }
852 }
853
854 daemon.setActiveUser(userId, faceDir.getAbsolutePath());
855 mCurrentUserId = userId;
856 }
857 mAuthenticatorIds.put(userId,
858 hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
859 } catch (RemoteException e) {
860 Slog.e(TAG, "Failed to setActiveUser():", e);
861 }
862 }
863 }
864
865 @Override
866 protected String getLockoutResetIntent() {
867 return ACTION_LOCKOUT_RESET;
868 }
869
870 @Override
871 protected String getLockoutBroadcastPermission() {
872 return RESET_FACE_LOCKOUT;
873 }
874
875 @Override
876 protected long getHalDeviceId() {
877 return mHalDeviceId;
878 }
879
880 @Override
Kevin Chyna38653c2019-02-11 17:46:21 -0800881 protected void handleUserSwitching(int userId) {
882 super.handleUserSwitching(userId);
883 // Will be updated when we get the callback from HAL
884 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
885 }
886
887 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700888 protected boolean hasEnrolledBiometrics(int userId) {
889 if (userId != UserHandle.getCallingUserId()) {
890 checkPermission(INTERACT_ACROSS_USERS);
891 }
892 return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
893 }
894
895 @Override
896 protected String getManageBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700897 return MANAGE_BIOMETRIC;
Kevin Chyna56dff72018-06-19 18:41:12 -0700898 }
899
900 @Override
901 protected void checkUseBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700902 // noop for Face. The permission checks are all done on the incoming binder call.
Kevin Chyna56dff72018-06-19 18:41:12 -0700903 }
904
905 @Override
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700906 protected boolean checkAppOps(int uid, String opPackageName) {
907 return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
908 == AppOpsManager.MODE_ALLOWED;
Kevin Chyna56dff72018-06-19 18:41:12 -0700909 }
910
911 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800912 protected List<Face> getEnrolledTemplates(int userId) {
913 return getBiometricUtils().getBiometricsForUser(getContext(), userId);
914 }
915
916 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700917 protected void notifyClientActiveCallbacks(boolean isActive) {
918 // noop for Face.
919 }
920
Kevin Chyn7782d142019-01-18 12:51:33 -0800921 @Override
922 protected int statsModality() {
923 return BiometricsProtoEnums.MODALITY_FACE;
924 }
925
Kevin Chyna38653c2019-02-11 17:46:21 -0800926 @Override
927 protected int getLockoutMode() {
928 return mCurrentUserLockoutMode;
929 }
930
Kevin Chyna56dff72018-06-19 18:41:12 -0700931 /** Gets the face daemon */
932 private synchronized IBiometricsFace getFaceDaemon() {
933 if (mDaemon == null) {
934 Slog.v(TAG, "mDaemon was null, reconnect to face");
935 try {
936 mDaemon = IBiometricsFace.getService();
937 } catch (java.util.NoSuchElementException e) {
938 // Service doesn't exist or cannot be opened. Logged below.
939 } catch (RemoteException e) {
940 Slog.e(TAG, "Failed to get biometric interface", e);
941 }
942 if (mDaemon == null) {
943 Slog.w(TAG, "face HIDL not available");
944 return null;
945 }
946
947 mDaemon.asBinder().linkToDeath(this, 0);
948
949 try {
950 mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
951 } catch (RemoteException e) {
952 Slog.e(TAG, "Failed to open face HAL", e);
953 mDaemon = null; // try again later!
954 }
955
956 if (DEBUG) Slog.v(TAG, "Face HAL id: " + mHalDeviceId);
957 if (mHalDeviceId != 0) {
958 loadAuthenticatorIds();
959 updateActiveGroup(ActivityManager.getCurrentUser(), null);
Kevin Chyn6737c572019-02-08 16:10:54 -0800960 doTemplateCleanupForUser(ActivityManager.getCurrentUser());
Kevin Chyna56dff72018-06-19 18:41:12 -0700961 } else {
962 Slog.w(TAG, "Failed to open Face HAL!");
963 MetricsLogger.count(getContext(), "faced_openhal_error", 1);
964 mDaemon = null;
965 }
966 }
967 return mDaemon;
968 }
969
Kevin Chynd79e24e2018-09-25 12:06:59 -0700970 private long startGenerateChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700971 IBiometricsFace daemon = getFaceDaemon();
972 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700973 Slog.w(TAG, "startGenerateChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -0700974 return 0;
975 }
976 try {
Kevin Chyne46a2162018-09-20 18:43:01 -0700977 return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
Kevin Chyna56dff72018-06-19 18:41:12 -0700978 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700979 Slog.e(TAG, "startGenerateChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -0700980 }
981 return 0;
982 }
983
Kevin Chynd79e24e2018-09-25 12:06:59 -0700984 private int startRevokeChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700985 IBiometricsFace daemon = getFaceDaemon();
986 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700987 Slog.w(TAG, "startRevokeChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -0700988 return 0;
989 }
990 try {
Kevin Chyn96c92972018-08-31 16:09:31 -0700991 return daemon.revokeChallenge();
Kevin Chyna56dff72018-06-19 18:41:12 -0700992 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700993 Slog.e(TAG, "startRevokeChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -0700994 }
995 return 0;
996 }
997
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200998 private void dumpInternal(PrintWriter pw) {
999 JSONObject dump = new JSONObject();
1000 try {
1001 dump.put("service", "Face Manager");
1002
1003 JSONArray sets = new JSONArray();
1004 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1005 final int userId = user.getUserHandle().getIdentifier();
Kevin Chyna56dff72018-06-19 18:41:12 -07001006 final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001007 PerformanceStats stats = mPerformanceMap.get(userId);
1008 PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
1009 JSONObject set = new JSONObject();
1010 set.put("id", userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001011 set.put("count", N);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001012 set.put("accept", (stats != null) ? stats.accept : 0);
1013 set.put("reject", (stats != null) ? stats.reject : 0);
1014 set.put("acquire", (stats != null) ? stats.acquire : 0);
1015 set.put("lockout", (stats != null) ? stats.lockout : 0);
1016 set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
1017 // cryptoStats measures statistics about secure face transactions
1018 // (e.g. to unlock password storage, make secure purchases, etc.)
1019 set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
1020 set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
1021 set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
1022 set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
Kevin Chyna56dff72018-06-19 18:41:12 -07001023 set.put("permanentLockoutCrypto",
1024 (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001025 sets.put(set);
1026 }
1027
1028 dump.put("prints", sets);
1029 } catch (JSONException e) {
1030 Slog.e(TAG, "dump formatting failure", e);
1031 }
1032 pw.println(dump);
Kevin Chyn9ba99912019-01-16 16:24:36 -08001033 pw.println("HAL Deaths: " + mHALDeathCount);
1034 mHALDeathCount = 0;
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001035 }
1036
1037 private void dumpProto(FileDescriptor fd) {
1038 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1039 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1040 final int userId = user.getUserHandle().getIdentifier();
1041
1042 final long userToken = proto.start(FaceServiceDumpProto.USERS);
1043
1044 proto.write(FaceUserStatsProto.USER_ID, userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001045 proto.write(FaceUserStatsProto.NUM_FACES,
1046 getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001047
1048 // Normal face authentications (e.g. lockscreen)
1049 final PerformanceStats normal = mPerformanceMap.get(userId);
1050 if (normal != null) {
1051 final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
1052 proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
1053 proto.write(FaceActionStatsProto.REJECT, normal.reject);
1054 proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
1055 proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
1056 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
1057 proto.end(countsToken);
1058 }
1059
1060 // Statistics about secure face transactions (e.g. to unlock password
1061 // storage, make secure purchases, etc.)
1062 final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
1063 if (crypto != null) {
1064 final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
1065 proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
1066 proto.write(FaceActionStatsProto.REJECT, crypto.reject);
1067 proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
1068 proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
1069 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
1070 proto.end(countsToken);
1071 }
1072
1073 proto.end(userToken);
1074 }
1075 proto.flush();
Kevin Chyna56dff72018-06-19 18:41:12 -07001076 mPerformanceMap.clear();
1077 mCryptoPerformanceMap.clear();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001078 }
Joe Onoratobf955d22019-03-25 00:16:58 -07001079
1080 private void dumpRestrictedImage(FileDescriptor fd) {
1081 // WARNING: CDD restricts image data from leaving TEE unencrypted on
1082 // production devices:
1083 // [C-1-10] MUST not allow unencrypted access to identifiable biometric
1084 // data or any data derived from it (such as embeddings) to the
1085 // Application Processor outside the context of the TEE.
1086 // As such, this API should only be enabled for testing purposes on
1087 // engineering and userdebug builds. All modules in the software stack
1088 // MUST enforce final build products do NOT have this functionality.
1089 // Additionally, the following check MUST NOT be removed.
1090 if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
1091 return;
1092 }
1093
1094 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1095
1096 final long setToken = proto.start(RestrictedImagesDumpProto.SETS);
1097
1098 // Name of the service
1099 proto.write(RestrictedImageSetProto.CATEGORY, "face");
1100
1101 // Individual images
1102 for (int i = 0; i < 5; i++) {
1103 final long imageToken = proto.start(RestrictedImageSetProto.IMAGES);
1104 proto.write(RestrictedImageProto.MIME_TYPE, "image/png");
1105 proto.write(RestrictedImageProto.IMAGE_DATA, new byte[] {
1106 // png image data
1107 -119, 80, 78, 71, 13, 10, 26, 10,
1108 0, 0, 0, 13, 73, 72, 68, 82,
1109 0, 0, 0, 100, 0, 0, 0, 100,
1110 1, 3, 0, 0, 0, 74, 44, 7,
1111 23, 0, 0, 0, 4, 103, 65, 77,
1112 65, 0, 0, -79, -113, 11, -4, 97,
1113 5, 0, 0, 0, 1, 115, 82, 71,
1114 66, 0, -82, -50, 28, -23, 0, 0,
1115 0, 6, 80, 76, 84, 69, -1, -1,
1116 -1, 0, 0, 0, 85, -62, -45, 126,
1117 0, 0, 0, -115, 73, 68, 65, 84,
1118 56, -53, -19, -46, -79, 17, -128, 32,
1119 12, 5, -48, 120, 22, -106, -116, -32,
1120 40, -84, 101, -121, -93, 57, 10, 35,
1121 88, 82, 112, 126, 3, -60, 104, 6,
1122 -112, 70, 127, -59, -69, -53, 29, 33,
1123 -127, -24, 79, -49, -52, -15, 41, 36,
1124 34, -105, 85, 124, -14, 88, 27, 6,
1125 28, 68, 1, 82, 62, 22, -95, -108,
1126 55, -95, 40, -9, -110, -12, 98, -107,
1127 76, -41, -105, -62, -50, 111, -60, 46,
1128 -14, -4, 24, -89, 42, -103, 16, 63,
1129 -72, -11, -15, 48, -62, 102, -44, 102,
1130 -73, -56, 56, -21, -128, 92, -70, -124,
1131 117, -46, -67, -77, 82, 80, 121, -44,
1132 -56, 116, 93, -45, -90, -5, -29, -24,
1133 -83, -75, 52, -34, 55, -22, 102, -21,
1134 -105, -124, -23, 71, 87, -7, -25, -59,
1135 -100, -73, -92, -122, -7, -109, -49, -80,
1136 -89, 0, 0, 0, 0, 73, 69, 78,
1137 68, -82, 66, 96, -126
1138 });
1139 // proto.write(RestrictedImageProto.METADATA, flattened_protobuf);
1140 proto.end(imageToken);
1141 }
1142
1143 // Face service metadata
1144 // proto.write(RestrictedImageSetProto.METADATA, flattened_protobuf);
1145
1146 proto.end(setToken);
1147 proto.flush();
1148 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001149}