blob: d7877581831f0d397209c3e8d7374c3731aec371 [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
Kevin Chyn05c21502018-09-18 13:07:19 -070014 * limitations under the License.
Kevin Chyn037c4d52018-06-11 19:17:32 -070015 */
16
Kevin Chyn836f2cf2018-08-27 11:06:39 -070017package com.android.server.biometrics;
Kevin Chyn037c4d52018-06-11 19:17:32 -070018
19import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
20
21import android.app.ActivityManager;
22import android.app.ActivityTaskManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070023import android.app.AppOpsManager;
24import android.app.IActivityTaskManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070025import android.app.SynchronousUserSwitchObserver;
26import android.app.TaskStackListener;
Kevin Chyn037c4d52018-06-11 19:17:32 -070027import android.content.ComponentName;
28import android.content.Context;
Kevin Chyn037c4d52018-06-11 19:17:32 -070029import android.content.pm.PackageManager;
30import android.content.pm.UserInfo;
31import android.hardware.biometrics.BiometricAuthenticator;
32import android.hardware.biometrics.BiometricConstants;
Kevin Chyn7782d142019-01-18 12:51:33 -080033import android.hardware.biometrics.BiometricsProtoEnums;
Kevin Chyne92cdae2018-11-21 16:35:04 -080034import android.hardware.biometrics.IBiometricService;
Kevin Chyna56dff72018-06-19 18:41:12 -070035import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
Kevin Chyn23289ef2018-11-28 16:32:36 -080036import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Kevin Chyna56dff72018-06-19 18:41:12 -070037import android.hardware.fingerprint.Fingerprint;
Kevin Chyn037c4d52018-06-11 19:17:32 -070038import android.os.Binder;
39import android.os.Bundle;
Kevin Chyna56dff72018-06-19 18:41:12 -070040import android.os.DeadObjectException;
Kevin Chyn037c4d52018-06-11 19:17:32 -070041import android.os.Handler;
42import android.os.IBinder;
43import android.os.IHwBinder;
Kevin Chyna56dff72018-06-19 18:41:12 -070044import android.os.IRemoteCallback;
Kevin Chyn037c4d52018-06-11 19:17:32 -070045import android.os.PowerManager;
46import android.os.RemoteException;
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -070047import android.os.ServiceManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070048import android.os.SystemClock;
49import android.os.UserHandle;
50import android.os.UserManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070051import android.util.Slog;
Kevin Chyn7782d142019-01-18 12:51:33 -080052import android.util.StatsLog;
Kevin Chyn037c4d52018-06-11 19:17:32 -070053
54import com.android.internal.logging.MetricsLogger;
55import com.android.internal.statusbar.IStatusBarService;
56import com.android.server.SystemService;
Kevin Chyn037c4d52018-06-11 19:17:32 -070057
58import java.util.ArrayList;
Kevin Chyna56dff72018-06-19 18:41:12 -070059import java.util.Collections;
Kevin Chyn037c4d52018-06-11 19:17:32 -070060import java.util.HashMap;
61import java.util.List;
Kevin Chyna56dff72018-06-19 18:41:12 -070062import java.util.Map;
Kevin Chyn037c4d52018-06-11 19:17:32 -070063
64/**
65 * Abstract base class containing all of the business logic for biometric services, e.g.
66 * Fingerprint, Face, Iris.
67 *
68 * @hide
69 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -070070public abstract class BiometricServiceBase extends SystemService
71 implements IHwBinder.DeathRecipient {
Kevin Chyn037c4d52018-06-11 19:17:32 -070072
73 protected static final boolean DEBUG = true;
74
Kevin Chyn6737c572019-02-08 16:10:54 -080075 private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
Kevin Chyn037c4d52018-06-11 19:17:32 -070076 private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
77 private static final int MSG_USER_SWITCHING = 10;
Kevin Chyn037c4d52018-06-11 19:17:32 -070078 private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
79
80 private final Context mContext;
81 private final String mKeyguardPackage;
Kevin Chyn037c4d52018-06-11 19:17:32 -070082 private final IActivityTaskManager mActivityTaskManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070083 private final PowerManager mPowerManager;
84 private final UserManager mUserManager;
85 private final MetricsLogger mMetricsLogger;
86 private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
87 private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
Kevin Chyna56dff72018-06-19 18:41:12 -070088 private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
Kevin Chyn037c4d52018-06-11 19:17:32 -070089
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -070090 protected final IStatusBarService mStatusBarService;
Kevin Chyna56dff72018-06-19 18:41:12 -070091 protected final Map<Integer, Long> mAuthenticatorIds =
92 Collections.synchronizedMap(new HashMap<>());
Kevin Chynb3c05aa2018-09-21 16:50:32 -070093 protected final AppOpsManager mAppOps;
Kevin Chyn037c4d52018-06-11 19:17:32 -070094 protected final H mHandler = new H();
95
Kevin Chyn6737c572019-02-08 16:10:54 -080096 private final IBinder mToken = new Binder(); // Used for internal enumeration
97 private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
98
Kevin Chyne92cdae2018-11-21 16:35:04 -080099 private IBiometricService mBiometricService;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700100 private ClientMonitor mCurrentClient;
101 private ClientMonitor mPendingClient;
102 private PerformanceStats mPerformanceStats;
103 protected int mCurrentUserId = UserHandle.USER_NULL;
Kevin Chyn6737c572019-02-08 16:10:54 -0800104 protected long mHalDeviceId;
Tej Singhd6d6d772018-09-05 17:41:25 -0700105 // Tracks if the current authentication makes use of CryptoObjects.
106 protected boolean mIsCrypto;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700107 // Normal authentications are tracked by mPerformanceMap.
108 protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
109 // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
110 protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
Kevin Chyn9ba99912019-01-16 16:24:36 -0800111 protected int mHALDeathCount;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700112
113 protected class PerformanceStats {
114 public int accept; // number of accepted biometrics
115 public int reject; // number of rejected biometrics
116 public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
117 // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
118 public int lockout; // total number of lockouts
119 public int permanentLockout; // total number of permanent lockouts
120 }
121
122 /**
123 * @return the log tag.
124 */
125 protected abstract String getTag();
126
127 /**
Kevin Chyn6737c572019-02-08 16:10:54 -0800128 * @return wrapper for the HAL
129 */
130 protected abstract DaemonWrapper getDaemonWrapper();
131
132 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700133 * @return the biometric utilities for a specific implementation.
134 */
135 protected abstract BiometricUtils getBiometricUtils();
136
137 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700138 * @return the metrics constants for a biometric implementation.
139 */
140 protected abstract Metrics getMetrics();
141
142 /**
143 * @param userId
144 * @return true if the enrollment limit has been reached.
145 */
146 protected abstract boolean hasReachedEnrollmentLimit(int userId);
147
148 /**
149 * Notifies the HAL that the user has changed.
150 * @param userId
151 * @param clientPackage
152 */
153 protected abstract void updateActiveGroup(int userId, String clientPackage);
154
155 /**
156 * @return The protected intent to reset lockout for a specific biometric.
157 */
158 protected abstract String getLockoutResetIntent();
159
160 /**
161 * @return The permission the sender is required to have in order for the lockout reset intent
162 * to be received by the BiometricService implementation.
163 */
164 protected abstract String getLockoutBroadcastPermission();
165
166 /**
167 * @return The HAL ID.
168 */
169 protected abstract long getHalDeviceId();
170
171 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700172 * @param userId
173 * @return Returns true if the user has any enrolled biometrics.
174 */
175 protected abstract boolean hasEnrolledBiometrics(int userId);
176
177 /**
178 * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
179 * etc.
180 */
181 protected abstract String getManageBiometricPermission();
182
183 /**
184 * Checks if the caller has permission to use the biometric service - throws a SecurityException
185 * if not.
186 */
187 protected abstract void checkUseBiometricPermission();
188
189 /**
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700190 * Checks if the caller passes the app ops check
Kevin Chyn037c4d52018-06-11 19:17:32 -0700191 */
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700192 protected abstract boolean checkAppOps(int uid, String opPackageName);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700193
Kevin Chyn6737c572019-02-08 16:10:54 -0800194 protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
195 int userId);
196
Kevin Chyn037c4d52018-06-11 19:17:32 -0700197 /**
198 * Notifies clients of any change in the biometric state (active / idle). This is mainly for
199 * Fingerprint navigation gestures.
200 * @param isActive
201 */
202 protected void notifyClientActiveCallbacks(boolean isActive) {}
203
Kevin Chyn7782d142019-01-18 12:51:33 -0800204 protected abstract int statsModality();
205
Kevin Chyna38653c2019-02-11 17:46:21 -0800206 /**
207 * @return one of the AuthenticationClient LOCKOUT constants
208 */
209 protected abstract int getLockoutMode();
210
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700211 protected abstract class AuthenticationClientImpl extends AuthenticationClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700212
Kevin Chyn7782d142019-01-18 12:51:33 -0800213 // Used to check if the public API that was invoked was from FingerprintManager. Only
214 // to be overridden by FingerprintService.
215 protected boolean isFingerprint() {
216 return false;
217 }
218
Kevin Chyn037c4d52018-06-11 19:17:32 -0700219 public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
220 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800221 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800222 super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800223 groupId, opId, restricted, owner, cookie, requireConfirmation);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700224 }
225
226 @Override
Kevin Chyn7782d142019-01-18 12:51:33 -0800227 protected int statsClient() {
228 if (isKeyguard(getOwnerString())) {
229 return BiometricsProtoEnums.CLIENT_KEYGUARD;
230 } else if (isBiometricPrompt()) {
231 return BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
232 } else if (isFingerprint()) {
233 return BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
234 } else {
235 return BiometricsProtoEnums.CLIENT_UNKNOWN;
236 }
237 }
238
239 @Override
Kevin Chyn037c4d52018-06-11 19:17:32 -0700240 public void onStart() {
241 try {
242 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
243 } catch (RemoteException e) {
244 Slog.e(getTag(), "Could not register task stack listener", e);
245 }
246 }
247
248 @Override
249 public void onStop() {
250 try {
251 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
252 } catch (RemoteException e) {
253 Slog.e(getTag(), "Could not unregister task stack listener", e);
254 }
255 }
256
257 @Override
Kevin Chyn037c4d52018-06-11 19:17:32 -0700258 public void notifyUserActivity() {
259 userActivity();
260 }
261
262 @Override
263 public int handleFailedAttempt() {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700264 final int lockoutMode = getLockoutMode();
265 if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
266 mPerformanceStats.permanentLockout++;
267 } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
268 mPerformanceStats.lockout++;
269 }
270
271 // Failing multiple times will continue to push out the lockout time
272 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700273 return lockoutMode;
274 }
275 return AuthenticationClient.LOCKOUT_NONE;
276 }
277 }
278
Kevin Chyn1429a312019-01-28 16:08:09 -0800279 protected abstract class EnrollClientImpl extends EnrollClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700280
281 public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
282 IBinder token, ServiceListener listener, int userId, int groupId,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800283 byte[] cryptoToken, boolean restricted, String owner,
284 final int[] disabledFeatures) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700285 super(context, getMetrics(), daemon, halDeviceId, token, listener,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800286 userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
287 disabledFeatures);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700288 }
289
290 @Override
291 public void notifyUserActivity() {
292 userActivity();
293 }
294 }
295
Kevin Chyn6737c572019-02-08 16:10:54 -0800296 /**
297 * An internal class to help clean up unknown templates in HAL and Framework
298 */
299 private final class InternalRemovalClient extends RemovalClient {
300 InternalRemovalClient(Context context,
301 DaemonWrapper daemon, long halDeviceId, IBinder token,
302 ServiceListener listener, int templateId, int groupId, int userId,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700303 boolean restricted, String owner) {
Kevin Chyn6737c572019-02-08 16:10:54 -0800304 super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700305 userId, restricted, owner, getBiometricUtils());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700306 }
307
Kevin Chyn037c4d52018-06-11 19:17:32 -0700308 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800309 protected int statsModality() {
310 return BiometricServiceBase.this.statsModality();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700311 }
312 }
313
Kevin Chyn6737c572019-02-08 16:10:54 -0800314 /**
315 * Internal class to help clean up unknown templates in the HAL and Framework
316 */
317 private final class InternalEnumerateClient extends EnumerateClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700318
Kevin Chyn6737c572019-02-08 16:10:54 -0800319 private BiometricUtils mUtils;
320 // List of templates that are known to the Framework. Remove from this list when enumerate
321 // returns a template that contains a match.
322 private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
323 // List of templates to remove from the HAL
324 private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
325
326 InternalEnumerateClient(Context context,
327 DaemonWrapper daemon, long halDeviceId, IBinder token,
328 ServiceListener listener, int groupId, int userId, boolean restricted,
329 String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
330 BiometricUtils utils) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700331 super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
332 restricted, owner);
Kevin Chyn6737c572019-02-08 16:10:54 -0800333 mEnrolledList = enrolledList;
334 mUtils = utils;
335 }
336
337 private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
338 if (identifier == null) {
339 return;
340 }
341 Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
342 boolean matched = false;
343 for (int i = 0; i < mEnrolledList.size(); i++) {
344 if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
345 mEnrolledList.remove(i);
346 matched = true;
347 break;
348 }
349 }
350
351 // TemplateId 0 means no templates in HAL
352 if (!matched && identifier.getBiometricId() != 0) {
353 mUnknownHALTemplates.add(identifier);
354 }
355 Slog.v(getTag(), "Matched: " + matched);
356 }
357
358 private void doTemplateCleanup() {
359 if (mEnrolledList == null) {
360 return;
361 }
362
363 // At this point, mEnrolledList only contains templates known to the framework and
364 // not the HAL.
365 for (int i = 0; i < mEnrolledList.size(); i++) {
366 BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
367 Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: "
368 + identifier.getBiometricId() + " "
369 + identifier.getName());
370 mUtils.removeBiometricForUser(getContext(),
371 getTargetUserId(), identifier.getBiometricId());
372 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
373 statsModality(),
374 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
375 }
376 mEnrolledList.clear();
377 }
378
379 public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
380 return mUnknownHALTemplates;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700381 }
382
383 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800384 public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
385 int remaining) {
386 handleEnumeratedTemplate(identifier);
387 if (remaining == 0) {
388 doTemplateCleanup();
389 }
390 return remaining == 0;
391 }
392
393 @Override
394 protected int statsModality() {
395 return BiometricServiceBase.this.statsModality();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700396 }
397 }
398
399 /**
400 * Wraps the callback interface from Service -> Manager
401 */
402 protected interface ServiceListener {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700403 default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
404 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700405
Kevin Chyne92cdae2018-11-21 16:35:04 -0800406 void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700407
Kevin Chyne92cdae2018-11-21 16:35:04 -0800408 default void onAuthenticationSucceeded(long deviceId,
409 BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
410 throw new UnsupportedOperationException("Stub!");
411 }
412
413 default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
414 throws RemoteException {
415 throw new UnsupportedOperationException("Stub!");
416 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700417
Kevin Chyn23289ef2018-11-28 16:32:36 -0800418 default void onAuthenticationFailed(long deviceId) throws RemoteException {
419 throw new UnsupportedOperationException("Stub!");
420 }
421
422 default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
423 throws RemoteException {
424 throw new UnsupportedOperationException("Stub!");
425 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700426
Kevin Chyn87f257a2018-11-27 16:26:07 -0800427 void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700428
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700429 default void onRemoved(BiometricAuthenticator.Identifier identifier,
430 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700431
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700432 default void onEnumerated(BiometricAuthenticator.Identifier identifier,
433 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700434 }
435
436 /**
Kevin Chyne92cdae2018-11-21 16:35:04 -0800437 * Wraps the callback interface from Service -> BiometricPrompt
438 */
439 protected abstract class BiometricServiceListener implements ServiceListener {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800440 private IBiometricServiceReceiverInternal mWrapperReceiver;
Kevin Chyne92cdae2018-11-21 16:35:04 -0800441
Kevin Chyn23289ef2018-11-28 16:32:36 -0800442 public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800443 mWrapperReceiver = wrapperReceiver;
444 }
445
Kevin Chyn23289ef2018-11-28 16:32:36 -0800446 public IBiometricServiceReceiverInternal getWrapperReceiver() {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800447 return mWrapperReceiver;
448 }
449
Kevin Chyne92cdae2018-11-21 16:35:04 -0800450 @Override
451 public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
452 throws RemoteException {
453 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800454 getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800455 }
456 }
457
458 @Override
Kevin Chyn23289ef2018-11-28 16:32:36 -0800459 public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
460 throws RemoteException {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800461 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800462 getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800463 }
464 }
465 }
466
467 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700468 * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
469 * subclasses.
470 */
471 protected interface DaemonWrapper {
Kevin Chyn6737c572019-02-08 16:10:54 -0800472 int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
Kevin Chyn037c4d52018-06-11 19:17:32 -0700473 int authenticate(long operationId, int groupId) throws RemoteException;
474 int cancel() throws RemoteException;
475 int remove(int groupId, int biometricId) throws RemoteException;
476 int enumerate() throws RemoteException;
Kevin Chyna38653c2019-02-11 17:46:21 -0800477 int enroll(byte[] token, int groupId, int timeout,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800478 ArrayList<Integer> disabledFeatures) throws RemoteException;
Kevin Chyna38653c2019-02-11 17:46:21 -0800479 void resetLockout(byte[] token) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700480 }
481
482 /**
483 * Handler which all subclasses should post events to.
484 */
485 protected final class H extends Handler {
486 @Override
487 public void handleMessage(android.os.Message msg) {
488 switch (msg.what) {
489 case MSG_USER_SWITCHING:
490 handleUserSwitching(msg.arg1);
491 break;
492
493 default:
494 Slog.w(getTag(), "Unknown message:" + msg.what);
495 }
496 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700497 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700498
499 private final class BiometricTaskStackListener extends TaskStackListener {
500 @Override
501 public void onTaskStackChanged() {
502 try {
503 if (!(mCurrentClient instanceof AuthenticationClient)) {
504 return;
505 }
506 final String currentClient = mCurrentClient.getOwnerString();
507 if (isKeyguard(currentClient)) {
508 return; // Keyguard is always allowed
509 }
510 List<ActivityManager.RunningTaskInfo> runningTasks =
511 mActivityTaskManager.getTasks(1);
512 if (!runningTasks.isEmpty()) {
513 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
Kevin Chync79856b2018-10-05 18:57:35 -0700514 if (!topPackage.contentEquals(currentClient)
515 && !mCurrentClient.isAlreadyDone()) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700516 Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
517 + " currentClient: " + currentClient);
518 mCurrentClient.stop(false /* initiatedByClient */);
519 }
520 }
521 } catch (RemoteException e) {
522 Slog.e(getTag(), "Unable to get running tasks", e);
523 }
524 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700525 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700526
527 private final class ResetClientStateRunnable implements Runnable {
528 @Override
529 public void run() {
530 /**
531 * Warning: if we get here, the driver never confirmed our call to cancel the current
532 * operation (authenticate, enroll, remove, enumerate, etc), which is
533 * really bad. The result will be a 3-second delay in starting each new client.
534 * If you see this on a device, make certain the driver notifies with
535 * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
536 * once it has successfully switched to the IDLE state in the HAL.
537 * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
538 * in response to an actual cancel() call.
539 */
540 Slog.w(getTag(), "Client "
541 + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
542 + " failed to respond to cancel, starting client "
543 + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
544
Kevin Chynd0b8b1f2019-02-28 13:41:31 -0800545 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
546 statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
547
Kevin Chyn037c4d52018-06-11 19:17:32 -0700548 mCurrentClient = null;
549 startClient(mPendingClient, false);
550 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700551 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700552
Kevin Chyn037c4d52018-06-11 19:17:32 -0700553
Kevin Chyna56dff72018-06-19 18:41:12 -0700554
555 private final class LockoutResetMonitor implements IBinder.DeathRecipient {
556 private static final long WAKELOCK_TIMEOUT_MS = 2000;
557 private final IBiometricServiceLockoutResetCallback mCallback;
558 private final PowerManager.WakeLock mWakeLock;
559
560 public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
561 mCallback = callback;
562 mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
563 "lockout reset callback");
564 try {
565 mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
566 } catch (RemoteException e) {
567 Slog.w(getTag(), "caught remote exception in linkToDeath", e);
568 }
569 }
570
571 public void sendLockoutReset() {
572 if (mCallback != null) {
573 try {
574 mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
575 mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
576 @Override
577 public void sendResult(Bundle data) throws RemoteException {
578 releaseWakelock();
579 }
580 });
581 } catch (DeadObjectException e) {
582 Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
583 mHandler.post(mRemoveCallbackRunnable);
584 } catch (RemoteException e) {
585 Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
586 releaseWakelock();
587 }
588 }
589 }
590
591 private final Runnable mRemoveCallbackRunnable = new Runnable() {
592 @Override
593 public void run() {
594 releaseWakelock();
595 removeLockoutResetCallback(LockoutResetMonitor.this);
596 }
597 };
598
599 @Override
600 public void binderDied() {
601 Slog.e(getTag(), "Lockout reset callback binder died");
602 mHandler.post(mRemoveCallbackRunnable);
603 }
604
605 private void releaseWakelock() {
606 if (mWakeLock.isHeld()) {
607 mWakeLock.release();
608 }
609 }
610 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700611
612 /**
Kevin Chyn6737c572019-02-08 16:10:54 -0800613 * Container for enumerated templates. Used to keep track when cleaning up unknown
614 * templates.
615 */
616 private final class UserTemplate {
617 final BiometricAuthenticator.Identifier mIdentifier;
618 final int mUserId;
619 UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
620 this.mIdentifier = identifier;
621 this.mUserId = userId;
622 }
623 }
624
625 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700626 * Initializes the system service.
627 * <p>
628 * Subclasses must define a single argument constructor that accepts the context
629 * and passes it to super.
630 * </p>
631 *
632 * @param context The system server context.
633 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -0700634 public BiometricServiceBase(Context context) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700635 super(context);
636 mContext = context;
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -0700637 mStatusBarService = IStatusBarService.Stub.asInterface(
638 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Kevin Chyn037c4d52018-06-11 19:17:32 -0700639 mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
640 com.android.internal.R.string.config_keyguardComponent)).getPackageName();
641 mAppOps = context.getSystemService(AppOpsManager.class);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700642 mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
643 Context.ACTIVITY_TASK_SERVICE)).getService();
644 mPowerManager = mContext.getSystemService(PowerManager.class);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700645 mUserManager = UserManager.get(mContext);
646 mMetricsLogger = new MetricsLogger();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700647 }
648
649 @Override
650 public void onStart() {
651 listenForUserSwitches();
652 }
653
654 @Override
655 public void serviceDied(long cookie) {
656 Slog.e(getTag(), "HAL died");
657 mMetricsLogger.count(getMetrics().tagHalDied(), 1);
Kevin Chyn9ba99912019-01-16 16:24:36 -0800658 mHALDeathCount++;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700659 handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
660 0 /*vendorCode */);
Kevin Chyn7782d142019-01-18 12:51:33 -0800661
Kevin Chyne5a37fb2019-02-08 13:10:36 -0800662 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(),
663 BiometricsProtoEnums.ISSUE_HAL_DEATH);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700664 }
665
666 protected ClientMonitor getCurrentClient() {
667 return mCurrentClient;
668 }
669
670 protected ClientMonitor getPendingClient() {
671 return mPendingClient;
672 }
673
674 /**
675 * Callback handlers from the daemon. The caller must put this on a handler.
676 */
677
678 protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
679 ClientMonitor client = mCurrentClient;
680 if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
681 removeClient(client);
682 }
683 if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
684 && client instanceof AuthenticationClient) {
685 // ignore enrollment acquisitions or acquisitions when we're locked out
686 mPerformanceStats.acquire++;
687 }
688 }
689
Kevin Chynb528d692018-07-20 11:53:14 -0700690 protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700691 ArrayList<Byte> token) {
692 ClientMonitor client = mCurrentClient;
Kevin Chynb528d692018-07-20 11:53:14 -0700693 final boolean authenticated = identifier.getBiometricId() != 0;
694
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700695 if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700696 removeClient(client);
697 }
Kevin Chynb528d692018-07-20 11:53:14 -0700698 if (authenticated) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700699 mPerformanceStats.accept++;
700 } else {
701 mPerformanceStats.reject++;
702 }
703 }
704
Kevin Chyna56dff72018-06-19 18:41:12 -0700705 protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
706 int remaining) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700707 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700708 if (client != null && client.onEnrollResult(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700709 removeClient(client);
710 // When enrollment finishes, update this group's authenticator id, as the HAL has
711 // already generated a new authenticator id when the new biometric is enrolled.
Kevin Chyna56dff72018-06-19 18:41:12 -0700712 if (identifier instanceof Fingerprint) {
713 updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
714 } else {
715 updateActiveGroup(mCurrentUserId, null);
716 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700717 }
718 }
719
720 protected void handleError(long deviceId, int error, int vendorCode) {
721 final ClientMonitor client = mCurrentClient;
722
723 if (DEBUG) Slog.v(getTag(), "handleError(client="
724 + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
725
Kevin Chyn6737c572019-02-08 16:10:54 -0800726 if (client instanceof InternalRemovalClient
727 || client instanceof InternalEnumerateClient) {
728 clearEnumerateState();
729 }
730
Kevin Chyna56dff72018-06-19 18:41:12 -0700731 if (client != null && client.onError(deviceId, error, vendorCode)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700732 removeClient(client);
733 }
734
735 if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
736 mHandler.removeCallbacks(mResetClientState);
737 if (mPendingClient != null) {
Kevin Chyn355c6bf2018-09-20 22:14:19 -0700738 if (DEBUG) Slog.v(getTag(), "start pending client " +
739 mPendingClient.getOwnerString());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700740 startClient(mPendingClient, false);
741 mPendingClient = null;
742 }
743 }
744 }
745
Kevin Chyna56dff72018-06-19 18:41:12 -0700746 protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700747 final int remaining) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700748 if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
749 + ", dev=" + identifier.getDeviceId()
Kevin Chyn037c4d52018-06-11 19:17:32 -0700750 + ", rem=" + remaining);
751
752 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700753 if (client != null && client.onRemoved(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700754 removeClient(client);
755 // When the last biometric of a group is removed, update the authenticator id
Kevin Chyna56dff72018-06-19 18:41:12 -0700756 int userId = mCurrentUserId;
757 if (identifier instanceof Fingerprint) {
758 userId = ((Fingerprint) identifier).getGroupId();
759 }
760 if (!hasEnrolledBiometrics(userId)) {
761 updateActiveGroup(userId, null);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700762 }
763 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800764
765 if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) {
766 startCleanupUnknownHALTemplates();
767 } else if (client instanceof InternalRemovalClient) {
768 clearEnumerateState();
769 }
770 }
771
772 protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
773 ClientMonitor client = getCurrentClient();
774
775 client.onEnumerationResult(identifier, remaining);
776
777 // All templates in the HAL for this user were enumerated
778 if (remaining == 0) {
779 if (client instanceof InternalEnumerateClient) {
780 List<BiometricAuthenticator.Identifier> unknownHALTemplates =
781 ((InternalEnumerateClient) client).getUnknownHALTemplates();
782
783 if (!unknownHALTemplates.isEmpty()) {
784 Slog.w(getTag(), "Adding " + unknownHALTemplates.size()
785 + " templates for deletion");
786 }
787 for (int i = 0; i < unknownHALTemplates.size(); i++) {
788 mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
789 client.getTargetUserId()));
790 }
791 removeClient(client);
792 startCleanupUnknownHALTemplates();
793 } else {
794 removeClient(client);
795 }
796 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700797 }
798
799 /**
800 * Calls from the Manager. These are still on the calling binder's thread.
801 */
802
803 protected void enrollInternal(EnrollClientImpl client, int userId) {
804 if (hasReachedEnrollmentLimit(userId)) {
805 return;
806 }
807
808 // Group ID is arbitrarily set to parent profile user ID. It just represents
809 // the default biometrics for the user.
810 if (!isCurrentUserOrProfile(userId)) {
811 return;
812 }
813
814 mHandler.post(() -> {
815 startClient(client, true /* initiatedByClient */);
816 });
817 }
818
819 protected void cancelEnrollmentInternal(IBinder token) {
820 mHandler.post(() -> {
821 ClientMonitor client = mCurrentClient;
822 if (client instanceof EnrollClient && client.getToken() == token) {
Kevin Chynd0b8b1f2019-02-28 13:41:31 -0800823 if (DEBUG) Slog.v(getTag(), "Cancelling enrollment");
Kevin Chyn037c4d52018-06-11 19:17:32 -0700824 client.stop(client.getToken() == token);
825 }
826 });
827 }
828
829 protected void authenticateInternal(AuthenticationClientImpl client, long opId,
830 String opPackageName) {
831 final int callingUid = Binder.getCallingUid();
832 final int callingPid = Binder.getCallingPid();
833 final int callingUserId = UserHandle.getCallingUserId();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700834 authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId);
835 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700836
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700837 protected void authenticateInternal(AuthenticationClientImpl client, long opId,
838 String opPackageName, int callingUid, int callingPid, int callingUserId) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700839 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
840 callingUserId)) {
841 if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
842 return;
843 }
844
845 mHandler.post(() -> {
846 mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
847
848 // Get performance stats object for this user.
849 HashMap<Integer, PerformanceStats> pmap
850 = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
851 PerformanceStats stats = pmap.get(mCurrentUserId);
852 if (stats == null) {
853 stats = new PerformanceStats();
854 pmap.put(mCurrentUserId, stats);
855 }
856 mPerformanceStats = stats;
Tej Singhd6d6d772018-09-05 17:41:25 -0700857 mIsCrypto = (opId != 0);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700858
859 startAuthentication(client, opPackageName);
860 });
861 }
862
863 protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
864 final int callingUid = Binder.getCallingUid();
865 final int callingPid = Binder.getCallingPid();
866 final int callingUserId = UserHandle.getCallingUserId();
Kevin Chyne92cdae2018-11-21 16:35:04 -0800867 cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
868 true /* fromClient */);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700869 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700870
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700871 protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
Kevin Chyne92cdae2018-11-21 16:35:04 -0800872 int callingUid, int callingPid, int callingUserId, boolean fromClient) {
873 if (fromClient) {
874 // Only check this if cancel was called from the client (app). If cancel was called
875 // from BiometricService, it means the dialog was dismissed due to user interaction.
876 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
877 callingUserId)) {
878 if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
879 return;
880 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700881 }
882
883 mHandler.post(() -> {
884 ClientMonitor client = mCurrentClient;
885 if (client instanceof AuthenticationClient) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800886 if (client.getToken() == token || !fromClient) {
887 if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
888 + ", fromClient: " + fromClient);
889 // If cancel was from BiometricService, it means the dialog was dismissed
890 // and authentication should be canceled.
Kevin Chyn037c4d52018-06-11 19:17:32 -0700891 client.stop(client.getToken() == token);
892 } else {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800893 if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
894 + " since tokens don't match. fromClient: " + fromClient);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700895 }
896 } else if (client != null) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800897 if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
Kevin Chyn037c4d52018-06-11 19:17:32 -0700898 + client.getOwnerString());
899 }
900 });
901 }
902
903 protected void setActiveUserInternal(int userId) {
904 mHandler.post(() -> {
905 updateActiveGroup(userId, null /* clientPackage */);
906 });
907 }
908
Kevin Chyn6737c572019-02-08 16:10:54 -0800909 protected void removeInternal(RemovalClient client) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700910 mHandler.post(() -> {
911 startClient(client, true /* initiatedByClient */);
912 });
913 }
914
Kevin Chyn6737c572019-02-08 16:10:54 -0800915 protected void enumerateInternal(EnumerateClient client) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700916 mHandler.post(() -> {
917 startClient(client, true /* initiatedByClient */);
918 });
919 }
920
921 // Should be done on a handler thread - not on the Binder's thread.
922 private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700923 if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
924
925 int lockoutMode = getLockoutMode();
926 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800927 Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
Kevin Chyn037c4d52018-06-11 19:17:32 -0700928 int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
929 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
930 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
Kevin Chyna56dff72018-06-19 18:41:12 -0700931 if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700932 Slog.w(getTag(), "Cannot send permanent lockout message to client");
933 }
934 return;
935 }
936 startClient(client, true /* initiatedByClient */);
937 }
938
Kevin Chyna56dff72018-06-19 18:41:12 -0700939 protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
940 mHandler.post(() -> {
941 final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
942 if (!mLockoutMonitors.contains(monitor)) {
943 mLockoutMonitors.add(monitor);
944 }
945 });
946 }
947
Kevin Chyn037c4d52018-06-11 19:17:32 -0700948 /**
949 * Helper methods.
950 */
951
952 /**
953 * @param opPackageName name of package for caller
954 * @param requireForeground only allow this call while app is in the foreground
955 * @return true if caller can use the biometric API
956 */
957 protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
958 int pid, int userId) {
959 checkUseBiometricPermission();
960
961 if (isKeyguard(opPackageName)) {
962 return true; // Keyguard is always allowed
963 }
964 if (!isCurrentUserOrProfile(userId)) {
965 Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
966 return false;
967 }
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700968 if (!checkAppOps(uid, opPackageName)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700969 Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
970 return false;
971 }
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700972
Kevin Chyn037c4d52018-06-11 19:17:32 -0700973 if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
974 opPackageName))) {
975 Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
976 return false;
977 }
978 return true;
979 }
980
981 /**
982 * @param opPackageName package of the caller
983 * @return true if this is the same client currently using the biometric
984 */
985 private boolean isCurrentClient(String opPackageName) {
986 return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
987 }
988
989 /**
990 * @return true if this is keyguard package
991 */
992 private boolean isKeyguard(String clientPackage) {
993 return mKeyguardPackage.equals(clientPackage);
994 }
995
Kevin Chyn037c4d52018-06-11 19:17:32 -0700996 private boolean isForegroundActivity(int uid, int pid) {
997 try {
998 List<ActivityManager.RunningAppProcessInfo> procs =
999 ActivityManager.getService().getRunningAppProcesses();
1000 int N = procs.size();
1001 for (int i = 0; i < N; i++) {
1002 ActivityManager.RunningAppProcessInfo proc = procs.get(i);
1003 if (proc.pid == pid && proc.uid == uid
1004 && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
1005 return true;
1006 }
1007 }
1008 } catch (RemoteException e) {
1009 Slog.w(getTag(), "am.getRunningAppProcesses() failed");
1010 }
1011 return false;
1012 }
1013
1014 /**
1015 * Calls the HAL to switch states to the new task. If there's already a current task,
1016 * it calls cancel() and sets mPendingClient to begin when the current task finishes
1017 * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
1018 *
1019 * @param newClient the new client that wants to connect
1020 * @param initiatedByClient true for authenticate, remove and enroll
1021 */
1022 private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
1023 ClientMonitor currentClient = mCurrentClient;
1024 if (currentClient != null) {
1025 if (DEBUG) Slog.v(getTag(), "request stop current client " +
1026 currentClient.getOwnerString());
Kevin Chyn037c4d52018-06-11 19:17:32 -07001027 // This check only matters for FingerprintService, since enumerate may call back
1028 // multiple times.
Kevin Chyn6737c572019-02-08 16:10:54 -08001029 if (currentClient instanceof InternalEnumerateClient
1030 || currentClient instanceof InternalRemovalClient) {
Kevin Chyn037c4d52018-06-11 19:17:32 -07001031 // This condition means we're currently running internal diagnostics to
Kevin Chyn6737c572019-02-08 16:10:54 -08001032 // remove extra templates in the hardware and/or the software
Kevin Chyn037c4d52018-06-11 19:17:32 -07001033 // TODO: design an escape hatch in case client never finishes
1034 if (newClient != null) {
1035 Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
1036 + newClient.getClass().getSuperclass().getSimpleName()
1037 + "(" + newClient.getOwnerString() + ")"
1038 + ", initiatedByClient = " + initiatedByClient);
1039 }
1040 } else {
1041 currentClient.stop(initiatedByClient);
1042 }
1043 mPendingClient = newClient;
1044 mHandler.removeCallbacks(mResetClientState);
1045 mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
1046 } else if (newClient != null) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001047 // For BiometricPrompt clients, do not start until
1048 // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
1049 // modalities are ready before initiating authentication.
1050 if (newClient instanceof AuthenticationClient) {
1051 AuthenticationClient client = (AuthenticationClient) newClient;
1052 if (client.isBiometricPrompt()) {
1053 if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
1054 mCurrentClient = newClient;
1055 if (mBiometricService == null) {
1056 mBiometricService = IBiometricService.Stub.asInterface(
1057 ServiceManager.getService(Context.BIOMETRIC_SERVICE));
1058 }
1059 try {
1060 mBiometricService.onReadyForAuthentication(client.getCookie(),
1061 client.getRequireConfirmation(), client.getTargetUserId());
1062 } catch (RemoteException e) {
1063 Slog.e(getTag(), "Remote exception", e);
1064 }
1065 return;
Kevin Chyne92cdae2018-11-21 16:35:04 -08001066 }
1067 }
1068
Kevin Chyn87f257a2018-11-27 16:26:07 -08001069 // We are not a BiometricPrompt client, start the client immediately
1070 mCurrentClient = newClient;
1071 startCurrentClient(mCurrentClient.getCookie());
Kevin Chyn037c4d52018-06-11 19:17:32 -07001072 }
1073 }
1074
Kevin Chyn87f257a2018-11-27 16:26:07 -08001075 protected void startCurrentClient(int cookie) {
1076 if (mCurrentClient == null) {
1077 Slog.e(getTag(), "Trying to start null client!");
1078 return;
1079 }
1080 if (DEBUG) Slog.v(getTag(), "starting client "
1081 + mCurrentClient.getClass().getSuperclass().getSimpleName()
1082 + "(" + mCurrentClient.getOwnerString() + ")"
1083 + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
1084 if (cookie != mCurrentClient.getCookie()) {
1085 Slog.e(getTag(), "Mismatched cookie");
1086 return;
1087 }
1088 notifyClientActiveCallbacks(true);
1089 mCurrentClient.start();
1090 }
1091
Kevin Chyn037c4d52018-06-11 19:17:32 -07001092 protected void removeClient(ClientMonitor client) {
1093 if (client != null) {
1094 client.destroy();
1095 if (client != mCurrentClient && mCurrentClient != null) {
1096 Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
1097 + mCurrentClient.getOwnerString());
1098 }
1099 }
1100 if (mCurrentClient != null) {
1101 if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
1102 mCurrentClient = null;
1103 }
1104 if (mPendingClient == null) {
1105 notifyClientActiveCallbacks(false);
1106 }
1107 }
1108
1109 /**
Kevin Chyna56dff72018-06-19 18:41:12 -07001110 * Populates existing authenticator ids. To be used only during the start of the service.
1111 */
1112 protected void loadAuthenticatorIds() {
1113 // This operation can be expensive, so keep track of the elapsed time. Might need to move to
1114 // background if it takes too long.
1115 long t = System.currentTimeMillis();
1116 mAuthenticatorIds.clear();
1117 for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
1118 int userId = getUserOrWorkProfileId(null, user.id);
1119 if (!mAuthenticatorIds.containsKey(userId)) {
1120 updateActiveGroup(userId, null);
1121 }
1122 }
1123
1124 t = System.currentTimeMillis() - t;
1125 if (t > 1000) {
1126 Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
1127 }
1128 }
1129
1130 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -07001131 * @param clientPackage the package of the caller
1132 * @return the profile id
1133 */
1134 protected int getUserOrWorkProfileId(String clientPackage, int userId) {
1135 if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
1136 return userId;
1137 }
1138 return getEffectiveUserId(userId);
1139 }
1140
1141 protected boolean isRestricted() {
1142 // Only give privileged apps (like Settings) access to biometric info
1143 final boolean restricted = !hasPermission(getManageBiometricPermission());
1144 return restricted;
1145 }
1146
1147 protected boolean hasPermission(String permission) {
1148 return getContext().checkCallingOrSelfPermission(permission)
1149 == PackageManager.PERMISSION_GRANTED;
1150 }
1151
1152 protected void checkPermission(String permission) {
1153 getContext().enforceCallingOrSelfPermission(permission,
1154 "Must have " + permission + " permission.");
1155 }
1156
1157 protected boolean isCurrentUserOrProfile(int userId) {
1158 UserManager um = UserManager.get(mContext);
1159 if (um == null) {
1160 Slog.e(getTag(), "Unable to acquire UserManager");
1161 return false;
1162 }
1163
1164 final long token = Binder.clearCallingIdentity();
1165 try {
1166 // Allow current user or profiles of the current user...
1167 for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
1168 if (profileId == userId) {
1169 return true;
1170 }
1171 }
1172 } finally {
1173 Binder.restoreCallingIdentity(token);
1174 }
1175
1176 return false;
1177 }
1178
Kevin Chyna56dff72018-06-19 18:41:12 -07001179 /***
1180 * @param opPackageName the name of the calling package
1181 * @return authenticator id for the calling user
1182 */
1183 protected long getAuthenticatorId(String opPackageName) {
1184 final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
1185 return mAuthenticatorIds.getOrDefault(userId, 0L);
1186 }
1187
Kevin Chyn6737c572019-02-08 16:10:54 -08001188 /**
1189 * This method should be called upon connection to the daemon, and when user switches.
1190 * @param userId
1191 */
1192 protected void doTemplateCleanupForUser(int userId) {
1193 if (CLEANUP_UNKNOWN_TEMPLATES) {
1194 enumerateUser(userId);
1195 }
1196 }
1197
1198 private void clearEnumerateState() {
1199 if (DEBUG) Slog.v(getTag(), "clearEnumerateState()");
1200 mUnknownHALTemplates.clear();
1201 }
1202
1203 /**
1204 * Remove unknown templates from HAL
1205 */
1206 private void startCleanupUnknownHALTemplates() {
1207 if (!mUnknownHALTemplates.isEmpty()) {
1208 UserTemplate template = mUnknownHALTemplates.get(0);
1209 mUnknownHALTemplates.remove(template);
1210 boolean restricted = !hasPermission(getManageBiometricPermission());
1211 InternalRemovalClient client = new InternalRemovalClient(getContext(),
1212 getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */,
1213 template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
1214 restricted, getContext().getPackageName());
1215 removeInternal(client);
1216 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
1217 statsModality(),
1218 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
1219 } else {
1220 clearEnumerateState();
1221 }
1222 }
1223
1224 private void enumerateUser(int userId) {
1225 if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")");
1226
1227 final boolean restricted = !hasPermission(getManageBiometricPermission());
1228 final List<? extends BiometricAuthenticator.Identifier> enrolledList =
1229 getEnrolledTemplates(userId);
1230
1231 InternalEnumerateClient client = new InternalEnumerateClient(getContext(),
1232 getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId,
1233 userId, restricted, getContext().getOpPackageName(), enrolledList,
1234 getBiometricUtils());
1235 enumerateInternal(client);
1236 }
1237
1238 /**
1239 * This method is called when the user switches. Implementations should probably notify the
1240 * HAL.
1241 */
Kevin Chyna38653c2019-02-11 17:46:21 -08001242 protected void handleUserSwitching(int userId) {
Kevin Chyn6737c572019-02-08 16:10:54 -08001243 if (getCurrentClient() instanceof InternalRemovalClient
1244 || getCurrentClient() instanceof InternalEnumerateClient) {
1245 Slog.w(getTag(), "User switched while performing cleanup");
1246 removeClient(getCurrentClient());
1247 clearEnumerateState();
1248 }
1249 updateActiveGroup(userId, null);
1250 doTemplateCleanupForUser(userId);
1251 }
1252
Kevin Chyna38653c2019-02-11 17:46:21 -08001253 protected void notifyLockoutResetMonitors() {
1254 for (int i = 0; i < mLockoutMonitors.size(); i++) {
1255 mLockoutMonitors.get(i).sendLockoutReset();
1256 }
Kevin Chyn037c4d52018-06-11 19:17:32 -07001257 }
1258
1259 private void userActivity() {
1260 long now = SystemClock.uptimeMillis();
1261 mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
1262 }
1263
1264 /**
1265 * @param userId
1266 * @return true if this is a work profile
1267 */
1268 private boolean isWorkProfile(int userId) {
1269 UserInfo userInfo = null;
1270 final long token = Binder.clearCallingIdentity();
1271 try {
1272 userInfo = mUserManager.getUserInfo(userId);
1273 } finally {
1274 Binder.restoreCallingIdentity(token);
1275 }
1276 return userInfo != null && userInfo.isManagedProfile();
1277 }
1278
1279
1280 private int getEffectiveUserId(int userId) {
1281 UserManager um = UserManager.get(mContext);
1282 if (um != null) {
1283 final long callingIdentity = Binder.clearCallingIdentity();
1284 userId = um.getCredentialOwnerProfile(userId);
1285 Binder.restoreCallingIdentity(callingIdentity);
1286 } else {
1287 Slog.e(getTag(), "Unable to acquire UserManager");
1288 }
1289 return userId;
1290 }
1291
Kevin Chyn037c4d52018-06-11 19:17:32 -07001292
1293 private void listenForUserSwitches() {
1294 try {
1295 ActivityManager.getService().registerUserSwitchObserver(
1296 new SynchronousUserSwitchObserver() {
1297 @Override
1298 public void onUserSwitching(int newUserId) throws RemoteException {
1299 mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
1300 .sendToTarget();
1301 }
1302 }, getTag());
1303 } catch (RemoteException e) {
1304 Slog.w(getTag(), "Failed to listen for user switching event" ,e);
1305 }
1306 }
1307
Kevin Chyna56dff72018-06-19 18:41:12 -07001308 private void removeLockoutResetCallback(
1309 LockoutResetMonitor monitor) {
1310 mLockoutMonitors.remove(monitor);
1311 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001312}