blob: b80dca6019faa8502b8172138c740afb1b29d541 [file] [log] [blame]
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.biometrics;
18
19import static android.Manifest.permission.USE_BIOMETRIC;
Kevin Chynb7b54a62018-09-28 18:48:12 -070020import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070021import static android.Manifest.permission.USE_FINGERPRINT;
22
Kevin Chynb7b54a62018-09-28 18:48:12 -070023import android.app.ActivityManager;
Kevin Chyn69183e52018-09-21 17:04:09 -070024import android.app.AppOpsManager;
Kevin Chynb7b54a62018-09-28 18:48:12 -070025import android.app.UserSwitchObserver;
26import android.content.ContentResolver;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070027import android.content.Context;
28import android.content.pm.PackageManager;
Kevin Chynb7b54a62018-09-28 18:48:12 -070029import android.database.ContentObserver;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070030import android.hardware.biometrics.BiometricAuthenticator;
31import android.hardware.biometrics.BiometricConstants;
Kevin Chyn3a0187192018-10-08 15:40:05 -070032import android.hardware.biometrics.BiometricPrompt;
Kevin Chynb7b54a62018-09-28 18:48:12 -070033import android.hardware.biometrics.BiometricSourceType;
34import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070035import android.hardware.biometrics.IBiometricPromptReceiver;
Kevin Chyn352adfe2018-09-20 22:28:50 -070036import android.hardware.biometrics.IBiometricService;
37import android.hardware.biometrics.IBiometricServiceReceiver;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070038import android.hardware.face.FaceManager;
39import android.hardware.face.IFaceService;
40import android.hardware.fingerprint.FingerprintManager;
41import android.hardware.fingerprint.IFingerprintService;
Kevin Chynb7b54a62018-09-28 18:48:12 -070042import android.net.Uri;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070043import android.os.Binder;
44import android.os.Bundle;
Kevin Chynb7b54a62018-09-28 18:48:12 -070045import android.os.DeadObjectException;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070046import android.os.Handler;
47import android.os.IBinder;
48import android.os.Looper;
49import android.os.RemoteException;
50import android.os.ServiceManager;
51import android.os.UserHandle;
Kevin Chynd79e24e2018-09-25 12:06:59 -070052import android.provider.Settings;
Kevin Chyne7411422018-09-27 17:28:20 -070053import android.util.Pair;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070054import android.util.Slog;
55
56import com.android.internal.R;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070057import com.android.server.SystemService;
58
59import java.util.ArrayList;
Kevin Chynb7b54a62018-09-28 18:48:12 -070060import java.util.List;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070061
62/**
63 * System service that arbitrates the modality for BiometricPrompt to use.
64 */
Kevin Chyn352adfe2018-09-20 22:28:50 -070065public class BiometricService extends SystemService {
Kevin Chyna24e9fd2018-08-27 12:39:17 -070066
Kevin Chynb68099a2018-10-04 00:49:41 -070067 private static final String TAG = "BiometricService";
Kevin Chyna24e9fd2018-08-27 12:39:17 -070068
69 /**
70 * No biometric methods or nothing has been enrolled.
71 * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
72 * modalities when calling authenticate().
73 */
74 private static final int BIOMETRIC_NONE = 0;
75
76 /**
77 * Constant representing fingerprint.
78 */
79 private static final int BIOMETRIC_FINGERPRINT = 1 << 0;
80
81 /**
82 * Constant representing iris.
83 */
84 private static final int BIOMETRIC_IRIS = 1 << 1;
85
86 /**
87 * Constant representing face.
88 */
89 private static final int BIOMETRIC_FACE = 1 << 2;
90
91 private static final int[] FEATURE_ID = {
92 BIOMETRIC_FINGERPRINT,
93 BIOMETRIC_IRIS,
94 BIOMETRIC_FACE
95 };
96
Kevin Chyn69183e52018-09-21 17:04:09 -070097 private final AppOpsManager mAppOps;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070098 private final Handler mHandler;
99 private final boolean mHasFeatureFingerprint;
100 private final boolean mHasFeatureIris;
101 private final boolean mHasFeatureFace;
Kevin Chynb7b54a62018-09-28 18:48:12 -0700102 private final SettingObserver mSettingObserver;
103 private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700104
105 private IFingerprintService mFingerprintService;
106 private IFaceService mFaceService;
107
108 // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
109 // polymorphism :/
110 final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
111
112 // Cache the current service that's being used. This is the service which
113 // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
114 // check (is caller the current client) is done in the <Biometric>Service.
115 // Since Settings/System (not application) is responsible for changing preference, this
116 // should be safe.
117 private int mCurrentModality;
118
119 private final class Authenticator {
120 int mType;
121 BiometricAuthenticator mAuthenticator;
122
123 Authenticator(int type, BiometricAuthenticator authenticator) {
124 mType = type;
125 mAuthenticator = authenticator;
126 }
127
128 int getType() {
129 return mType;
130 }
131
132 BiometricAuthenticator getAuthenticator() {
133 return mAuthenticator;
134 }
135 }
136
Kevin Chynb7b54a62018-09-28 18:48:12 -0700137 private final class SettingObserver extends ContentObserver {
138 private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
139 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
140 private final Uri FACE_UNLOCK_APP_ENABLED =
141 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
142
143 private final ContentResolver mContentResolver;
144 private boolean mFaceEnabledOnKeyguard;
145 private boolean mFaceEnabledForApps;
146
147 /**
148 * Creates a content observer.
149 *
150 * @param handler The handler to run {@link #onChange} on, or null if none.
151 */
152 SettingObserver(Handler handler) {
153 super(handler);
154 mContentResolver = getContext().getContentResolver();
155 updateContentObserver();
156 }
157
158 void updateContentObserver() {
159 mContentResolver.unregisterContentObserver(this);
160 mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
161 false /* notifyForDescendents */,
162 this /* observer */,
163 UserHandle.USER_CURRENT);
164 mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
165 false /* notifyForDescendents */,
166 this /* observer */,
167 UserHandle.USER_CURRENT);
168
169 // Update the value immediately
170 onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
171 onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED);
172 }
173
174 @Override
175 public void onChange(boolean selfChange, Uri uri) {
176 if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
177 mFaceEnabledOnKeyguard =
178 Settings.Secure.getIntForUser(
179 mContentResolver,
180 Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
181 1 /* default */,
182 UserHandle.USER_CURRENT) != 0;
183
184 List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
185 for (int i = 0; i < callbacks.size(); i++) {
186 callbacks.get(i).notify(BiometricSourceType.FACE, mFaceEnabledOnKeyguard);
187 }
188 } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
189 mFaceEnabledForApps =
190 Settings.Secure.getIntForUser(
191 mContentResolver,
192 Settings.Secure.FACE_UNLOCK_APP_ENABLED,
193 1 /* default */,
194 UserHandle.USER_CURRENT) != 0;
195 }
196 }
197
198 boolean getFaceEnabledOnKeyguard() {
199 return mFaceEnabledOnKeyguard;
200 }
201
202 boolean getFaceEnabledForApps() {
203 return mFaceEnabledForApps;
204 }
205 }
206
207 private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
208
209 private final IBiometricEnabledOnKeyguardCallback mCallback;
210
211 EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
212 mCallback = callback;
213 try {
214 mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
215 } catch (RemoteException e) {
216 Slog.w(TAG, "Unable to linkToDeath", e);
217 }
218 }
219
220 void notify(BiometricSourceType sourceType, boolean enabled) {
221 try {
222 mCallback.onChanged(sourceType, enabled);
223 } catch (DeadObjectException e) {
224 Slog.w(TAG, "Death while invoking notify", e);
225 mEnabledOnKeyguardCallbacks.remove(this);
226 } catch (RemoteException e) {
227 Slog.w(TAG, "Failed to invoke onChanged", e);
228 }
229 }
230
231 @Override
232 public void binderDied() {
233 Slog.e(TAG, "Enabled callback binder died");
234 mEnabledOnKeyguardCallbacks.remove(this);
235 }
236 }
237
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700238 /**
239 * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
240 * should not carry any state. The reality is we need to keep a tiny amount of state so that
241 * cancelAuthentication() can go to the right place.
242 */
Kevin Chynbf830a32018-10-07 15:58:46 -0700243 private final class BiometricServiceWrapper extends IBiometricService.Stub {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700244
245 @Override // Binder call
246 public void authenticate(IBinder token, long sessionId, int userId,
Kevin Chyn352adfe2018-09-20 22:28:50 -0700247 IBiometricServiceReceiver receiver, int flags, String opPackageName,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700248 Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
Kevin Chyne7411422018-09-27 17:28:20 -0700249 // Check the USE_BIOMETRIC permission here. In the BiometricServiceBase, check do the
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700250 // AppOps and foreground check.
251 checkPermission();
252
Kevin Chyn7c2371a2018-09-12 01:54:18 -0700253 if (token == null || receiver == null || opPackageName == null || bundle == null
254 || dialogReceiver == null) {
255 Slog.e(TAG, "Unable to authenticate, one or more null arguments");
256 return;
257 }
258
Kevin Chyn3a0187192018-10-08 15:40:05 -0700259 // Check the usage of this in system server. Need to remove this check if it becomes
260 // a public API.
261 if (bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
262 checkInternalPermission();
263 }
264
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700265 final int callingUid = Binder.getCallingUid();
266 final int callingPid = Binder.getCallingPid();
267 final int callingUserId = UserHandle.getCallingUserId();
268
269 mHandler.post(() -> {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700270 final Pair<Integer, Integer> result = checkAndGetBiometricModality(callingUserId);
Kevin Chyne7411422018-09-27 17:28:20 -0700271 final int modality = result.first;
272 final int error = result.second;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700273
Kevin Chyne7411422018-09-27 17:28:20 -0700274 // Check for errors, notify callback, and return
275 if (error != BiometricConstants.BIOMETRIC_ERROR_NONE) {
276 try {
277 final String hardwareUnavailable =
278 getContext().getString(R.string.biometric_error_hw_unavailable);
279 switch (error) {
280 case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
281 receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
282 break;
283 case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
284 receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
285 break;
286 case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
287 receiver.onError(0 /* deviceId */, error,
288 getErrorString(modality, error, 0 /* vendorCode */));
289 break;
290 default:
291 Slog.e(TAG, "Unhandled error");
292 break;
293 }
294 } catch (RemoteException e) {
295 Slog.e(TAG, "Unable to send error", e);
296 }
297 return;
298 }
299
300 // Actually start authentication
301 mCurrentModality = modality;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700302 try {
303 // No polymorphism :(
304 if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
305 mFingerprintService.authenticateFromService(token, sessionId, userId,
306 receiver, flags, opPackageName, bundle, dialogReceiver,
307 callingUid, callingPid, callingUserId);
308 } else if (mCurrentModality == BIOMETRIC_IRIS) {
309 Slog.w(TAG, "Unsupported modality");
310 } else if (mCurrentModality == BIOMETRIC_FACE) {
Kevin Chyne7411422018-09-27 17:28:20 -0700311 mFaceService.authenticateFromService(true /* requireConfirmation */,
312 token, sessionId, userId, receiver, flags, opPackageName,
313 bundle, dialogReceiver, callingUid, callingPid, callingUserId);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700314 } else {
315 Slog.w(TAG, "Unsupported modality");
316 }
317 } catch (RemoteException e) {
318 Slog.e(TAG, "Unable to start authentication", e);
319 }
320 });
321 }
322
323 @Override // Binder call
324 public void cancelAuthentication(IBinder token, String opPackageName)
325 throws RemoteException {
326 checkPermission();
327
Kevin Chyn7c2371a2018-09-12 01:54:18 -0700328 if (token == null || opPackageName == null) {
329 Slog.e(TAG, "Unable to cancel, one or more null arguments");
330 return;
331 }
332
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700333 final int callingUid = Binder.getCallingUid();
334 final int callingPid = Binder.getCallingPid();
335 final int callingUserId = UserHandle.getCallingUserId();
336
337 mHandler.post(() -> {
338 try {
339 if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
340 mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
341 callingUid, callingPid, callingUserId);
342 } else if (mCurrentModality == BIOMETRIC_IRIS) {
343 Slog.w(TAG, "Unsupported modality");
344 } else if (mCurrentModality == BIOMETRIC_FACE) {
345 mFaceService.cancelAuthenticationFromService(token, opPackageName,
346 callingUid, callingPid, callingUserId);
347 } else {
348 Slog.w(TAG, "Unsupported modality");
349 }
350 } catch (RemoteException e) {
351 Slog.e(TAG, "Unable to cancel authentication");
352 }
353 });
354 }
Kevin Chyn05c21502018-09-18 13:07:19 -0700355
356 @Override // Binder call
Kevin Chyne7411422018-09-27 17:28:20 -0700357 public int canAuthenticate(String opPackageName) {
Kevin Chyn05c21502018-09-18 13:07:19 -0700358 checkPermission();
Kevin Chyne7411422018-09-27 17:28:20 -0700359 checkAppOp(opPackageName, Binder.getCallingUid());
Kevin Chyn69183e52018-09-21 17:04:09 -0700360
Kevin Chyn75dbb832018-10-05 17:32:57 -0700361 final int userId = UserHandle.getCallingUserId();
Kevin Chyn05c21502018-09-18 13:07:19 -0700362 final long ident = Binder.clearCallingIdentity();
Kevin Chyne7411422018-09-27 17:28:20 -0700363 int error;
Kevin Chyn05c21502018-09-18 13:07:19 -0700364 try {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700365 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
Kevin Chyne7411422018-09-27 17:28:20 -0700366 error = result.second;
Kevin Chyn05c21502018-09-18 13:07:19 -0700367 } finally {
368 Binder.restoreCallingIdentity(ident);
369 }
Kevin Chyne7411422018-09-27 17:28:20 -0700370 return error;
371 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700372
Kevin Chynbf830a32018-10-07 15:58:46 -0700373 @Override // Binder call
Kevin Chynb7b54a62018-09-28 18:48:12 -0700374 public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
375 throws RemoteException {
376 checkInternalPermission();
377 mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
378 try {
379 callback.onChanged(BiometricSourceType.FACE,
380 mSettingObserver.getFaceEnabledOnKeyguard());
381 } catch (RemoteException e) {
382 Slog.w(TAG, "Remote exception", e);
383 }
384 }
Kevin Chynbf830a32018-10-07 15:58:46 -0700385
386 @Override // Binder call
387 public void setActiveUser(int userId) {
388 checkInternalPermission();
389 final long ident = Binder.clearCallingIdentity();
390 try {
391 for (int i = 0; i < mAuthenticators.size(); i++) {
392 mAuthenticators.get(i).getAuthenticator().setActiveUser(userId);
393 }
394 } finally {
395 Binder.restoreCallingIdentity(ident);
396 }
397 }
Kevin Chyne7411422018-09-27 17:28:20 -0700398 }
399
400 private void checkAppOp(String opPackageName, int callingUid) {
401 if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
402 opPackageName) != AppOpsManager.MODE_ALLOWED) {
403 Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
404 throw new SecurityException("Permission denied");
Kevin Chyn05c21502018-09-18 13:07:19 -0700405 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700406 }
407
Kevin Chynb7b54a62018-09-28 18:48:12 -0700408 private void checkInternalPermission() {
409 getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
Kevin Chyn3a0187192018-10-08 15:40:05 -0700410 "Must have USE_BIOMETRIC_INTERNAL permission");
Kevin Chynb7b54a62018-09-28 18:48:12 -0700411 }
412
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700413 private void checkPermission() {
414 if (getContext().checkCallingPermission(USE_FINGERPRINT)
415 != PackageManager.PERMISSION_GRANTED) {
416 getContext().enforceCallingPermission(USE_BIOMETRIC,
417 "Must have USE_BIOMETRIC permission");
418 }
419 }
420
421 /**
422 * Initializes the system service.
423 * <p>
424 * Subclasses must define a single argument constructor that accepts the context
425 * and passes it to super.
426 * </p>
427 *
428 * @param context The system server context.
429 */
Kevin Chyn352adfe2018-09-20 22:28:50 -0700430 public BiometricService(Context context) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700431 super(context);
432
Kevin Chyn69183e52018-09-21 17:04:09 -0700433 mAppOps = context.getSystemService(AppOpsManager.class);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700434 mHandler = new Handler(Looper.getMainLooper());
Kevin Chynb7b54a62018-09-28 18:48:12 -0700435 mEnabledOnKeyguardCallbacks = new ArrayList<>();
436 mSettingObserver = new SettingObserver(mHandler);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700437
438 final PackageManager pm = context.getPackageManager();
439 mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
440 mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
441 mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700442
443 try {
444 ActivityManager.getService().registerUserSwitchObserver(
445 new UserSwitchObserver() {
446 @Override
447 public void onUserSwitchComplete(int newUserId) {
448 mSettingObserver.updateContentObserver();
449 }
450 }, BiometricService.class.getName()
451 );
452 } catch (RemoteException e) {
453 Slog.e(TAG, "Failed to register user switch observer", e);
454 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700455 }
456
457 @Override
458 public void onStart() {
459 // TODO: maybe get these on-demand
460 if (mHasFeatureFingerprint) {
461 mFingerprintService = IFingerprintService.Stub.asInterface(
462 ServiceManager.getService(Context.FINGERPRINT_SERVICE));
463 }
464 if (mHasFeatureFace) {
465 mFaceService = IFaceService.Stub.asInterface(
466 ServiceManager.getService(Context.FACE_SERVICE));
467 }
468
469 // Cache the authenticators
470 for (int i = 0; i < FEATURE_ID.length; i++) {
471 if (hasFeature(FEATURE_ID[i])) {
472 Authenticator authenticator =
473 new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
474 mAuthenticators.add(authenticator);
475 }
476 }
477
Kevin Chynbf830a32018-10-07 15:58:46 -0700478 publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700479 }
480
481 /**
482 * Checks if there are any available biometrics, and returns the modality. This method also
483 * returns errors through the callback (no biometric feature, hardware not detected, no
484 * templates enrolled, etc). This service must not start authentication if errors are sent.
Kevin Chyne7411422018-09-27 17:28:20 -0700485 *
486 * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
487 * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
488 * and the error containing one of the {@link BiometricConstants} errors.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700489 */
Kevin Chyn75dbb832018-10-05 17:32:57 -0700490 private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700491 int modality = BIOMETRIC_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700492
493 // No biometric features, send error
494 if (mAuthenticators.isEmpty()) {
Kevin Chyne7411422018-09-27 17:28:20 -0700495 return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700496 }
497
Kevin Chyne7411422018-09-27 17:28:20 -0700498 // Assuming that authenticators are listed in priority-order, the rest of this function
499 // will go through and find the first authenticator that's available, enrolled, and enabled.
500 // The tricky part is returning the correct error. Error strings that are modality-specific
501 // should also respect the priority-order.
502
503 // Find first authenticator that's detected, enrolled, and enabled.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700504 boolean isHardwareDetected = false;
505 boolean hasTemplatesEnrolled = false;
Kevin Chyne7411422018-09-27 17:28:20 -0700506 boolean enabledForApps = false;
507
508 int firstHwAvailable = BIOMETRIC_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700509 for (int i = 0; i < mAuthenticators.size(); i++) {
Kevin Chyne7411422018-09-27 17:28:20 -0700510 modality = mAuthenticators.get(i).getType();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700511 BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
512 if (authenticator.isHardwareDetected()) {
513 isHardwareDetected = true;
Kevin Chyne7411422018-09-27 17:28:20 -0700514 if (firstHwAvailable == BIOMETRIC_NONE) {
515 // Store the first one since we want to return the error in correct priority
516 // order.
517 firstHwAvailable = modality;
518 }
Kevin Chyn75dbb832018-10-05 17:32:57 -0700519 if (authenticator.hasEnrolledTemplates(callingUid)) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700520 hasTemplatesEnrolled = true;
Kevin Chyne7411422018-09-27 17:28:20 -0700521 if (isEnabledForApp(modality)) {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700522 // TODO(b/110907543): When face settings (and other settings) have both a
523 // user toggle as well as a work profile settings page, this needs to be
524 // updated to reflect the correct setting.
Kevin Chyne7411422018-09-27 17:28:20 -0700525 enabledForApps = true;
526 break;
527 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700528 }
529 }
530 }
531
532 // Check error conditions
533 if (!isHardwareDetected) {
Kevin Chyne7411422018-09-27 17:28:20 -0700534 return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
535 } else if (!hasTemplatesEnrolled) {
536 // Return the modality here so the correct error string can be sent. This error is
537 // preferred over !enabledForApps
538 return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
539 } else if (!enabledForApps) {
540 return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700541 }
542
Kevin Chyne7411422018-09-27 17:28:20 -0700543 return new Pair<>(modality, BiometricConstants.BIOMETRIC_ERROR_NONE);
544 }
545
546 private boolean isEnabledForApp(int modality) {
547 switch(modality) {
548 case BIOMETRIC_FINGERPRINT:
549 return true;
550 case BIOMETRIC_IRIS:
551 return true;
552 case BIOMETRIC_FACE:
Kevin Chynb7b54a62018-09-28 18:48:12 -0700553 return mSettingObserver.getFaceEnabledForApps();
Kevin Chyne7411422018-09-27 17:28:20 -0700554 default:
555 Slog.w(TAG, "Unsupported modality: " + modality);
556 return false;
557 }
558 }
559
560 private String getErrorString(int type, int error, int vendorCode) {
561 switch (type) {
562 case BIOMETRIC_FINGERPRINT:
563 return FingerprintManager.getErrorString(getContext(), error, vendorCode);
564 case BIOMETRIC_IRIS:
565 Slog.w(TAG, "Modality not supported");
566 return null; // not supported
567 case BIOMETRIC_FACE:
568 return FaceManager.getErrorString(getContext(), error, vendorCode);
569 default:
570 Slog.w(TAG, "Unable to get error string for modality: " + type);
571 return null;
572 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700573 }
574
575 private BiometricAuthenticator getAuthenticator(int type) {
576 switch (type) {
577 case BIOMETRIC_FINGERPRINT:
578 return (FingerprintManager)
579 getContext().getSystemService(Context.FINGERPRINT_SERVICE);
580 case BIOMETRIC_IRIS:
581 return null;
582 case BIOMETRIC_FACE:
583 return (FaceManager)
584 getContext().getSystemService(Context.FACE_SERVICE);
585 default:
586 return null;
587 }
588 }
589
590 private boolean hasFeature(int type) {
591 switch (type) {
592 case BIOMETRIC_FINGERPRINT:
593 return mHasFeatureFingerprint;
594 case BIOMETRIC_IRIS:
595 return mHasFeatureIris;
596 case BIOMETRIC_FACE:
597 return mHasFeatureFace;
598 default:
599 return false;
600 }
601 }
602}