blob: 0c4f0bd0edc6d81013ac5da96c1942af30393341 [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;
Kevin Chyn13648922019-03-11 20:28:38 -070046import android.os.Process;
Kevin Chyn037c4d52018-06-11 19:17:32 -070047import android.os.RemoteException;
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -070048import android.os.ServiceManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070049import android.os.SystemClock;
50import android.os.UserHandle;
51import android.os.UserManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070052import android.util.Slog;
Kevin Chyn7782d142019-01-18 12:51:33 -080053import android.util.StatsLog;
Kevin Chyn037c4d52018-06-11 19:17:32 -070054
55import com.android.internal.logging.MetricsLogger;
56import com.android.internal.statusbar.IStatusBarService;
57import com.android.server.SystemService;
Kevin Chyn037c4d52018-06-11 19:17:32 -070058
59import java.util.ArrayList;
Kevin Chyna56dff72018-06-19 18:41:12 -070060import java.util.Collections;
Kevin Chyn037c4d52018-06-11 19:17:32 -070061import java.util.HashMap;
62import java.util.List;
Kevin Chyna56dff72018-06-19 18:41:12 -070063import java.util.Map;
Kevin Chyn037c4d52018-06-11 19:17:32 -070064
65/**
66 * Abstract base class containing all of the business logic for biometric services, e.g.
67 * Fingerprint, Face, Iris.
68 *
69 * @hide
70 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -070071public abstract class BiometricServiceBase extends SystemService
72 implements IHwBinder.DeathRecipient {
Kevin Chyn037c4d52018-06-11 19:17:32 -070073
74 protected static final boolean DEBUG = true;
75
Kevin Chyn6737c572019-02-08 16:10:54 -080076 private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
Kevin Chyn037c4d52018-06-11 19:17:32 -070077 private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
78 private static final int MSG_USER_SWITCHING = 10;
Kevin Chyn037c4d52018-06-11 19:17:32 -070079 private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
80
81 private final Context mContext;
82 private final String mKeyguardPackage;
Kevin Chyn037c4d52018-06-11 19:17:32 -070083 private final IActivityTaskManager mActivityTaskManager;
Kevin Chyn037c4d52018-06-11 19:17:32 -070084 private final PowerManager mPowerManager;
85 private final UserManager mUserManager;
86 private final MetricsLogger mMetricsLogger;
87 private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
88 private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
Kevin Chyna56dff72018-06-19 18:41:12 -070089 private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
Kevin Chyn037c4d52018-06-11 19:17:32 -070090
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -070091 protected final IStatusBarService mStatusBarService;
Kevin Chyna56dff72018-06-19 18:41:12 -070092 protected final Map<Integer, Long> mAuthenticatorIds =
93 Collections.synchronizedMap(new HashMap<>());
Kevin Chynb3c05aa2018-09-21 16:50:32 -070094 protected final AppOpsManager mAppOps;
Kevin Chyn037c4d52018-06-11 19:17:32 -070095 protected final H mHandler = new H();
96
Kevin Chyn6737c572019-02-08 16:10:54 -080097 private final IBinder mToken = new Binder(); // Used for internal enumeration
98 private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
99
Kevin Chyne92cdae2018-11-21 16:35:04 -0800100 private IBiometricService mBiometricService;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700101 private ClientMonitor mCurrentClient;
102 private ClientMonitor mPendingClient;
103 private PerformanceStats mPerformanceStats;
104 protected int mCurrentUserId = UserHandle.USER_NULL;
Kevin Chyn6737c572019-02-08 16:10:54 -0800105 protected long mHalDeviceId;
Tej Singhd6d6d772018-09-05 17:41:25 -0700106 // Tracks if the current authentication makes use of CryptoObjects.
107 protected boolean mIsCrypto;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700108 // Normal authentications are tracked by mPerformanceMap.
109 protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
110 // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
111 protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
Kevin Chyn9ba99912019-01-16 16:24:36 -0800112 protected int mHALDeathCount;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700113
114 protected class PerformanceStats {
115 public int accept; // number of accepted biometrics
116 public int reject; // number of rejected biometrics
117 public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
118 // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
119 public int lockout; // total number of lockouts
120 public int permanentLockout; // total number of permanent lockouts
121 }
122
123 /**
124 * @return the log tag.
125 */
126 protected abstract String getTag();
127
128 /**
Kevin Chyn6737c572019-02-08 16:10:54 -0800129 * @return wrapper for the HAL
130 */
131 protected abstract DaemonWrapper getDaemonWrapper();
132
133 /**
Kevin Chyna56dff72018-06-19 18:41:12 -0700134 * @return the biometric utilities for a specific implementation.
135 */
136 protected abstract BiometricUtils getBiometricUtils();
137
138 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700139 * @return the metrics constants for a biometric implementation.
140 */
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700141 protected abstract Constants getConstants();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700142
143 /**
144 * @param userId
145 * @return true if the enrollment limit has been reached.
146 */
147 protected abstract boolean hasReachedEnrollmentLimit(int userId);
148
149 /**
150 * Notifies the HAL that the user has changed.
151 * @param userId
152 * @param clientPackage
153 */
154 protected abstract void updateActiveGroup(int userId, String clientPackage);
155
156 /**
157 * @return The protected intent to reset lockout for a specific biometric.
158 */
159 protected abstract String getLockoutResetIntent();
160
161 /**
162 * @return The permission the sender is required to have in order for the lockout reset intent
163 * to be received by the BiometricService implementation.
164 */
165 protected abstract String getLockoutBroadcastPermission();
166
167 /**
168 * @return The HAL ID.
169 */
170 protected abstract long getHalDeviceId();
171
172 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700173 * @param userId
174 * @return Returns true if the user has any enrolled biometrics.
175 */
176 protected abstract boolean hasEnrolledBiometrics(int userId);
177
178 /**
179 * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
180 * etc.
181 */
182 protected abstract String getManageBiometricPermission();
183
184 /**
185 * Checks if the caller has permission to use the biometric service - throws a SecurityException
186 * if not.
187 */
188 protected abstract void checkUseBiometricPermission();
189
190 /**
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700191 * Checks if the caller passes the app ops check
Kevin Chyn037c4d52018-06-11 19:17:32 -0700192 */
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700193 protected abstract boolean checkAppOps(int uid, String opPackageName);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700194
Kevin Chyn6737c572019-02-08 16:10:54 -0800195 protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
196 int userId);
197
Kevin Chyn037c4d52018-06-11 19:17:32 -0700198 /**
199 * Notifies clients of any change in the biometric state (active / idle). This is mainly for
200 * Fingerprint navigation gestures.
201 * @param isActive
202 */
203 protected void notifyClientActiveCallbacks(boolean isActive) {}
204
Kevin Chyn7782d142019-01-18 12:51:33 -0800205 protected abstract int statsModality();
206
Kevin Chyna38653c2019-02-11 17:46:21 -0800207 /**
208 * @return one of the AuthenticationClient LOCKOUT constants
209 */
210 protected abstract int getLockoutMode();
211
Kevin Chyn8b7a0372018-09-17 15:06:05 -0700212 protected abstract class AuthenticationClientImpl extends AuthenticationClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700213
Kevin Chyn7782d142019-01-18 12:51:33 -0800214 // Used to check if the public API that was invoked was from FingerprintManager. Only
215 // to be overridden by FingerprintService.
216 protected boolean isFingerprint() {
217 return false;
218 }
219
Kevin Chyn037c4d52018-06-11 19:17:32 -0700220 public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
221 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800222 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700223 super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId,
Kevin Chyn87f257a2018-11-27 16:26:07 -0800224 groupId, opId, restricted, owner, cookie, requireConfirmation);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700225 }
226
227 @Override
Kevin Chyn7782d142019-01-18 12:51:33 -0800228 protected int statsClient() {
229 if (isKeyguard(getOwnerString())) {
230 return BiometricsProtoEnums.CLIENT_KEYGUARD;
231 } else if (isBiometricPrompt()) {
232 return BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
233 } else if (isFingerprint()) {
234 return BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
235 } else {
236 return BiometricsProtoEnums.CLIENT_UNKNOWN;
237 }
238 }
239
240 @Override
Kevin Chyn037c4d52018-06-11 19:17:32 -0700241 public void onStart() {
242 try {
243 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
244 } catch (RemoteException e) {
245 Slog.e(getTag(), "Could not register task stack listener", e);
246 }
247 }
248
249 @Override
250 public void onStop() {
251 try {
252 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
253 } catch (RemoteException e) {
254 Slog.e(getTag(), "Could not unregister task stack listener", e);
255 }
256 }
257
258 @Override
Kevin Chyn037c4d52018-06-11 19:17:32 -0700259 public void notifyUserActivity() {
260 userActivity();
261 }
262
263 @Override
264 public int handleFailedAttempt() {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700265 final int lockoutMode = getLockoutMode();
266 if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
267 mPerformanceStats.permanentLockout++;
268 } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
269 mPerformanceStats.lockout++;
270 }
271
272 // Failing multiple times will continue to push out the lockout time
273 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700274 return lockoutMode;
275 }
276 return AuthenticationClient.LOCKOUT_NONE;
277 }
278 }
279
Kevin Chyn1429a312019-01-28 16:08:09 -0800280 protected abstract class EnrollClientImpl extends EnrollClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700281
282 public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
283 IBinder token, ServiceListener listener, int userId, int groupId,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800284 byte[] cryptoToken, boolean restricted, String owner,
285 final int[] disabledFeatures) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700286 super(context, getConstants(), daemon, halDeviceId, token, listener,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800287 userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
288 disabledFeatures);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700289 }
290
291 @Override
292 public void notifyUserActivity() {
293 userActivity();
294 }
295 }
296
Kevin Chyn6737c572019-02-08 16:10:54 -0800297 /**
298 * An internal class to help clean up unknown templates in HAL and Framework
299 */
300 private final class InternalRemovalClient extends RemovalClient {
301 InternalRemovalClient(Context context,
302 DaemonWrapper daemon, long halDeviceId, IBinder token,
303 ServiceListener listener, int templateId, int groupId, int userId,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700304 boolean restricted, String owner) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700305 super(context, getConstants(), daemon, halDeviceId, token, listener, templateId, groupId,
Kevin Chyna56dff72018-06-19 18:41:12 -0700306 userId, restricted, owner, getBiometricUtils());
Kevin Chyn037c4d52018-06-11 19:17:32 -0700307 }
308
Kevin Chyn037c4d52018-06-11 19:17:32 -0700309 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800310 protected int statsModality() {
311 return BiometricServiceBase.this.statsModality();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700312 }
313 }
314
Kevin Chyn6737c572019-02-08 16:10:54 -0800315 /**
316 * Internal class to help clean up unknown templates in the HAL and Framework
317 */
318 private final class InternalEnumerateClient extends EnumerateClient {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700319
Kevin Chyn6737c572019-02-08 16:10:54 -0800320 private BiometricUtils mUtils;
321 // List of templates that are known to the Framework. Remove from this list when enumerate
322 // returns a template that contains a match.
323 private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
324 // List of templates to remove from the HAL
325 private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
326
327 InternalEnumerateClient(Context context,
328 DaemonWrapper daemon, long halDeviceId, IBinder token,
329 ServiceListener listener, int groupId, int userId, boolean restricted,
330 String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
331 BiometricUtils utils) {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700332 super(context, getConstants(), daemon, halDeviceId, token, listener, groupId, userId,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700333 restricted, owner);
Kevin Chyn6737c572019-02-08 16:10:54 -0800334 mEnrolledList = enrolledList;
335 mUtils = utils;
336 }
337
338 private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
339 if (identifier == null) {
340 return;
341 }
342 Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
343 boolean matched = false;
344 for (int i = 0; i < mEnrolledList.size(); i++) {
345 if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
346 mEnrolledList.remove(i);
347 matched = true;
348 break;
349 }
350 }
351
352 // TemplateId 0 means no templates in HAL
353 if (!matched && identifier.getBiometricId() != 0) {
354 mUnknownHALTemplates.add(identifier);
355 }
356 Slog.v(getTag(), "Matched: " + matched);
357 }
358
359 private void doTemplateCleanup() {
360 if (mEnrolledList == null) {
361 return;
362 }
363
364 // At this point, mEnrolledList only contains templates known to the framework and
365 // not the HAL.
366 for (int i = 0; i < mEnrolledList.size(); i++) {
367 BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
368 Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: "
369 + identifier.getBiometricId() + " "
370 + identifier.getName());
371 mUtils.removeBiometricForUser(getContext(),
372 getTargetUserId(), identifier.getBiometricId());
373 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
374 statsModality(),
375 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
376 }
377 mEnrolledList.clear();
378 }
379
380 public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
381 return mUnknownHALTemplates;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700382 }
383
384 @Override
Kevin Chyn6737c572019-02-08 16:10:54 -0800385 public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
386 int remaining) {
387 handleEnumeratedTemplate(identifier);
388 if (remaining == 0) {
389 doTemplateCleanup();
390 }
391 return remaining == 0;
392 }
393
394 @Override
395 protected int statsModality() {
396 return BiometricServiceBase.this.statsModality();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700397 }
398 }
399
400 /**
401 * Wraps the callback interface from Service -> Manager
402 */
403 protected interface ServiceListener {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700404 default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
405 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700406
Kevin Chyne92cdae2018-11-21 16:35:04 -0800407 void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700408
Kevin Chyne92cdae2018-11-21 16:35:04 -0800409 default void onAuthenticationSucceeded(long deviceId,
410 BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
411 throw new UnsupportedOperationException("Stub!");
412 }
413
414 default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
415 throws RemoteException {
416 throw new UnsupportedOperationException("Stub!");
417 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700418
Kevin Chyn23289ef2018-11-28 16:32:36 -0800419 default void onAuthenticationFailed(long deviceId) throws RemoteException {
420 throw new UnsupportedOperationException("Stub!");
421 }
422
423 default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
424 throws RemoteException {
425 throw new UnsupportedOperationException("Stub!");
426 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700427
Kevin Chyn87f257a2018-11-27 16:26:07 -0800428 void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700429
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700430 default void onRemoved(BiometricAuthenticator.Identifier identifier,
431 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700432
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700433 default void onEnumerated(BiometricAuthenticator.Identifier identifier,
434 int remaining) throws RemoteException {};
Kevin Chyn037c4d52018-06-11 19:17:32 -0700435 }
436
437 /**
Kevin Chyne92cdae2018-11-21 16:35:04 -0800438 * Wraps the callback interface from Service -> BiometricPrompt
439 */
440 protected abstract class BiometricServiceListener implements ServiceListener {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800441 private IBiometricServiceReceiverInternal mWrapperReceiver;
Kevin Chyne92cdae2018-11-21 16:35:04 -0800442
Kevin Chyn23289ef2018-11-28 16:32:36 -0800443 public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800444 mWrapperReceiver = wrapperReceiver;
445 }
446
Kevin Chyn23289ef2018-11-28 16:32:36 -0800447 public IBiometricServiceReceiverInternal getWrapperReceiver() {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800448 return mWrapperReceiver;
449 }
450
Kevin Chyne92cdae2018-11-21 16:35:04 -0800451 @Override
452 public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
453 throws RemoteException {
454 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800455 getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800456 }
457 }
458
459 @Override
Kevin Chyn23289ef2018-11-28 16:32:36 -0800460 public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
461 throws RemoteException {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800462 if (getWrapperReceiver() != null) {
Kevin Chyn23289ef2018-11-28 16:32:36 -0800463 getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
Kevin Chyne92cdae2018-11-21 16:35:04 -0800464 }
465 }
466 }
467
468 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700469 * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
470 * subclasses.
471 */
472 protected interface DaemonWrapper {
Kevin Chyn6737c572019-02-08 16:10:54 -0800473 int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
Kevin Chyn037c4d52018-06-11 19:17:32 -0700474 int authenticate(long operationId, int groupId) throws RemoteException;
475 int cancel() throws RemoteException;
476 int remove(int groupId, int biometricId) throws RemoteException;
477 int enumerate() throws RemoteException;
Kevin Chyna38653c2019-02-11 17:46:21 -0800478 int enroll(byte[] token, int groupId, int timeout,
Kevin Chyn1f16c2d2018-12-07 13:06:08 -0800479 ArrayList<Integer> disabledFeatures) throws RemoteException;
Kevin Chyna38653c2019-02-11 17:46:21 -0800480 void resetLockout(byte[] token) throws RemoteException;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700481 }
482
483 /**
484 * Handler which all subclasses should post events to.
485 */
486 protected final class H extends Handler {
487 @Override
488 public void handleMessage(android.os.Message msg) {
489 switch (msg.what) {
490 case MSG_USER_SWITCHING:
491 handleUserSwitching(msg.arg1);
492 break;
493
494 default:
495 Slog.w(getTag(), "Unknown message:" + msg.what);
496 }
497 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700498 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700499
500 private final class BiometricTaskStackListener extends TaskStackListener {
501 @Override
502 public void onTaskStackChanged() {
503 try {
504 if (!(mCurrentClient instanceof AuthenticationClient)) {
505 return;
506 }
507 final String currentClient = mCurrentClient.getOwnerString();
508 if (isKeyguard(currentClient)) {
509 return; // Keyguard is always allowed
510 }
511 List<ActivityManager.RunningTaskInfo> runningTasks =
512 mActivityTaskManager.getTasks(1);
513 if (!runningTasks.isEmpty()) {
514 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
Kevin Chync79856b2018-10-05 18:57:35 -0700515 if (!topPackage.contentEquals(currentClient)
516 && !mCurrentClient.isAlreadyDone()) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700517 Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
518 + " currentClient: " + currentClient);
519 mCurrentClient.stop(false /* initiatedByClient */);
520 }
521 }
522 } catch (RemoteException e) {
523 Slog.e(getTag(), "Unable to get running tasks", e);
524 }
525 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700526 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700527
528 private final class ResetClientStateRunnable implements Runnable {
529 @Override
530 public void run() {
531 /**
532 * Warning: if we get here, the driver never confirmed our call to cancel the current
533 * operation (authenticate, enroll, remove, enumerate, etc), which is
534 * really bad. The result will be a 3-second delay in starting each new client.
535 * If you see this on a device, make certain the driver notifies with
536 * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
537 * once it has successfully switched to the IDLE state in the HAL.
538 * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
539 * in response to an actual cancel() call.
540 */
541 Slog.w(getTag(), "Client "
542 + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
543 + " failed to respond to cancel, starting client "
544 + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
545
Kevin Chynd0b8b1f2019-02-28 13:41:31 -0800546 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
547 statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
548
Kevin Chyn037c4d52018-06-11 19:17:32 -0700549 mCurrentClient = null;
550 startClient(mPendingClient, false);
551 }
Kevin Chyna56dff72018-06-19 18:41:12 -0700552 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700553
Kevin Chyn037c4d52018-06-11 19:17:32 -0700554
Kevin Chyna56dff72018-06-19 18:41:12 -0700555
556 private final class LockoutResetMonitor implements IBinder.DeathRecipient {
557 private static final long WAKELOCK_TIMEOUT_MS = 2000;
558 private final IBiometricServiceLockoutResetCallback mCallback;
559 private final PowerManager.WakeLock mWakeLock;
560
561 public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
562 mCallback = callback;
563 mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
564 "lockout reset callback");
565 try {
566 mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
567 } catch (RemoteException e) {
568 Slog.w(getTag(), "caught remote exception in linkToDeath", e);
569 }
570 }
571
572 public void sendLockoutReset() {
573 if (mCallback != null) {
574 try {
575 mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
576 mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
577 @Override
578 public void sendResult(Bundle data) throws RemoteException {
579 releaseWakelock();
580 }
581 });
582 } catch (DeadObjectException e) {
583 Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
584 mHandler.post(mRemoveCallbackRunnable);
585 } catch (RemoteException e) {
586 Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
587 releaseWakelock();
588 }
589 }
590 }
591
592 private final Runnable mRemoveCallbackRunnable = new Runnable() {
593 @Override
594 public void run() {
595 releaseWakelock();
596 removeLockoutResetCallback(LockoutResetMonitor.this);
597 }
598 };
599
600 @Override
601 public void binderDied() {
602 Slog.e(getTag(), "Lockout reset callback binder died");
603 mHandler.post(mRemoveCallbackRunnable);
604 }
605
606 private void releaseWakelock() {
607 if (mWakeLock.isHeld()) {
608 mWakeLock.release();
609 }
610 }
611 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700612
613 /**
Kevin Chyn6737c572019-02-08 16:10:54 -0800614 * Container for enumerated templates. Used to keep track when cleaning up unknown
615 * templates.
616 */
617 private final class UserTemplate {
618 final BiometricAuthenticator.Identifier mIdentifier;
619 final int mUserId;
620 UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
621 this.mIdentifier = identifier;
622 this.mUserId = userId;
623 }
624 }
625
626 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -0700627 * Initializes the system service.
628 * <p>
629 * Subclasses must define a single argument constructor that accepts the context
630 * and passes it to super.
631 * </p>
632 *
633 * @param context The system server context.
634 */
Kevin Chyn355c6bf2018-09-20 22:14:19 -0700635 public BiometricServiceBase(Context context) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700636 super(context);
637 mContext = context;
Kevin Chyn5a2ff5d2018-08-29 19:07:30 -0700638 mStatusBarService = IStatusBarService.Stub.asInterface(
639 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Kevin Chyn037c4d52018-06-11 19:17:32 -0700640 mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
641 com.android.internal.R.string.config_keyguardComponent)).getPackageName();
642 mAppOps = context.getSystemService(AppOpsManager.class);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700643 mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
644 Context.ACTIVITY_TASK_SERVICE)).getService();
645 mPowerManager = mContext.getSystemService(PowerManager.class);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700646 mUserManager = UserManager.get(mContext);
647 mMetricsLogger = new MetricsLogger();
Kevin Chyn037c4d52018-06-11 19:17:32 -0700648 }
649
650 @Override
651 public void onStart() {
652 listenForUserSwitches();
653 }
654
655 @Override
656 public void serviceDied(long cookie) {
657 Slog.e(getTag(), "HAL died");
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700658 mMetricsLogger.count(getConstants().tagHalDied(), 1);
Kevin Chyn9ba99912019-01-16 16:24:36 -0800659 mHALDeathCount++;
Kevin Chyncaa2ea72019-04-02 18:28:12 -0700660 mCurrentUserId = UserHandle.USER_NULL;
Kevin Chyn037c4d52018-06-11 19:17:32 -0700661 handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
662 0 /*vendorCode */);
Kevin Chyn7782d142019-01-18 12:51:33 -0800663
Kevin Chyne5a37fb2019-02-08 13:10:36 -0800664 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(),
665 BiometricsProtoEnums.ISSUE_HAL_DEATH);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700666 }
667
668 protected ClientMonitor getCurrentClient() {
669 return mCurrentClient;
670 }
671
672 protected ClientMonitor getPendingClient() {
673 return mPendingClient;
674 }
675
676 /**
677 * Callback handlers from the daemon. The caller must put this on a handler.
678 */
679
680 protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
681 ClientMonitor client = mCurrentClient;
682 if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
683 removeClient(client);
684 }
685 if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
686 && client instanceof AuthenticationClient) {
687 // ignore enrollment acquisitions or acquisitions when we're locked out
688 mPerformanceStats.acquire++;
689 }
690 }
691
Kevin Chynb528d692018-07-20 11:53:14 -0700692 protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
Kevin Chyn037c4d52018-06-11 19:17:32 -0700693 ArrayList<Byte> token) {
694 ClientMonitor client = mCurrentClient;
Kevin Chynb528d692018-07-20 11:53:14 -0700695 final boolean authenticated = identifier.getBiometricId() != 0;
696
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700697 if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700698 removeClient(client);
699 }
Kevin Chynb528d692018-07-20 11:53:14 -0700700 if (authenticated) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700701 mPerformanceStats.accept++;
702 } else {
703 mPerformanceStats.reject++;
704 }
705 }
706
Kevin Chyna56dff72018-06-19 18:41:12 -0700707 protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
708 int remaining) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700709 ClientMonitor client = mCurrentClient;
Kevin Chyna56dff72018-06-19 18:41:12 -0700710 if (client != null && client.onEnrollResult(identifier, remaining)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700711 removeClient(client);
712 // When enrollment finishes, update this group's authenticator id, as the HAL has
713 // already generated a new authenticator id when the new biometric is enrolled.
Kevin Chyna56dff72018-06-19 18:41:12 -0700714 if (identifier instanceof Fingerprint) {
715 updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
Kevin Chyna56dff72018-06-19 18:41:12 -0700716 }
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(() -> {
Kevin Chyn4cc49f72019-04-24 13:53:35 -0700846 mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700847
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) {
Kevin Chyn77765362019-04-03 18:12:48 -0700904 // Do not put on handler, since it should finish before returning to caller.
905 updateActiveGroup(userId, null /* clientPackage */);
Kevin Chyn037c4d52018-06-11 19:17:32 -0700906 }
907
Kevin Chyn6737c572019-02-08 16:10:54 -0800908 protected void removeInternal(RemovalClient client) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700909 mHandler.post(() -> {
910 startClient(client, true /* initiatedByClient */);
911 });
912 }
913
Kevin Chyn6737c572019-02-08 16:10:54 -0800914 protected void enumerateInternal(EnumerateClient client) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700915 mHandler.post(() -> {
916 startClient(client, true /* initiatedByClient */);
917 });
918 }
919
920 // Should be done on a handler thread - not on the Binder's thread.
921 private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700922 if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
923
924 int lockoutMode = getLockoutMode();
925 if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800926 Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
Kevin Chyn037c4d52018-06-11 19:17:32 -0700927 int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
928 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
929 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
Kevin Chyna56dff72018-06-19 18:41:12 -0700930 if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700931 Slog.w(getTag(), "Cannot send permanent lockout message to client");
932 }
933 return;
934 }
935 startClient(client, true /* initiatedByClient */);
936 }
937
Kevin Chyna56dff72018-06-19 18:41:12 -0700938 protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
939 mHandler.post(() -> {
940 final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
941 if (!mLockoutMonitors.contains(monitor)) {
942 mLockoutMonitors.add(monitor);
943 }
944 });
945 }
946
Kevin Chyn037c4d52018-06-11 19:17:32 -0700947 /**
948 * Helper methods.
949 */
950
951 /**
952 * @param opPackageName name of package for caller
953 * @param requireForeground only allow this call while app is in the foreground
954 * @return true if caller can use the biometric API
955 */
956 protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
957 int pid, int userId) {
958 checkUseBiometricPermission();
959
Kevin Chyn13648922019-03-11 20:28:38 -0700960
961 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
962 return true; // System process (BiometricService, etc) is always allowed
963 }
Kevin Chyn037c4d52018-06-11 19:17:32 -0700964 if (isKeyguard(opPackageName)) {
965 return true; // Keyguard is always allowed
966 }
967 if (!isCurrentUserOrProfile(userId)) {
968 Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
969 return false;
970 }
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700971 if (!checkAppOps(uid, opPackageName)) {
Kevin Chyn037c4d52018-06-11 19:17:32 -0700972 Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
973 return false;
974 }
Kevin Chynb3c05aa2018-09-21 16:50:32 -0700975
Kevin Chyn037c4d52018-06-11 19:17:32 -0700976 if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
977 opPackageName))) {
978 Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
979 return false;
980 }
981 return true;
982 }
983
984 /**
985 * @param opPackageName package of the caller
986 * @return true if this is the same client currently using the biometric
987 */
988 private boolean isCurrentClient(String opPackageName) {
989 return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
990 }
991
992 /**
993 * @return true if this is keyguard package
994 */
995 private boolean isKeyguard(String clientPackage) {
996 return mKeyguardPackage.equals(clientPackage);
997 }
998
Kevin Chyn037c4d52018-06-11 19:17:32 -0700999 private boolean isForegroundActivity(int uid, int pid) {
1000 try {
1001 List<ActivityManager.RunningAppProcessInfo> procs =
1002 ActivityManager.getService().getRunningAppProcesses();
1003 int N = procs.size();
1004 for (int i = 0; i < N; i++) {
1005 ActivityManager.RunningAppProcessInfo proc = procs.get(i);
1006 if (proc.pid == pid && proc.uid == uid
1007 && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
1008 return true;
1009 }
1010 }
1011 } catch (RemoteException e) {
1012 Slog.w(getTag(), "am.getRunningAppProcesses() failed");
1013 }
1014 return false;
1015 }
1016
1017 /**
1018 * Calls the HAL to switch states to the new task. If there's already a current task,
1019 * it calls cancel() and sets mPendingClient to begin when the current task finishes
1020 * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
1021 *
1022 * @param newClient the new client that wants to connect
1023 * @param initiatedByClient true for authenticate, remove and enroll
1024 */
1025 private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
1026 ClientMonitor currentClient = mCurrentClient;
1027 if (currentClient != null) {
1028 if (DEBUG) Slog.v(getTag(), "request stop current client " +
1029 currentClient.getOwnerString());
Kevin Chyn037c4d52018-06-11 19:17:32 -07001030 // This check only matters for FingerprintService, since enumerate may call back
1031 // multiple times.
Kevin Chyn6737c572019-02-08 16:10:54 -08001032 if (currentClient instanceof InternalEnumerateClient
1033 || currentClient instanceof InternalRemovalClient) {
Kevin Chyn037c4d52018-06-11 19:17:32 -07001034 // This condition means we're currently running internal diagnostics to
Kevin Chyn6737c572019-02-08 16:10:54 -08001035 // remove extra templates in the hardware and/or the software
Kevin Chyn037c4d52018-06-11 19:17:32 -07001036 // TODO: design an escape hatch in case client never finishes
1037 if (newClient != null) {
1038 Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
1039 + newClient.getClass().getSuperclass().getSimpleName()
1040 + "(" + newClient.getOwnerString() + ")"
1041 + ", initiatedByClient = " + initiatedByClient);
1042 }
1043 } else {
1044 currentClient.stop(initiatedByClient);
Kevin Chyndf755c82019-04-03 14:39:04 -07001045
1046 // Only post the reset runnable for non-cleanup clients. Cleanup clients should
1047 // never be forcibly stopped since they ensure synchronization between HAL and
1048 // framework. Thus, we should instead just start the pending client once cleanup
1049 // finishes instead of using the reset runnable.
1050 mHandler.removeCallbacks(mResetClientState);
1051 mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
Kevin Chyn037c4d52018-06-11 19:17:32 -07001052 }
1053 mPendingClient = newClient;
Kevin Chyn037c4d52018-06-11 19:17:32 -07001054 } else if (newClient != null) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001055 // For BiometricPrompt clients, do not start until
1056 // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
1057 // modalities are ready before initiating authentication.
1058 if (newClient instanceof AuthenticationClient) {
1059 AuthenticationClient client = (AuthenticationClient) newClient;
1060 if (client.isBiometricPrompt()) {
1061 if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
1062 mCurrentClient = newClient;
1063 if (mBiometricService == null) {
1064 mBiometricService = IBiometricService.Stub.asInterface(
1065 ServiceManager.getService(Context.BIOMETRIC_SERVICE));
1066 }
1067 try {
1068 mBiometricService.onReadyForAuthentication(client.getCookie(),
1069 client.getRequireConfirmation(), client.getTargetUserId());
1070 } catch (RemoteException e) {
1071 Slog.e(getTag(), "Remote exception", e);
1072 }
1073 return;
Kevin Chyne92cdae2018-11-21 16:35:04 -08001074 }
1075 }
1076
Kevin Chyn87f257a2018-11-27 16:26:07 -08001077 // We are not a BiometricPrompt client, start the client immediately
1078 mCurrentClient = newClient;
1079 startCurrentClient(mCurrentClient.getCookie());
Kevin Chyn037c4d52018-06-11 19:17:32 -07001080 }
1081 }
1082
Kevin Chyn87f257a2018-11-27 16:26:07 -08001083 protected void startCurrentClient(int cookie) {
1084 if (mCurrentClient == null) {
1085 Slog.e(getTag(), "Trying to start null client!");
1086 return;
1087 }
1088 if (DEBUG) Slog.v(getTag(), "starting client "
1089 + mCurrentClient.getClass().getSuperclass().getSimpleName()
1090 + "(" + mCurrentClient.getOwnerString() + ")"
Kevin Chynbe67ce02019-06-10 16:14:22 -07001091 + " targetUserId: " + mCurrentClient.getTargetUserId()
1092 + " currentUserId: " + mCurrentUserId
Kevin Chyn87f257a2018-11-27 16:26:07 -08001093 + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
1094 if (cookie != mCurrentClient.getCookie()) {
1095 Slog.e(getTag(), "Mismatched cookie");
1096 return;
1097 }
1098 notifyClientActiveCallbacks(true);
1099 mCurrentClient.start();
1100 }
1101
Kevin Chyn037c4d52018-06-11 19:17:32 -07001102 protected void removeClient(ClientMonitor client) {
1103 if (client != null) {
1104 client.destroy();
1105 if (client != mCurrentClient && mCurrentClient != null) {
1106 Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
1107 + mCurrentClient.getOwnerString());
1108 }
1109 }
1110 if (mCurrentClient != null) {
1111 if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
1112 mCurrentClient = null;
1113 }
1114 if (mPendingClient == null) {
1115 notifyClientActiveCallbacks(false);
1116 }
1117 }
1118
1119 /**
Kevin Chyna56dff72018-06-19 18:41:12 -07001120 * Populates existing authenticator ids. To be used only during the start of the service.
1121 */
1122 protected void loadAuthenticatorIds() {
1123 // This operation can be expensive, so keep track of the elapsed time. Might need to move to
1124 // background if it takes too long.
1125 long t = System.currentTimeMillis();
1126 mAuthenticatorIds.clear();
1127 for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
1128 int userId = getUserOrWorkProfileId(null, user.id);
1129 if (!mAuthenticatorIds.containsKey(userId)) {
1130 updateActiveGroup(userId, null);
1131 }
1132 }
1133
1134 t = System.currentTimeMillis() - t;
1135 if (t > 1000) {
1136 Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
1137 }
1138 }
1139
1140 /**
Kevin Chyn037c4d52018-06-11 19:17:32 -07001141 * @param clientPackage the package of the caller
1142 * @return the profile id
1143 */
1144 protected int getUserOrWorkProfileId(String clientPackage, int userId) {
1145 if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
1146 return userId;
1147 }
1148 return getEffectiveUserId(userId);
1149 }
1150
1151 protected boolean isRestricted() {
1152 // Only give privileged apps (like Settings) access to biometric info
1153 final boolean restricted = !hasPermission(getManageBiometricPermission());
1154 return restricted;
1155 }
1156
1157 protected boolean hasPermission(String permission) {
1158 return getContext().checkCallingOrSelfPermission(permission)
1159 == PackageManager.PERMISSION_GRANTED;
1160 }
1161
1162 protected void checkPermission(String permission) {
1163 getContext().enforceCallingOrSelfPermission(permission,
1164 "Must have " + permission + " permission.");
1165 }
1166
1167 protected boolean isCurrentUserOrProfile(int userId) {
1168 UserManager um = UserManager.get(mContext);
1169 if (um == null) {
1170 Slog.e(getTag(), "Unable to acquire UserManager");
1171 return false;
1172 }
1173
1174 final long token = Binder.clearCallingIdentity();
1175 try {
1176 // Allow current user or profiles of the current user...
1177 for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
1178 if (profileId == userId) {
1179 return true;
1180 }
1181 }
1182 } finally {
1183 Binder.restoreCallingIdentity(token);
1184 }
1185
1186 return false;
1187 }
1188
Kevin Chyna56dff72018-06-19 18:41:12 -07001189 /***
1190 * @param opPackageName the name of the calling package
1191 * @return authenticator id for the calling user
1192 */
1193 protected long getAuthenticatorId(String opPackageName) {
1194 final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
1195 return mAuthenticatorIds.getOrDefault(userId, 0L);
1196 }
1197
Kevin Chyn6737c572019-02-08 16:10:54 -08001198 /**
1199 * This method should be called upon connection to the daemon, and when user switches.
1200 * @param userId
1201 */
1202 protected void doTemplateCleanupForUser(int userId) {
1203 if (CLEANUP_UNKNOWN_TEMPLATES) {
1204 enumerateUser(userId);
1205 }
1206 }
1207
1208 private void clearEnumerateState() {
1209 if (DEBUG) Slog.v(getTag(), "clearEnumerateState()");
1210 mUnknownHALTemplates.clear();
1211 }
1212
1213 /**
1214 * Remove unknown templates from HAL
1215 */
1216 private void startCleanupUnknownHALTemplates() {
1217 if (!mUnknownHALTemplates.isEmpty()) {
1218 UserTemplate template = mUnknownHALTemplates.get(0);
1219 mUnknownHALTemplates.remove(template);
1220 boolean restricted = !hasPermission(getManageBiometricPermission());
1221 InternalRemovalClient client = new InternalRemovalClient(getContext(),
1222 getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */,
1223 template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
1224 restricted, getContext().getPackageName());
1225 removeInternal(client);
1226 StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
1227 statsModality(),
1228 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
1229 } else {
1230 clearEnumerateState();
Kevin Chyn925701f462019-03-16 13:55:08 -07001231 if (mPendingClient != null) {
Kevin Chyndf755c82019-04-03 14:39:04 -07001232 Slog.d(getTag(), "Enumerate finished, starting pending client");
Kevin Chyn925701f462019-03-16 13:55:08 -07001233 startClient(mPendingClient, false /* initiatedByClient */);
Kevin Chyn1c100832019-04-11 17:47:32 -07001234 mPendingClient = null;
Kevin Chyn925701f462019-03-16 13:55:08 -07001235 }
Kevin Chyn6737c572019-02-08 16:10:54 -08001236 }
1237 }
1238
1239 private void enumerateUser(int userId) {
1240 if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")");
1241
1242 final boolean restricted = !hasPermission(getManageBiometricPermission());
1243 final List<? extends BiometricAuthenticator.Identifier> enrolledList =
1244 getEnrolledTemplates(userId);
1245
1246 InternalEnumerateClient client = new InternalEnumerateClient(getContext(),
1247 getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId,
1248 userId, restricted, getContext().getOpPackageName(), enrolledList,
1249 getBiometricUtils());
1250 enumerateInternal(client);
1251 }
1252
1253 /**
1254 * This method is called when the user switches. Implementations should probably notify the
1255 * HAL.
1256 */
Kevin Chyna38653c2019-02-11 17:46:21 -08001257 protected void handleUserSwitching(int userId) {
Kevin Chyn6737c572019-02-08 16:10:54 -08001258 if (getCurrentClient() instanceof InternalRemovalClient
1259 || getCurrentClient() instanceof InternalEnumerateClient) {
1260 Slog.w(getTag(), "User switched while performing cleanup");
Kevin Chyn6737c572019-02-08 16:10:54 -08001261 }
1262 updateActiveGroup(userId, null);
1263 doTemplateCleanupForUser(userId);
1264 }
1265
Kevin Chyna38653c2019-02-11 17:46:21 -08001266 protected void notifyLockoutResetMonitors() {
1267 for (int i = 0; i < mLockoutMonitors.size(); i++) {
1268 mLockoutMonitors.get(i).sendLockoutReset();
1269 }
Kevin Chyn037c4d52018-06-11 19:17:32 -07001270 }
1271
1272 private void userActivity() {
1273 long now = SystemClock.uptimeMillis();
1274 mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
1275 }
1276
1277 /**
1278 * @param userId
1279 * @return true if this is a work profile
1280 */
1281 private boolean isWorkProfile(int userId) {
1282 UserInfo userInfo = null;
1283 final long token = Binder.clearCallingIdentity();
1284 try {
1285 userInfo = mUserManager.getUserInfo(userId);
1286 } finally {
1287 Binder.restoreCallingIdentity(token);
1288 }
1289 return userInfo != null && userInfo.isManagedProfile();
1290 }
1291
1292
1293 private int getEffectiveUserId(int userId) {
1294 UserManager um = UserManager.get(mContext);
1295 if (um != null) {
1296 final long callingIdentity = Binder.clearCallingIdentity();
1297 userId = um.getCredentialOwnerProfile(userId);
1298 Binder.restoreCallingIdentity(callingIdentity);
1299 } else {
1300 Slog.e(getTag(), "Unable to acquire UserManager");
1301 }
1302 return userId;
1303 }
1304
Kevin Chyn037c4d52018-06-11 19:17:32 -07001305
1306 private void listenForUserSwitches() {
1307 try {
1308 ActivityManager.getService().registerUserSwitchObserver(
1309 new SynchronousUserSwitchObserver() {
1310 @Override
1311 public void onUserSwitching(int newUserId) throws RemoteException {
1312 mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
1313 .sendToTarget();
1314 }
1315 }, getTag());
1316 } catch (RemoteException e) {
1317 Slog.w(getTag(), "Failed to listen for user switching event" ,e);
1318 }
1319 }
1320
Kevin Chyna56dff72018-06-19 18:41:12 -07001321 private void removeLockoutResetCallback(
1322 LockoutResetMonitor monitor) {
1323 mLockoutMonitors.remove(monitor);
1324 }
Kevin Chyn8b7a0372018-09-17 15:06:05 -07001325}