blob: 41b15751b26c974287dfe855133d90b0fd0792d8 [file] [log] [blame]
Kevin Chyn037c4d52018-06-11 19:17:32 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.biometrics.common;
18
19import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
20
21import android.app.ActivityManager;
22import android.app.ActivityTaskManager;
23import android.app.AlarmManager;
24import android.app.AppOpsManager;
25import android.app.IActivityTaskManager;
26import android.app.PendingIntent;
27import android.app.SynchronousUserSwitchObserver;
28import android.app.TaskStackListener;
29import android.content.BroadcastReceiver;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.pm.PackageManager;
35import android.content.pm.UserInfo;
36import android.hardware.biometrics.BiometricAuthenticator;
37import android.hardware.biometrics.BiometricConstants;
38import android.hardware.biometrics.IBiometricPromptReceiver;
Kevin Chyna56dff72018-06-19 18:41:12 -070039import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
40import android.hardware.fingerprint.Fingerprint;
Kevin Chyn037c4d52018-06-11 19:17:32 -070041import android.os.Binder;
42import android.os.Bundle;
Kevin Chyna56dff72018-06-19 18:41:12 -070043import android.os.DeadObjectException;
Kevin Chyn037c4d52018-06-11 19:17:32 -070044import android.os.Handler;
45import android.os.IBinder;
46import android.os.IHwBinder;
Kevin Chyna56dff72018-06-19 18:41:12 -070047import android.os.IRemoteCallback;
Kevin Chyn037c4d52018-06-11 19:17:32 -070048import android.os.PowerManager;
49import android.os.RemoteException;
50import android.os.SystemClock;
51import android.os.UserHandle;
52import android.os.UserManager;
53import android.security.KeyStore;
54import android.util.Slog;
55import android.util.SparseBooleanArray;
56import android.util.SparseIntArray;
57
58import com.android.internal.logging.MetricsLogger;
59import com.android.internal.statusbar.IStatusBarService;
60import com.android.server.SystemService;
Kevin Chyna56dff72018-06-19 18:41:12 -070061import com.android.server.biometrics.face.FaceService;
Kevin Chyn037c4d52018-06-11 19:17:32 -070062import com.android.server.biometrics.fingerprint.FingerprintService;
63
64import java.util.ArrayList;
Kevin Chyna56dff72018-06-19 18:41:12 -070065import java.util.Collections;
Kevin Chyn037c4d52018-06-11 19:17:32 -070066import java.util.HashMap;
67import java.util.List;
Kevin Chyna56dff72018-06-19 18:41:12 -070068import java.util.Map;
Kevin Chyn037c4d52018-06-11 19:17:32 -070069
70/**
71 * Abstract base class containing all of the business logic for biometric services, e.g.
72 * Fingerprint, Face, Iris.
73 *
74 * @hide
75 */
76public abstract class BiometricService extends SystemService implements IHwBinder.DeathRecipient {
77
78 protected static final boolean DEBUG = true;
79
80 private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
81 private static final int MSG_USER_SWITCHING = 10;
82 private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
83 private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
84
85 private final Context mContext;
86 private final String mKeyguardPackage;
87 private final AppOpsManager mAppOps;
88 private final SparseBooleanArray mTimedLockoutCleared;
89 private final SparseIntArray mFailedAttempts;
90 private final IActivityTaskManager mActivityTaskManager;
91 private final AlarmManager mAlarmManager;
92 private final PowerManager mPowerManager;
93 private final UserManager mUserManager;
94 private final MetricsLogger mMetricsLogger;
95 private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
96 private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
97 private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
Kevin Chyna56dff72018-06-19 18:41:12 -070098 private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
Kevin Chyn037c4d52018-06-11 19:17:32 -070099
Kevin Chyna56dff72018-06-19 18:41:12 -0700100 protected final Map<Integer, Long> mAuthenticatorIds =
101 Collections.synchronizedMap(new HashMap<>());
102 protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
103 new ResetFailedAttemptsForUserRunnable();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700104 protected final H mHandler = new H();
105
106 private ClientMonitor mCurrentClient;
107 private ClientMonitor mPendingClient;
108 private PerformanceStats mPerformanceStats;
109 protected int mCurrentUserId = UserHandle.USER_NULL;
110 // Normal authentications are tracked by mPerformanceMap.
111 protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
112 // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
113 protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
114
115 protected class PerformanceStats {
116 public int accept; // number of accepted biometrics
117 public int reject; // number of rejected biometrics
118 public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
119 // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
120 public int lockout; // total number of lockouts
121 public int permanentLockout; // total number of permanent lockouts
122 }
123
124 /**
125 * @return the log tag.
126 */
127 protected abstract String getTag();
128
129 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700130 * @return the biometric utilities for a specific implementation.
131 */
132 protected abstract BiometricUtils getBiometricUtils();
133
134 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700135 * @return the number of failed attempts after which the user will be temporarily locked out
136 * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
137 */
138 protected abstract int getFailedAttemptsLockoutTimed();
139
140 /**
141 * @return the number of failed attempts after which the user will be permanently locked out
142 * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
143 */
144 protected abstract int getFailedAttemptsLockoutPermanent();
145
146 /**
147 * @return the metrics constants for a biometric implementation.
148 */
149 protected abstract Metrics getMetrics();
150
151 /**
152 * @param userId
153 * @return true if the enrollment limit has been reached.
154 */
155 protected abstract boolean hasReachedEnrollmentLimit(int userId);
156
157 /**
158 * Notifies the HAL that the user has changed.
159 * @param userId
160 * @param clientPackage
161 */
162 protected abstract void updateActiveGroup(int userId, String clientPackage);
163
164 /**
165 * @return The protected intent to reset lockout for a specific biometric.
166 */
167 protected abstract String getLockoutResetIntent();
168
169 /**
170 * @return The permission the sender is required to have in order for the lockout reset intent
171 * to be received by the BiometricService implementation.
172 */
173 protected abstract String getLockoutBroadcastPermission();
174
175 /**
176 * @return The HAL ID.
177 */
178 protected abstract long getHalDeviceId();
179
180 /**
181 * This method is called when the user switches. Implementations should probably notify the
182 * HAL.
183 * @param userId
184 */
185 protected abstract void handleUserSwitching(int userId);
186
187 /**
188 * @param userId
189 * @return Returns true if the user has any enrolled biometrics.
190 */
191 protected abstract boolean hasEnrolledBiometrics(int userId);
192
193 /**
194 * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
195 * etc.
196 */
197 protected abstract String getManageBiometricPermission();
198
199 /**
200 * Checks if the caller has permission to use the biometric service - throws a SecurityException
201 * if not.
202 */
203 protected abstract void checkUseBiometricPermission();
204
205 /**
206 * @return Returns one of the {@link AppOpsManager} constants which pertains to the specific
207 * biometric service.
208 */
209 protected abstract int getAppOp();
210
Kevin Chyn037c4d52018-06-11 19:17:32 -0700211
212 /**
213 * Notifies clients of any change in the biometric state (active / idle). This is mainly for
214 * Fingerprint navigation gestures.
215 * @param isActive
216 */
217 protected void notifyClientActiveCallbacks(boolean isActive) {}
218
219 protected class AuthenticationClientImpl extends AuthenticationClient {
220
221 public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
222 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
223 boolean restricted, String owner, Bundle bundle,
224 IBiometricPromptReceiver dialogReceiver,
225 IStatusBarService statusBarService) {
226 super(context, getMetrics(), daemon, halDeviceId, token, listener,
227 targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
228 statusBarService);
229 }
230
231 @Override
232 public void onStart() {
233 try {
234 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
235 } catch (RemoteException e) {
236 Slog.e(getTag(), "Could not register task stack listener", e);
237 }
238 }
239
240 @Override
241 public void onStop() {
242 try {
243 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
244 } catch (RemoteException e) {
245 Slog.e(getTag(), "Could not unregister task stack listener", e);
246 }
247 }
248
249 @Override
250 public void resetFailedAttempts() {
251 resetFailedAttemptsForUser(true /* clearAttemptCounter */,
252 ActivityManager.getCurrentUser());
253 }
254
255 @Override
256 public void notifyUserActivity() {
257 userActivity();
258 }
259
260 @Override
261 public int handleFailedAttempt() {
262 final int currentUser = ActivityManager.getCurrentUser();
263 mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
264 mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
265 final int lockoutMode = getLockoutMode();
266 if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
267 mPerformanceStats.permanentLockout++;
268 } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
269 mPerformanceStats.lockout++;
270 }
271
272 // Failing multiple times will continue to push out the lockout time
273 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
274 scheduleLockoutResetForUser(currentUser);
275 return lockoutMode;
276 }
277 return AuthenticationClient.LOCKOUT_NONE;
278 }
279 }
280
281 protected class EnrollClientImpl extends EnrollClient {
282
283 public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
284 IBinder token, ServiceListener listener, int userId, int groupId,
285 byte[] cryptoToken, boolean restricted, String owner) {
286 super(context, getMetrics(), daemon, halDeviceId, token, listener,
Kevin Chyna56dff72018-06-19 18:41:12 -0700287 userId, groupId, cryptoToken, restricted, owner, getBiometricUtils());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700288 }
289
290 @Override
291 public void notifyUserActivity() {
292 userActivity();
293 }
294 }
295
296 protected class RemovalClientImpl extends RemovalClient {
297 private boolean mShouldNotify;
298
299 public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
300 IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
301 boolean restricted, String owner) {
302 super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700303 userId, restricted, owner, getBiometricUtils());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700304 }
305
306 public void setShouldNotifyUserActivity(boolean shouldNotify) {
307 mShouldNotify = shouldNotify;
308 }
309
310 @Override
311 public void notifyUserActivity() {
312 if (mShouldNotify) {
313 userActivity();
314 }
315 }
316 }
317
318 protected class EnumerateClientImpl extends EnumerateClient {
319
320 public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
321 IBinder token, ServiceListener listener, int groupId, int userId,
322 boolean restricted, String owner) {
323 super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
324 restricted, owner);
325 }
326
327 @Override
328 public void notifyUserActivity() {
329 userActivity();
330 }
331 }
332
333 /**
334 * Wraps the callback interface from Service -> Manager
335 */
336 protected interface ServiceListener {
Kevin Chyna56dff72018-06-19 18:41:12 -0700337 void onEnrollResult(BiometricAuthenticator.Identifier identifier,
338 int remaining) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700339
340 void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
341 throws RemoteException;
342
343 void onAuthenticationSucceeded(long deviceId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700344 BiometricAuthenticator.Identifier biometric, int userId)
Kevin Chyn037c4d52018-06-11 19:17:32 -0700345 throws RemoteException;
346
347 void onAuthenticationFailed(long deviceId)
348 throws RemoteException;
349
350 void onError(long deviceId, int error, int vendorCode)
351 throws RemoteException;
352
Kevin Chyna56dff72018-06-19 18:41:12 -0700353 void onRemoved(BiometricAuthenticator.Identifier identifier,
354 int remaining) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700355
Kevin Chyna56dff72018-06-19 18:41:12 -0700356 void onEnumerated(BiometricAuthenticator.Identifier identifier,
357 int remaining) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700358 }
359
360 /**
361 * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
362 * subclasses.
363 */
364 protected interface DaemonWrapper {
Kevin Chyna56dff72018-06-19 18:41:12 -0700365 int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
Kevin Chyn037c4d52018-06-11 19:17:32 -0700366 int authenticate(long operationId, int groupId) throws RemoteException;
367 int cancel() throws RemoteException;
368 int remove(int groupId, int biometricId) throws RemoteException;
369 int enumerate() throws RemoteException;
370 int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
371 }
372
373 /**
374 * Handler which all subclasses should post events to.
375 */
376 protected final class H extends Handler {
377 @Override
378 public void handleMessage(android.os.Message msg) {
379 switch (msg.what) {
380 case MSG_USER_SWITCHING:
381 handleUserSwitching(msg.arg1);
382 break;
383
384 default:
385 Slog.w(getTag(), "Unknown message:" + msg.what);
386 }
387 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700388 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700389
390 private final class BiometricTaskStackListener extends TaskStackListener {
391 @Override
392 public void onTaskStackChanged() {
393 try {
394 if (!(mCurrentClient instanceof AuthenticationClient)) {
395 return;
396 }
397 final String currentClient = mCurrentClient.getOwnerString();
398 if (isKeyguard(currentClient)) {
399 return; // Keyguard is always allowed
400 }
401 List<ActivityManager.RunningTaskInfo> runningTasks =
402 mActivityTaskManager.getTasks(1);
403 if (!runningTasks.isEmpty()) {
404 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
405 if (!topPackage.contentEquals(currentClient)) {
406 Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
407 + " currentClient: " + currentClient);
408 mCurrentClient.stop(false /* initiatedByClient */);
409 }
410 }
411 } catch (RemoteException e) {
412 Slog.e(getTag(), "Unable to get running tasks", e);
413 }
414 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700415 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700416
417 private final class ResetClientStateRunnable implements Runnable {
418 @Override
419 public void run() {
420 /**
421 * Warning: if we get here, the driver never confirmed our call to cancel the current
422 * operation (authenticate, enroll, remove, enumerate, etc), which is
423 * really bad. The result will be a 3-second delay in starting each new client.
424 * If you see this on a device, make certain the driver notifies with
425 * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
426 * once it has successfully switched to the IDLE state in the HAL.
427 * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
428 * in response to an actual cancel() call.
429 */
430 Slog.w(getTag(), "Client "
431 + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
432 + " failed to respond to cancel, starting client "
433 + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
434
435 mCurrentClient = null;
436 startClient(mPendingClient, false);
437 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700438 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700439
440 private final class LockoutReceiver extends BroadcastReceiver {
441 @Override
442 public void onReceive(Context context, Intent intent) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700443 Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700444 if (getLockoutResetIntent().equals(intent.getAction())) {
445 final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
446 resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
447 }
448 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700449 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700450
Kevin Chyna56dff72018-06-19 18:41:12 -0700451 private final class ResetFailedAttemptsForUserRunnable implements Runnable {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700452 @Override
453 public void run() {
454 resetFailedAttemptsForUser(true /* clearAttemptCounter */,
455 ActivityManager.getCurrentUser());
456 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700457 }
458
459 private final class LockoutResetMonitor implements IBinder.DeathRecipient {
460 private static final long WAKELOCK_TIMEOUT_MS = 2000;
461 private final IBiometricServiceLockoutResetCallback mCallback;
462 private final PowerManager.WakeLock mWakeLock;
463
464 public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
465 mCallback = callback;
466 mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
467 "lockout reset callback");
468 try {
469 mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
470 } catch (RemoteException e) {
471 Slog.w(getTag(), "caught remote exception in linkToDeath", e);
472 }
473 }
474
475 public void sendLockoutReset() {
476 if (mCallback != null) {
477 try {
478 mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
479 mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
480 @Override
481 public void sendResult(Bundle data) throws RemoteException {
482 releaseWakelock();
483 }
484 });
485 } catch (DeadObjectException e) {
486 Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
487 mHandler.post(mRemoveCallbackRunnable);
488 } catch (RemoteException e) {
489 Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
490 releaseWakelock();
491 }
492 }
493 }
494
495 private final Runnable mRemoveCallbackRunnable = new Runnable() {
496 @Override
497 public void run() {
498 releaseWakelock();
499 removeLockoutResetCallback(LockoutResetMonitor.this);
500 }
501 };
502
503 @Override
504 public void binderDied() {
505 Slog.e(getTag(), "Lockout reset callback binder died");
506 mHandler.post(mRemoveCallbackRunnable);
507 }
508
509 private void releaseWakelock() {
510 if (mWakeLock.isHeld()) {
511 mWakeLock.release();
512 }
513 }
514 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700515
516 /**
517 * Initializes the system service.
518 * <p>
519 * Subclasses must define a single argument constructor that accepts the context
520 * and passes it to super.
521 * </p>
522 *
523 * @param context The system server context.
524 */
525 public BiometricService(Context context) {
526 super(context);
527 mContext = context;
528 mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
529 com.android.internal.R.string.config_keyguardComponent)).getPackageName();
530 mAppOps = context.getSystemService(AppOpsManager.class);
531 mTimedLockoutCleared = new SparseBooleanArray();
532 mFailedAttempts = new SparseIntArray();
533 mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
534 Context.ACTIVITY_TASK_SERVICE)).getService();
535 mPowerManager = mContext.getSystemService(PowerManager.class);
536 mAlarmManager = mContext.getSystemService(AlarmManager.class);
537 mUserManager = UserManager.get(mContext);
538 mMetricsLogger = new MetricsLogger();
539 mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
540 getLockoutBroadcastPermission(), null /* handler */);
541 }
542
543 @Override
544 public void onStart() {
545 listenForUserSwitches();
546 }
547
548 @Override
549 public void serviceDied(long cookie) {
550 Slog.e(getTag(), "HAL died");
551 mMetricsLogger.count(getMetrics().tagHalDied(), 1);
552 handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
553 0 /*vendorCode */);
554 }
555
556 protected ClientMonitor getCurrentClient() {
557 return mCurrentClient;
558 }
559
560 protected ClientMonitor getPendingClient() {
561 return mPendingClient;
562 }
563
564 /**
565 * Callback handlers from the daemon. The caller must put this on a handler.
566 */
567
568 protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
569 ClientMonitor client = mCurrentClient;
570 if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
571 removeClient(client);
572 }
573 if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
574 && client instanceof AuthenticationClient) {
575 // ignore enrollment acquisitions or acquisitions when we're locked out
576 mPerformanceStats.acquire++;
577 }
578 }
579
580 protected void handleAuthenticated(long deviceId, int biometricId, int groupId,
581 ArrayList<Byte> token) {
582 ClientMonitor client = mCurrentClient;
583 if (biometricId != 0) {
584 final byte[] byteToken = new byte[token.size()];
585 for (int i = 0; i < token.size(); i++) {
586 byteToken[i] = token.get(i);
587 }
588 KeyStore.getInstance().addAuthToken(byteToken);
589 }
590 if (client != null && client.onAuthenticated(biometricId, groupId)) {
591 removeClient(client);
592 }
593 if (biometricId != 0) {
594 mPerformanceStats.accept++;
595 } else {
596 mPerformanceStats.reject++;
597 }
598 }
599
Kevin Chyna56dff72018-06-19 18:41:12 -0700600 protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
601 int remaining) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700602 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700603 if (client != null && client.onEnrollResult(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700604 removeClient(client);
605 // When enrollment finishes, update this group's authenticator id, as the HAL has
606 // already generated a new authenticator id when the new biometric is enrolled.
Kevin Chyna56dff72018-06-19 18:41:12 -0700607 if (identifier instanceof Fingerprint) {
608 updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
609 } else {
610 updateActiveGroup(mCurrentUserId, null);
611 }
612
Kevin Chyn037c4d52018-06-11 19:17:32 -0700613 }
614 }
615
616 protected void handleError(long deviceId, int error, int vendorCode) {
617 final ClientMonitor client = mCurrentClient;
618
619 if (DEBUG) Slog.v(getTag(), "handleError(client="
620 + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
621
Kevin Chyna56dff72018-06-19 18:41:12 -0700622 if (client != null && client.onError(deviceId, error, vendorCode)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700623 removeClient(client);
624 }
625
626 if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
627 mHandler.removeCallbacks(mResetClientState);
628 if (mPendingClient != null) {
629 if (DEBUG) Slog.v(getTag(), "start pending client " + mPendingClient.getOwnerString());
630 startClient(mPendingClient, false);
631 mPendingClient = null;
632 }
633 }
634 }
635
Kevin Chyna56dff72018-06-19 18:41:12 -0700636 protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700637 final int remaining) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700638 if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
639 + ", dev=" + identifier.getDeviceId()
Kevin Chyn037c4d52018-06-11 19:17:32 -0700640 + ", rem=" + remaining);
641
642 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700643 if (client != null && client.onRemoved(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700644 removeClient(client);
645 // When the last biometric of a group is removed, update the authenticator id
Kevin Chyna56dff72018-06-19 18:41:12 -0700646 int userId = mCurrentUserId;
647 if (identifier instanceof Fingerprint) {
648 userId = ((Fingerprint) identifier).getGroupId();
649 }
650 if (!hasEnrolledBiometrics(userId)) {
651 updateActiveGroup(userId, null);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700652 }
653 }
654 }
655
656 /**
657 * Calls from the Manager. These are still on the calling binder's thread.
658 */
659
660 protected void enrollInternal(EnrollClientImpl client, int userId) {
661 if (hasReachedEnrollmentLimit(userId)) {
662 return;
663 }
664
665 // Group ID is arbitrarily set to parent profile user ID. It just represents
666 // the default biometrics for the user.
667 if (!isCurrentUserOrProfile(userId)) {
668 return;
669 }
670
671 mHandler.post(() -> {
672 startClient(client, true /* initiatedByClient */);
673 });
674 }
675
676 protected void cancelEnrollmentInternal(IBinder token) {
677 mHandler.post(() -> {
678 ClientMonitor client = mCurrentClient;
679 if (client instanceof EnrollClient && client.getToken() == token) {
680 client.stop(client.getToken() == token);
681 }
682 });
683 }
684
685 protected void authenticateInternal(AuthenticationClientImpl client, long opId,
686 String opPackageName) {
687 final int callingUid = Binder.getCallingUid();
688 final int callingPid = Binder.getCallingPid();
689 final int callingUserId = UserHandle.getCallingUserId();
690
691 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
692 callingUserId)) {
693 if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
694 return;
695 }
696
697 mHandler.post(() -> {
698 mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
699
700 // Get performance stats object for this user.
701 HashMap<Integer, PerformanceStats> pmap
702 = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
703 PerformanceStats stats = pmap.get(mCurrentUserId);
704 if (stats == null) {
705 stats = new PerformanceStats();
706 pmap.put(mCurrentUserId, stats);
707 }
708 mPerformanceStats = stats;
709
710 startAuthentication(client, opPackageName);
711 });
712 }
713
714 protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
715 final int callingUid = Binder.getCallingUid();
716 final int callingPid = Binder.getCallingPid();
717 final int callingUserId = UserHandle.getCallingUserId();
718
719 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
720 callingUserId)) {
721 if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
722 return;
723 }
724
725 mHandler.post(() -> {
726 ClientMonitor client = mCurrentClient;
727 if (client instanceof AuthenticationClient) {
728 if (client.getToken() == token) {
729 if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
730 client.stop(client.getToken() == token);
731 } else {
732 if (DEBUG) Slog.v(getTag(), "can't stop client "
733 + client.getOwnerString() + " since tokens don't match");
734 }
735 } else if (client != null) {
736 if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
737 + client.getOwnerString());
738 }
739 });
740 }
741
742 protected void setActiveUserInternal(int userId) {
743 mHandler.post(() -> {
744 updateActiveGroup(userId, null /* clientPackage */);
745 });
746 }
747
748 protected void removeInternal(RemovalClientImpl client) {
749 mHandler.post(() -> {
750 startClient(client, true /* initiatedByClient */);
751 });
752 }
753
754 protected void enumerateInternal(EnumerateClientImpl client) {
755 mHandler.post(() -> {
756 startClient(client, true /* initiatedByClient */);
757 });
758 }
759
760 // Should be done on a handler thread - not on the Binder's thread.
761 private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
762 updateActiveGroup(client.getGroupId(), opPackageName);
763
764 if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
765
766 int lockoutMode = getLockoutMode();
767 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
768 Slog.v(getTag(), "In lockout mode(" + lockoutMode +
769 ") ; disallowing authentication");
770 int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
771 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
772 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
Kevin Chyna56dff72018-06-19 18:41:12 -0700773 if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700774 Slog.w(getTag(), "Cannot send permanent lockout message to client");
775 }
776 return;
777 }
778 startClient(client, true /* initiatedByClient */);
779 }
780
Kevin Chyna56dff72018-06-19 18:41:12 -0700781 protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
782 mHandler.post(() -> {
783 final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
784 if (!mLockoutMonitors.contains(monitor)) {
785 mLockoutMonitors.add(monitor);
786 }
787 });
788 }
789
Kevin Chyn037c4d52018-06-11 19:17:32 -0700790 /**
791 * Helper methods.
792 */
793
794 /**
795 * @param opPackageName name of package for caller
796 * @param requireForeground only allow this call while app is in the foreground
797 * @return true if caller can use the biometric API
798 */
799 protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
800 int pid, int userId) {
801 checkUseBiometricPermission();
802
803 if (isKeyguard(opPackageName)) {
804 return true; // Keyguard is always allowed
805 }
806 if (!isCurrentUserOrProfile(userId)) {
807 Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
808 return false;
809 }
810 if (mAppOps.noteOp(getAppOp(), uid, opPackageName) != AppOpsManager.MODE_ALLOWED) {
811 Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
812 return false;
813 }
814 if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
815 opPackageName))) {
816 Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
817 return false;
818 }
819 return true;
820 }
821
822 /**
823 * @param opPackageName package of the caller
824 * @return true if this is the same client currently using the biometric
825 */
826 private boolean isCurrentClient(String opPackageName) {
827 return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
828 }
829
830 /**
831 * @return true if this is keyguard package
832 */
833 private boolean isKeyguard(String clientPackage) {
834 return mKeyguardPackage.equals(clientPackage);
835 }
836
837 private int getLockoutMode() {
838 final int currentUser = ActivityManager.getCurrentUser();
839 final int failedAttempts = mFailedAttempts.get(currentUser, 0);
840 if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
841 return AuthenticationClient.LOCKOUT_PERMANENT;
842 } else if (failedAttempts > 0 &&
843 mTimedLockoutCleared.get(currentUser, false) == false
844 && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
845 return AuthenticationClient.LOCKOUT_TIMED;
846 }
847 return AuthenticationClient.LOCKOUT_NONE;
848 }
849
850 private boolean isForegroundActivity(int uid, int pid) {
851 try {
852 List<ActivityManager.RunningAppProcessInfo> procs =
853 ActivityManager.getService().getRunningAppProcesses();
854 int N = procs.size();
855 for (int i = 0; i < N; i++) {
856 ActivityManager.RunningAppProcessInfo proc = procs.get(i);
857 if (proc.pid == pid && proc.uid == uid
858 && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
859 return true;
860 }
861 }
862 } catch (RemoteException e) {
863 Slog.w(getTag(), "am.getRunningAppProcesses() failed");
864 }
865 return false;
866 }
867
868 /**
869 * Calls the HAL to switch states to the new task. If there's already a current task,
870 * it calls cancel() and sets mPendingClient to begin when the current task finishes
871 * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
872 *
873 * @param newClient the new client that wants to connect
874 * @param initiatedByClient true for authenticate, remove and enroll
875 */
876 private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
877 ClientMonitor currentClient = mCurrentClient;
878 if (currentClient != null) {
879 if (DEBUG) Slog.v(getTag(), "request stop current client " +
880 currentClient.getOwnerString());
881
882 // This check only matters for FingerprintService, since enumerate may call back
883 // multiple times.
884 if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
885 currentClient instanceof FingerprintService.RemovalClientImpl) {
886 // This condition means we're currently running internal diagnostics to
887 // remove extra fingerprints in the hardware and/or the software
888 // TODO: design an escape hatch in case client never finishes
889 if (newClient != null) {
890 Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
891 + newClient.getClass().getSuperclass().getSimpleName()
892 + "(" + newClient.getOwnerString() + ")"
893 + ", initiatedByClient = " + initiatedByClient);
894 }
895 } else {
896 currentClient.stop(initiatedByClient);
897 }
898 mPendingClient = newClient;
899 mHandler.removeCallbacks(mResetClientState);
900 mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
901 } else if (newClient != null) {
902 mCurrentClient = newClient;
903 if (DEBUG) Slog.v(getTag(), "starting client "
904 + newClient.getClass().getSuperclass().getSimpleName()
905 + "(" + newClient.getOwnerString() + ")"
906 + ", initiatedByClient = " + initiatedByClient);
907 notifyClientActiveCallbacks(true);
908
909 newClient.start();
910 }
911 }
912
913 protected void removeClient(ClientMonitor client) {
914 if (client != null) {
915 client.destroy();
916 if (client != mCurrentClient && mCurrentClient != null) {
917 Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
918 + mCurrentClient.getOwnerString());
919 }
920 }
921 if (mCurrentClient != null) {
922 if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
923 mCurrentClient = null;
924 }
925 if (mPendingClient == null) {
926 notifyClientActiveCallbacks(false);
927 }
928 }
929
930 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700931 * Populates existing authenticator ids. To be used only during the start of the service.
932 */
933 protected void loadAuthenticatorIds() {
934 // This operation can be expensive, so keep track of the elapsed time. Might need to move to
935 // background if it takes too long.
936 long t = System.currentTimeMillis();
937 mAuthenticatorIds.clear();
938 for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
939 int userId = getUserOrWorkProfileId(null, user.id);
940 if (!mAuthenticatorIds.containsKey(userId)) {
941 updateActiveGroup(userId, null);
942 }
943 }
944
945 t = System.currentTimeMillis() - t;
946 if (t > 1000) {
947 Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
948 }
949 }
950
951 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700952 * @param clientPackage the package of the caller
953 * @return the profile id
954 */
955 protected int getUserOrWorkProfileId(String clientPackage, int userId) {
956 if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
957 return userId;
958 }
959 return getEffectiveUserId(userId);
960 }
961
962 protected boolean isRestricted() {
963 // Only give privileged apps (like Settings) access to biometric info
964 final boolean restricted = !hasPermission(getManageBiometricPermission());
965 return restricted;
966 }
967
968 protected boolean hasPermission(String permission) {
969 return getContext().checkCallingOrSelfPermission(permission)
970 == PackageManager.PERMISSION_GRANTED;
971 }
972
973 protected void checkPermission(String permission) {
974 getContext().enforceCallingOrSelfPermission(permission,
975 "Must have " + permission + " permission.");
976 }
977
978 protected boolean isCurrentUserOrProfile(int userId) {
979 UserManager um = UserManager.get(mContext);
980 if (um == null) {
981 Slog.e(getTag(), "Unable to acquire UserManager");
982 return false;
983 }
984
985 final long token = Binder.clearCallingIdentity();
986 try {
987 // Allow current user or profiles of the current user...
988 for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
989 if (profileId == userId) {
990 return true;
991 }
992 }
993 } finally {
994 Binder.restoreCallingIdentity(token);
995 }
996
997 return false;
998 }
999
Kevin Chyna56dff72018-06-19 18:41:12 -07001000 /***
1001 * @param opPackageName the name of the calling package
1002 * @return authenticator id for the calling user
1003 */
1004 protected long getAuthenticatorId(String opPackageName) {
1005 final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
1006 return mAuthenticatorIds.getOrDefault(userId, 0L);
1007 }
1008
Kevin Chyn037c4d52018-06-11 19:17:32 -07001009 private void scheduleLockoutResetForUser(int userId) {
1010 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1011 SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
1012 getLockoutResetIntentForUser(userId));
1013 }
1014
1015 private PendingIntent getLockoutResetIntentForUser(int userId) {
1016 return PendingIntent.getBroadcast(mContext, userId,
1017 new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
1018 PendingIntent.FLAG_UPDATE_CURRENT);
1019 }
1020
1021 private void userActivity() {
1022 long now = SystemClock.uptimeMillis();
1023 mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
1024 }
1025
1026 /**
1027 * @param userId
1028 * @return true if this is a work profile
1029 */
1030 private boolean isWorkProfile(int userId) {
1031 UserInfo userInfo = null;
1032 final long token = Binder.clearCallingIdentity();
1033 try {
1034 userInfo = mUserManager.getUserInfo(userId);
1035 } finally {
1036 Binder.restoreCallingIdentity(token);
1037 }
1038 return userInfo != null && userInfo.isManagedProfile();
1039 }
1040
1041
1042 private int getEffectiveUserId(int userId) {
1043 UserManager um = UserManager.get(mContext);
1044 if (um != null) {
1045 final long callingIdentity = Binder.clearCallingIdentity();
1046 userId = um.getCredentialOwnerProfile(userId);
1047 Binder.restoreCallingIdentity(callingIdentity);
1048 } else {
1049 Slog.e(getTag(), "Unable to acquire UserManager");
1050 }
1051 return userId;
1052 }
1053
1054 // Attempt counter should only be cleared when Keyguard goes away or when
1055 // a biometric is successfully authenticated.
1056 private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
1057 if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
1058 Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
1059 }
1060 if (clearAttemptCounter) {
1061 mFailedAttempts.put(userId, 0);
1062 }
1063 mTimedLockoutCleared.put(userId, true);
1064 // If we're asked to reset failed attempts externally (i.e. from Keyguard),
1065 // the alarm might still be pending; remove it.
1066 cancelLockoutResetForUser(userId);
1067 notifyLockoutResetMonitors();
1068 }
1069
1070 private void cancelLockoutResetForUser(int userId) {
1071 mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
1072 }
1073
1074 private void listenForUserSwitches() {
1075 try {
1076 ActivityManager.getService().registerUserSwitchObserver(
1077 new SynchronousUserSwitchObserver() {
1078 @Override
1079 public void onUserSwitching(int newUserId) throws RemoteException {
1080 mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
1081 .sendToTarget();
1082 }
1083 }, getTag());
1084 } catch (RemoteException e) {
1085 Slog.w(getTag(), "Failed to listen for user switching event" ,e);
1086 }
1087 }
1088
Kevin Chyna56dff72018-06-19 18:41:12 -07001089 private void notifyLockoutResetMonitors() {
1090 for (int i = 0; i < mLockoutMonitors.size(); i++) {
1091 mLockoutMonitors.get(i).sendLockoutReset();
1092 }
1093 }
1094
1095 private void removeLockoutResetCallback(
1096 LockoutResetMonitor monitor) {
1097 mLockoutMonitors.remove(monitor);
1098 }
Kevin Chyn037c4d52018-06-11 19:17:32 -07001099}