blob: fe762c06458a54f050a2c6907e0e56b1b717f9fd [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.RestrictedImagesDumpProto;
50import android.service.restricted_image.RestrictedImageProto;
51import android.service.restricted_image.RestrictedImageSetProto;
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 Chyna38653c2019-02-11 17:46:21 -0800386 try {
387 mDaemonWrapper.resetLockout(token);
388 } catch (RemoteException e) {
389 Slog.e(getTag(), "Unable to reset lockout", e);
390 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700391 }
Kevin Chynd79e24e2018-09-25 12:06:59 -0700392
393 @Override
Kevin Chynb95f1522019-03-04 16:45:15 -0800394 public boolean setFeature(int feature, boolean enabled, final byte[] token) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700395 checkPermission(MANAGE_BIOMETRIC);
396
Kevin Chynb95f1522019-03-04 16:45:15 -0800397 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
398 Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
399 return false;
400 }
401
Kevin Chynd79e24e2018-09-25 12:06:59 -0700402 final ArrayList<Byte> byteToken = new ArrayList<>();
403 for (int i = 0; i < token.length; i++) {
404 byteToken.add(token[i]);
405 }
406
Kevin Chynb95f1522019-03-04 16:45:15 -0800407 // TODO: Support multiple faces
408 final int faceId = getFirstTemplateForUser(mCurrentUserId);
Kevin Chynd79e24e2018-09-25 12:06:59 -0700409
Kevin Chynb95f1522019-03-04 16:45:15 -0800410 if (mDaemon != null) {
411 try {
412 return mDaemon.setFeature(feature, enabled, byteToken, faceId) == Status.OK;
413 } catch (RemoteException e) {
414 Slog.e(getTag(), "Unable to set feature: " + feature + " to enabled:" + enabled,
415 e);
416 }
417 }
418 return false;
Kevin Chynd79e24e2018-09-25 12:06:59 -0700419 }
420
421 @Override
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800422 public boolean getFeature(int feature) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700423 checkPermission(MANAGE_BIOMETRIC);
424
Kevin Chynb95f1522019-03-04 16:45:15 -0800425 // This should ideally return tri-state, but the user isn't shown settings unless
426 // they are enrolled so it's fine for now.
427 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
428 Slog.e(TAG, "No enrolled biometrics while getting feature: " + feature);
429 return false;
Kevin Chynd79e24e2018-09-25 12:06:59 -0700430 }
Kevin Chynb95f1522019-03-04 16:45:15 -0800431
432 // TODO: Support multiple faces
433 final int faceId = getFirstTemplateForUser(mCurrentUserId);
434
435 if (mDaemon != null) {
436 try {
437 OptionalBool result = mDaemon.getFeature(feature, faceId);
438 if (result.status == Status.OK) {
439 return result.value;
440 } else {
441 // Same tri-state comment applies here.
442 return false;
443 }
444 } catch (RemoteException e) {
445 Slog.e(getTag(), "Unable to getRequireAttention", e);
446 }
447 }
448 return false;
Kevin Chynd79e24e2018-09-25 12:06:59 -0700449 }
Kevin Chyn57f119b2018-10-25 12:03:41 -0700450
451 @Override
452 public void userActivity() {
453 checkPermission(MANAGE_BIOMETRIC);
454
455 if (mDaemon != null) {
456 try {
457 mDaemon.userActivity();
458 } catch (RemoteException e) {
459 Slog.e(getTag(), "Unable to send userActivity", e);
460 }
461 }
462 }
Kevin Chynb95f1522019-03-04 16:45:15 -0800463
464 // TODO: Support multiple faces
465 private int getFirstTemplateForUser(int user) {
466 final List<Face> faces = FaceService.this.getEnrolledTemplates(user);
467 if (!faces.isEmpty()) {
468 return faces.get(0).getBiometricId();
469 }
470 return 0;
471 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700472 }
473
474 /**
475 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700476 * BiometricPrompt.
477 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800478 private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800479 BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800480 super(wrapperReceiver);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700481 }
482
483 @Override
484 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
485 throws RemoteException {
486 /**
487 * Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
488 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800489 if (getWrapperReceiver() != null) {
490 getWrapperReceiver().onAcquired(
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700491 FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
492 FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700493 }
494 }
495
496 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800497 public void onError(long deviceId, int error, int vendorCode, int cookie)
498 throws RemoteException {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800499 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800500 getWrapperReceiver().onError(cookie, error,
501 FaceManager.getErrorString(getContext(), error, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700502 }
503 }
504 }
505
506 /**
507 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna56dff72018-06-19 18:41:12 -0700508 * the FaceManager.
509 */
510 private class ServiceListenerImpl implements ServiceListener {
Kevin Chyna56dff72018-06-19 18:41:12 -0700511 private IFaceServiceReceiver mFaceServiceReceiver;
512
513 public ServiceListenerImpl(IFaceServiceReceiver receiver) {
514 mFaceServiceReceiver = receiver;
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200515 }
516
517 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700518 public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200519 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700520 if (mFaceServiceReceiver != null) {
521 mFaceServiceReceiver.onEnrollResult(identifier.getDeviceId(),
522 identifier.getBiometricId(),
523 remaining);
524 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200525 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700526
527 @Override
528 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
529 throws RemoteException {
530 if (mFaceServiceReceiver != null) {
531 mFaceServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
532 }
533 }
534
535 @Override
536 public void onAuthenticationSucceeded(long deviceId,
537 BiometricAuthenticator.Identifier biometric, int userId)
538 throws RemoteException {
539 if (mFaceServiceReceiver != null) {
Kevin Chyn628b7182018-11-13 12:00:48 -0800540 if (biometric == null || biometric instanceof Face) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700541 mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face)biometric);
542 } else {
543 Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
544 }
545 }
546 }
547
548 @Override
549 public void onAuthenticationFailed(long deviceId) throws RemoteException {
550 if (mFaceServiceReceiver != null) {
551 mFaceServiceReceiver.onAuthenticationFailed(deviceId);
552 }
553 }
554
555 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800556 public void onError(long deviceId, int error, int vendorCode, int cookie)
557 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700558 if (mFaceServiceReceiver != null) {
559 mFaceServiceReceiver.onError(deviceId, error, vendorCode);
560 }
561 }
562
563 @Override
564 public void onRemoved(BiometricAuthenticator.Identifier identifier,
565 int remaining) throws RemoteException {
566 if (mFaceServiceReceiver != null) {
567 mFaceServiceReceiver.onRemoved(identifier.getDeviceId(),
568 identifier.getBiometricId(), remaining);
569 }
570 }
571
572 @Override
573 public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
574 throws RemoteException {
575 if (mFaceServiceReceiver != null) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800576 mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(),
577 identifier.getBiometricId(), remaining);
Kevin Chyna56dff72018-06-19 18:41:12 -0700578 }
579 }
580 }
581
582 private final FaceMetrics mFaceMetrics = new FaceMetrics();
583
584 @GuardedBy("this")
585 private IBiometricsFace mDaemon;
Kevin Chyna38653c2019-02-11 17:46:21 -0800586 // One of the AuthenticationClient constants
587 private int mCurrentUserLockoutMode;
Kevin Chyna56dff72018-06-19 18:41:12 -0700588
589 /**
590 * Receives callbacks from the HAL.
591 */
592 private IBiometricsFaceClientCallback mDaemonCallback =
593 new IBiometricsFaceClientCallback.Stub() {
Kevin Chyn6737c572019-02-08 16:10:54 -0800594 @Override
595 public void onEnrollResult(final long deviceId, int faceId, int userId,
596 int remaining) {
597 mHandler.post(() -> {
598 final Face face = new Face(getBiometricUtils()
599 .getUniqueName(getContext(), userId), faceId, deviceId);
600 FaceService.super.handleEnrollResult(face, remaining);
601 });
602 }
603
604 @Override
605 public void onAcquired(final long deviceId, final int userId,
606 final int acquiredInfo,
607 final int vendorCode) {
608 mHandler.post(() -> {
609 FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
610 });
611 }
612
613 @Override
614 public void onAuthenticated(final long deviceId, final int faceId, final int userId,
615 ArrayList<Byte> token) {
616 mHandler.post(() -> {
617 Face face = new Face("", faceId, deviceId);
618 FaceService.super.handleAuthenticated(face, token);
619 });
620 }
621
622 @Override
623 public void onError(final long deviceId, final int userId, final int error,
624 final int vendorCode) {
625 mHandler.post(() -> {
626 FaceService.super.handleError(deviceId, error, vendorCode);
627
628 // TODO: this chunk of code should be common to all biometric services
629 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
630 // If we get HW_UNAVAILABLE, try to connect again later...
631 Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
632 synchronized (this) {
633 mDaemon = null;
634 mHalDeviceId = 0;
635 mCurrentUserId = UserHandle.USER_NULL;
636 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700637 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800638 });
639 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700640
Kevin Chyn6737c572019-02-08 16:10:54 -0800641 @Override
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700642 public void onRemoved(final long deviceId, ArrayList<Integer> faceIds, final int userId) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800643 mHandler.post(() -> {
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700644 if (!faceIds.isEmpty()) {
645 for (int i = 0; i < faceIds.size(); i++) {
646 final Face face = new Face("", faceIds.get(i), deviceId);
647 // Convert to old behavior
648 FaceService.super.handleRemoved(face, faceIds.size() - i - 1);
649 }
650 } else {
651 final Face face = new Face("", 0 /* identifier */, deviceId);
652 FaceService.super.handleRemoved(face, 0 /* remaining */);
653 }
654
Kevin Chyn6737c572019-02-08 16:10:54 -0800655 });
656 }
657
658 @Override
659 public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
660 throws RemoteException {
661 mHandler.post(() -> {
662 if (!faceIds.isEmpty()) {
663 for (int i = 0; i < faceIds.size(); i++) {
664 final Face face = new Face("", faceIds.get(i), deviceId);
665 // Convert to old old behavior
666 FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
667 }
668 } else {
669 // For face, the HIDL contract is to receive an empty list when there are no
670 // templates enrolled. Send a null identifier since we don't consume them
671 // anywhere, and send remaining == 0 to plumb this with existing common code.
672 FaceService.super.handleEnumerate(null /* identifier */, 0);
Kevin Chyna56dff72018-06-19 18:41:12 -0700673 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800674 });
675 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700676
Kevin Chyn6737c572019-02-08 16:10:54 -0800677 @Override
678 public void onLockoutChanged(long duration) {
Kevin Chyna38653c2019-02-11 17:46:21 -0800679 Slog.d(TAG, "onLockoutChanged: " + duration);
680 if (duration == 0) {
681 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
682 } else if (duration == Long.MAX_VALUE) {
683 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
684 } else {
685 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
686 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700687
Kevin Chyna38653c2019-02-11 17:46:21 -0800688 mHandler.post(() -> {
689 if (duration == 0) {
690 notifyLockoutResetMonitors();
691 }
692 });
Kevin Chyn6737c572019-02-08 16:10:54 -0800693 }
694 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700695
696 /**
697 * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
698 * can be shared between the multiple biometric services.
699 */
700 private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
701 @Override
702 public int authenticate(long operationId, int groupId) throws RemoteException {
703 IBiometricsFace daemon = getFaceDaemon();
704 if (daemon == null) {
705 Slog.w(TAG, "authenticate(): no face HAL!");
706 return ERROR_ESRCH;
707 }
708 return daemon.authenticate(operationId);
709 }
710
711 @Override
712 public int cancel() throws RemoteException {
713 IBiometricsFace daemon = getFaceDaemon();
714 if (daemon == null) {
715 Slog.w(TAG, "cancel(): no face HAL!");
716 return ERROR_ESRCH;
717 }
718 return daemon.cancel();
719 }
720
721 @Override
722 public int remove(int groupId, int biometricId) throws RemoteException {
723 IBiometricsFace daemon = getFaceDaemon();
724 if (daemon == null) {
725 Slog.w(TAG, "remove(): no face HAL!");
726 return ERROR_ESRCH;
727 }
728 return daemon.remove(biometricId);
729 }
730
731 @Override
732 public int enumerate() throws RemoteException {
733 IBiometricsFace daemon = getFaceDaemon();
734 if (daemon == null) {
735 Slog.w(TAG, "enumerate(): no face HAL!");
736 return ERROR_ESRCH;
737 }
738 return daemon.enumerate();
739 }
740
741 @Override
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800742 public int enroll(byte[] cryptoToken, int groupId, int timeout,
743 ArrayList<Integer> disabledFeatures) throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700744 IBiometricsFace daemon = getFaceDaemon();
745 if (daemon == null) {
746 Slog.w(TAG, "enroll(): no face HAL!");
747 return ERROR_ESRCH;
748 }
749 final ArrayList<Byte> token = new ArrayList<>();
750 for (int i = 0; i < cryptoToken.length; i++) {
751 token.add(cryptoToken[i]);
752 }
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800753 return daemon.enroll(token, timeout, disabledFeatures);
Kevin Chyna56dff72018-06-19 18:41:12 -0700754 }
Kevin Chyna38653c2019-02-11 17:46:21 -0800755
756 @Override
757 public void resetLockout(byte[] cryptoToken) throws RemoteException {
758 IBiometricsFace daemon = getFaceDaemon();
759 if (daemon == null) {
760 Slog.w(TAG, "resetLockout(): no face HAL!");
761 return;
762 }
763 final ArrayList<Byte> token = new ArrayList<>();
764 for (int i = 0; i < cryptoToken.length; i++) {
765 token.add(cryptoToken[i]);
766 }
767 daemon.resetLockout(token);
768 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700769 };
770
771
772 public FaceService(Context context) {
773 super(context);
Kevin Chyna56dff72018-06-19 18:41:12 -0700774 }
775
776 @Override
777 public void onStart() {
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -0700778 super.onStart();
Kevin Chyna56dff72018-06-19 18:41:12 -0700779 publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
780 SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
781 }
782
783 @Override
784 public String getTag() {
785 return TAG;
786 }
787
788 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800789 protected DaemonWrapper getDaemonWrapper() {
790 return mDaemonWrapper;
791 }
792
793 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700794 protected BiometricUtils getBiometricUtils() {
795 return FaceUtils.getInstance();
796 }
797
798 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700799 protected Metrics getMetrics() {
800 return mFaceMetrics;
801 }
802
803 @Override
804 protected boolean hasReachedEnrollmentLimit(int userId) {
805 final int limit = getContext().getResources().getInteger(
Kevin Chyn017e76e2018-06-27 18:35:06 -0700806 com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
Kevin Chyn6737c572019-02-08 16:10:54 -0800807 final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
Kevin Chyna56dff72018-06-19 18:41:12 -0700808 if (enrolled >= limit) {
809 Slog.w(TAG, "Too many faces registered");
810 return true;
811 }
812 return false;
813 }
814
815 @Override
Kevin Chyn9ba99912019-01-16 16:24:36 -0800816 public void serviceDied(long cookie) {
817 super.serviceDied(cookie);
818 mDaemon = null;
Jim Miller7b78b222019-02-07 16:47:38 -0800819
820 mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate
Kevin Chyn9ba99912019-01-16 16:24:36 -0800821 }
822
823 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700824 protected void updateActiveGroup(int userId, String clientPackage) {
825 IBiometricsFace daemon = getFaceDaemon();
826
827 if (daemon != null) {
828 try {
829 userId = getUserOrWorkProfileId(clientPackage, userId);
830 if (userId != mCurrentUserId) {
831 final File baseDir = Environment.getDataVendorDeDirectory(userId);
832 final File faceDir = new File(baseDir, FACE_DATA_DIR);
833 if (!faceDir.exists()) {
834 if (!faceDir.mkdir()) {
835 Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
836 return;
837 }
838 // Calling mkdir() from this process will create a directory with our
839 // permissions (inherited from the containing dir). This command fixes
840 // the label.
841 if (!SELinux.restorecon(faceDir)) {
842 Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
843 return;
844 }
845 }
846
847 daemon.setActiveUser(userId, faceDir.getAbsolutePath());
848 mCurrentUserId = userId;
849 }
850 mAuthenticatorIds.put(userId,
851 hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
852 } catch (RemoteException e) {
853 Slog.e(TAG, "Failed to setActiveUser():", e);
854 }
855 }
856 }
857
858 @Override
859 protected String getLockoutResetIntent() {
860 return ACTION_LOCKOUT_RESET;
861 }
862
863 @Override
864 protected String getLockoutBroadcastPermission() {
865 return RESET_FACE_LOCKOUT;
866 }
867
868 @Override
869 protected long getHalDeviceId() {
870 return mHalDeviceId;
871 }
872
873 @Override
Kevin Chyna38653c2019-02-11 17:46:21 -0800874 protected void handleUserSwitching(int userId) {
875 super.handleUserSwitching(userId);
876 // Will be updated when we get the callback from HAL
877 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
878 }
879
880 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700881 protected boolean hasEnrolledBiometrics(int userId) {
882 if (userId != UserHandle.getCallingUserId()) {
883 checkPermission(INTERACT_ACROSS_USERS);
884 }
885 return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
886 }
887
888 @Override
889 protected String getManageBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700890 return MANAGE_BIOMETRIC;
Kevin Chyna56dff72018-06-19 18:41:12 -0700891 }
892
893 @Override
894 protected void checkUseBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700895 // noop for Face. The permission checks are all done on the incoming binder call.
Kevin Chyna56dff72018-06-19 18:41:12 -0700896 }
897
898 @Override
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700899 protected boolean checkAppOps(int uid, String opPackageName) {
900 return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
901 == AppOpsManager.MODE_ALLOWED;
Kevin Chyna56dff72018-06-19 18:41:12 -0700902 }
903
904 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800905 protected List<Face> getEnrolledTemplates(int userId) {
906 return getBiometricUtils().getBiometricsForUser(getContext(), userId);
907 }
908
909 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700910 protected void notifyClientActiveCallbacks(boolean isActive) {
911 // noop for Face.
912 }
913
Kevin Chyn7782d142019-01-18 12:51:33 -0800914 @Override
915 protected int statsModality() {
916 return BiometricsProtoEnums.MODALITY_FACE;
917 }
918
Kevin Chyna38653c2019-02-11 17:46:21 -0800919 @Override
920 protected int getLockoutMode() {
921 return mCurrentUserLockoutMode;
922 }
923
Kevin Chyna56dff72018-06-19 18:41:12 -0700924 /** Gets the face daemon */
925 private synchronized IBiometricsFace getFaceDaemon() {
926 if (mDaemon == null) {
927 Slog.v(TAG, "mDaemon was null, reconnect to face");
928 try {
929 mDaemon = IBiometricsFace.getService();
930 } catch (java.util.NoSuchElementException e) {
931 // Service doesn't exist or cannot be opened. Logged below.
932 } catch (RemoteException e) {
933 Slog.e(TAG, "Failed to get biometric interface", e);
934 }
935 if (mDaemon == null) {
936 Slog.w(TAG, "face HIDL not available");
937 return null;
938 }
939
940 mDaemon.asBinder().linkToDeath(this, 0);
941
942 try {
943 mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
944 } catch (RemoteException e) {
945 Slog.e(TAG, "Failed to open face HAL", e);
946 mDaemon = null; // try again later!
947 }
948
949 if (DEBUG) Slog.v(TAG, "Face HAL id: " + mHalDeviceId);
950 if (mHalDeviceId != 0) {
951 loadAuthenticatorIds();
952 updateActiveGroup(ActivityManager.getCurrentUser(), null);
Kevin Chyn6737c572019-02-08 16:10:54 -0800953 doTemplateCleanupForUser(ActivityManager.getCurrentUser());
Kevin Chyna56dff72018-06-19 18:41:12 -0700954 } else {
955 Slog.w(TAG, "Failed to open Face HAL!");
956 MetricsLogger.count(getContext(), "faced_openhal_error", 1);
957 mDaemon = null;
958 }
959 }
960 return mDaemon;
961 }
962
Kevin Chynd79e24e2018-09-25 12:06:59 -0700963 private long startGenerateChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700964 IBiometricsFace daemon = getFaceDaemon();
965 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700966 Slog.w(TAG, "startGenerateChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -0700967 return 0;
968 }
969 try {
Kevin Chyne46a2162018-09-20 18:43:01 -0700970 return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
Kevin Chyna56dff72018-06-19 18:41:12 -0700971 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700972 Slog.e(TAG, "startGenerateChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -0700973 }
974 return 0;
975 }
976
Kevin Chynd79e24e2018-09-25 12:06:59 -0700977 private int startRevokeChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700978 IBiometricsFace daemon = getFaceDaemon();
979 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700980 Slog.w(TAG, "startRevokeChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -0700981 return 0;
982 }
983 try {
Kevin Chyn96c92972018-08-31 16:09:31 -0700984 return daemon.revokeChallenge();
Kevin Chyna56dff72018-06-19 18:41:12 -0700985 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700986 Slog.e(TAG, "startRevokeChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -0700987 }
988 return 0;
989 }
990
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200991 private void dumpInternal(PrintWriter pw) {
992 JSONObject dump = new JSONObject();
993 try {
994 dump.put("service", "Face Manager");
995
996 JSONArray sets = new JSONArray();
997 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
998 final int userId = user.getUserHandle().getIdentifier();
Kevin Chyna56dff72018-06-19 18:41:12 -0700999 final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001000 PerformanceStats stats = mPerformanceMap.get(userId);
1001 PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
1002 JSONObject set = new JSONObject();
1003 set.put("id", userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001004 set.put("count", N);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001005 set.put("accept", (stats != null) ? stats.accept : 0);
1006 set.put("reject", (stats != null) ? stats.reject : 0);
1007 set.put("acquire", (stats != null) ? stats.acquire : 0);
1008 set.put("lockout", (stats != null) ? stats.lockout : 0);
1009 set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
1010 // cryptoStats measures statistics about secure face transactions
1011 // (e.g. to unlock password storage, make secure purchases, etc.)
1012 set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
1013 set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
1014 set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
1015 set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
Kevin Chyna56dff72018-06-19 18:41:12 -07001016 set.put("permanentLockoutCrypto",
1017 (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001018 sets.put(set);
1019 }
1020
1021 dump.put("prints", sets);
1022 } catch (JSONException e) {
1023 Slog.e(TAG, "dump formatting failure", e);
1024 }
1025 pw.println(dump);
Kevin Chyn9ba99912019-01-16 16:24:36 -08001026 pw.println("HAL Deaths: " + mHALDeathCount);
1027 mHALDeathCount = 0;
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001028 }
1029
1030 private void dumpProto(FileDescriptor fd) {
1031 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1032 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1033 final int userId = user.getUserHandle().getIdentifier();
1034
1035 final long userToken = proto.start(FaceServiceDumpProto.USERS);
1036
1037 proto.write(FaceUserStatsProto.USER_ID, userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001038 proto.write(FaceUserStatsProto.NUM_FACES,
1039 getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001040
1041 // Normal face authentications (e.g. lockscreen)
1042 final PerformanceStats normal = mPerformanceMap.get(userId);
1043 if (normal != null) {
1044 final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
1045 proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
1046 proto.write(FaceActionStatsProto.REJECT, normal.reject);
1047 proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
1048 proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
1049 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
1050 proto.end(countsToken);
1051 }
1052
1053 // Statistics about secure face transactions (e.g. to unlock password
1054 // storage, make secure purchases, etc.)
1055 final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
1056 if (crypto != null) {
1057 final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
1058 proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
1059 proto.write(FaceActionStatsProto.REJECT, crypto.reject);
1060 proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
1061 proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
1062 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
1063 proto.end(countsToken);
1064 }
1065
1066 proto.end(userToken);
1067 }
1068 proto.flush();
Kevin Chyna56dff72018-06-19 18:41:12 -07001069 mPerformanceMap.clear();
1070 mCryptoPerformanceMap.clear();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001071 }
Joe Onoratobf955d22019-03-25 00:16:58 -07001072
1073 private void dumpRestrictedImage(FileDescriptor fd) {
1074 // WARNING: CDD restricts image data from leaving TEE unencrypted on
1075 // production devices:
1076 // [C-1-10] MUST not allow unencrypted access to identifiable biometric
1077 // data or any data derived from it (such as embeddings) to the
1078 // Application Processor outside the context of the TEE.
1079 // As such, this API should only be enabled for testing purposes on
1080 // engineering and userdebug builds. All modules in the software stack
1081 // MUST enforce final build products do NOT have this functionality.
1082 // Additionally, the following check MUST NOT be removed.
1083 if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
1084 return;
1085 }
1086
1087 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1088
1089 final long setToken = proto.start(RestrictedImagesDumpProto.SETS);
1090
1091 // Name of the service
1092 proto.write(RestrictedImageSetProto.CATEGORY, "face");
1093
1094 // Individual images
1095 for (int i = 0; i < 5; i++) {
1096 final long imageToken = proto.start(RestrictedImageSetProto.IMAGES);
1097 proto.write(RestrictedImageProto.MIME_TYPE, "image/png");
1098 proto.write(RestrictedImageProto.IMAGE_DATA, new byte[] {
1099 // png image data
1100 -119, 80, 78, 71, 13, 10, 26, 10,
1101 0, 0, 0, 13, 73, 72, 68, 82,
1102 0, 0, 0, 100, 0, 0, 0, 100,
1103 1, 3, 0, 0, 0, 74, 44, 7,
1104 23, 0, 0, 0, 4, 103, 65, 77,
1105 65, 0, 0, -79, -113, 11, -4, 97,
1106 5, 0, 0, 0, 1, 115, 82, 71,
1107 66, 0, -82, -50, 28, -23, 0, 0,
1108 0, 6, 80, 76, 84, 69, -1, -1,
1109 -1, 0, 0, 0, 85, -62, -45, 126,
1110 0, 0, 0, -115, 73, 68, 65, 84,
1111 56, -53, -19, -46, -79, 17, -128, 32,
1112 12, 5, -48, 120, 22, -106, -116, -32,
1113 40, -84, 101, -121, -93, 57, 10, 35,
1114 88, 82, 112, 126, 3, -60, 104, 6,
1115 -112, 70, 127, -59, -69, -53, 29, 33,
1116 -127, -24, 79, -49, -52, -15, 41, 36,
1117 34, -105, 85, 124, -14, 88, 27, 6,
1118 28, 68, 1, 82, 62, 22, -95, -108,
1119 55, -95, 40, -9, -110, -12, 98, -107,
1120 76, -41, -105, -62, -50, 111, -60, 46,
1121 -14, -4, 24, -89, 42, -103, 16, 63,
1122 -72, -11, -15, 48, -62, 102, -44, 102,
1123 -73, -56, 56, -21, -128, 92, -70, -124,
1124 117, -46, -67, -77, 82, 80, 121, -44,
1125 -56, 116, 93, -45, -90, -5, -29, -24,
1126 -83, -75, 52, -34, 55, -22, 102, -21,
1127 -105, -124, -23, 71, 87, -7, -25, -59,
1128 -100, -73, -92, -122, -7, -109, -49, -80,
1129 -89, 0, 0, 0, 0, 73, 69, 78,
1130 68, -82, 66, 96, -126
1131 });
1132 // proto.write(RestrictedImageProto.METADATA, flattened_protobuf);
1133 proto.end(imageToken);
1134 }
1135
1136 // Face service metadata
1137 // proto.write(RestrictedImageSetProto.METADATA, flattened_protobuf);
1138
1139 proto.end(setToken);
1140 proto.flush();
1141 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001142}