blob: 14ba70aa5eac88963656405d3c667088029e16cc [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;
Joe Onorato108413a2019-04-03 18:20:52 -070047import android.os.SystemProperties;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020048import android.os.UserHandle;
49import android.os.UserManager;
Joe Onoratobf955d22019-03-25 00:16:58 -070050import android.service.restricted_image.RestrictedImageProto;
51import android.service.restricted_image.RestrictedImageSetProto;
Kevin Chyn1d6a2862019-04-02 16:20:21 -070052import android.service.restricted_image.RestrictedImagesDumpProto;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020053import android.util.Slog;
54import android.util.proto.ProtoOutputStream;
55
56import com.android.internal.annotations.GuardedBy;
57import com.android.internal.logging.MetricsLogger;
58import com.android.internal.util.DumpUtils;
59import com.android.server.SystemServerInitThreadPool;
Kevin Chyna38653c2019-02-11 17:46:21 -080060import com.android.server.biometrics.AuthenticationClient;
Kevin Chyn355c6bf2018-09-20 22:14:19 -070061import com.android.server.biometrics.BiometricServiceBase;
Kevin Chyn836f2cf2018-08-27 11:06:39 -070062import com.android.server.biometrics.BiometricUtils;
Kevin Chyn6737c572019-02-08 16:10:54 -080063import com.android.server.biometrics.EnumerateClient;
Kevin Chyn836f2cf2018-08-27 11:06:39 -070064import com.android.server.biometrics.Metrics;
Kevin Chyn6737c572019-02-08 16:10:54 -080065import com.android.server.biometrics.RemovalClient;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020066
67import org.json.JSONArray;
68import org.json.JSONException;
69import org.json.JSONObject;
70
71import java.io.File;
72import java.io.FileDescriptor;
73import java.io.PrintWriter;
74import java.util.ArrayList;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020075import java.util.List;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020076
77/**
78 * A service to manage multiple clients that want to access the face HAL API.
79 * The service is responsible for maintaining a list of clients and dispatching all
Kevin Chyn51676d22018-11-05 18:00:43 -080080 * face-related events.
Gilad Brettercb51b8b2018-03-22 17:04:51 +020081 *
82 * @hide
83 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -070084public class FaceService extends BiometricServiceBase {
Kevin Chyna56dff72018-06-19 18:41:12 -070085
86 protected static final String TAG = "FaceService";
87 private static final boolean DEBUG = true;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020088 private static final String FACE_DATA_DIR = "facedata";
Gilad Brettercb51b8b2018-03-22 17:04:51 +020089 private static final String ACTION_LOCKOUT_RESET =
Kevin Chyn2ffadb32018-06-19 11:29:38 -070090 "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
Kevin Chyne46a2162018-09-20 18:43:01 -070091 private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
Gilad Brettercb51b8b2018-03-22 17:04:51 +020092
Kevin Chyn8b7a0372018-09-17 15:06:05 -070093 private final class FaceAuthClient extends AuthenticationClientImpl {
94 public FaceAuthClient(Context context,
95 DaemonWrapper daemon, long halDeviceId, IBinder token,
96 ServiceListener listener, int targetUserId, int groupId, long opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -080097 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
Kevin Chyn8b7a0372018-09-17 15:06:05 -070098 super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -080099 restricted, owner, cookie, requireConfirmation);
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700100 }
Kevin Chyn7782d142019-01-18 12:51:33 -0800101
102 @Override
103 protected int statsModality() {
104 return FaceService.this.statsModality();
105 }
Kevin Chyna38653c2019-02-11 17:46:21 -0800106
107 @Override
108 public boolean shouldFrameworkHandleLockout() {
109 return false;
110 }
Kevin Chyn56d6b072019-02-13 18:39:01 -0800111
112 @Override
113 public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
114 boolean authenticated, ArrayList<Byte> token) {
115 final boolean result = super.onAuthenticated(identifier, authenticated, token);
116
117 // For face, the authentication lifecycle ends either when
118 // 1) Authenticated == true
119 // 2) Error occurred
120 // 3) Authenticated == false
121 // Fingerprint currently does not end when the third condition is met which is a bug,
122 // but let's leave it as-is for now.
123 return result || !authenticated;
124 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700125 }
126
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200127 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700128 * Receives the incoming binder calls from FaceManager.
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200129 */
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200130 private final class FaceServiceWrapper extends IFaceService.Stub {
Kevin Chyna56dff72018-06-19 18:41:12 -0700131
132 /**
133 * The following methods contain common code which is shared in biometrics/common.
134 */
Kevin Chyna38653c2019-02-11 17:46:21 -0800135
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200136 @Override // Binder call
Kevin Chynd79e24e2018-09-25 12:06:59 -0700137 public long generateChallenge(IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700138 checkPermission(MANAGE_BIOMETRIC);
Kevin Chynd79e24e2018-09-25 12:06:59 -0700139 return startGenerateChallenge(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200140 }
141
142 @Override // Binder call
Kevin Chynd79e24e2018-09-25 12:06:59 -0700143 public int revokeChallenge(IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700144 checkPermission(MANAGE_BIOMETRIC);
Kevin Chynd79e24e2018-09-25 12:06:59 -0700145 return startRevokeChallenge(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200146 }
147
148 @Override // Binder call
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800149 public void enroll(final IBinder token, final byte[] cryptoToken,
150 final IFaceServiceReceiver receiver, final String opPackageName,
151 final int[] disabledFeatures) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700152 checkPermission(MANAGE_BIOMETRIC);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200153
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200154 final boolean restricted = isRestricted();
Kevin Chyna56dff72018-06-19 18:41:12 -0700155 final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
156 mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
Kevin Chyn1429a312019-01-28 16:08:09 -0800157 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) {
158 @Override
159 public boolean shouldVibrate() {
160 return false;
161 }
Kevin Chyn7782d142019-01-18 12:51:33 -0800162
163 @Override
164 protected int statsModality() {
165 return FaceService.this.statsModality();
166 }
Kevin Chyn1429a312019-01-28 16:08:09 -0800167 };
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200168
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800169 enrollInternal(client, UserHandle.getCallingUserId());
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200170 }
171
172 @Override // Binder call
173 public void cancelEnrollment(final IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700174 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700175 cancelEnrollmentInternal(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200176 }
177
178 @Override // Binder call
Kevin Chyn747e29b2019-01-11 17:01:53 -0800179 public void authenticate(final IBinder token, final long opId, int userId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700180 final IFaceServiceReceiver receiver, final int flags,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700181 final String opPackageName) {
182 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyn747e29b2019-01-11 17:01:53 -0800183 updateActiveGroup(userId, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200184 final boolean restricted = isRestricted();
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700185 final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
Kevin Chyna56dff72018-06-19 18:41:12 -0700186 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700187 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800188 0 /* cookie */, false /* requireConfirmation */);
Kevin Chyna56dff72018-06-19 18:41:12 -0700189 authenticateInternal(client, opId, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200190 }
191
192 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800193 public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
Kevin Chyn23289ef2018-11-28 16:32:36 -0800194 int groupId, IBiometricServiceReceiverInternal wrapperReceiver,
195 String opPackageName, int cookie, int callingUid, int callingPid,
196 int callingUserId) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700197 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyn41a80902019-02-06 08:12:15 -0800198 updateActiveGroup(groupId, opPackageName);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700199 final boolean restricted = true; // BiometricPrompt is always restricted
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700200 final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700201 mDaemonWrapper, mHalDeviceId, token,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800202 new BiometricPromptServiceListenerImpl(wrapperReceiver),
203 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
Kevin Chyn158fefb2019-01-03 18:59:05 -0800204 requireConfirmation);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700205 authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
206 callingUserId);
207 }
208
209 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800210 public void startPreparedClient(int cookie) {
211 checkPermission(MANAGE_BIOMETRIC);
212 startCurrentClient(cookie);
213 }
214
215 @Override // Binder call
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200216 public void cancelAuthentication(final IBinder token, final String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700217 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700218 cancelAuthenticationInternal(token, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200219 }
220
221 @Override // Binder call
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700222 public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
Kevin Chyne92cdae2018-11-21 16:35:04 -0800223 int callingUid, int callingPid, int callingUserId, boolean fromClient) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700224 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800225 cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
226 callingUserId, fromClient);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700227 }
228
229 @Override // Binder call
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200230 public void setActiveUser(final int userId) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700231 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700232 setActiveUserInternal(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200233 }
234
235 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700236 public void remove(final IBinder token, final int faceId, final int userId,
237 final IFaceServiceReceiver receiver) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700238 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700239
240 if (token == null) {
241 Slog.w(TAG, "remove(): token is null");
242 return;
243 }
244
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200245 final boolean restricted = isRestricted();
Kevin Chyn6737c572019-02-08 16:10:54 -0800246 final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
247 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
248 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
Kevin Chyn7782d142019-01-18 12:51:33 -0800249 @Override
250 protected int statsModality() {
251 return FaceService.this.statsModality();
252 }
253 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700254 removeInternal(client);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200255 }
256
Kevin Chyna56dff72018-06-19 18:41:12 -0700257 @Override
258 public void enumerate(final IBinder token, final int userId,
259 final IFaceServiceReceiver receiver) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700260 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700261
262 final boolean restricted = isRestricted();
Kevin Chyn6737c572019-02-08 16:10:54 -0800263 final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
264 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
265 userId, restricted, getContext().getOpPackageName()) {
Kevin Chyn7782d142019-01-18 12:51:33 -0800266 @Override
267 protected int statsModality() {
268 return FaceService.this.statsModality();
269 }
270 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700271 enumerateInternal(client);
272 }
273
274 @Override
275 public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
276 throws RemoteException {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700277 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700278 FaceService.super.addLockoutResetCallback(callback);
279 }
280
281 @Override // Binder call
282 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
283 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
284 return;
285 }
286
287 final long ident = Binder.clearCallingIdentity();
288 try {
Joe Onoratobf955d22019-03-25 00:16:58 -0700289 if (args.length == 1 && "--restricted_image".equals(args[0])) {
290 dumpRestrictedImage(fd);
291 } else if (args.length > 0 && "--proto".equals(args[0])) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700292 dumpProto(fd);
293 } else {
294 dumpInternal(pw);
295 }
296 } finally {
297 Binder.restoreCallingIdentity(ident);
298 }
299 }
300
301 /**
302 * The following methods don't use any common code from BiometricService
303 */
304
305 // TODO: refactor out common code here
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200306 @Override // Binder call
307 public boolean isHardwareDetected(long deviceId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700308 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700309 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200310 Binder.getCallingUid(), Binder.getCallingPid(),
311 UserHandle.getCallingUserId())) {
312 return false;
313 }
314
315 final long token = Binder.clearCallingIdentity();
316 try {
317 IBiometricsFace daemon = getFaceDaemon();
318 return daemon != null && mHalDeviceId != 0;
319 } finally {
320 Binder.restoreCallingIdentity(token);
321 }
322 }
323
324 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700325 public void rename(final int faceId, final String name) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700326 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700327 if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
328 return;
329 }
330 mHandler.post(new Runnable() {
331 @Override
332 public void run() {
333 getBiometricUtils().renameBiometricForUser(getContext(), mCurrentUserId,
334 faceId, name);
335 }
336 });
337 }
338
339 @Override // Binder call
340 public List<Face> getEnrolledFaces(int userId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700341 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700342 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200343 Binder.getCallingUid(), Binder.getCallingPid(),
344 UserHandle.getCallingUserId())) {
345 return null;
346 }
347
Kevin Chyn6737c572019-02-08 16:10:54 -0800348 return FaceService.this.getEnrolledTemplates(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200349 }
350
351 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700352 public boolean hasEnrolledFaces(int userId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700353 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700354 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200355 Binder.getCallingUid(), Binder.getCallingPid(),
356 UserHandle.getCallingUserId())) {
357 return false;
358 }
359
Kevin Chyna56dff72018-06-19 18:41:12 -0700360 return FaceService.this.hasEnrolledBiometrics(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200361 }
362
363 @Override // Binder call
364 public long getAuthenticatorId(String opPackageName) {
365 // In this method, we're not checking whether the caller is permitted to use face
366 // API because current authenticator ID is leaked (in a more contrived way) via Android
367 // Keystore (android.security.keystore package): the user of that API can create a key
368 // which requires face authentication for its use, and then query the key's
369 // characteristics (hidden API) which returns, among other things, face
370 // authenticator ID which was active at key creation time.
371 //
372 // Reason: The part of Android Keystore which runs inside an app's process invokes this
373 // method in certain cases. Those cases are not always where the developer demonstrates
374 // explicit intent to use face functionality. Thus, to avoiding throwing an
375 // unexpected SecurityException this method does not check whether its caller is
376 // permitted to use face API.
377 //
378 // The permission check should be restored once Android Keystore no longer invokes this
379 // method from inside app processes.
380
381 return FaceService.this.getAuthenticatorId(opPackageName);
382 }
383
384 @Override // Binder call
Kevin Chyna38653c2019-02-11 17:46:21 -0800385 public void resetLockout(byte[] token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700386 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyn1d6a2862019-04-02 16:20:21 -0700387
388 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
389 Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
390 return;
391 }
392
Kevin Chyna38653c2019-02-11 17:46:21 -0800393 try {
394 mDaemonWrapper.resetLockout(token);
395 } catch (RemoteException e) {
396 Slog.e(getTag(), "Unable to reset lockout", e);
397 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700398 }
Kevin Chynd79e24e2018-09-25 12:06:59 -0700399
400 @Override
Kevin Chyne62749a2019-04-02 19:33:56 -0700401 public void setFeature(int feature, boolean enabled, final byte[] token,
402 IFaceServiceReceiver receiver) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700403 checkPermission(MANAGE_BIOMETRIC);
404
Kevin Chyne62749a2019-04-02 19:33:56 -0700405 mHandler.post(() -> {
406 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
407 Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
408 return;
Kevin Chynb95f1522019-03-04 16:45:15 -0800409 }
Kevin Chyne62749a2019-04-02 19:33:56 -0700410
411 final ArrayList<Byte> byteToken = new ArrayList<>();
412 for (int i = 0; i < token.length; i++) {
413 byteToken.add(token[i]);
414 }
415
416 // TODO: Support multiple faces
417 final int faceId = getFirstTemplateForUser(mCurrentUserId);
418
419 if (mDaemon != null) {
420 try {
421 final int result = mDaemon.setFeature(feature, enabled, byteToken, faceId);
422 receiver.onFeatureSet(result == Status.OK, feature);
423 } catch (RemoteException e) {
424 Slog.e(getTag(), "Unable to set feature: " + feature
425 + " to enabled:" + enabled, e);
426 }
427 }
428 });
429
Kevin Chynd79e24e2018-09-25 12:06:59 -0700430 }
431
432 @Override
Kevin Chyne62749a2019-04-02 19:33:56 -0700433 public void getFeature(int feature, IFaceServiceReceiver receiver) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700434 checkPermission(MANAGE_BIOMETRIC);
435
Kevin Chyne62749a2019-04-02 19:33:56 -0700436 mHandler.post(() -> {
437 // This should ideally return tri-state, but the user isn't shown settings unless
438 // they are enrolled so it's fine for now.
439 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
440 Slog.e(TAG, "No enrolled biometrics while getting feature: " + feature);
441 return;
Kevin Chynb95f1522019-03-04 16:45:15 -0800442 }
Kevin Chyne62749a2019-04-02 19:33:56 -0700443
444 // TODO: Support multiple faces
445 final int faceId = getFirstTemplateForUser(mCurrentUserId);
446
447 if (mDaemon != null) {
448 try {
449 OptionalBool result = mDaemon.getFeature(feature, faceId);
450 receiver.onFeatureGet(result.status == Status.OK, feature, result.value);
451 } catch (RemoteException e) {
452 Slog.e(getTag(), "Unable to getRequireAttention", e);
453 }
454 }
455 });
456
Kevin Chynd79e24e2018-09-25 12:06:59 -0700457 }
Kevin Chyn57f119b2018-10-25 12:03:41 -0700458
459 @Override
460 public void userActivity() {
461 checkPermission(MANAGE_BIOMETRIC);
462
463 if (mDaemon != null) {
464 try {
465 mDaemon.userActivity();
466 } catch (RemoteException e) {
467 Slog.e(getTag(), "Unable to send userActivity", e);
468 }
469 }
470 }
Kevin Chynb95f1522019-03-04 16:45:15 -0800471
472 // TODO: Support multiple faces
473 private int getFirstTemplateForUser(int user) {
474 final List<Face> faces = FaceService.this.getEnrolledTemplates(user);
475 if (!faces.isEmpty()) {
476 return faces.get(0).getBiometricId();
477 }
478 return 0;
479 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700480 }
481
482 /**
483 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700484 * BiometricPrompt.
485 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800486 private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800487 BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800488 super(wrapperReceiver);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700489 }
490
491 @Override
492 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
493 throws RemoteException {
494 /**
495 * Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
496 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800497 if (getWrapperReceiver() != null) {
498 getWrapperReceiver().onAcquired(
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700499 FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
500 FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700501 }
502 }
503
504 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800505 public void onError(long deviceId, int error, int vendorCode, int cookie)
506 throws RemoteException {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800507 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800508 getWrapperReceiver().onError(cookie, error,
509 FaceManager.getErrorString(getContext(), error, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700510 }
511 }
512 }
513
514 /**
515 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna56dff72018-06-19 18:41:12 -0700516 * the FaceManager.
517 */
518 private class ServiceListenerImpl implements ServiceListener {
Kevin Chyna56dff72018-06-19 18:41:12 -0700519 private IFaceServiceReceiver mFaceServiceReceiver;
520
521 public ServiceListenerImpl(IFaceServiceReceiver receiver) {
522 mFaceServiceReceiver = receiver;
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200523 }
524
525 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700526 public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200527 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700528 if (mFaceServiceReceiver != null) {
529 mFaceServiceReceiver.onEnrollResult(identifier.getDeviceId(),
530 identifier.getBiometricId(),
531 remaining);
532 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200533 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700534
535 @Override
536 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
537 throws RemoteException {
538 if (mFaceServiceReceiver != null) {
539 mFaceServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
540 }
541 }
542
543 @Override
544 public void onAuthenticationSucceeded(long deviceId,
545 BiometricAuthenticator.Identifier biometric, int userId)
546 throws RemoteException {
547 if (mFaceServiceReceiver != null) {
Kevin Chyn628b7182018-11-13 12:00:48 -0800548 if (biometric == null || biometric instanceof Face) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700549 mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face)biometric);
550 } else {
551 Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
552 }
553 }
554 }
555
556 @Override
557 public void onAuthenticationFailed(long deviceId) throws RemoteException {
558 if (mFaceServiceReceiver != null) {
559 mFaceServiceReceiver.onAuthenticationFailed(deviceId);
560 }
561 }
562
563 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800564 public void onError(long deviceId, int error, int vendorCode, int cookie)
565 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700566 if (mFaceServiceReceiver != null) {
567 mFaceServiceReceiver.onError(deviceId, error, vendorCode);
568 }
569 }
570
571 @Override
572 public void onRemoved(BiometricAuthenticator.Identifier identifier,
573 int remaining) throws RemoteException {
574 if (mFaceServiceReceiver != null) {
575 mFaceServiceReceiver.onRemoved(identifier.getDeviceId(),
576 identifier.getBiometricId(), remaining);
577 }
578 }
579
580 @Override
581 public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
582 throws RemoteException {
583 if (mFaceServiceReceiver != null) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800584 mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(),
585 identifier.getBiometricId(), remaining);
Kevin Chyna56dff72018-06-19 18:41:12 -0700586 }
587 }
588 }
589
590 private final FaceMetrics mFaceMetrics = new FaceMetrics();
591
592 @GuardedBy("this")
593 private IBiometricsFace mDaemon;
Kevin Chyna38653c2019-02-11 17:46:21 -0800594 // One of the AuthenticationClient constants
595 private int mCurrentUserLockoutMode;
Kevin Chyna56dff72018-06-19 18:41:12 -0700596
597 /**
598 * Receives callbacks from the HAL.
599 */
600 private IBiometricsFaceClientCallback mDaemonCallback =
601 new IBiometricsFaceClientCallback.Stub() {
Kevin Chyn6737c572019-02-08 16:10:54 -0800602 @Override
603 public void onEnrollResult(final long deviceId, int faceId, int userId,
604 int remaining) {
605 mHandler.post(() -> {
606 final Face face = new Face(getBiometricUtils()
607 .getUniqueName(getContext(), userId), faceId, deviceId);
608 FaceService.super.handleEnrollResult(face, remaining);
609 });
610 }
611
612 @Override
613 public void onAcquired(final long deviceId, final int userId,
614 final int acquiredInfo,
615 final int vendorCode) {
616 mHandler.post(() -> {
617 FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
618 });
619 }
620
621 @Override
622 public void onAuthenticated(final long deviceId, final int faceId, final int userId,
623 ArrayList<Byte> token) {
624 mHandler.post(() -> {
625 Face face = new Face("", faceId, deviceId);
626 FaceService.super.handleAuthenticated(face, token);
627 });
628 }
629
630 @Override
631 public void onError(final long deviceId, final int userId, final int error,
632 final int vendorCode) {
633 mHandler.post(() -> {
634 FaceService.super.handleError(deviceId, error, vendorCode);
635
636 // TODO: this chunk of code should be common to all biometric services
637 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
638 // If we get HW_UNAVAILABLE, try to connect again later...
639 Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
640 synchronized (this) {
641 mDaemon = null;
642 mHalDeviceId = 0;
643 mCurrentUserId = UserHandle.USER_NULL;
644 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700645 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800646 });
647 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700648
Kevin Chyn6737c572019-02-08 16:10:54 -0800649 @Override
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700650 public void onRemoved(final long deviceId, ArrayList<Integer> faceIds, final int userId) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800651 mHandler.post(() -> {
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700652 if (!faceIds.isEmpty()) {
653 for (int i = 0; i < faceIds.size(); i++) {
654 final Face face = new Face("", faceIds.get(i), deviceId);
655 // Convert to old behavior
656 FaceService.super.handleRemoved(face, faceIds.size() - i - 1);
657 }
658 } else {
659 final Face face = new Face("", 0 /* identifier */, deviceId);
660 FaceService.super.handleRemoved(face, 0 /* remaining */);
661 }
662
Kevin Chyn6737c572019-02-08 16:10:54 -0800663 });
664 }
665
666 @Override
667 public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
668 throws RemoteException {
669 mHandler.post(() -> {
670 if (!faceIds.isEmpty()) {
671 for (int i = 0; i < faceIds.size(); i++) {
672 final Face face = new Face("", faceIds.get(i), deviceId);
673 // Convert to old old behavior
674 FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
675 }
676 } else {
677 // For face, the HIDL contract is to receive an empty list when there are no
678 // templates enrolled. Send a null identifier since we don't consume them
679 // anywhere, and send remaining == 0 to plumb this with existing common code.
680 FaceService.super.handleEnumerate(null /* identifier */, 0);
Kevin Chyna56dff72018-06-19 18:41:12 -0700681 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800682 });
683 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700684
Kevin Chyn6737c572019-02-08 16:10:54 -0800685 @Override
686 public void onLockoutChanged(long duration) {
Kevin Chyna38653c2019-02-11 17:46:21 -0800687 Slog.d(TAG, "onLockoutChanged: " + duration);
688 if (duration == 0) {
689 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
690 } else if (duration == Long.MAX_VALUE) {
691 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
692 } else {
693 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
694 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700695
Kevin Chyna38653c2019-02-11 17:46:21 -0800696 mHandler.post(() -> {
697 if (duration == 0) {
698 notifyLockoutResetMonitors();
699 }
700 });
Kevin Chyn6737c572019-02-08 16:10:54 -0800701 }
702 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700703
704 /**
705 * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
706 * can be shared between the multiple biometric services.
707 */
708 private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
709 @Override
710 public int authenticate(long operationId, int groupId) throws RemoteException {
711 IBiometricsFace daemon = getFaceDaemon();
712 if (daemon == null) {
713 Slog.w(TAG, "authenticate(): no face HAL!");
714 return ERROR_ESRCH;
715 }
716 return daemon.authenticate(operationId);
717 }
718
719 @Override
720 public int cancel() throws RemoteException {
721 IBiometricsFace daemon = getFaceDaemon();
722 if (daemon == null) {
723 Slog.w(TAG, "cancel(): no face HAL!");
724 return ERROR_ESRCH;
725 }
726 return daemon.cancel();
727 }
728
729 @Override
730 public int remove(int groupId, int biometricId) throws RemoteException {
731 IBiometricsFace daemon = getFaceDaemon();
732 if (daemon == null) {
733 Slog.w(TAG, "remove(): no face HAL!");
734 return ERROR_ESRCH;
735 }
736 return daemon.remove(biometricId);
737 }
738
739 @Override
740 public int enumerate() throws RemoteException {
741 IBiometricsFace daemon = getFaceDaemon();
742 if (daemon == null) {
743 Slog.w(TAG, "enumerate(): no face HAL!");
744 return ERROR_ESRCH;
745 }
746 return daemon.enumerate();
747 }
748
749 @Override
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800750 public int enroll(byte[] cryptoToken, int groupId, int timeout,
751 ArrayList<Integer> disabledFeatures) throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700752 IBiometricsFace daemon = getFaceDaemon();
753 if (daemon == null) {
754 Slog.w(TAG, "enroll(): no face HAL!");
755 return ERROR_ESRCH;
756 }
757 final ArrayList<Byte> token = new ArrayList<>();
758 for (int i = 0; i < cryptoToken.length; i++) {
759 token.add(cryptoToken[i]);
760 }
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800761 return daemon.enroll(token, timeout, disabledFeatures);
Kevin Chyna56dff72018-06-19 18:41:12 -0700762 }
Kevin Chyna38653c2019-02-11 17:46:21 -0800763
764 @Override
765 public void resetLockout(byte[] cryptoToken) throws RemoteException {
766 IBiometricsFace daemon = getFaceDaemon();
767 if (daemon == null) {
768 Slog.w(TAG, "resetLockout(): no face HAL!");
769 return;
770 }
771 final ArrayList<Byte> token = new ArrayList<>();
772 for (int i = 0; i < cryptoToken.length; i++) {
773 token.add(cryptoToken[i]);
774 }
775 daemon.resetLockout(token);
776 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700777 };
778
779
780 public FaceService(Context context) {
781 super(context);
Kevin Chyna56dff72018-06-19 18:41:12 -0700782 }
783
784 @Override
785 public void onStart() {
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -0700786 super.onStart();
Kevin Chyna56dff72018-06-19 18:41:12 -0700787 publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
788 SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
789 }
790
791 @Override
792 public String getTag() {
793 return TAG;
794 }
795
796 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800797 protected DaemonWrapper getDaemonWrapper() {
798 return mDaemonWrapper;
799 }
800
801 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700802 protected BiometricUtils getBiometricUtils() {
803 return FaceUtils.getInstance();
804 }
805
806 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700807 protected Metrics getMetrics() {
808 return mFaceMetrics;
809 }
810
811 @Override
812 protected boolean hasReachedEnrollmentLimit(int userId) {
813 final int limit = getContext().getResources().getInteger(
Kevin Chyn017e76e2018-06-27 18:35:06 -0700814 com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
Kevin Chyn6737c572019-02-08 16:10:54 -0800815 final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
Kevin Chyna56dff72018-06-19 18:41:12 -0700816 if (enrolled >= limit) {
817 Slog.w(TAG, "Too many faces registered");
818 return true;
819 }
820 return false;
821 }
822
823 @Override
Kevin Chyn9ba99912019-01-16 16:24:36 -0800824 public void serviceDied(long cookie) {
825 super.serviceDied(cookie);
826 mDaemon = null;
Jim Miller7b78b222019-02-07 16:47:38 -0800827
828 mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate
Kevin Chyn9ba99912019-01-16 16:24:36 -0800829 }
830
831 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700832 protected void updateActiveGroup(int userId, String clientPackage) {
833 IBiometricsFace daemon = getFaceDaemon();
834
835 if (daemon != null) {
836 try {
837 userId = getUserOrWorkProfileId(clientPackage, userId);
838 if (userId != mCurrentUserId) {
839 final File baseDir = Environment.getDataVendorDeDirectory(userId);
840 final File faceDir = new File(baseDir, FACE_DATA_DIR);
841 if (!faceDir.exists()) {
842 if (!faceDir.mkdir()) {
843 Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
844 return;
845 }
846 // Calling mkdir() from this process will create a directory with our
847 // permissions (inherited from the containing dir). This command fixes
848 // the label.
849 if (!SELinux.restorecon(faceDir)) {
850 Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
851 return;
852 }
853 }
854
855 daemon.setActiveUser(userId, faceDir.getAbsolutePath());
856 mCurrentUserId = userId;
857 }
858 mAuthenticatorIds.put(userId,
859 hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
860 } catch (RemoteException e) {
861 Slog.e(TAG, "Failed to setActiveUser():", e);
862 }
863 }
864 }
865
866 @Override
867 protected String getLockoutResetIntent() {
868 return ACTION_LOCKOUT_RESET;
869 }
870
871 @Override
872 protected String getLockoutBroadcastPermission() {
873 return RESET_FACE_LOCKOUT;
874 }
875
876 @Override
877 protected long getHalDeviceId() {
878 return mHalDeviceId;
879 }
880
881 @Override
Kevin Chyna38653c2019-02-11 17:46:21 -0800882 protected void handleUserSwitching(int userId) {
883 super.handleUserSwitching(userId);
884 // Will be updated when we get the callback from HAL
885 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
886 }
887
888 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700889 protected boolean hasEnrolledBiometrics(int userId) {
890 if (userId != UserHandle.getCallingUserId()) {
891 checkPermission(INTERACT_ACROSS_USERS);
892 }
893 return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
894 }
895
896 @Override
897 protected String getManageBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700898 return MANAGE_BIOMETRIC;
Kevin Chyna56dff72018-06-19 18:41:12 -0700899 }
900
901 @Override
902 protected void checkUseBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700903 // noop for Face. The permission checks are all done on the incoming binder call.
Kevin Chyna56dff72018-06-19 18:41:12 -0700904 }
905
906 @Override
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700907 protected boolean checkAppOps(int uid, String opPackageName) {
908 return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
909 == AppOpsManager.MODE_ALLOWED;
Kevin Chyna56dff72018-06-19 18:41:12 -0700910 }
911
912 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800913 protected List<Face> getEnrolledTemplates(int userId) {
914 return getBiometricUtils().getBiometricsForUser(getContext(), userId);
915 }
916
917 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700918 protected void notifyClientActiveCallbacks(boolean isActive) {
919 // noop for Face.
920 }
921
Kevin Chyn7782d142019-01-18 12:51:33 -0800922 @Override
923 protected int statsModality() {
924 return BiometricsProtoEnums.MODALITY_FACE;
925 }
926
Kevin Chyna38653c2019-02-11 17:46:21 -0800927 @Override
928 protected int getLockoutMode() {
929 return mCurrentUserLockoutMode;
930 }
931
Kevin Chyna56dff72018-06-19 18:41:12 -0700932 /** Gets the face daemon */
933 private synchronized IBiometricsFace getFaceDaemon() {
934 if (mDaemon == null) {
935 Slog.v(TAG, "mDaemon was null, reconnect to face");
936 try {
937 mDaemon = IBiometricsFace.getService();
938 } catch (java.util.NoSuchElementException e) {
939 // Service doesn't exist or cannot be opened. Logged below.
940 } catch (RemoteException e) {
941 Slog.e(TAG, "Failed to get biometric interface", e);
942 }
943 if (mDaemon == null) {
944 Slog.w(TAG, "face HIDL not available");
945 return null;
946 }
947
948 mDaemon.asBinder().linkToDeath(this, 0);
949
950 try {
951 mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
952 } catch (RemoteException e) {
953 Slog.e(TAG, "Failed to open face HAL", e);
954 mDaemon = null; // try again later!
955 }
956
957 if (DEBUG) Slog.v(TAG, "Face HAL id: " + mHalDeviceId);
958 if (mHalDeviceId != 0) {
959 loadAuthenticatorIds();
960 updateActiveGroup(ActivityManager.getCurrentUser(), null);
Kevin Chyn6737c572019-02-08 16:10:54 -0800961 doTemplateCleanupForUser(ActivityManager.getCurrentUser());
Kevin Chyna56dff72018-06-19 18:41:12 -0700962 } else {
963 Slog.w(TAG, "Failed to open Face HAL!");
964 MetricsLogger.count(getContext(), "faced_openhal_error", 1);
965 mDaemon = null;
966 }
967 }
968 return mDaemon;
969 }
970
Kevin Chynd79e24e2018-09-25 12:06:59 -0700971 private long startGenerateChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700972 IBiometricsFace daemon = getFaceDaemon();
973 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700974 Slog.w(TAG, "startGenerateChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -0700975 return 0;
976 }
977 try {
Kevin Chyne46a2162018-09-20 18:43:01 -0700978 return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
Kevin Chyna56dff72018-06-19 18:41:12 -0700979 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700980 Slog.e(TAG, "startGenerateChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -0700981 }
982 return 0;
983 }
984
Kevin Chynd79e24e2018-09-25 12:06:59 -0700985 private int startRevokeChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700986 IBiometricsFace daemon = getFaceDaemon();
987 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700988 Slog.w(TAG, "startRevokeChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -0700989 return 0;
990 }
991 try {
Kevin Chyn96c92972018-08-31 16:09:31 -0700992 return daemon.revokeChallenge();
Kevin Chyna56dff72018-06-19 18:41:12 -0700993 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700994 Slog.e(TAG, "startRevokeChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -0700995 }
996 return 0;
997 }
998
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200999 private void dumpInternal(PrintWriter pw) {
1000 JSONObject dump = new JSONObject();
1001 try {
1002 dump.put("service", "Face Manager");
1003
1004 JSONArray sets = new JSONArray();
1005 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1006 final int userId = user.getUserHandle().getIdentifier();
Kevin Chyna56dff72018-06-19 18:41:12 -07001007 final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001008 PerformanceStats stats = mPerformanceMap.get(userId);
1009 PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
1010 JSONObject set = new JSONObject();
1011 set.put("id", userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001012 set.put("count", N);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001013 set.put("accept", (stats != null) ? stats.accept : 0);
1014 set.put("reject", (stats != null) ? stats.reject : 0);
1015 set.put("acquire", (stats != null) ? stats.acquire : 0);
1016 set.put("lockout", (stats != null) ? stats.lockout : 0);
1017 set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
1018 // cryptoStats measures statistics about secure face transactions
1019 // (e.g. to unlock password storage, make secure purchases, etc.)
1020 set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
1021 set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
1022 set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
1023 set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
Kevin Chyna56dff72018-06-19 18:41:12 -07001024 set.put("permanentLockoutCrypto",
1025 (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001026 sets.put(set);
1027 }
1028
1029 dump.put("prints", sets);
1030 } catch (JSONException e) {
1031 Slog.e(TAG, "dump formatting failure", e);
1032 }
1033 pw.println(dump);
Kevin Chyn9ba99912019-01-16 16:24:36 -08001034 pw.println("HAL Deaths: " + mHALDeathCount);
1035 mHALDeathCount = 0;
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001036 }
1037
1038 private void dumpProto(FileDescriptor fd) {
1039 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1040 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1041 final int userId = user.getUserHandle().getIdentifier();
1042
1043 final long userToken = proto.start(FaceServiceDumpProto.USERS);
1044
1045 proto.write(FaceUserStatsProto.USER_ID, userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001046 proto.write(FaceUserStatsProto.NUM_FACES,
1047 getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001048
1049 // Normal face authentications (e.g. lockscreen)
1050 final PerformanceStats normal = mPerformanceMap.get(userId);
1051 if (normal != null) {
1052 final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
1053 proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
1054 proto.write(FaceActionStatsProto.REJECT, normal.reject);
1055 proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
1056 proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
1057 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
1058 proto.end(countsToken);
1059 }
1060
1061 // Statistics about secure face transactions (e.g. to unlock password
1062 // storage, make secure purchases, etc.)
1063 final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
1064 if (crypto != null) {
1065 final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
1066 proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
1067 proto.write(FaceActionStatsProto.REJECT, crypto.reject);
1068 proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
1069 proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
1070 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
1071 proto.end(countsToken);
1072 }
1073
1074 proto.end(userToken);
1075 }
1076 proto.flush();
Kevin Chyna56dff72018-06-19 18:41:12 -07001077 mPerformanceMap.clear();
1078 mCryptoPerformanceMap.clear();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001079 }
Joe Onoratobf955d22019-03-25 00:16:58 -07001080
1081 private void dumpRestrictedImage(FileDescriptor fd) {
1082 // WARNING: CDD restricts image data from leaving TEE unencrypted on
1083 // production devices:
1084 // [C-1-10] MUST not allow unencrypted access to identifiable biometric
1085 // data or any data derived from it (such as embeddings) to the
1086 // Application Processor outside the context of the TEE.
1087 // As such, this API should only be enabled for testing purposes on
1088 // engineering and userdebug builds. All modules in the software stack
1089 // MUST enforce final build products do NOT have this functionality.
1090 // Additionally, the following check MUST NOT be removed.
1091 if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
1092 return;
1093 }
1094
Joe Onorato108413a2019-04-03 18:20:52 -07001095 // Additionally, this flag allows turning off face for a device
1096 // (either permanently through the build or on an individual device).
1097 if (SystemProperties.getBoolean("ro.face.disable_debug_data", false)
1098 || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) {
1099 return;
1100 }
1101
Joe Onoratobf955d22019-03-25 00:16:58 -07001102 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1103
1104 final long setToken = proto.start(RestrictedImagesDumpProto.SETS);
1105
1106 // Name of the service
1107 proto.write(RestrictedImageSetProto.CATEGORY, "face");
1108
1109 // Individual images
1110 for (int i = 0; i < 5; i++) {
1111 final long imageToken = proto.start(RestrictedImageSetProto.IMAGES);
1112 proto.write(RestrictedImageProto.MIME_TYPE, "image/png");
1113 proto.write(RestrictedImageProto.IMAGE_DATA, new byte[] {
1114 // png image data
1115 -119, 80, 78, 71, 13, 10, 26, 10,
1116 0, 0, 0, 13, 73, 72, 68, 82,
1117 0, 0, 0, 100, 0, 0, 0, 100,
1118 1, 3, 0, 0, 0, 74, 44, 7,
1119 23, 0, 0, 0, 4, 103, 65, 77,
1120 65, 0, 0, -79, -113, 11, -4, 97,
1121 5, 0, 0, 0, 1, 115, 82, 71,
1122 66, 0, -82, -50, 28, -23, 0, 0,
1123 0, 6, 80, 76, 84, 69, -1, -1,
1124 -1, 0, 0, 0, 85, -62, -45, 126,
1125 0, 0, 0, -115, 73, 68, 65, 84,
1126 56, -53, -19, -46, -79, 17, -128, 32,
1127 12, 5, -48, 120, 22, -106, -116, -32,
1128 40, -84, 101, -121, -93, 57, 10, 35,
1129 88, 82, 112, 126, 3, -60, 104, 6,
1130 -112, 70, 127, -59, -69, -53, 29, 33,
1131 -127, -24, 79, -49, -52, -15, 41, 36,
1132 34, -105, 85, 124, -14, 88, 27, 6,
1133 28, 68, 1, 82, 62, 22, -95, -108,
1134 55, -95, 40, -9, -110, -12, 98, -107,
1135 76, -41, -105, -62, -50, 111, -60, 46,
1136 -14, -4, 24, -89, 42, -103, 16, 63,
1137 -72, -11, -15, 48, -62, 102, -44, 102,
1138 -73, -56, 56, -21, -128, 92, -70, -124,
1139 117, -46, -67, -77, 82, 80, 121, -44,
1140 -56, 116, 93, -45, -90, -5, -29, -24,
1141 -83, -75, 52, -34, 55, -22, 102, -21,
1142 -105, -124, -23, 71, 87, -7, -25, -59,
1143 -100, -73, -92, -122, -7, -109, -49, -80,
1144 -89, 0, 0, 0, 0, 73, 69, 78,
1145 68, -82, 66, 96, -126
1146 });
1147 // proto.write(RestrictedImageProto.METADATA, flattened_protobuf);
1148 proto.end(imageToken);
1149 }
1150
1151 // Face service metadata
1152 // proto.write(RestrictedImageSetProto.METADATA, flattened_protobuf);
1153
1154 proto.end(setToken);
1155 proto.flush();
1156 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001157}