blob: 44fd84602ebe15d03613f40d01fd642b49f70578 [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;
Kevin Chyn56e4c3d2019-04-23 11:28:43 -070026import android.app.Notification;
27import android.app.NotificationChannel;
28import android.app.NotificationManager;
29import android.app.PendingIntent;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020030import android.content.Context;
Kevin Chyn56e4c3d2019-04-23 11:28:43 -070031import android.content.Intent;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020032import android.content.pm.UserInfo;
Kevin Chyna56dff72018-06-19 18:41:12 -070033import android.hardware.biometrics.BiometricAuthenticator;
34import android.hardware.biometrics.BiometricConstants;
Kevin Chyn7782d142019-01-18 12:51:33 -080035import android.hardware.biometrics.BiometricsProtoEnums;
Kevin Chyna56dff72018-06-19 18:41:12 -070036import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
Kevin Chyn23289ef2018-11-28 16:32:36 -080037import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020038import android.hardware.biometrics.face.V1_0.IBiometricsFace;
39import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
Kevin Chynb95f1522019-03-04 16:45:15 -080040import android.hardware.biometrics.face.V1_0.OptionalBool;
Kevin Chynd79e24e2018-09-25 12:06:59 -070041import android.hardware.biometrics.face.V1_0.Status;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020042import android.hardware.face.Face;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070043import android.hardware.face.FaceManager;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020044import android.hardware.face.IFaceService;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020045import android.hardware.face.IFaceServiceReceiver;
46import android.os.Binder;
Joe Onoratobf955d22019-03-25 00:16:58 -070047import android.os.Build;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020048import android.os.Environment;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020049import android.os.IBinder;
Joe Onoratodb396002019-04-05 19:49:27 -070050import android.os.NativeHandle;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020051import android.os.RemoteException;
52import android.os.SELinux;
Joe Onorato108413a2019-04-03 18:20:52 -070053import android.os.SystemProperties;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020054import android.os.UserHandle;
55import android.os.UserManager;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020056import android.util.Slog;
57import android.util.proto.ProtoOutputStream;
58
Kevin Chyn56e4c3d2019-04-23 11:28:43 -070059import com.android.internal.R;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020060import com.android.internal.annotations.GuardedBy;
61import com.android.internal.logging.MetricsLogger;
62import com.android.internal.util.DumpUtils;
63import com.android.server.SystemServerInitThreadPool;
Kevin Chyna38653c2019-02-11 17:46:21 -080064import com.android.server.biometrics.AuthenticationClient;
Kevin Chyn355c6bf2018-09-20 22:14:19 -070065import com.android.server.biometrics.BiometricServiceBase;
Kevin Chyn836f2cf2018-08-27 11:06:39 -070066import com.android.server.biometrics.BiometricUtils;
Kevin Chyn6737c572019-02-08 16:10:54 -080067import com.android.server.biometrics.EnumerateClient;
Kevin Chyn836f2cf2018-08-27 11:06:39 -070068import com.android.server.biometrics.Metrics;
Kevin Chyn6737c572019-02-08 16:10:54 -080069import com.android.server.biometrics.RemovalClient;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020070
71import org.json.JSONArray;
72import org.json.JSONException;
73import org.json.JSONObject;
74
75import java.io.File;
76import java.io.FileDescriptor;
Joe Onoratodb396002019-04-05 19:49:27 -070077import java.io.FileOutputStream;
78import java.io.IOException;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020079import java.io.PrintWriter;
80import java.util.ArrayList;
Joe Onoratodb396002019-04-05 19:49:27 -070081import java.util.Arrays;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020082import java.util.List;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020083
84/**
85 * A service to manage multiple clients that want to access the face HAL API.
86 * The service is responsible for maintaining a list of clients and dispatching all
Kevin Chyn51676d22018-11-05 18:00:43 -080087 * face-related events.
Gilad Brettercb51b8b2018-03-22 17:04:51 +020088 *
89 * @hide
90 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -070091public class FaceService extends BiometricServiceBase {
Kevin Chyna56dff72018-06-19 18:41:12 -070092
93 protected static final String TAG = "FaceService";
94 private static final boolean DEBUG = true;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020095 private static final String FACE_DATA_DIR = "facedata";
Gilad Brettercb51b8b2018-03-22 17:04:51 +020096 private static final String ACTION_LOCKOUT_RESET =
Kevin Chyn2ffadb32018-06-19 11:29:38 -070097 "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
Kevin Chyne46a2162018-09-20 18:43:01 -070098 private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
Gilad Brettercb51b8b2018-03-22 17:04:51 +020099
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700100 private final class FaceAuthClient extends AuthenticationClientImpl {
101 public FaceAuthClient(Context context,
102 DaemonWrapper daemon, long halDeviceId, IBinder token,
103 ServiceListener listener, int targetUserId, int groupId, long opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800104 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700105 super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800106 restricted, owner, cookie, requireConfirmation);
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700107 }
Kevin Chyn7782d142019-01-18 12:51:33 -0800108
109 @Override
110 protected int statsModality() {
111 return FaceService.this.statsModality();
112 }
Kevin Chyna38653c2019-02-11 17:46:21 -0800113
114 @Override
115 public boolean shouldFrameworkHandleLockout() {
116 return false;
117 }
Kevin Chyn56d6b072019-02-13 18:39:01 -0800118
119 @Override
120 public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
121 boolean authenticated, ArrayList<Byte> token) {
122 final boolean result = super.onAuthenticated(identifier, authenticated, token);
123
124 // For face, the authentication lifecycle ends either when
125 // 1) Authenticated == true
126 // 2) Error occurred
127 // 3) Authenticated == false
128 // Fingerprint currently does not end when the third condition is met which is a bug,
129 // but let's leave it as-is for now.
130 return result || !authenticated;
131 }
Kevin Chyn56e4c3d2019-04-23 11:28:43 -0700132
133 @Override
134 public boolean onAcquired(int acquireInfo, int vendorCode) {
135
136 if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
137 final String name =
138 getContext().getString(R.string.face_recalibrate_notification_name);
139 final String title =
140 getContext().getString(R.string.face_recalibrate_notification_title);
141 final String content =
142 getContext().getString(R.string.face_recalibrate_notification_content);
143
144 final Intent intent = new Intent("android.settings.FACE_SETTINGS");
145 intent.setPackage("com.android.settings");
146
147 final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(),
148 0 /* requestCode */, intent, 0 /* flags */, null /* options */,
149 UserHandle.CURRENT);
150
151 final String id = "FaceService";
152
153 NotificationManager nm =
154 getContext().getSystemService(NotificationManager.class);
155 NotificationChannel channel = new NotificationChannel(id, name,
156 NotificationManager.IMPORTANCE_HIGH);
157 Notification notification = new Notification.Builder(getContext(), id)
158 .setSmallIcon(R.drawable.ic_lock)
159 .setContentTitle(title)
160 .setContentText(content)
161 .setSubText(name)
162 .setOnlyAlertOnce(true)
163 .setLocalOnly(true)
164 .setAutoCancel(true)
165 .setCategory(Notification.CATEGORY_SYSTEM)
166 .setContentIntent(pendingIntent)
167 .build();
168
169 nm.createNotificationChannel(channel);
170 nm.notifyAsUser(null /* tag */, 0 /* id */, notification, UserHandle.CURRENT);
171 }
172
173 return super.onAcquired(acquireInfo, vendorCode);
174 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700175 }
176
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200177 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700178 * Receives the incoming binder calls from FaceManager.
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200179 */
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200180 private final class FaceServiceWrapper extends IFaceService.Stub {
Kevin Chyna56dff72018-06-19 18:41:12 -0700181
182 /**
183 * The following methods contain common code which is shared in biometrics/common.
184 */
Kevin Chyna38653c2019-02-11 17:46:21 -0800185
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200186 @Override // Binder call
Kevin Chynd79e24e2018-09-25 12:06:59 -0700187 public long generateChallenge(IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700188 checkPermission(MANAGE_BIOMETRIC);
Kevin Chynd79e24e2018-09-25 12:06:59 -0700189 return startGenerateChallenge(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200190 }
191
192 @Override // Binder call
Kevin Chynd79e24e2018-09-25 12:06:59 -0700193 public int revokeChallenge(IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700194 checkPermission(MANAGE_BIOMETRIC);
Kevin Chynd79e24e2018-09-25 12:06:59 -0700195 return startRevokeChallenge(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200196 }
197
198 @Override // Binder call
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800199 public void enroll(final IBinder token, final byte[] cryptoToken,
200 final IFaceServiceReceiver receiver, final String opPackageName,
201 final int[] disabledFeatures) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700202 checkPermission(MANAGE_BIOMETRIC);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200203
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200204 final boolean restricted = isRestricted();
Kevin Chyna56dff72018-06-19 18:41:12 -0700205 final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
206 mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
Kevin Chyn1429a312019-01-28 16:08:09 -0800207 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) {
208 @Override
209 public boolean shouldVibrate() {
210 return false;
211 }
Kevin Chyn7782d142019-01-18 12:51:33 -0800212
213 @Override
214 protected int statsModality() {
215 return FaceService.this.statsModality();
216 }
Kevin Chyn1429a312019-01-28 16:08:09 -0800217 };
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200218
Kevin Chyn1a878c12019-04-04 15:50:11 -0700219 enrollInternal(client, mCurrentUserId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200220 }
221
222 @Override // Binder call
223 public void cancelEnrollment(final IBinder token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700224 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700225 cancelEnrollmentInternal(token);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200226 }
227
228 @Override // Binder call
Kevin Chyn747e29b2019-01-11 17:01:53 -0800229 public void authenticate(final IBinder token, final long opId, int userId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700230 final IFaceServiceReceiver receiver, final int flags,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700231 final String opPackageName) {
232 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyn747e29b2019-01-11 17:01:53 -0800233 updateActiveGroup(userId, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200234 final boolean restricted = isRestricted();
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700235 final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
Kevin Chyna56dff72018-06-19 18:41:12 -0700236 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700237 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800238 0 /* cookie */, false /* requireConfirmation */);
Kevin Chyna56dff72018-06-19 18:41:12 -0700239 authenticateInternal(client, opId, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200240 }
241
242 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800243 public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
Kevin Chyn23289ef2018-11-28 16:32:36 -0800244 int groupId, IBiometricServiceReceiverInternal wrapperReceiver,
245 String opPackageName, int cookie, int callingUid, int callingPid,
246 int callingUserId) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700247 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyn41a80902019-02-06 08:12:15 -0800248 updateActiveGroup(groupId, opPackageName);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700249 final boolean restricted = true; // BiometricPrompt is always restricted
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700250 final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700251 mDaemonWrapper, mHalDeviceId, token,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800252 new BiometricPromptServiceListenerImpl(wrapperReceiver),
253 mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
Kevin Chyn158fefb2019-01-03 18:59:05 -0800254 requireConfirmation);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700255 authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
256 callingUserId);
257 }
258
259 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800260 public void startPreparedClient(int cookie) {
261 checkPermission(MANAGE_BIOMETRIC);
262 startCurrentClient(cookie);
263 }
264
265 @Override // Binder call
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200266 public void cancelAuthentication(final IBinder token, final String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700267 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700268 cancelAuthenticationInternal(token, opPackageName);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200269 }
270
271 @Override // Binder call
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700272 public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
Kevin Chyne92cdae2018-11-21 16:35:04 -0800273 int callingUid, int callingPid, int callingUserId, boolean fromClient) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700274 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800275 cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
276 callingUserId, fromClient);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700277 }
278
279 @Override // Binder call
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200280 public void setActiveUser(final int userId) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700281 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700282 setActiveUserInternal(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200283 }
284
285 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700286 public void remove(final IBinder token, final int faceId, final int userId,
287 final IFaceServiceReceiver receiver) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700288 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700289
290 if (token == null) {
291 Slog.w(TAG, "remove(): token is null");
292 return;
293 }
294
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200295 final boolean restricted = isRestricted();
Kevin Chyn6737c572019-02-08 16:10:54 -0800296 final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
297 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
298 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
Kevin Chyn7782d142019-01-18 12:51:33 -0800299 @Override
300 protected int statsModality() {
301 return FaceService.this.statsModality();
302 }
303 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700304 removeInternal(client);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200305 }
306
Kevin Chyna56dff72018-06-19 18:41:12 -0700307 @Override
308 public void enumerate(final IBinder token, final int userId,
309 final IFaceServiceReceiver receiver) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700310 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700311
312 final boolean restricted = isRestricted();
Kevin Chyn6737c572019-02-08 16:10:54 -0800313 final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
314 mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
315 userId, restricted, getContext().getOpPackageName()) {
Kevin Chyn7782d142019-01-18 12:51:33 -0800316 @Override
317 protected int statsModality() {
318 return FaceService.this.statsModality();
319 }
320 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700321 enumerateInternal(client);
322 }
323
324 @Override
325 public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
326 throws RemoteException {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700327 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700328 FaceService.super.addLockoutResetCallback(callback);
329 }
330
331 @Override // Binder call
332 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
333 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
334 return;
335 }
336
337 final long ident = Binder.clearCallingIdentity();
338 try {
Joe Onoratodb396002019-04-05 19:49:27 -0700339 if (args.length > 1 && "--hal".equals(args[0])) {
340 dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
Joe Onoratobf955d22019-03-25 00:16:58 -0700341 } else if (args.length > 0 && "--proto".equals(args[0])) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700342 dumpProto(fd);
343 } else {
344 dumpInternal(pw);
345 }
346 } finally {
347 Binder.restoreCallingIdentity(ident);
348 }
349 }
350
351 /**
352 * The following methods don't use any common code from BiometricService
353 */
354
355 // TODO: refactor out common code here
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200356 @Override // Binder call
357 public boolean isHardwareDetected(long deviceId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700358 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700359 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200360 Binder.getCallingUid(), Binder.getCallingPid(),
361 UserHandle.getCallingUserId())) {
362 return false;
363 }
364
365 final long token = Binder.clearCallingIdentity();
366 try {
367 IBiometricsFace daemon = getFaceDaemon();
368 return daemon != null && mHalDeviceId != 0;
369 } finally {
370 Binder.restoreCallingIdentity(token);
371 }
372 }
373
374 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700375 public void rename(final int faceId, final String name) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700376 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700377 if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
378 return;
379 }
380 mHandler.post(new Runnable() {
381 @Override
382 public void run() {
383 getBiometricUtils().renameBiometricForUser(getContext(), mCurrentUserId,
384 faceId, name);
385 }
386 });
387 }
388
389 @Override // Binder call
390 public List<Face> getEnrolledFaces(int userId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700391 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyna56dff72018-06-19 18:41:12 -0700392 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200393 Binder.getCallingUid(), Binder.getCallingPid(),
394 UserHandle.getCallingUserId())) {
395 return null;
396 }
397
Kevin Chyn6737c572019-02-08 16:10:54 -0800398 return FaceService.this.getEnrolledTemplates(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200399 }
400
401 @Override // Binder call
Kevin Chyna56dff72018-06-19 18:41:12 -0700402 public boolean hasEnrolledFaces(int userId, String opPackageName) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700403 checkPermission(USE_BIOMETRIC_INTERNAL);
Kevin Chyna56dff72018-06-19 18:41:12 -0700404 if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200405 Binder.getCallingUid(), Binder.getCallingPid(),
406 UserHandle.getCallingUserId())) {
407 return false;
408 }
409
Kevin Chyna56dff72018-06-19 18:41:12 -0700410 return FaceService.this.hasEnrolledBiometrics(userId);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200411 }
412
413 @Override // Binder call
414 public long getAuthenticatorId(String opPackageName) {
415 // In this method, we're not checking whether the caller is permitted to use face
416 // API because current authenticator ID is leaked (in a more contrived way) via Android
417 // Keystore (android.security.keystore package): the user of that API can create a key
418 // which requires face authentication for its use, and then query the key's
419 // characteristics (hidden API) which returns, among other things, face
420 // authenticator ID which was active at key creation time.
421 //
422 // Reason: The part of Android Keystore which runs inside an app's process invokes this
423 // method in certain cases. Those cases are not always where the developer demonstrates
424 // explicit intent to use face functionality. Thus, to avoiding throwing an
425 // unexpected SecurityException this method does not check whether its caller is
426 // permitted to use face API.
427 //
428 // The permission check should be restored once Android Keystore no longer invokes this
429 // method from inside app processes.
430
431 return FaceService.this.getAuthenticatorId(opPackageName);
432 }
433
434 @Override // Binder call
Kevin Chyna38653c2019-02-11 17:46:21 -0800435 public void resetLockout(byte[] token) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700436 checkPermission(MANAGE_BIOMETRIC);
Kevin Chyn1d6a2862019-04-02 16:20:21 -0700437
438 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
439 Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
440 return;
441 }
442
Kevin Chyna38653c2019-02-11 17:46:21 -0800443 try {
444 mDaemonWrapper.resetLockout(token);
445 } catch (RemoteException e) {
446 Slog.e(getTag(), "Unable to reset lockout", e);
447 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700448 }
Kevin Chynd79e24e2018-09-25 12:06:59 -0700449
450 @Override
Kevin Chyne62749a2019-04-02 19:33:56 -0700451 public void setFeature(int feature, boolean enabled, final byte[] token,
452 IFaceServiceReceiver receiver) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700453 checkPermission(MANAGE_BIOMETRIC);
454
Kevin Chyne62749a2019-04-02 19:33:56 -0700455 mHandler.post(() -> {
456 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
457 Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
458 return;
Kevin Chynb95f1522019-03-04 16:45:15 -0800459 }
Kevin Chyne62749a2019-04-02 19:33:56 -0700460
461 final ArrayList<Byte> byteToken = new ArrayList<>();
462 for (int i = 0; i < token.length; i++) {
463 byteToken.add(token[i]);
464 }
465
466 // TODO: Support multiple faces
467 final int faceId = getFirstTemplateForUser(mCurrentUserId);
468
469 if (mDaemon != null) {
470 try {
471 final int result = mDaemon.setFeature(feature, enabled, byteToken, faceId);
472 receiver.onFeatureSet(result == Status.OK, feature);
473 } catch (RemoteException e) {
474 Slog.e(getTag(), "Unable to set feature: " + feature
475 + " to enabled:" + enabled, e);
476 }
477 }
478 });
479
Kevin Chynd79e24e2018-09-25 12:06:59 -0700480 }
481
482 @Override
Kevin Chyne62749a2019-04-02 19:33:56 -0700483 public void getFeature(int feature, IFaceServiceReceiver receiver) {
Kevin Chynd79e24e2018-09-25 12:06:59 -0700484 checkPermission(MANAGE_BIOMETRIC);
485
Kevin Chyne62749a2019-04-02 19:33:56 -0700486 mHandler.post(() -> {
487 // This should ideally return tri-state, but the user isn't shown settings unless
488 // they are enrolled so it's fine for now.
489 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
490 Slog.e(TAG, "No enrolled biometrics while getting feature: " + feature);
491 return;
Kevin Chynb95f1522019-03-04 16:45:15 -0800492 }
Kevin Chyne62749a2019-04-02 19:33:56 -0700493
494 // TODO: Support multiple faces
495 final int faceId = getFirstTemplateForUser(mCurrentUserId);
496
497 if (mDaemon != null) {
498 try {
499 OptionalBool result = mDaemon.getFeature(feature, faceId);
500 receiver.onFeatureGet(result.status == Status.OK, feature, result.value);
501 } catch (RemoteException e) {
502 Slog.e(getTag(), "Unable to getRequireAttention", e);
503 }
504 }
505 });
506
Kevin Chynd79e24e2018-09-25 12:06:59 -0700507 }
Kevin Chyn57f119b2018-10-25 12:03:41 -0700508
509 @Override
510 public void userActivity() {
511 checkPermission(MANAGE_BIOMETRIC);
512
513 if (mDaemon != null) {
514 try {
515 mDaemon.userActivity();
516 } catch (RemoteException e) {
517 Slog.e(getTag(), "Unable to send userActivity", e);
518 }
519 }
520 }
Kevin Chynb95f1522019-03-04 16:45:15 -0800521
522 // TODO: Support multiple faces
523 private int getFirstTemplateForUser(int user) {
524 final List<Face> faces = FaceService.this.getEnrolledTemplates(user);
525 if (!faces.isEmpty()) {
526 return faces.get(0).getBiometricId();
527 }
528 return 0;
529 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700530 }
531
532 /**
533 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700534 * BiometricPrompt.
535 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800536 private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800537 BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800538 super(wrapperReceiver);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700539 }
540
541 @Override
542 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
543 throws RemoteException {
544 /**
545 * Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
546 */
Kevin Chyne92cdae2018-11-21 16:35:04 -0800547 if (getWrapperReceiver() != null) {
548 getWrapperReceiver().onAcquired(
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700549 FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
550 FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700551 }
552 }
553
554 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800555 public void onError(long deviceId, int error, int vendorCode, int cookie)
556 throws RemoteException {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800557 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800558 getWrapperReceiver().onError(cookie, error,
559 FaceManager.getErrorString(getContext(), error, vendorCode));
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700560 }
561 }
562 }
563
564 /**
565 * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
Kevin Chyna56dff72018-06-19 18:41:12 -0700566 * the FaceManager.
567 */
568 private class ServiceListenerImpl implements ServiceListener {
Kevin Chyna56dff72018-06-19 18:41:12 -0700569 private IFaceServiceReceiver mFaceServiceReceiver;
570
571 public ServiceListenerImpl(IFaceServiceReceiver receiver) {
572 mFaceServiceReceiver = receiver;
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200573 }
574
575 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700576 public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200577 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700578 if (mFaceServiceReceiver != null) {
579 mFaceServiceReceiver.onEnrollResult(identifier.getDeviceId(),
580 identifier.getBiometricId(),
581 remaining);
582 }
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200583 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700584
585 @Override
586 public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
587 throws RemoteException {
588 if (mFaceServiceReceiver != null) {
589 mFaceServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
590 }
591 }
592
593 @Override
594 public void onAuthenticationSucceeded(long deviceId,
595 BiometricAuthenticator.Identifier biometric, int userId)
596 throws RemoteException {
597 if (mFaceServiceReceiver != null) {
Kevin Chyn628b7182018-11-13 12:00:48 -0800598 if (biometric == null || biometric instanceof Face) {
Kevin Chyn8d2694a2019-04-11 18:30:40 -0700599 mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face) biometric,
600 userId);
Kevin Chyna56dff72018-06-19 18:41:12 -0700601 } else {
602 Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
603 }
604 }
605 }
606
607 @Override
608 public void onAuthenticationFailed(long deviceId) throws RemoteException {
609 if (mFaceServiceReceiver != null) {
610 mFaceServiceReceiver.onAuthenticationFailed(deviceId);
611 }
612 }
613
614 @Override
Kevin Chyn87f257a2018-11-27 16:26:07 -0800615 public void onError(long deviceId, int error, int vendorCode, int cookie)
616 throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700617 if (mFaceServiceReceiver != null) {
618 mFaceServiceReceiver.onError(deviceId, error, vendorCode);
619 }
620 }
621
622 @Override
623 public void onRemoved(BiometricAuthenticator.Identifier identifier,
624 int remaining) throws RemoteException {
625 if (mFaceServiceReceiver != null) {
626 mFaceServiceReceiver.onRemoved(identifier.getDeviceId(),
627 identifier.getBiometricId(), remaining);
628 }
629 }
630
631 @Override
632 public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
633 throws RemoteException {
634 if (mFaceServiceReceiver != null) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800635 mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(),
636 identifier.getBiometricId(), remaining);
Kevin Chyna56dff72018-06-19 18:41:12 -0700637 }
638 }
639 }
640
641 private final FaceMetrics mFaceMetrics = new FaceMetrics();
642
643 @GuardedBy("this")
644 private IBiometricsFace mDaemon;
Kevin Chyna38653c2019-02-11 17:46:21 -0800645 // One of the AuthenticationClient constants
646 private int mCurrentUserLockoutMode;
Kevin Chyna56dff72018-06-19 18:41:12 -0700647
648 /**
649 * Receives callbacks from the HAL.
650 */
651 private IBiometricsFaceClientCallback mDaemonCallback =
652 new IBiometricsFaceClientCallback.Stub() {
Kevin Chyn6737c572019-02-08 16:10:54 -0800653 @Override
654 public void onEnrollResult(final long deviceId, int faceId, int userId,
655 int remaining) {
656 mHandler.post(() -> {
657 final Face face = new Face(getBiometricUtils()
658 .getUniqueName(getContext(), userId), faceId, deviceId);
659 FaceService.super.handleEnrollResult(face, remaining);
660 });
661 }
662
663 @Override
664 public void onAcquired(final long deviceId, final int userId,
665 final int acquiredInfo,
666 final int vendorCode) {
667 mHandler.post(() -> {
668 FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
669 });
670 }
671
672 @Override
673 public void onAuthenticated(final long deviceId, final int faceId, final int userId,
674 ArrayList<Byte> token) {
675 mHandler.post(() -> {
676 Face face = new Face("", faceId, deviceId);
677 FaceService.super.handleAuthenticated(face, token);
678 });
679 }
680
681 @Override
682 public void onError(final long deviceId, final int userId, final int error,
683 final int vendorCode) {
684 mHandler.post(() -> {
685 FaceService.super.handleError(deviceId, error, vendorCode);
686
687 // TODO: this chunk of code should be common to all biometric services
688 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
689 // If we get HW_UNAVAILABLE, try to connect again later...
690 Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
691 synchronized (this) {
692 mDaemon = null;
693 mHalDeviceId = 0;
694 mCurrentUserId = UserHandle.USER_NULL;
695 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700696 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800697 });
698 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700699
Kevin Chyn6737c572019-02-08 16:10:54 -0800700 @Override
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700701 public void onRemoved(final long deviceId, ArrayList<Integer> faceIds, final int userId) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800702 mHandler.post(() -> {
Kevin Chyn31ae8d22019-03-11 14:12:32 -0700703 if (!faceIds.isEmpty()) {
704 for (int i = 0; i < faceIds.size(); i++) {
705 final Face face = new Face("", faceIds.get(i), deviceId);
706 // Convert to old behavior
707 FaceService.super.handleRemoved(face, faceIds.size() - i - 1);
708 }
709 } else {
710 final Face face = new Face("", 0 /* identifier */, deviceId);
711 FaceService.super.handleRemoved(face, 0 /* remaining */);
712 }
713
Kevin Chyn6737c572019-02-08 16:10:54 -0800714 });
715 }
716
717 @Override
718 public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
719 throws RemoteException {
720 mHandler.post(() -> {
721 if (!faceIds.isEmpty()) {
722 for (int i = 0; i < faceIds.size(); i++) {
723 final Face face = new Face("", faceIds.get(i), deviceId);
724 // Convert to old old behavior
725 FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
726 }
727 } else {
728 // For face, the HIDL contract is to receive an empty list when there are no
729 // templates enrolled. Send a null identifier since we don't consume them
730 // anywhere, and send remaining == 0 to plumb this with existing common code.
731 FaceService.super.handleEnumerate(null /* identifier */, 0);
Kevin Chyna56dff72018-06-19 18:41:12 -0700732 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800733 });
734 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700735
Kevin Chyn6737c572019-02-08 16:10:54 -0800736 @Override
737 public void onLockoutChanged(long duration) {
Kevin Chyna38653c2019-02-11 17:46:21 -0800738 Slog.d(TAG, "onLockoutChanged: " + duration);
739 if (duration == 0) {
740 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
741 } else if (duration == Long.MAX_VALUE) {
742 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
743 } else {
744 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
745 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700746
Kevin Chyna38653c2019-02-11 17:46:21 -0800747 mHandler.post(() -> {
748 if (duration == 0) {
749 notifyLockoutResetMonitors();
750 }
751 });
Kevin Chyn6737c572019-02-08 16:10:54 -0800752 }
753 };
Kevin Chyna56dff72018-06-19 18:41:12 -0700754
755 /**
756 * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
757 * can be shared between the multiple biometric services.
758 */
759 private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
760 @Override
761 public int authenticate(long operationId, int groupId) throws RemoteException {
762 IBiometricsFace daemon = getFaceDaemon();
763 if (daemon == null) {
764 Slog.w(TAG, "authenticate(): no face HAL!");
765 return ERROR_ESRCH;
766 }
767 return daemon.authenticate(operationId);
768 }
769
770 @Override
771 public int cancel() throws RemoteException {
772 IBiometricsFace daemon = getFaceDaemon();
773 if (daemon == null) {
774 Slog.w(TAG, "cancel(): no face HAL!");
775 return ERROR_ESRCH;
776 }
777 return daemon.cancel();
778 }
779
780 @Override
781 public int remove(int groupId, int biometricId) throws RemoteException {
782 IBiometricsFace daemon = getFaceDaemon();
783 if (daemon == null) {
784 Slog.w(TAG, "remove(): no face HAL!");
785 return ERROR_ESRCH;
786 }
787 return daemon.remove(biometricId);
788 }
789
790 @Override
791 public int enumerate() throws RemoteException {
792 IBiometricsFace daemon = getFaceDaemon();
793 if (daemon == null) {
794 Slog.w(TAG, "enumerate(): no face HAL!");
795 return ERROR_ESRCH;
796 }
797 return daemon.enumerate();
798 }
799
800 @Override
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800801 public int enroll(byte[] cryptoToken, int groupId, int timeout,
802 ArrayList<Integer> disabledFeatures) throws RemoteException {
Kevin Chyna56dff72018-06-19 18:41:12 -0700803 IBiometricsFace daemon = getFaceDaemon();
804 if (daemon == null) {
805 Slog.w(TAG, "enroll(): no face HAL!");
806 return ERROR_ESRCH;
807 }
808 final ArrayList<Byte> token = new ArrayList<>();
809 for (int i = 0; i < cryptoToken.length; i++) {
810 token.add(cryptoToken[i]);
811 }
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800812 return daemon.enroll(token, timeout, disabledFeatures);
Kevin Chyna56dff72018-06-19 18:41:12 -0700813 }
Kevin Chyna38653c2019-02-11 17:46:21 -0800814
815 @Override
816 public void resetLockout(byte[] cryptoToken) throws RemoteException {
817 IBiometricsFace daemon = getFaceDaemon();
818 if (daemon == null) {
819 Slog.w(TAG, "resetLockout(): no face HAL!");
820 return;
821 }
822 final ArrayList<Byte> token = new ArrayList<>();
823 for (int i = 0; i < cryptoToken.length; i++) {
824 token.add(cryptoToken[i]);
825 }
826 daemon.resetLockout(token);
827 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700828 };
829
830
831 public FaceService(Context context) {
832 super(context);
Kevin Chyna56dff72018-06-19 18:41:12 -0700833 }
834
835 @Override
836 public void onStart() {
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -0700837 super.onStart();
Kevin Chyna56dff72018-06-19 18:41:12 -0700838 publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
839 SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
840 }
841
842 @Override
843 public String getTag() {
844 return TAG;
845 }
846
847 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800848 protected DaemonWrapper getDaemonWrapper() {
849 return mDaemonWrapper;
850 }
851
852 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700853 protected BiometricUtils getBiometricUtils() {
854 return FaceUtils.getInstance();
855 }
856
857 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700858 protected Metrics getMetrics() {
859 return mFaceMetrics;
860 }
861
862 @Override
863 protected boolean hasReachedEnrollmentLimit(int userId) {
864 final int limit = getContext().getResources().getInteger(
Kevin Chyn017e76e2018-06-27 18:35:06 -0700865 com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
Kevin Chyn6737c572019-02-08 16:10:54 -0800866 final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
Kevin Chyna56dff72018-06-19 18:41:12 -0700867 if (enrolled >= limit) {
Kevin Chyn1a878c12019-04-04 15:50:11 -0700868 Slog.w(TAG, "Too many faces registered, user: " + userId);
Kevin Chyna56dff72018-06-19 18:41:12 -0700869 return true;
870 }
871 return false;
872 }
873
874 @Override
Kevin Chyn9ba99912019-01-16 16:24:36 -0800875 public void serviceDied(long cookie) {
876 super.serviceDied(cookie);
877 mDaemon = null;
Jim Miller7b78b222019-02-07 16:47:38 -0800878
879 mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate
Kevin Chyn9ba99912019-01-16 16:24:36 -0800880 }
881
882 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700883 protected void updateActiveGroup(int userId, String clientPackage) {
884 IBiometricsFace daemon = getFaceDaemon();
885
886 if (daemon != null) {
887 try {
888 userId = getUserOrWorkProfileId(clientPackage, userId);
889 if (userId != mCurrentUserId) {
890 final File baseDir = Environment.getDataVendorDeDirectory(userId);
891 final File faceDir = new File(baseDir, FACE_DATA_DIR);
892 if (!faceDir.exists()) {
893 if (!faceDir.mkdir()) {
894 Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
895 return;
896 }
897 // Calling mkdir() from this process will create a directory with our
898 // permissions (inherited from the containing dir). This command fixes
899 // the label.
900 if (!SELinux.restorecon(faceDir)) {
901 Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
902 return;
903 }
904 }
905
906 daemon.setActiveUser(userId, faceDir.getAbsolutePath());
907 mCurrentUserId = userId;
908 }
909 mAuthenticatorIds.put(userId,
910 hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
911 } catch (RemoteException e) {
912 Slog.e(TAG, "Failed to setActiveUser():", e);
913 }
914 }
915 }
916
917 @Override
918 protected String getLockoutResetIntent() {
919 return ACTION_LOCKOUT_RESET;
920 }
921
922 @Override
923 protected String getLockoutBroadcastPermission() {
924 return RESET_FACE_LOCKOUT;
925 }
926
927 @Override
928 protected long getHalDeviceId() {
929 return mHalDeviceId;
930 }
931
932 @Override
Kevin Chyna38653c2019-02-11 17:46:21 -0800933 protected void handleUserSwitching(int userId) {
934 super.handleUserSwitching(userId);
935 // Will be updated when we get the callback from HAL
936 mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
937 }
938
939 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700940 protected boolean hasEnrolledBiometrics(int userId) {
941 if (userId != UserHandle.getCallingUserId()) {
942 checkPermission(INTERACT_ACROSS_USERS);
943 }
944 return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
945 }
946
947 @Override
948 protected String getManageBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700949 return MANAGE_BIOMETRIC;
Kevin Chyna56dff72018-06-19 18:41:12 -0700950 }
951
952 @Override
953 protected void checkUseBiometricPermission() {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700954 // noop for Face. The permission checks are all done on the incoming binder call.
Kevin Chyna56dff72018-06-19 18:41:12 -0700955 }
956
957 @Override
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700958 protected boolean checkAppOps(int uid, String opPackageName) {
959 return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
960 == AppOpsManager.MODE_ALLOWED;
Kevin Chyna56dff72018-06-19 18:41:12 -0700961 }
962
963 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800964 protected List<Face> getEnrolledTemplates(int userId) {
965 return getBiometricUtils().getBiometricsForUser(getContext(), userId);
966 }
967
968 @Override
Kevin Chyna56dff72018-06-19 18:41:12 -0700969 protected void notifyClientActiveCallbacks(boolean isActive) {
970 // noop for Face.
971 }
972
Kevin Chyn7782d142019-01-18 12:51:33 -0800973 @Override
974 protected int statsModality() {
975 return BiometricsProtoEnums.MODALITY_FACE;
976 }
977
Kevin Chyna38653c2019-02-11 17:46:21 -0800978 @Override
979 protected int getLockoutMode() {
980 return mCurrentUserLockoutMode;
981 }
982
Kevin Chyna56dff72018-06-19 18:41:12 -0700983 /** Gets the face daemon */
984 private synchronized IBiometricsFace getFaceDaemon() {
985 if (mDaemon == null) {
986 Slog.v(TAG, "mDaemon was null, reconnect to face");
987 try {
988 mDaemon = IBiometricsFace.getService();
989 } catch (java.util.NoSuchElementException e) {
990 // Service doesn't exist or cannot be opened. Logged below.
991 } catch (RemoteException e) {
992 Slog.e(TAG, "Failed to get biometric interface", e);
993 }
994 if (mDaemon == null) {
995 Slog.w(TAG, "face HIDL not available");
996 return null;
997 }
998
999 mDaemon.asBinder().linkToDeath(this, 0);
1000
1001 try {
1002 mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
1003 } catch (RemoteException e) {
1004 Slog.e(TAG, "Failed to open face HAL", e);
1005 mDaemon = null; // try again later!
1006 }
1007
1008 if (DEBUG) Slog.v(TAG, "Face HAL id: " + mHalDeviceId);
1009 if (mHalDeviceId != 0) {
1010 loadAuthenticatorIds();
1011 updateActiveGroup(ActivityManager.getCurrentUser(), null);
Kevin Chyn6737c572019-02-08 16:10:54 -08001012 doTemplateCleanupForUser(ActivityManager.getCurrentUser());
Kevin Chyna56dff72018-06-19 18:41:12 -07001013 } else {
1014 Slog.w(TAG, "Failed to open Face HAL!");
1015 MetricsLogger.count(getContext(), "faced_openhal_error", 1);
1016 mDaemon = null;
1017 }
1018 }
1019 return mDaemon;
1020 }
1021
Kevin Chynd79e24e2018-09-25 12:06:59 -07001022 private long startGenerateChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -07001023 IBiometricsFace daemon = getFaceDaemon();
1024 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -07001025 Slog.w(TAG, "startGenerateChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -07001026 return 0;
1027 }
1028 try {
Kevin Chyne46a2162018-09-20 18:43:01 -07001029 return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
Kevin Chyna56dff72018-06-19 18:41:12 -07001030 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -07001031 Slog.e(TAG, "startGenerateChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -07001032 }
1033 return 0;
1034 }
1035
Kevin Chynd79e24e2018-09-25 12:06:59 -07001036 private int startRevokeChallenge(IBinder token) {
Kevin Chyna56dff72018-06-19 18:41:12 -07001037 IBiometricsFace daemon = getFaceDaemon();
1038 if (daemon == null) {
Kevin Chynd79e24e2018-09-25 12:06:59 -07001039 Slog.w(TAG, "startRevokeChallenge: no face HAL!");
Kevin Chyna56dff72018-06-19 18:41:12 -07001040 return 0;
1041 }
1042 try {
Kevin Chyn96c92972018-08-31 16:09:31 -07001043 return daemon.revokeChallenge();
Kevin Chyna56dff72018-06-19 18:41:12 -07001044 } catch (RemoteException e) {
Kevin Chynd79e24e2018-09-25 12:06:59 -07001045 Slog.e(TAG, "startRevokeChallenge failed", e);
Kevin Chyna56dff72018-06-19 18:41:12 -07001046 }
1047 return 0;
1048 }
1049
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001050 private void dumpInternal(PrintWriter pw) {
1051 JSONObject dump = new JSONObject();
1052 try {
1053 dump.put("service", "Face Manager");
1054
1055 JSONArray sets = new JSONArray();
1056 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1057 final int userId = user.getUserHandle().getIdentifier();
Kevin Chyna56dff72018-06-19 18:41:12 -07001058 final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001059 PerformanceStats stats = mPerformanceMap.get(userId);
1060 PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
1061 JSONObject set = new JSONObject();
1062 set.put("id", userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001063 set.put("count", N);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001064 set.put("accept", (stats != null) ? stats.accept : 0);
1065 set.put("reject", (stats != null) ? stats.reject : 0);
1066 set.put("acquire", (stats != null) ? stats.acquire : 0);
1067 set.put("lockout", (stats != null) ? stats.lockout : 0);
1068 set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
1069 // cryptoStats measures statistics about secure face transactions
1070 // (e.g. to unlock password storage, make secure purchases, etc.)
1071 set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
1072 set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
1073 set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
1074 set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
Kevin Chyna56dff72018-06-19 18:41:12 -07001075 set.put("permanentLockoutCrypto",
1076 (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001077 sets.put(set);
1078 }
1079
1080 dump.put("prints", sets);
1081 } catch (JSONException e) {
1082 Slog.e(TAG, "dump formatting failure", e);
1083 }
1084 pw.println(dump);
Kevin Chyn9ba99912019-01-16 16:24:36 -08001085 pw.println("HAL Deaths: " + mHALDeathCount);
1086 mHALDeathCount = 0;
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001087 }
1088
1089 private void dumpProto(FileDescriptor fd) {
1090 final ProtoOutputStream proto = new ProtoOutputStream(fd);
1091 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1092 final int userId = user.getUserHandle().getIdentifier();
1093
1094 final long userToken = proto.start(FaceServiceDumpProto.USERS);
1095
1096 proto.write(FaceUserStatsProto.USER_ID, userId);
Kevin Chyna56dff72018-06-19 18:41:12 -07001097 proto.write(FaceUserStatsProto.NUM_FACES,
1098 getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001099
1100 // Normal face authentications (e.g. lockscreen)
1101 final PerformanceStats normal = mPerformanceMap.get(userId);
1102 if (normal != null) {
1103 final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
1104 proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
1105 proto.write(FaceActionStatsProto.REJECT, normal.reject);
1106 proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
1107 proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
1108 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
1109 proto.end(countsToken);
1110 }
1111
1112 // Statistics about secure face transactions (e.g. to unlock password
1113 // storage, make secure purchases, etc.)
1114 final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
1115 if (crypto != null) {
1116 final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
1117 proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
1118 proto.write(FaceActionStatsProto.REJECT, crypto.reject);
1119 proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
1120 proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
1121 proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
1122 proto.end(countsToken);
1123 }
1124
1125 proto.end(userToken);
1126 }
1127 proto.flush();
Kevin Chyna56dff72018-06-19 18:41:12 -07001128 mPerformanceMap.clear();
1129 mCryptoPerformanceMap.clear();
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001130 }
Joe Onoratobf955d22019-03-25 00:16:58 -07001131
Joe Onoratodb396002019-04-05 19:49:27 -07001132 private void dumpHal(FileDescriptor fd, String[] args) {
Joe Onoratobf955d22019-03-25 00:16:58 -07001133 // WARNING: CDD restricts image data from leaving TEE unencrypted on
1134 // production devices:
1135 // [C-1-10] MUST not allow unencrypted access to identifiable biometric
1136 // data or any data derived from it (such as embeddings) to the
1137 // Application Processor outside the context of the TEE.
1138 // As such, this API should only be enabled for testing purposes on
1139 // engineering and userdebug builds. All modules in the software stack
1140 // MUST enforce final build products do NOT have this functionality.
1141 // Additionally, the following check MUST NOT be removed.
1142 if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
1143 return;
1144 }
1145
Joe Onorato108413a2019-04-03 18:20:52 -07001146 // Additionally, this flag allows turning off face for a device
1147 // (either permanently through the build or on an individual device).
1148 if (SystemProperties.getBoolean("ro.face.disable_debug_data", false)
1149 || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) {
1150 return;
1151 }
1152
Joe Onoratodb396002019-04-05 19:49:27 -07001153 // The debug method takes two file descriptors. The first is for text
1154 // output, which we will drop. The second is for binary data, which
1155 // will be the protobuf data.
1156 final IBiometricsFace daemon = getFaceDaemon();
1157 if (daemon != null) {
1158 FileOutputStream devnull = null;
1159 try {
1160 devnull = new FileOutputStream("/dev/null");
1161 final NativeHandle handle = new NativeHandle(
1162 new FileDescriptor[] { devnull.getFD(), fd },
1163 new int[0], false);
1164 daemon.debug(handle, new ArrayList<String>(Arrays.asList(args)));
1165 } catch (IOException | RemoteException ex) {
1166 Slog.d(TAG, "error while reading face debugging data", ex);
1167 } finally {
1168 if (devnull != null) {
1169 try {
1170 devnull.close();
1171 } catch (IOException ex) {
1172 }
1173 }
1174 }
Joe Onoratobf955d22019-03-25 00:16:58 -07001175 }
Joe Onoratobf955d22019-03-25 00:16:58 -07001176 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001177}