blob: 60f0e8e6c63de66de142aa08a66c86c3bcb8fc9b [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
Kevin Chyndbaecba2019-12-17 17:01:44 -080019import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
Kevin Chyn037c4d52018-06-11 19:17:32 -070020import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
21
22import android.app.ActivityManager;
23import android.app.ActivityTaskManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070024import android.app.AppOpsManager;
25import android.app.IActivityTaskManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070026import android.app.SynchronousUserSwitchObserver;
27import android.app.TaskStackListener;
Kevin Chyn037c4d52018-06-11 19:17:32 -070028import android.content.ComponentName;
29import android.content.Context;
Kevin Chyn037c4d52018-06-11 19:17:32 -070030import android.content.pm.PackageManager;
31import android.content.pm.UserInfo;
32import android.hardware.biometrics.BiometricAuthenticator;
33import android.hardware.biometrics.BiometricConstants;
Kevin Chyn7782d142019-01-18 12:51:33 -080034import android.hardware.biometrics.BiometricsProtoEnums;
Kevin Chyne92cdae2018-11-21 16:35:04 -080035import android.hardware.biometrics.IBiometricService;
Kevin Chyna56dff72018-06-19 18:41:12 -070036import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
Kevin Chyn23289ef2018-11-28 16:32:36 -080037import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Kevin Chyna56dff72018-06-19 18:41:12 -070038import android.hardware.fingerprint.Fingerprint;
Kevin Chyn037c4d52018-06-11 19:17:32 -070039import android.os.Binder;
40import android.os.Bundle;
Kevin Chyna56dff72018-06-19 18:41:12 -070041import android.os.DeadObjectException;
Kevin Chyn037c4d52018-06-11 19:17:32 -070042import android.os.Handler;
43import android.os.IBinder;
44import android.os.IHwBinder;
Kevin Chyna56dff72018-06-19 18:41:12 -070045import android.os.IRemoteCallback;
Kevin Chyn037c4d52018-06-11 19:17:32 -070046import android.os.PowerManager;
Kevin Chyn13648922019-03-11 20:28:38 -070047import android.os.Process;
Kevin Chyn037c4d52018-06-11 19:17:32 -070048import android.os.RemoteException;
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -070049import android.os.ServiceManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070050import android.os.SystemClock;
51import android.os.UserHandle;
52import android.os.UserManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070053import android.util.Slog;
Kevin Chyn7782d142019-01-18 12:51:33 -080054import android.util.StatsLog;
Kevin Chyn037c4d52018-06-11 19:17:32 -070055
56import com.android.internal.logging.MetricsLogger;
57import com.android.internal.statusbar.IStatusBarService;
58import com.android.server.SystemService;
Kevin Chyn037c4d52018-06-11 19:17:32 -070059
60import java.util.ArrayList;
Kevin Chyna56dff72018-06-19 18:41:12 -070061import java.util.Collections;
Kevin Chyn037c4d52018-06-11 19:17:32 -070062import java.util.HashMap;
63import java.util.List;
Kevin Chyna56dff72018-06-19 18:41:12 -070064import java.util.Map;
Kevin Chyn037c4d52018-06-11 19:17:32 -070065
66/**
67 * Abstract base class containing all of the business logic for biometric services, e.g.
68 * Fingerprint, Face, Iris.
69 *
70 * @hide
71 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -070072public abstract class BiometricServiceBase extends SystemService
73 implements IHwBinder.DeathRecipient {
Kevin Chyn037c4d52018-06-11 19:17:32 -070074
75 protected static final boolean DEBUG = true;
76
Kevin Chyn6737c572019-02-08 16:10:54 -080077 private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
Kevin Chyn037c4d52018-06-11 19:17:32 -070078 private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
79 private static final int MSG_USER_SWITCHING = 10;
Kevin Chyn037c4d52018-06-11 19:17:32 -070080 private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
81
82 private final Context mContext;
83 private final String mKeyguardPackage;
Kevin Chyn037c4d52018-06-11 19:17:32 -070084 private final IActivityTaskManager mActivityTaskManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070085 private final PowerManager mPowerManager;
86 private final UserManager mUserManager;
87 private final MetricsLogger mMetricsLogger;
88 private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
89 private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
Kevin Chyna56dff72018-06-19 18:41:12 -070090 private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
Kevin Chyn037c4d52018-06-11 19:17:32 -070091
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -070092 protected final IStatusBarService mStatusBarService;
Kevin Chyna56dff72018-06-19 18:41:12 -070093 protected final Map<Integer, Long> mAuthenticatorIds =
94 Collections.synchronizedMap(new HashMap<>());
Kevin Chynb3c05aa2018-09-21 16:50:32 -070095 protected final AppOpsManager mAppOps;
Kevin Chyn037c4d52018-06-11 19:17:32 -070096 protected final H mHandler = new H();
97
Kevin Chyn6737c572019-02-08 16:10:54 -080098 private final IBinder mToken = new Binder(); // Used for internal enumeration
99 private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
100
Kevin Chyne92cdae2018-11-21 16:35:04 -0800101 private IBiometricService mBiometricService;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700102 private ClientMonitor mCurrentClient;
103 private ClientMonitor mPendingClient;
104 private PerformanceStats mPerformanceStats;
105 protected int mCurrentUserId = UserHandle.USER_NULL;
Kevin Chyn6737c572019-02-08 16:10:54 -0800106 protected long mHalDeviceId;
Tej Singhd6d6d772018-09-05 17:41:25 -0700107 // Tracks if the current authentication makes use of CryptoObjects.
108 protected boolean mIsCrypto;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700109 // Normal authentications are tracked by mPerformanceMap.
110 protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
111 // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
112 protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
Kevin Chyn9ba99912019-01-16 16:24:36 -0800113 protected int mHALDeathCount;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700114
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 Chyn6737c572019-02-08 16:10:54 -0800130 * @return wrapper for the HAL
131 */
132 protected abstract DaemonWrapper getDaemonWrapper();
133
134 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700135 * @return the biometric utilities for a specific implementation.
136 */
137 protected abstract BiometricUtils getBiometricUtils();
138
139 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700140 * @return the metrics constants for a biometric implementation.
141 */
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700142 protected abstract Constants getConstants();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700143
144 /**
145 * @param userId
146 * @return true if the enrollment limit has been reached.
147 */
148 protected abstract boolean hasReachedEnrollmentLimit(int userId);
149
150 /**
151 * Notifies the HAL that the user has changed.
152 * @param userId
153 * @param clientPackage
154 */
155 protected abstract void updateActiveGroup(int userId, String clientPackage);
156
157 /**
158 * @return The protected intent to reset lockout for a specific biometric.
159 */
160 protected abstract String getLockoutResetIntent();
161
162 /**
163 * @return The permission the sender is required to have in order for the lockout reset intent
164 * to be received by the BiometricService implementation.
165 */
166 protected abstract String getLockoutBroadcastPermission();
167
168 /**
169 * @return The HAL ID.
170 */
171 protected abstract long getHalDeviceId();
172
173 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700174 * @param userId
175 * @return Returns true if the user has any enrolled biometrics.
176 */
177 protected abstract boolean hasEnrolledBiometrics(int userId);
178
179 /**
180 * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
181 * etc.
182 */
183 protected abstract String getManageBiometricPermission();
184
185 /**
186 * Checks if the caller has permission to use the biometric service - throws a SecurityException
187 * if not.
188 */
189 protected abstract void checkUseBiometricPermission();
190
191 /**
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700192 * Checks if the caller passes the app ops check
Kevin Chyn037c4d52018-06-11 19:17:32 -0700193 */
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700194 protected abstract boolean checkAppOps(int uid, String opPackageName);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700195
Kevin Chyn6737c572019-02-08 16:10:54 -0800196 protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
197 int userId);
198
Kevin Chyn037c4d52018-06-11 19:17:32 -0700199 /**
200 * Notifies clients of any change in the biometric state (active / idle). This is mainly for
201 * Fingerprint navigation gestures.
202 * @param isActive
203 */
204 protected void notifyClientActiveCallbacks(boolean isActive) {}
205
Kevin Chyn7782d142019-01-18 12:51:33 -0800206 protected abstract int statsModality();
207
Kevin Chyna38653c2019-02-11 17:46:21 -0800208 /**
209 * @return one of the AuthenticationClient LOCKOUT constants
210 */
211 protected abstract int getLockoutMode();
212
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700213 protected abstract class AuthenticationClientImpl extends AuthenticationClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700214
Kevin Chyn7782d142019-01-18 12:51:33 -0800215 // Used to check if the public API that was invoked was from FingerprintManager. Only
216 // to be overridden by FingerprintService.
217 protected boolean isFingerprint() {
218 return false;
219 }
220
Kevin Chyn037c4d52018-06-11 19:17:32 -0700221 public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
222 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800223 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700224 super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800225 groupId, opId, restricted, owner, cookie, requireConfirmation);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700226 }
227
228 @Override
Kevin Chyn7782d142019-01-18 12:51:33 -0800229 protected int statsClient() {
230 if (isKeyguard(getOwnerString())) {
231 return BiometricsProtoEnums.CLIENT_KEYGUARD;
232 } else if (isBiometricPrompt()) {
233 return BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
234 } else if (isFingerprint()) {
235 return BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
236 } else {
237 return BiometricsProtoEnums.CLIENT_UNKNOWN;
238 }
239 }
240
241 @Override
Kevin Chyn037c4d52018-06-11 19:17:32 -0700242 public void onStart() {
243 try {
244 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
245 } catch (RemoteException e) {
246 Slog.e(getTag(), "Could not register task stack listener", e);
247 }
248 }
249
250 @Override
251 public void onStop() {
252 try {
253 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
254 } catch (RemoteException e) {
255 Slog.e(getTag(), "Could not unregister task stack listener", e);
256 }
257 }
258
259 @Override
Kevin Chyn037c4d52018-06-11 19:17:32 -0700260 public void notifyUserActivity() {
261 userActivity();
262 }
263
264 @Override
265 public int handleFailedAttempt() {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700266 final int lockoutMode = getLockoutMode();
267 if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
268 mPerformanceStats.permanentLockout++;
269 } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
270 mPerformanceStats.lockout++;
271 }
272
273 // Failing multiple times will continue to push out the lockout time
274 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700275 return lockoutMode;
276 }
277 return AuthenticationClient.LOCKOUT_NONE;
278 }
279 }
280
Kevin Chyn1429a312019-01-28 16:08:09 -0800281 protected abstract class EnrollClientImpl extends EnrollClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700282
283 public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
284 IBinder token, ServiceListener listener, int userId, int groupId,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800285 byte[] cryptoToken, boolean restricted, String owner,
Curtis Belmonte45788542019-07-23 11:07:27 -0700286 final int[] disabledFeatures, int timeoutSec) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700287 super(context, getConstants(), daemon, halDeviceId, token, listener,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800288 userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
Curtis Belmonte45788542019-07-23 11:07:27 -0700289 disabledFeatures, timeoutSec);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700290 }
291
292 @Override
293 public void notifyUserActivity() {
294 userActivity();
295 }
296 }
297
Kevin Chyn6737c572019-02-08 16:10:54 -0800298 /**
299 * An internal class to help clean up unknown templates in HAL and Framework
300 */
301 private final class InternalRemovalClient extends RemovalClient {
302 InternalRemovalClient(Context context,
303 DaemonWrapper daemon, long halDeviceId, IBinder token,
304 ServiceListener listener, int templateId, int groupId, int userId,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700305 boolean restricted, String owner) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700306 super(context, getConstants(), daemon, halDeviceId, token, listener, templateId, groupId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700307 userId, restricted, owner, getBiometricUtils());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700308 }
309
Kevin Chyn037c4d52018-06-11 19:17:32 -0700310 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800311 protected int statsModality() {
312 return BiometricServiceBase.this.statsModality();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700313 }
314 }
315
Kevin Chyn6737c572019-02-08 16:10:54 -0800316 /**
317 * Internal class to help clean up unknown templates in the HAL and Framework
318 */
319 private final class InternalEnumerateClient extends EnumerateClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700320
Kevin Chyn6737c572019-02-08 16:10:54 -0800321 private BiometricUtils mUtils;
322 // List of templates that are known to the Framework. Remove from this list when enumerate
323 // returns a template that contains a match.
324 private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
325 // List of templates to remove from the HAL
326 private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
327
328 InternalEnumerateClient(Context context,
329 DaemonWrapper daemon, long halDeviceId, IBinder token,
330 ServiceListener listener, int groupId, int userId, boolean restricted,
331 String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
332 BiometricUtils utils) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700333 super(context, getConstants(), daemon, halDeviceId, token, listener, groupId, userId,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700334 restricted, owner);
Kevin Chyn6737c572019-02-08 16:10:54 -0800335 mEnrolledList = enrolledList;
336 mUtils = utils;
337 }
338
339 private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
340 if (identifier == null) {
341 return;
342 }
343 Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
344 boolean matched = false;
345 for (int i = 0; i < mEnrolledList.size(); i++) {
346 if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
347 mEnrolledList.remove(i);
348 matched = true;
349 break;
350 }
351 }
352
353 // TemplateId 0 means no templates in HAL
354 if (!matched && identifier.getBiometricId() != 0) {
355 mUnknownHALTemplates.add(identifier);
356 }
357 Slog.v(getTag(), "Matched: " + matched);
358 }
359
360 private void doTemplateCleanup() {
361 if (mEnrolledList == null) {
362 return;
363 }
364
365 // At this point, mEnrolledList only contains templates known to the framework and
366 // not the HAL.
367 for (int i = 0; i < mEnrolledList.size(); i++) {
368 BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
369 Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: "
370 + identifier.getBiometricId() + " "
371 + identifier.getName());
372 mUtils.removeBiometricForUser(getContext(),
373 getTargetUserId(), identifier.getBiometricId());
374 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
375 statsModality(),
376 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
377 }
378 mEnrolledList.clear();
379 }
380
381 public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
382 return mUnknownHALTemplates;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700383 }
384
385 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800386 public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
387 int remaining) {
388 handleEnumeratedTemplate(identifier);
389 if (remaining == 0) {
390 doTemplateCleanup();
391 }
392 return remaining == 0;
393 }
394
395 @Override
396 protected int statsModality() {
397 return BiometricServiceBase.this.statsModality();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700398 }
399 }
400
401 /**
402 * Wraps the callback interface from Service -> Manager
403 */
404 protected interface ServiceListener {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700405 default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
406 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700407
Kevin Chyne92cdae2018-11-21 16:35:04 -0800408 void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700409
Kevin Chyne92cdae2018-11-21 16:35:04 -0800410 default void onAuthenticationSucceeded(long deviceId,
411 BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
412 throw new UnsupportedOperationException("Stub!");
413 }
414
415 default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
416 throws RemoteException {
417 throw new UnsupportedOperationException("Stub!");
418 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700419
Kevin Chyn23289ef2018-11-28 16:32:36 -0800420 default void onAuthenticationFailed(long deviceId) throws RemoteException {
421 throw new UnsupportedOperationException("Stub!");
422 }
423
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700424 default void onAuthenticationFailedInternal()
Kevin Chyn23289ef2018-11-28 16:32:36 -0800425 throws RemoteException {
426 throw new UnsupportedOperationException("Stub!");
427 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700428
Kevin Chyn87f257a2018-11-27 16:26:07 -0800429 void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700430
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700431 default void onRemoved(BiometricAuthenticator.Identifier identifier,
432 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700433
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700434 default void onEnumerated(BiometricAuthenticator.Identifier identifier,
435 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700436 }
437
438 /**
Kevin Chyne92cdae2018-11-21 16:35:04 -0800439 * Wraps the callback interface from Service -> BiometricPrompt
440 */
441 protected abstract class BiometricServiceListener implements ServiceListener {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800442 private IBiometricServiceReceiverInternal mWrapperReceiver;
Kevin Chyne92cdae2018-11-21 16:35:04 -0800443
Kevin Chyn23289ef2018-11-28 16:32:36 -0800444 public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800445 mWrapperReceiver = wrapperReceiver;
446 }
447
Kevin Chyn23289ef2018-11-28 16:32:36 -0800448 public IBiometricServiceReceiverInternal getWrapperReceiver() {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800449 return mWrapperReceiver;
450 }
451
Kevin Chyne92cdae2018-11-21 16:35:04 -0800452 @Override
453 public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
454 throws RemoteException {
455 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800456 getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800457 }
458 }
459
460 @Override
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700461 public void onAuthenticationFailedInternal()
Kevin Chyn23289ef2018-11-28 16:32:36 -0800462 throws RemoteException {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800463 if (getWrapperReceiver() != null) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700464 getWrapperReceiver().onAuthenticationFailed();
Kevin Chyne92cdae2018-11-21 16:35:04 -0800465 }
466 }
467 }
468
469 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700470 * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
471 * subclasses.
472 */
473 protected interface DaemonWrapper {
Kevin Chyn6737c572019-02-08 16:10:54 -0800474 int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
Kevin Chyn037c4d52018-06-11 19:17:32 -0700475 int authenticate(long operationId, int groupId) throws RemoteException;
476 int cancel() throws RemoteException;
477 int remove(int groupId, int biometricId) throws RemoteException;
478 int enumerate() throws RemoteException;
Kevin Chyna38653c2019-02-11 17:46:21 -0800479 int enroll(byte[] token, int groupId, int timeout,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800480 ArrayList<Integer> disabledFeatures) throws RemoteException;
Kevin Chyna38653c2019-02-11 17:46:21 -0800481 void resetLockout(byte[] token) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700482 }
483
484 /**
485 * Handler which all subclasses should post events to.
486 */
487 protected final class H extends Handler {
488 @Override
489 public void handleMessage(android.os.Message msg) {
490 switch (msg.what) {
491 case MSG_USER_SWITCHING:
492 handleUserSwitching(msg.arg1);
493 break;
494
495 default:
496 Slog.w(getTag(), "Unknown message:" + msg.what);
497 }
498 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700499 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700500
Kevin Chynb2246222019-07-11 15:01:26 -0700501 private final Runnable mOnTaskStackChangedRunnable = new Runnable() {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700502 @Override
Kevin Chynb2246222019-07-11 15:01:26 -0700503 public void run() {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700504 try {
505 if (!(mCurrentClient instanceof AuthenticationClient)) {
506 return;
507 }
508 final String currentClient = mCurrentClient.getOwnerString();
509 if (isKeyguard(currentClient)) {
510 return; // Keyguard is always allowed
511 }
512 List<ActivityManager.RunningTaskInfo> runningTasks =
513 mActivityTaskManager.getTasks(1);
514 if (!runningTasks.isEmpty()) {
515 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
Kevin Chync79856b2018-10-05 18:57:35 -0700516 if (!topPackage.contentEquals(currentClient)
517 && !mCurrentClient.isAlreadyDone()) {
Kevin Chynb2246222019-07-11 15:01:26 -0700518 Slog.e(getTag(), "Stopping background authentication, top: "
519 + topPackage + " currentClient: " + currentClient);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700520 mCurrentClient.stop(false /* initiatedByClient */);
521 }
522 }
523 } catch (RemoteException e) {
524 Slog.e(getTag(), "Unable to get running tasks", e);
525 }
526 }
Kevin Chynb2246222019-07-11 15:01:26 -0700527 };
528
529 private final class BiometricTaskStackListener extends TaskStackListener {
530 @Override
531 public void onTaskStackChanged() {
532 mHandler.post(mOnTaskStackChangedRunnable);
533 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700534 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700535
536 private final class ResetClientStateRunnable implements Runnable {
537 @Override
538 public void run() {
539 /**
540 * Warning: if we get here, the driver never confirmed our call to cancel the current
541 * operation (authenticate, enroll, remove, enumerate, etc), which is
542 * really bad. The result will be a 3-second delay in starting each new client.
543 * If you see this on a device, make certain the driver notifies with
544 * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
545 * once it has successfully switched to the IDLE state in the HAL.
546 * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
547 * in response to an actual cancel() call.
548 */
549 Slog.w(getTag(), "Client "
550 + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
551 + " failed to respond to cancel, starting client "
552 + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
553
Kevin Chynd0b8b1f2019-02-28 13:41:31 -0800554 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
555 statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
556
Kevin Chyn037c4d52018-06-11 19:17:32 -0700557 mCurrentClient = null;
558 startClient(mPendingClient, false);
559 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700560 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700561
Kevin Chyn037c4d52018-06-11 19:17:32 -0700562
Kevin Chyna56dff72018-06-19 18:41:12 -0700563
564 private final class LockoutResetMonitor implements IBinder.DeathRecipient {
565 private static final long WAKELOCK_TIMEOUT_MS = 2000;
566 private final IBiometricServiceLockoutResetCallback mCallback;
567 private final PowerManager.WakeLock mWakeLock;
568
569 public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
570 mCallback = callback;
571 mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
572 "lockout reset callback");
573 try {
574 mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
575 } catch (RemoteException e) {
576 Slog.w(getTag(), "caught remote exception in linkToDeath", e);
577 }
578 }
579
580 public void sendLockoutReset() {
581 if (mCallback != null) {
582 try {
583 mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
584 mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
585 @Override
586 public void sendResult(Bundle data) throws RemoteException {
587 releaseWakelock();
588 }
589 });
590 } catch (DeadObjectException e) {
591 Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
592 mHandler.post(mRemoveCallbackRunnable);
593 } catch (RemoteException e) {
594 Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
595 releaseWakelock();
596 }
597 }
598 }
599
600 private final Runnable mRemoveCallbackRunnable = new Runnable() {
601 @Override
602 public void run() {
603 releaseWakelock();
604 removeLockoutResetCallback(LockoutResetMonitor.this);
605 }
606 };
607
608 @Override
609 public void binderDied() {
610 Slog.e(getTag(), "Lockout reset callback binder died");
611 mHandler.post(mRemoveCallbackRunnable);
612 }
613
614 private void releaseWakelock() {
615 if (mWakeLock.isHeld()) {
616 mWakeLock.release();
617 }
618 }
619 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700620
621 /**
Kevin Chyn6737c572019-02-08 16:10:54 -0800622 * Container for enumerated templates. Used to keep track when cleaning up unknown
623 * templates.
624 */
625 private final class UserTemplate {
626 final BiometricAuthenticator.Identifier mIdentifier;
627 final int mUserId;
628 UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
629 this.mIdentifier = identifier;
630 this.mUserId = userId;
631 }
632 }
633
634 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700635 * Initializes the system service.
636 * <p>
637 * Subclasses must define a single argument constructor that accepts the context
638 * and passes it to super.
639 * </p>
640 *
641 * @param context The system server context.
642 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -0700643 public BiometricServiceBase(Context context) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700644 super(context);
645 mContext = context;
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -0700646 mStatusBarService = IStatusBarService.Stub.asInterface(
647 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Kevin Chyn037c4d52018-06-11 19:17:32 -0700648 mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
649 com.android.internal.R.string.config_keyguardComponent)).getPackageName();
650 mAppOps = context.getSystemService(AppOpsManager.class);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700651 mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
652 Context.ACTIVITY_TASK_SERVICE)).getService();
653 mPowerManager = mContext.getSystemService(PowerManager.class);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700654 mUserManager = UserManager.get(mContext);
655 mMetricsLogger = new MetricsLogger();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700656 }
657
658 @Override
659 public void onStart() {
660 listenForUserSwitches();
661 }
662
663 @Override
664 public void serviceDied(long cookie) {
665 Slog.e(getTag(), "HAL died");
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700666 mMetricsLogger.count(getConstants().tagHalDied(), 1);
Kevin Chyn9ba99912019-01-16 16:24:36 -0800667 mHALDeathCount++;
Kevin Chyncaa2ea72019-04-02 18:28:12 -0700668 mCurrentUserId = UserHandle.USER_NULL;
Kevin Chyn2e312792019-07-18 13:37:23 -0700669
670 // All client lifecycle must be managed on the handler.
671 mHandler.post(() -> {
672 handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
673 0 /*vendorCode */);
674 });
Kevin Chyn7782d142019-01-18 12:51:33 -0800675
Kevin Chyne5a37fb2019-02-08 13:10:36 -0800676 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(),
677 BiometricsProtoEnums.ISSUE_HAL_DEATH);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700678 }
679
680 protected ClientMonitor getCurrentClient() {
681 return mCurrentClient;
682 }
683
684 protected ClientMonitor getPendingClient() {
685 return mPendingClient;
686 }
687
688 /**
689 * Callback handlers from the daemon. The caller must put this on a handler.
690 */
691
692 protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
693 ClientMonitor client = mCurrentClient;
694 if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
695 removeClient(client);
696 }
697 if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
698 && client instanceof AuthenticationClient) {
699 // ignore enrollment acquisitions or acquisitions when we're locked out
700 mPerformanceStats.acquire++;
701 }
702 }
703
Kevin Chynb528d692018-07-20 11:53:14 -0700704 protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700705 ArrayList<Byte> token) {
706 ClientMonitor client = mCurrentClient;
Kevin Chynb528d692018-07-20 11:53:14 -0700707 final boolean authenticated = identifier.getBiometricId() != 0;
708
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700709 if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700710 removeClient(client);
711 }
Kevin Chynb528d692018-07-20 11:53:14 -0700712 if (authenticated) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700713 mPerformanceStats.accept++;
714 } else {
715 mPerformanceStats.reject++;
716 }
717 }
718
Kevin Chyna56dff72018-06-19 18:41:12 -0700719 protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
720 int remaining) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700721 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700722 if (client != null && client.onEnrollResult(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700723 removeClient(client);
724 // When enrollment finishes, update this group's authenticator id, as the HAL has
725 // already generated a new authenticator id when the new biometric is enrolled.
Kevin Chyna56dff72018-06-19 18:41:12 -0700726 if (identifier instanceof Fingerprint) {
727 updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
Kevin Chyna56dff72018-06-19 18:41:12 -0700728 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700729 }
730 }
731
732 protected void handleError(long deviceId, int error, int vendorCode) {
733 final ClientMonitor client = mCurrentClient;
734
735 if (DEBUG) Slog.v(getTag(), "handleError(client="
736 + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
737
Kevin Chyn6737c572019-02-08 16:10:54 -0800738 if (client instanceof InternalRemovalClient
739 || client instanceof InternalEnumerateClient) {
740 clearEnumerateState();
741 }
742
Kevin Chyna56dff72018-06-19 18:41:12 -0700743 if (client != null && client.onError(deviceId, error, vendorCode)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700744 removeClient(client);
745 }
746
747 if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
748 mHandler.removeCallbacks(mResetClientState);
749 if (mPendingClient != null) {
Kevin Chyn355c6bf2018-09-20 22:14:19 -0700750 if (DEBUG) Slog.v(getTag(), "start pending client " +
751 mPendingClient.getOwnerString());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700752 startClient(mPendingClient, false);
753 mPendingClient = null;
754 }
755 }
756 }
757
Kevin Chyna56dff72018-06-19 18:41:12 -0700758 protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700759 final int remaining) {
Kevin Chyna56dff72018-06-19 18:41:12 -0700760 if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
761 + ", dev=" + identifier.getDeviceId()
Kevin Chyn037c4d52018-06-11 19:17:32 -0700762 + ", rem=" + remaining);
763
764 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700765 if (client != null && client.onRemoved(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700766 removeClient(client);
767 // When the last biometric of a group is removed, update the authenticator id
Kevin Chyna56dff72018-06-19 18:41:12 -0700768 int userId = mCurrentUserId;
769 if (identifier instanceof Fingerprint) {
770 userId = ((Fingerprint) identifier).getGroupId();
771 }
772 if (!hasEnrolledBiometrics(userId)) {
773 updateActiveGroup(userId, null);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700774 }
775 }
Kevin Chyn6737c572019-02-08 16:10:54 -0800776
777 if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) {
778 startCleanupUnknownHALTemplates();
779 } else if (client instanceof InternalRemovalClient) {
780 clearEnumerateState();
781 }
782 }
783
784 protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
785 ClientMonitor client = getCurrentClient();
786
787 client.onEnumerationResult(identifier, remaining);
788
789 // All templates in the HAL for this user were enumerated
790 if (remaining == 0) {
791 if (client instanceof InternalEnumerateClient) {
792 List<BiometricAuthenticator.Identifier> unknownHALTemplates =
793 ((InternalEnumerateClient) client).getUnknownHALTemplates();
794
795 if (!unknownHALTemplates.isEmpty()) {
796 Slog.w(getTag(), "Adding " + unknownHALTemplates.size()
797 + " templates for deletion");
798 }
799 for (int i = 0; i < unknownHALTemplates.size(); i++) {
800 mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
801 client.getTargetUserId()));
802 }
803 removeClient(client);
804 startCleanupUnknownHALTemplates();
805 } else {
806 removeClient(client);
807 }
808 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700809 }
810
811 /**
812 * Calls from the Manager. These are still on the calling binder's thread.
813 */
814
815 protected void enrollInternal(EnrollClientImpl client, int userId) {
816 if (hasReachedEnrollmentLimit(userId)) {
817 return;
818 }
819
820 // Group ID is arbitrarily set to parent profile user ID. It just represents
821 // the default biometrics for the user.
822 if (!isCurrentUserOrProfile(userId)) {
823 return;
824 }
825
826 mHandler.post(() -> {
827 startClient(client, true /* initiatedByClient */);
828 });
829 }
830
831 protected void cancelEnrollmentInternal(IBinder token) {
832 mHandler.post(() -> {
833 ClientMonitor client = mCurrentClient;
834 if (client instanceof EnrollClient && client.getToken() == token) {
Kevin Chynd0b8b1f2019-02-28 13:41:31 -0800835 if (DEBUG) Slog.v(getTag(), "Cancelling enrollment");
Kevin Chyn037c4d52018-06-11 19:17:32 -0700836 client.stop(client.getToken() == token);
837 }
838 });
839 }
840
841 protected void authenticateInternal(AuthenticationClientImpl client, long opId,
842 String opPackageName) {
843 final int callingUid = Binder.getCallingUid();
844 final int callingPid = Binder.getCallingPid();
845 final int callingUserId = UserHandle.getCallingUserId();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700846 authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId);
847 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700848
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700849 protected void authenticateInternal(AuthenticationClientImpl client, long opId,
850 String opPackageName, int callingUid, int callingPid, int callingUserId) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700851 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
852 callingUserId)) {
853 if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
854 return;
855 }
856
857 mHandler.post(() -> {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700858 mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700859
860 // Get performance stats object for this user.
861 HashMap<Integer, PerformanceStats> pmap
862 = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
863 PerformanceStats stats = pmap.get(mCurrentUserId);
864 if (stats == null) {
865 stats = new PerformanceStats();
866 pmap.put(mCurrentUserId, stats);
867 }
868 mPerformanceStats = stats;
Tej Singhd6d6d772018-09-05 17:41:25 -0700869 mIsCrypto = (opId != 0);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700870
871 startAuthentication(client, opPackageName);
872 });
873 }
874
875 protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
876 final int callingUid = Binder.getCallingUid();
877 final int callingPid = Binder.getCallingPid();
878 final int callingUserId = UserHandle.getCallingUserId();
Kevin Chyne92cdae2018-11-21 16:35:04 -0800879 cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
880 true /* fromClient */);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700881 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700882
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700883 protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
Kevin Chyne92cdae2018-11-21 16:35:04 -0800884 int callingUid, int callingPid, int callingUserId, boolean fromClient) {
885 if (fromClient) {
886 // Only check this if cancel was called from the client (app). If cancel was called
887 // from BiometricService, it means the dialog was dismissed due to user interaction.
888 if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
889 callingUserId)) {
890 if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
891 return;
892 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700893 }
894
895 mHandler.post(() -> {
896 ClientMonitor client = mCurrentClient;
897 if (client instanceof AuthenticationClient) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800898 if (client.getToken() == token || !fromClient) {
899 if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
900 + ", fromClient: " + fromClient);
901 // If cancel was from BiometricService, it means the dialog was dismissed
902 // and authentication should be canceled.
Kevin Chyn037c4d52018-06-11 19:17:32 -0700903 client.stop(client.getToken() == token);
904 } else {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800905 if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
906 + " since tokens don't match. fromClient: " + fromClient);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700907 }
908 } else if (client != null) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800909 if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
Kevin Chyn037c4d52018-06-11 19:17:32 -0700910 + client.getOwnerString());
911 }
912 });
913 }
914
915 protected void setActiveUserInternal(int userId) {
joshmccloskeya4772062019-07-24 17:13:16 -0700916 mHandler.post(() -> {
917 if (DEBUG) {
918 Slog.d(getTag(), "setActiveUser(" + userId + ")");
919 }
920 updateActiveGroup(userId, null /* clientPackage */);
921 });
Kevin Chyn037c4d52018-06-11 19:17:32 -0700922 }
923
Kevin Chyn6737c572019-02-08 16:10:54 -0800924 protected void removeInternal(RemovalClient client) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700925 mHandler.post(() -> {
926 startClient(client, true /* initiatedByClient */);
927 });
928 }
929
Kevin Chyn6737c572019-02-08 16:10:54 -0800930 protected void enumerateInternal(EnumerateClient client) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700931 mHandler.post(() -> {
932 startClient(client, true /* initiatedByClient */);
933 });
934 }
935
936 // Should be done on a handler thread - not on the Binder's thread.
937 private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700938 if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
939
940 int lockoutMode = getLockoutMode();
941 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800942 Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
Kevin Chyn037c4d52018-06-11 19:17:32 -0700943 int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
944 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
945 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
Kevin Chyna56dff72018-06-19 18:41:12 -0700946 if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700947 Slog.w(getTag(), "Cannot send permanent lockout message to client");
948 }
949 return;
950 }
951 startClient(client, true /* initiatedByClient */);
952 }
953
Kevin Chyna56dff72018-06-19 18:41:12 -0700954 protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
955 mHandler.post(() -> {
956 final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
957 if (!mLockoutMonitors.contains(monitor)) {
958 mLockoutMonitors.add(monitor);
959 }
960 });
961 }
962
Kevin Chyn037c4d52018-06-11 19:17:32 -0700963 /**
964 * Helper methods.
965 */
966
967 /**
968 * @param opPackageName name of package for caller
969 * @param requireForeground only allow this call while app is in the foreground
970 * @return true if caller can use the biometric API
971 */
972 protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
973 int pid, int userId) {
974 checkUseBiometricPermission();
975
Kevin Chyn13648922019-03-11 20:28:38 -0700976
977 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
978 return true; // System process (BiometricService, etc) is always allowed
979 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700980 if (isKeyguard(opPackageName)) {
981 return true; // Keyguard is always allowed
982 }
983 if (!isCurrentUserOrProfile(userId)) {
984 Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
985 return false;
986 }
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700987 if (!checkAppOps(uid, opPackageName)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700988 Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
989 return false;
990 }
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700991
Kevin Chyn037c4d52018-06-11 19:17:32 -0700992 if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
993 opPackageName))) {
994 Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
995 return false;
996 }
997 return true;
998 }
999
1000 /**
1001 * @param opPackageName package of the caller
1002 * @return true if this is the same client currently using the biometric
1003 */
1004 private boolean isCurrentClient(String opPackageName) {
1005 return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
1006 }
1007
1008 /**
1009 * @return true if this is keyguard package
1010 */
1011 private boolean isKeyguard(String clientPackage) {
1012 return mKeyguardPackage.equals(clientPackage);
1013 }
1014
Kevin Chyn037c4d52018-06-11 19:17:32 -07001015 private boolean isForegroundActivity(int uid, int pid) {
1016 try {
Kevin Chyn24e90732019-12-17 15:55:55 -08001017 final List<ActivityManager.RunningAppProcessInfo> procs =
Kevin Chyn037c4d52018-06-11 19:17:32 -07001018 ActivityManager.getService().getRunningAppProcesses();
Kevin Chyn24e90732019-12-17 15:55:55 -08001019 if (procs == null) {
1020 Slog.e(getTag(), "Processes null, defaulting to true");
1021 return true;
1022 }
1023
Kevin Chyn037c4d52018-06-11 19:17:32 -07001024 int N = procs.size();
1025 for (int i = 0; i < N; i++) {
1026 ActivityManager.RunningAppProcessInfo proc = procs.get(i);
1027 if (proc.pid == pid && proc.uid == uid
1028 && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
1029 return true;
1030 }
1031 }
1032 } catch (RemoteException e) {
1033 Slog.w(getTag(), "am.getRunningAppProcesses() failed");
1034 }
1035 return false;
1036 }
1037
1038 /**
1039 * Calls the HAL to switch states to the new task. If there's already a current task,
1040 * it calls cancel() and sets mPendingClient to begin when the current task finishes
1041 * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
1042 *
1043 * @param newClient the new client that wants to connect
1044 * @param initiatedByClient true for authenticate, remove and enroll
1045 */
1046 private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
1047 ClientMonitor currentClient = mCurrentClient;
1048 if (currentClient != null) {
1049 if (DEBUG) Slog.v(getTag(), "request stop current client " +
1050 currentClient.getOwnerString());
Kevin Chyn037c4d52018-06-11 19:17:32 -07001051 // This check only matters for FingerprintService, since enumerate may call back
1052 // multiple times.
Kevin Chyn6737c572019-02-08 16:10:54 -08001053 if (currentClient instanceof InternalEnumerateClient
1054 || currentClient instanceof InternalRemovalClient) {
Kevin Chyn037c4d52018-06-11 19:17:32 -07001055 // This condition means we're currently running internal diagnostics to
Kevin Chyn6737c572019-02-08 16:10:54 -08001056 // remove extra templates in the hardware and/or the software
Kevin Chyn037c4d52018-06-11 19:17:32 -07001057 // TODO: design an escape hatch in case client never finishes
1058 if (newClient != null) {
1059 Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
1060 + newClient.getClass().getSuperclass().getSimpleName()
1061 + "(" + newClient.getOwnerString() + ")"
1062 + ", initiatedByClient = " + initiatedByClient);
1063 }
1064 } else {
1065 currentClient.stop(initiatedByClient);
Kevin Chyndf755c82019-04-03 14:39:04 -07001066
1067 // Only post the reset runnable for non-cleanup clients. Cleanup clients should
1068 // never be forcibly stopped since they ensure synchronization between HAL and
1069 // framework. Thus, we should instead just start the pending client once cleanup
1070 // finishes instead of using the reset runnable.
1071 mHandler.removeCallbacks(mResetClientState);
1072 mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
Kevin Chyn037c4d52018-06-11 19:17:32 -07001073 }
1074 mPendingClient = newClient;
Kevin Chyn037c4d52018-06-11 19:17:32 -07001075 } else if (newClient != null) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001076 // For BiometricPrompt clients, do not start until
1077 // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
1078 // modalities are ready before initiating authentication.
1079 if (newClient instanceof AuthenticationClient) {
1080 AuthenticationClient client = (AuthenticationClient) newClient;
1081 if (client.isBiometricPrompt()) {
1082 if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
1083 mCurrentClient = newClient;
1084 if (mBiometricService == null) {
1085 mBiometricService = IBiometricService.Stub.asInterface(
1086 ServiceManager.getService(Context.BIOMETRIC_SERVICE));
1087 }
1088 try {
1089 mBiometricService.onReadyForAuthentication(client.getCookie(),
1090 client.getRequireConfirmation(), client.getTargetUserId());
1091 } catch (RemoteException e) {
1092 Slog.e(getTag(), "Remote exception", e);
1093 }
1094 return;
Kevin Chyne92cdae2018-11-21 16:35:04 -08001095 }
1096 }
1097
Kevin Chyn87f257a2018-11-27 16:26:07 -08001098 // We are not a BiometricPrompt client, start the client immediately
1099 mCurrentClient = newClient;
1100 startCurrentClient(mCurrentClient.getCookie());
Kevin Chyn037c4d52018-06-11 19:17:32 -07001101 }
1102 }
1103
Kevin Chyn87f257a2018-11-27 16:26:07 -08001104 protected void startCurrentClient(int cookie) {
1105 if (mCurrentClient == null) {
1106 Slog.e(getTag(), "Trying to start null client!");
1107 return;
1108 }
1109 if (DEBUG) Slog.v(getTag(), "starting client "
1110 + mCurrentClient.getClass().getSuperclass().getSimpleName()
1111 + "(" + mCurrentClient.getOwnerString() + ")"
Kevin Chynbe67ce02019-06-10 16:14:22 -07001112 + " targetUserId: " + mCurrentClient.getTargetUserId()
1113 + " currentUserId: " + mCurrentUserId
Kevin Chyn87f257a2018-11-27 16:26:07 -08001114 + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
1115 if (cookie != mCurrentClient.getCookie()) {
1116 Slog.e(getTag(), "Mismatched cookie");
1117 return;
1118 }
1119 notifyClientActiveCallbacks(true);
1120 mCurrentClient.start();
1121 }
1122
Kevin Chyn037c4d52018-06-11 19:17:32 -07001123 protected void removeClient(ClientMonitor client) {
1124 if (client != null) {
1125 client.destroy();
1126 if (client != mCurrentClient && mCurrentClient != null) {
1127 Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
1128 + mCurrentClient.getOwnerString());
1129 }
1130 }
1131 if (mCurrentClient != null) {
1132 if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
1133 mCurrentClient = null;
1134 }
1135 if (mPendingClient == null) {
1136 notifyClientActiveCallbacks(false);
1137 }
1138 }
1139
1140 /**
Kevin Chyna56dff72018-06-19 18:41:12 -07001141 * Populates existing authenticator ids. To be used only during the start of the service.
1142 */
1143 protected void loadAuthenticatorIds() {
1144 // This operation can be expensive, so keep track of the elapsed time. Might need to move to
1145 // background if it takes too long.
1146 long t = System.currentTimeMillis();
1147 mAuthenticatorIds.clear();
1148 for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
1149 int userId = getUserOrWorkProfileId(null, user.id);
1150 if (!mAuthenticatorIds.containsKey(userId)) {
1151 updateActiveGroup(userId, null);
1152 }
1153 }
1154
1155 t = System.currentTimeMillis() - t;
1156 if (t > 1000) {
1157 Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
1158 }
1159 }
1160
1161 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -07001162 * @param clientPackage the package of the caller
1163 * @return the profile id
1164 */
1165 protected int getUserOrWorkProfileId(String clientPackage, int userId) {
1166 if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
1167 return userId;
1168 }
1169 return getEffectiveUserId(userId);
1170 }
1171
1172 protected boolean isRestricted() {
1173 // Only give privileged apps (like Settings) access to biometric info
1174 final boolean restricted = !hasPermission(getManageBiometricPermission());
1175 return restricted;
1176 }
1177
1178 protected boolean hasPermission(String permission) {
1179 return getContext().checkCallingOrSelfPermission(permission)
1180 == PackageManager.PERMISSION_GRANTED;
1181 }
1182
1183 protected void checkPermission(String permission) {
1184 getContext().enforceCallingOrSelfPermission(permission,
1185 "Must have " + permission + " permission.");
1186 }
1187
1188 protected boolean isCurrentUserOrProfile(int userId) {
1189 UserManager um = UserManager.get(mContext);
1190 if (um == null) {
1191 Slog.e(getTag(), "Unable to acquire UserManager");
1192 return false;
1193 }
1194
1195 final long token = Binder.clearCallingIdentity();
1196 try {
1197 // Allow current user or profiles of the current user...
1198 for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
1199 if (profileId == userId) {
1200 return true;
1201 }
1202 }
1203 } finally {
1204 Binder.restoreCallingIdentity(token);
1205 }
1206
1207 return false;
1208 }
1209
Kevin Chyna56dff72018-06-19 18:41:12 -07001210 /***
1211 * @param opPackageName the name of the calling package
1212 * @return authenticator id for the calling user
1213 */
1214 protected long getAuthenticatorId(String opPackageName) {
Kevin Chyndbaecba2019-12-17 17:01:44 -08001215 if (isKeyguard(opPackageName)) {
1216 // If an app tells us it's keyguard, check that it actually is.
1217 checkPermission(USE_BIOMETRIC_INTERNAL);
1218 }
1219
Kevin Chyna56dff72018-06-19 18:41:12 -07001220 final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
1221 return mAuthenticatorIds.getOrDefault(userId, 0L);
1222 }
1223
Kevin Chyn6737c572019-02-08 16:10:54 -08001224 /**
1225 * This method should be called upon connection to the daemon, and when user switches.
1226 * @param userId
1227 */
1228 protected void doTemplateCleanupForUser(int userId) {
1229 if (CLEANUP_UNKNOWN_TEMPLATES) {
1230 enumerateUser(userId);
1231 }
1232 }
1233
1234 private void clearEnumerateState() {
1235 if (DEBUG) Slog.v(getTag(), "clearEnumerateState()");
1236 mUnknownHALTemplates.clear();
1237 }
1238
1239 /**
1240 * Remove unknown templates from HAL
1241 */
1242 private void startCleanupUnknownHALTemplates() {
1243 if (!mUnknownHALTemplates.isEmpty()) {
1244 UserTemplate template = mUnknownHALTemplates.get(0);
1245 mUnknownHALTemplates.remove(template);
1246 boolean restricted = !hasPermission(getManageBiometricPermission());
1247 InternalRemovalClient client = new InternalRemovalClient(getContext(),
1248 getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */,
1249 template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
1250 restricted, getContext().getPackageName());
1251 removeInternal(client);
1252 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
1253 statsModality(),
1254 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
1255 } else {
1256 clearEnumerateState();
Kevin Chyn925701f462019-03-16 13:55:08 -07001257 if (mPendingClient != null) {
Kevin Chyndf755c82019-04-03 14:39:04 -07001258 Slog.d(getTag(), "Enumerate finished, starting pending client");
Kevin Chyn925701f462019-03-16 13:55:08 -07001259 startClient(mPendingClient, false /* initiatedByClient */);
Kevin Chyn1c100832019-04-11 17:47:32 -07001260 mPendingClient = null;
Kevin Chyn925701f462019-03-16 13:55:08 -07001261 }
Kevin Chyn6737c572019-02-08 16:10:54 -08001262 }
1263 }
1264
1265 private void enumerateUser(int userId) {
1266 if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")");
1267
1268 final boolean restricted = !hasPermission(getManageBiometricPermission());
1269 final List<? extends BiometricAuthenticator.Identifier> enrolledList =
1270 getEnrolledTemplates(userId);
1271
1272 InternalEnumerateClient client = new InternalEnumerateClient(getContext(),
1273 getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId,
1274 userId, restricted, getContext().getOpPackageName(), enrolledList,
1275 getBiometricUtils());
1276 enumerateInternal(client);
1277 }
1278
1279 /**
1280 * This method is called when the user switches. Implementations should probably notify the
1281 * HAL.
1282 */
Kevin Chyna38653c2019-02-11 17:46:21 -08001283 protected void handleUserSwitching(int userId) {
Kevin Chyn6737c572019-02-08 16:10:54 -08001284 if (getCurrentClient() instanceof InternalRemovalClient
1285 || getCurrentClient() instanceof InternalEnumerateClient) {
1286 Slog.w(getTag(), "User switched while performing cleanup");
Kevin Chyn6737c572019-02-08 16:10:54 -08001287 }
1288 updateActiveGroup(userId, null);
1289 doTemplateCleanupForUser(userId);
1290 }
1291
Kevin Chyna38653c2019-02-11 17:46:21 -08001292 protected void notifyLockoutResetMonitors() {
1293 for (int i = 0; i < mLockoutMonitors.size(); i++) {
1294 mLockoutMonitors.get(i).sendLockoutReset();
1295 }
Kevin Chyn037c4d52018-06-11 19:17:32 -07001296 }
1297
1298 private void userActivity() {
1299 long now = SystemClock.uptimeMillis();
1300 mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
1301 }
1302
1303 /**
1304 * @param userId
1305 * @return true if this is a work profile
1306 */
1307 private boolean isWorkProfile(int userId) {
1308 UserInfo userInfo = null;
1309 final long token = Binder.clearCallingIdentity();
1310 try {
1311 userInfo = mUserManager.getUserInfo(userId);
1312 } finally {
1313 Binder.restoreCallingIdentity(token);
1314 }
1315 return userInfo != null && userInfo.isManagedProfile();
1316 }
1317
1318
1319 private int getEffectiveUserId(int userId) {
1320 UserManager um = UserManager.get(mContext);
1321 if (um != null) {
1322 final long callingIdentity = Binder.clearCallingIdentity();
1323 userId = um.getCredentialOwnerProfile(userId);
1324 Binder.restoreCallingIdentity(callingIdentity);
1325 } else {
1326 Slog.e(getTag(), "Unable to acquire UserManager");
1327 }
1328 return userId;
1329 }
1330
Kevin Chyn037c4d52018-06-11 19:17:32 -07001331
1332 private void listenForUserSwitches() {
1333 try {
1334 ActivityManager.getService().registerUserSwitchObserver(
1335 new SynchronousUserSwitchObserver() {
1336 @Override
1337 public void onUserSwitching(int newUserId) throws RemoteException {
1338 mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
1339 .sendToTarget();
1340 }
1341 }, getTag());
1342 } catch (RemoteException e) {
1343 Slog.w(getTag(), "Failed to listen for user switching event" ,e);
1344 }
1345 }
1346
Kevin Chyna56dff72018-06-19 18:41:12 -07001347 private void removeLockoutResetCallback(
1348 LockoutResetMonitor monitor) {
1349 mLockoutMonitors.remove(monitor);
1350 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001351}