blob: f54baefa71612ed50f86a80e413e6457f92f54a6 [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
Kevin Chynb528d692018-07-20 11:53:14 -0700580 protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700581 ArrayList<Byte> token) {
582 ClientMonitor client = mCurrentClient;
Kevin Chynb528d692018-07-20 11:53:14 -0700583 final boolean authenticated = identifier.getBiometricId() != 0;
584
585 if (authenticated) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700586 final byte[] byteToken = new byte[token.size()];
587 for (int i = 0; i < token.size(); i++) {
588 byteToken[i] = token.get(i);
589 }
590 KeyStore.getInstance().addAuthToken(byteToken);
591 }
Kevin Chynb528d692018-07-20 11:53:14 -0700592 if (client != null && client.onAuthenticated(identifier, authenticated)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700593 removeClient(client);
594 }
Kevin Chynb528d692018-07-20 11:53:14 -0700595 if (authenticated) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700596 mPerformanceStats.accept++;
597 } else {
598 mPerformanceStats.reject++;
599 }
600 }
601
Kevin Chyna56dff72018-06-19 18:41:12 -0700602 protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
603 int remaining) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700604 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700605 if (client != null && client.onEnrollResult(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700606 removeClient(client);
607 // When enrollment finishes, update this group's authenticator id, as the HAL has
608 // already generated a new authenticator id when the new biometric is enrolled.
Kevin Chyna56dff72018-06-19 18:41:12 -0700609 if (identifier instanceof Fingerprint) {
610 updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
611 } else {
612 updateActiveGroup(mCurrentUserId, null);
613 }
614
Kevin Chyn037c4d52018-06-11 19:17:32 -0700615 }
616 }
617
618 protected void handleError(long deviceId, int error, int vendorCode) {
619 final ClientMonitor client = mCurrentClient;
620
621 if (DEBUG) Slog.v(getTag(), "handleError(client="
622 + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
623
Kevin Chyna56dff72018-06-19 18:41:12 -0700624 if (client != null && client.onError(deviceId, error, vendorCode)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700625 removeClient(client);
626 }
627
628 if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
629 mHandler.removeCallbacks(mResetClientState);
630 if (mPendingClient != null) {
631 if (DEBUG) Slog.v(getTag(), "start pending client " + mPendingClient.getOwnerString());
632 startClient(mPendingClient, false);
633 mPendingClient = null;
634 }
635 }
636 }
637
Kevin Chyna56dff72018-06-19 18:41:12 -0700638 protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700639 final int remaining) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700640 if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
641 + ", dev=" + identifier.getDeviceId()
Kevin Chyn037c4d52018-06-11 19:17:32 -0700642 + ", rem=" + remaining);
643
644 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700645 if (client != null && client.onRemoved(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700646 removeClient(client);
647 // When the last biometric of a group is removed, update the authenticator id
Kevin Chyna56dff72018-06-19 18:41:12 -0700648 int userId = mCurrentUserId;
649 if (identifier instanceof Fingerprint) {
650 userId = ((Fingerprint) identifier).getGroupId();
651 }
652 if (!hasEnrolledBiometrics(userId)) {
653 updateActiveGroup(userId, null);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700654 }
655 }
656 }
657
658 /**
659 * Calls from the Manager. These are still on the calling binder's thread.
660 */
661
662 protected void enrollInternal(EnrollClientImpl client, int userId) {
663 if (hasReachedEnrollmentLimit(userId)) {
664 return;
665 }
666
667 // Group ID is arbitrarily set to parent profile user ID. It just represents
668 // the default biometrics for the user.
669 if (!isCurrentUserOrProfile(userId)) {
670 return;
671 }
672
673 mHandler.post(() -> {
674 startClient(client, true /* initiatedByClient */);
675 });
676 }
677
678 protected void cancelEnrollmentInternal(IBinder token) {
679 mHandler.post(() -> {
680 ClientMonitor client = mCurrentClient;
681 if (client instanceof EnrollClient && client.getToken() == token) {
682 client.stop(client.getToken() == token);
683 }
684 });
685 }
686
687 protected void authenticateInternal(AuthenticationClientImpl client, long opId,
688 String opPackageName) {
689 final int callingUid = Binder.getCallingUid();
690 final int callingPid = Binder.getCallingPid();
691 final int callingUserId = UserHandle.getCallingUserId();
692
693 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
694 callingUserId)) {
695 if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
696 return;
697 }
698
699 mHandler.post(() -> {
700 mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
701
702 // Get performance stats object for this user.
703 HashMap<Integer, PerformanceStats> pmap
704 = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
705 PerformanceStats stats = pmap.get(mCurrentUserId);
706 if (stats == null) {
707 stats = new PerformanceStats();
708 pmap.put(mCurrentUserId, stats);
709 }
710 mPerformanceStats = stats;
711
712 startAuthentication(client, opPackageName);
713 });
714 }
715
716 protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
717 final int callingUid = Binder.getCallingUid();
718 final int callingPid = Binder.getCallingPid();
719 final int callingUserId = UserHandle.getCallingUserId();
720
721 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
722 callingUserId)) {
723 if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
724 return;
725 }
726
727 mHandler.post(() -> {
728 ClientMonitor client = mCurrentClient;
729 if (client instanceof AuthenticationClient) {
730 if (client.getToken() == token) {
731 if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
732 client.stop(client.getToken() == token);
733 } else {
734 if (DEBUG) Slog.v(getTag(), "can't stop client "
735 + client.getOwnerString() + " since tokens don't match");
736 }
737 } else if (client != null) {
738 if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
739 + client.getOwnerString());
740 }
741 });
742 }
743
744 protected void setActiveUserInternal(int userId) {
745 mHandler.post(() -> {
746 updateActiveGroup(userId, null /* clientPackage */);
747 });
748 }
749
750 protected void removeInternal(RemovalClientImpl client) {
751 mHandler.post(() -> {
752 startClient(client, true /* initiatedByClient */);
753 });
754 }
755
756 protected void enumerateInternal(EnumerateClientImpl client) {
757 mHandler.post(() -> {
758 startClient(client, true /* initiatedByClient */);
759 });
760 }
761
762 // Should be done on a handler thread - not on the Binder's thread.
763 private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
764 updateActiveGroup(client.getGroupId(), opPackageName);
765
766 if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
767
768 int lockoutMode = getLockoutMode();
769 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
770 Slog.v(getTag(), "In lockout mode(" + lockoutMode +
771 ") ; disallowing authentication");
772 int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
773 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
774 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
Kevin Chyna56dff72018-06-19 18:41:12 -0700775 if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700776 Slog.w(getTag(), "Cannot send permanent lockout message to client");
777 }
778 return;
779 }
780 startClient(client, true /* initiatedByClient */);
781 }
782
Kevin Chyna56dff72018-06-19 18:41:12 -0700783 protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
784 mHandler.post(() -> {
785 final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
786 if (!mLockoutMonitors.contains(monitor)) {
787 mLockoutMonitors.add(monitor);
788 }
789 });
790 }
791
Kevin Chyn037c4d52018-06-11 19:17:32 -0700792 /**
793 * Helper methods.
794 */
795
796 /**
797 * @param opPackageName name of package for caller
798 * @param requireForeground only allow this call while app is in the foreground
799 * @return true if caller can use the biometric API
800 */
801 protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
802 int pid, int userId) {
803 checkUseBiometricPermission();
804
805 if (isKeyguard(opPackageName)) {
806 return true; // Keyguard is always allowed
807 }
808 if (!isCurrentUserOrProfile(userId)) {
809 Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
810 return false;
811 }
812 if (mAppOps.noteOp(getAppOp(), uid, opPackageName) != AppOpsManager.MODE_ALLOWED) {
813 Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
814 return false;
815 }
816 if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
817 opPackageName))) {
818 Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
819 return false;
820 }
821 return true;
822 }
823
824 /**
825 * @param opPackageName package of the caller
826 * @return true if this is the same client currently using the biometric
827 */
828 private boolean isCurrentClient(String opPackageName) {
829 return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
830 }
831
832 /**
833 * @return true if this is keyguard package
834 */
835 private boolean isKeyguard(String clientPackage) {
836 return mKeyguardPackage.equals(clientPackage);
837 }
838
839 private int getLockoutMode() {
840 final int currentUser = ActivityManager.getCurrentUser();
841 final int failedAttempts = mFailedAttempts.get(currentUser, 0);
842 if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
843 return AuthenticationClient.LOCKOUT_PERMANENT;
844 } else if (failedAttempts > 0 &&
845 mTimedLockoutCleared.get(currentUser, false) == false
846 && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
847 return AuthenticationClient.LOCKOUT_TIMED;
848 }
849 return AuthenticationClient.LOCKOUT_NONE;
850 }
851
852 private boolean isForegroundActivity(int uid, int pid) {
853 try {
854 List<ActivityManager.RunningAppProcessInfo> procs =
855 ActivityManager.getService().getRunningAppProcesses();
856 int N = procs.size();
857 for (int i = 0; i < N; i++) {
858 ActivityManager.RunningAppProcessInfo proc = procs.get(i);
859 if (proc.pid == pid && proc.uid == uid
860 && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
861 return true;
862 }
863 }
864 } catch (RemoteException e) {
865 Slog.w(getTag(), "am.getRunningAppProcesses() failed");
866 }
867 return false;
868 }
869
870 /**
871 * Calls the HAL to switch states to the new task. If there's already a current task,
872 * it calls cancel() and sets mPendingClient to begin when the current task finishes
873 * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
874 *
875 * @param newClient the new client that wants to connect
876 * @param initiatedByClient true for authenticate, remove and enroll
877 */
878 private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
879 ClientMonitor currentClient = mCurrentClient;
880 if (currentClient != null) {
881 if (DEBUG) Slog.v(getTag(), "request stop current client " +
882 currentClient.getOwnerString());
883
884 // This check only matters for FingerprintService, since enumerate may call back
885 // multiple times.
886 if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
887 currentClient instanceof FingerprintService.RemovalClientImpl) {
888 // This condition means we're currently running internal diagnostics to
889 // remove extra fingerprints in the hardware and/or the software
890 // TODO: design an escape hatch in case client never finishes
891 if (newClient != null) {
892 Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
893 + newClient.getClass().getSuperclass().getSimpleName()
894 + "(" + newClient.getOwnerString() + ")"
895 + ", initiatedByClient = " + initiatedByClient);
896 }
897 } else {
898 currentClient.stop(initiatedByClient);
899 }
900 mPendingClient = newClient;
901 mHandler.removeCallbacks(mResetClientState);
902 mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
903 } else if (newClient != null) {
904 mCurrentClient = newClient;
905 if (DEBUG) Slog.v(getTag(), "starting client "
906 + newClient.getClass().getSuperclass().getSimpleName()
907 + "(" + newClient.getOwnerString() + ")"
908 + ", initiatedByClient = " + initiatedByClient);
909 notifyClientActiveCallbacks(true);
910
911 newClient.start();
912 }
913 }
914
915 protected void removeClient(ClientMonitor client) {
916 if (client != null) {
917 client.destroy();
918 if (client != mCurrentClient && mCurrentClient != null) {
919 Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
920 + mCurrentClient.getOwnerString());
921 }
922 }
923 if (mCurrentClient != null) {
924 if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
925 mCurrentClient = null;
926 }
927 if (mPendingClient == null) {
928 notifyClientActiveCallbacks(false);
929 }
930 }
931
932 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700933 * Populates existing authenticator ids. To be used only during the start of the service.
934 */
935 protected void loadAuthenticatorIds() {
936 // This operation can be expensive, so keep track of the elapsed time. Might need to move to
937 // background if it takes too long.
938 long t = System.currentTimeMillis();
939 mAuthenticatorIds.clear();
940 for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
941 int userId = getUserOrWorkProfileId(null, user.id);
942 if (!mAuthenticatorIds.containsKey(userId)) {
943 updateActiveGroup(userId, null);
944 }
945 }
946
947 t = System.currentTimeMillis() - t;
948 if (t > 1000) {
949 Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
950 }
951 }
952
953 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700954 * @param clientPackage the package of the caller
955 * @return the profile id
956 */
957 protected int getUserOrWorkProfileId(String clientPackage, int userId) {
958 if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
959 return userId;
960 }
961 return getEffectiveUserId(userId);
962 }
963
964 protected boolean isRestricted() {
965 // Only give privileged apps (like Settings) access to biometric info
966 final boolean restricted = !hasPermission(getManageBiometricPermission());
967 return restricted;
968 }
969
970 protected boolean hasPermission(String permission) {
971 return getContext().checkCallingOrSelfPermission(permission)
972 == PackageManager.PERMISSION_GRANTED;
973 }
974
975 protected void checkPermission(String permission) {
976 getContext().enforceCallingOrSelfPermission(permission,
977 "Must have " + permission + " permission.");
978 }
979
980 protected boolean isCurrentUserOrProfile(int userId) {
981 UserManager um = UserManager.get(mContext);
982 if (um == null) {
983 Slog.e(getTag(), "Unable to acquire UserManager");
984 return false;
985 }
986
987 final long token = Binder.clearCallingIdentity();
988 try {
989 // Allow current user or profiles of the current user...
990 for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
991 if (profileId == userId) {
992 return true;
993 }
994 }
995 } finally {
996 Binder.restoreCallingIdentity(token);
997 }
998
999 return false;
1000 }
1001
Kevin Chyna56dff72018-06-19 18:41:12 -07001002 /***
1003 * @param opPackageName the name of the calling package
1004 * @return authenticator id for the calling user
1005 */
1006 protected long getAuthenticatorId(String opPackageName) {
1007 final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
1008 return mAuthenticatorIds.getOrDefault(userId, 0L);
1009 }
1010
Kevin Chyn037c4d52018-06-11 19:17:32 -07001011 private void scheduleLockoutResetForUser(int userId) {
1012 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1013 SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
1014 getLockoutResetIntentForUser(userId));
1015 }
1016
1017 private PendingIntent getLockoutResetIntentForUser(int userId) {
1018 return PendingIntent.getBroadcast(mContext, userId,
1019 new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
1020 PendingIntent.FLAG_UPDATE_CURRENT);
1021 }
1022
1023 private void userActivity() {
1024 long now = SystemClock.uptimeMillis();
1025 mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
1026 }
1027
1028 /**
1029 * @param userId
1030 * @return true if this is a work profile
1031 */
1032 private boolean isWorkProfile(int userId) {
1033 UserInfo userInfo = null;
1034 final long token = Binder.clearCallingIdentity();
1035 try {
1036 userInfo = mUserManager.getUserInfo(userId);
1037 } finally {
1038 Binder.restoreCallingIdentity(token);
1039 }
1040 return userInfo != null && userInfo.isManagedProfile();
1041 }
1042
1043
1044 private int getEffectiveUserId(int userId) {
1045 UserManager um = UserManager.get(mContext);
1046 if (um != null) {
1047 final long callingIdentity = Binder.clearCallingIdentity();
1048 userId = um.getCredentialOwnerProfile(userId);
1049 Binder.restoreCallingIdentity(callingIdentity);
1050 } else {
1051 Slog.e(getTag(), "Unable to acquire UserManager");
1052 }
1053 return userId;
1054 }
1055
1056 // Attempt counter should only be cleared when Keyguard goes away or when
1057 // a biometric is successfully authenticated.
1058 private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
1059 if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
1060 Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
1061 }
1062 if (clearAttemptCounter) {
1063 mFailedAttempts.put(userId, 0);
1064 }
1065 mTimedLockoutCleared.put(userId, true);
1066 // If we're asked to reset failed attempts externally (i.e. from Keyguard),
1067 // the alarm might still be pending; remove it.
1068 cancelLockoutResetForUser(userId);
1069 notifyLockoutResetMonitors();
1070 }
1071
1072 private void cancelLockoutResetForUser(int userId) {
1073 mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
1074 }
1075
1076 private void listenForUserSwitches() {
1077 try {
1078 ActivityManager.getService().registerUserSwitchObserver(
1079 new SynchronousUserSwitchObserver() {
1080 @Override
1081 public void onUserSwitching(int newUserId) throws RemoteException {
1082 mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
1083 .sendToTarget();
1084 }
1085 }, getTag());
1086 } catch (RemoteException e) {
1087 Slog.w(getTag(), "Failed to listen for user switching event" ,e);
1088 }
1089 }
1090
Kevin Chyna56dff72018-06-19 18:41:12 -07001091 private void notifyLockoutResetMonitors() {
1092 for (int i = 0; i < mLockoutMonitors.size(); i++) {
1093 mLockoutMonitors.get(i).sendLockoutReset();
1094 }
1095 }
1096
1097 private void removeLockoutResetCallback(
1098 LockoutResetMonitor monitor) {
1099 mLockoutMonitors.remove(monitor);
1100 }
Kevin Chyn037c4d52018-06-11 19:17:32 -07001101}