blob: df6f73b914a86c6c41bae9f02109a658cf014203 [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;
Kevin Chyn87f257a2018-11-27 16:26:07 -080022import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
23import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
24import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
25import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070026
Kevin Chynb7b54a62018-09-28 18:48:12 -070027import android.app.ActivityManager;
Kevin Chyn23289ef2018-11-28 16:32:36 -080028import android.app.ActivityTaskManager;
Kevin Chyn69183e52018-09-21 17:04:09 -070029import android.app.AppOpsManager;
Kevin Chyn23289ef2018-11-28 16:32:36 -080030import android.app.IActivityTaskManager;
Kevin Chyn1b2137c2019-01-24 16:32:38 -080031import android.app.KeyguardManager;
Kevin Chyn23289ef2018-11-28 16:32:36 -080032import android.app.TaskStackListener;
Kevin Chynb7b54a62018-09-28 18:48:12 -070033import android.app.UserSwitchObserver;
34import android.content.ContentResolver;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070035import android.content.Context;
Kevin Chyn1b2137c2019-01-24 16:32:38 -080036import android.content.Intent;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070037import android.content.pm.PackageManager;
Kevin Chynb7b54a62018-09-28 18:48:12 -070038import android.database.ContentObserver;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070039import android.hardware.biometrics.BiometricAuthenticator;
40import android.hardware.biometrics.BiometricConstants;
Kevin Chyn3a0187192018-10-08 15:40:05 -070041import android.hardware.biometrics.BiometricPrompt;
Kevin Chynb7b54a62018-09-28 18:48:12 -070042import android.hardware.biometrics.BiometricSourceType;
Kevin Chyn7782d142019-01-18 12:51:33 -080043import android.hardware.biometrics.BiometricsProtoEnums;
Kevin Chyn5a90a652019-03-25 18:11:16 -070044import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
Kevin Chynb7b54a62018-09-28 18:48:12 -070045import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
Kevin Chyn352adfe2018-09-20 22:28:50 -070046import android.hardware.biometrics.IBiometricService;
47import android.hardware.biometrics.IBiometricServiceReceiver;
Kevin Chyn23289ef2018-11-28 16:32:36 -080048import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070049import android.hardware.face.FaceManager;
50import android.hardware.face.IFaceService;
51import android.hardware.fingerprint.FingerprintManager;
52import android.hardware.fingerprint.IFingerprintService;
Kevin Chynb7b54a62018-09-28 18:48:12 -070053import android.net.Uri;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070054import android.os.Binder;
55import android.os.Bundle;
Kevin Chynb7b54a62018-09-28 18:48:12 -070056import android.os.DeadObjectException;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070057import android.os.Handler;
58import android.os.IBinder;
59import android.os.Looper;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070060import android.os.Message;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070061import android.os.RemoteException;
62import android.os.ServiceManager;
63import android.os.UserHandle;
Kevin Chynd79e24e2018-09-25 12:06:59 -070064import android.provider.Settings;
Kevin Chyne92cdae2018-11-21 16:35:04 -080065import android.security.KeyStore;
Kevin Chyn87f257a2018-11-27 16:26:07 -080066import android.text.TextUtils;
Kevin Chyne7411422018-09-27 17:28:20 -070067import android.util.Pair;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070068import android.util.Slog;
Kevin Chyn7782d142019-01-18 12:51:33 -080069import android.util.StatsLog;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070070
71import com.android.internal.R;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070072import com.android.internal.os.SomeArgs;
Kevin Chyne92cdae2018-11-21 16:35:04 -080073import com.android.internal.statusbar.IStatusBarService;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070074import com.android.server.SystemService;
75
76import java.util.ArrayList;
Kevin Chyn87f257a2018-11-27 16:26:07 -080077import java.util.HashMap;
78import java.util.Iterator;
Kevin Chynb7b54a62018-09-28 18:48:12 -070079import java.util.List;
Kevin Chyn87f257a2018-11-27 16:26:07 -080080import java.util.Map;
81import java.util.Random;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070082
83/**
84 * System service that arbitrates the modality for BiometricPrompt to use.
85 */
Kevin Chyn352adfe2018-09-20 22:28:50 -070086public class BiometricService extends SystemService {
Kevin Chyna24e9fd2018-08-27 12:39:17 -070087
Kevin Chynb68099a2018-10-04 00:49:41 -070088 private static final String TAG = "BiometricService";
Kevin Chyn5a90a652019-03-25 18:11:16 -070089 private static final boolean DEBUG = true;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070090
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070091 private static final int MSG_ON_TASK_STACK_CHANGED = 1;
92 private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
93 private static final int MSG_ON_AUTHENTICATION_FAILED = 3;
94 private static final int MSG_ON_ERROR = 4;
95 private static final int MSG_ON_ACQUIRED = 5;
96 private static final int MSG_ON_DISMISSED = 6;
97 private static final int MSG_ON_TRY_AGAIN_PRESSED = 7;
98 private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8;
99 private static final int MSG_AUTHENTICATE = 9;
100 private static final int MSG_CANCEL_AUTHENTICATION = 10;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700101 private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11;
102 private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12;
103 private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700104
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700105 private static final int[] FEATURE_ID = {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800106 TYPE_FINGERPRINT,
107 TYPE_IRIS,
108 TYPE_FACE
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700109 };
110
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700111 /**
112 * Authentication either just called and we have not transitioned to the CALLED state, or
113 * authentication terminated (success or error).
114 */
115 private static final int STATE_AUTH_IDLE = 0;
116 /**
117 * Authentication was called and we are waiting for the <Biometric>Services to return their
118 * cookies before starting the hardware and showing the BiometricPrompt.
119 */
120 private static final int STATE_AUTH_CALLED = 1;
121 /**
122 * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
123 */
124 private static final int STATE_AUTH_STARTED = 2;
125 /**
126 * Authentication is paused, waiting for the user to press "try again" button. Only
127 * passive modalities such as Face or Iris should have this state. Note that for passive
128 * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
129 * fingerprint.
130 */
131 private static final int STATE_AUTH_PAUSED = 3;
132 /**
133 * Authentication is successful, but we're waiting for the user to press "confirm" button.
134 */
135 private static final int STATE_AUTH_PENDING_CONFIRM = 5;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700136 /**
137 * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential
138 */
139 private static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700140
Kevin Chyn5a90a652019-03-25 18:11:16 -0700141 private final class AuthSession implements IBinder.DeathRecipient {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700142 // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
143 // <Biometric>Services before we can start authenticating. Pairs that have been returned
144 // are moved to mModalitiesMatched.
145 final HashMap<Integer, Integer> mModalitiesWaiting;
146 // Pairs that have been matched.
147 final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
148
149 // The following variables are passed to authenticateInternal, which initiates the
150 // appropriate <Biometric>Services.
151 final IBinder mToken;
152 final long mSessionId;
153 final int mUserId;
154 // Original receiver from BiometricPrompt.
155 final IBiometricServiceReceiver mClientReceiver;
156 final String mOpPackageName;
157 // Info to be shown on BiometricDialog when all cookies are returned.
158 final Bundle mBundle;
159 final int mCallingUid;
160 final int mCallingPid;
161 final int mCallingUserId;
162 // Continue authentication with the same modality/modalities after "try again" is
163 // pressed
164 final int mModality;
165 final boolean mRequireConfirmation;
166
167 // The current state, which can be either idle, called, or started
168 private int mState = STATE_AUTH_IDLE;
169 // For explicit confirmation, do not send to keystore until the user has confirmed
170 // the authentication.
171 byte[] mTokenEscrow;
172
Kevin Chyn7fca2362019-06-20 17:20:42 -0700173 // Timestamp when authentication started
174 private long mStartTimeMs;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700175 // Timestamp when hardware authentication occurred
176 private long mAuthenticatedTimeMs;
177
Kevin Chyn5a90a652019-03-25 18:11:16 -0700178 // TODO(b/123378871): Remove when moved.
179 private IBiometricConfirmDeviceCredentialCallback mConfirmDeviceCredentialCallback;
180
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700181 AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
182 int userId, IBiometricServiceReceiver receiver, String opPackageName,
183 Bundle bundle, int callingUid, int callingPid, int callingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -0700184 int modality, boolean requireConfirmation,
185 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700186 mModalitiesWaiting = modalities;
187 mToken = token;
188 mSessionId = sessionId;
189 mUserId = userId;
190 mClientReceiver = receiver;
191 mOpPackageName = opPackageName;
192 mBundle = bundle;
193 mCallingUid = callingUid;
194 mCallingPid = callingPid;
195 mCallingUserId = callingUserId;
196 mModality = modality;
197 mRequireConfirmation = requireConfirmation;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700198 mConfirmDeviceCredentialCallback = callback;
199
200 if (isFromConfirmDeviceCredential()) {
201 try {
202 token.linkToDeath(this, 0 /* flags */);
203 } catch (RemoteException e) {
204 Slog.e(TAG, "Unable to link to death", e);
205 }
206 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700207 }
208
209 boolean isCrypto() {
210 return mSessionId != 0;
211 }
212
Kevin Chyn5a90a652019-03-25 18:11:16 -0700213 boolean isFromConfirmDeviceCredential() {
214 return mBundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
215 }
216
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700217 boolean containsCookie(int cookie) {
218 if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
219 return true;
220 }
221 if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
222 return true;
223 }
224 return false;
225 }
Kevin Chyn5a90a652019-03-25 18:11:16 -0700226
227 // TODO(b/123378871): Remove when moved.
228 @Override
229 public void binderDied() {
230 mHandler.post(() -> {
231 Slog.e(TAG, "Binder died, killing ConfirmDeviceCredential");
232 if (mConfirmDeviceCredentialCallback == null) {
233 Slog.e(TAG, "Callback is null");
234 return;
235 }
236
237 try {
238 mConfirmDeviceCredentialCallback.cancel();
239 mConfirmDeviceCredentialCallback = null;
240 } catch (RemoteException e) {
241 Slog.e(TAG, "Unable to send cancel", e);
242 }
243 });
244 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700245 }
246
247 private final class BiometricTaskStackListener extends TaskStackListener {
248 @Override
249 public void onTaskStackChanged() {
250 mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED);
251 }
252 }
253
Kevin Chyn69183e52018-09-21 17:04:09 -0700254 private final AppOpsManager mAppOps;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700255 private final boolean mHasFeatureFingerprint;
256 private final boolean mHasFeatureIris;
257 private final boolean mHasFeatureFace;
Kevin Chynb7b54a62018-09-28 18:48:12 -0700258 private final SettingObserver mSettingObserver;
259 private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700260 private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
261 private final Random mRandom = new Random();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700262
263 private IFingerprintService mFingerprintService;
264 private IFaceService mFaceService;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700265 private IActivityTaskManager mActivityTaskManager;
266 private IStatusBarService mStatusBarService;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700267
268 // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
269 // polymorphism :/
270 final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
271
272 // Cache the current service that's being used. This is the service which
273 // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
274 // check (is caller the current client) is done in the <Biometric>Service.
275 // Since Settings/System (not application) is responsible for changing preference, this
276 // should be safe.
277 private int mCurrentModality;
278
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700279 // The current authentication session, null if idle/done. We need to track both the current
280 // and pending sessions since errors may be sent to either.
281 private AuthSession mCurrentAuthSession;
282 private AuthSession mPendingAuthSession;
283
Kevin Chyn5a90a652019-03-25 18:11:16 -0700284 // TODO(b/123378871): Remove when moved.
285 // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
286 // client (app) receiver. BiometricService internally launches CDCA which invokes
287 // BiometricService to start authentication (normal path). When auth is success/rejected,
288 // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded
289 // to this receiver.
290 private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
291
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700292 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
293 @Override
294 public void handleMessage(Message msg) {
295 switch (msg.what) {
296 case MSG_ON_TASK_STACK_CHANGED: {
297 handleTaskStackChanged();
298 break;
299 }
300
301 case MSG_ON_AUTHENTICATION_SUCCEEDED: {
302 SomeArgs args = (SomeArgs) msg.obj;
303 handleAuthenticationSucceeded(
304 (boolean) args.arg1 /* requireConfirmation */,
305 (byte[]) args.arg2 /* token */);
306 args.recycle();
307 break;
308 }
309
310 case MSG_ON_AUTHENTICATION_FAILED: {
Kevin Chyne674e852019-04-24 12:39:40 -0700311 handleAuthenticationFailed((String) msg.obj /* failureReason */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700312 break;
313 }
314
315 case MSG_ON_ERROR: {
316 SomeArgs args = (SomeArgs) msg.obj;
317 handleOnError(
318 args.argi1 /* cookie */,
319 args.argi2 /* error */,
320 (String) args.arg1 /* message */);
321 args.recycle();
322 break;
323 }
324
325 case MSG_ON_ACQUIRED: {
326 SomeArgs args = (SomeArgs) msg.obj;
327 handleOnAcquired(
328 args.argi1 /* acquiredInfo */,
329 (String) args.arg1 /* message */);
330 args.recycle();
331 break;
332 }
333
334 case MSG_ON_DISMISSED: {
335 handleOnDismissed(msg.arg1);
336 break;
337 }
338
339 case MSG_ON_TRY_AGAIN_PRESSED: {
340 handleOnTryAgainPressed();
341 break;
342 }
343
344 case MSG_ON_READY_FOR_AUTHENTICATION: {
345 SomeArgs args = (SomeArgs) msg.obj;
346 handleOnReadyForAuthentication(
347 args.argi1 /* cookie */,
348 (boolean) args.arg1 /* requireConfirmation */,
349 args.argi2 /* userId */);
350 args.recycle();
351 break;
352 }
353
354 case MSG_AUTHENTICATE: {
355 SomeArgs args = (SomeArgs) msg.obj;
356 handleAuthenticate(
357 (IBinder) args.arg1 /* token */,
358 (long) args.arg2 /* sessionId */,
359 args.argi1 /* userid */,
360 (IBiometricServiceReceiver) args.arg3 /* receiver */,
361 (String) args.arg4 /* opPackageName */,
362 (Bundle) args.arg5 /* bundle */,
363 args.argi2 /* callingUid */,
364 args.argi3 /* callingPid */,
Kevin Chyn5a90a652019-03-25 18:11:16 -0700365 args.argi4 /* callingUserId */,
366 (IBiometricConfirmDeviceCredentialCallback) args.arg6 /* callback */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700367 args.recycle();
368 break;
369 }
370
371 case MSG_CANCEL_AUTHENTICATION: {
372 SomeArgs args = (SomeArgs) msg.obj;
373 handleCancelAuthentication(
374 (IBinder) args.arg1 /* token */,
375 (String) args.arg2 /* opPackageName */);
376 args.recycle();
377 break;
378 }
379
Kevin Chyn5a90a652019-03-25 18:11:16 -0700380 case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS: {
381 handleOnConfirmDeviceCredentialSuccess();
382 break;
383 }
384
385 case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR: {
386 SomeArgs args = (SomeArgs) msg.obj;
387 handleOnConfirmDeviceCredentialError(
388 args.argi1 /* error */,
389 (String) args.arg1 /* errorMsg */);
390 args.recycle();
391 break;
392 }
393
394 case MSG_REGISTER_CANCELLATION_CALLBACK: {
395 handleRegisterCancellationCallback(
396 (IBiometricConfirmDeviceCredentialCallback) msg.obj /* callback */);
397 break;
398 }
399
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700400 default:
Kevin Chyn5a90a652019-03-25 18:11:16 -0700401 Slog.e(TAG, "Unknown message: " + msg);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700402 break;
403 }
404 }
405 };
406
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700407 private final class Authenticator {
408 int mType;
409 BiometricAuthenticator mAuthenticator;
410
411 Authenticator(int type, BiometricAuthenticator authenticator) {
412 mType = type;
413 mAuthenticator = authenticator;
414 }
415
416 int getType() {
417 return mType;
418 }
419
420 BiometricAuthenticator getAuthenticator() {
421 return mAuthenticator;
422 }
423 }
424
Kevin Chynb7b54a62018-09-28 18:48:12 -0700425 private final class SettingObserver extends ContentObserver {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700426
427 private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
428 private static final boolean DEFAULT_APP_ENABLED = true;
429 private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
430
Kevin Chynb7b54a62018-09-28 18:48:12 -0700431 private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
432 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
433 private final Uri FACE_UNLOCK_APP_ENABLED =
434 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
Kevin Chyn31402832019-01-10 15:56:41 -0800435 private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
436 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700437
438 private final ContentResolver mContentResolver;
Kevin Chyn2170ab72019-04-03 20:28:28 -0700439
440 private Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
441 private Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
442 private Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
Kevin Chynb7b54a62018-09-28 18:48:12 -0700443
444 /**
445 * Creates a content observer.
446 *
447 * @param handler The handler to run {@link #onChange} on, or null if none.
448 */
449 SettingObserver(Handler handler) {
450 super(handler);
451 mContentResolver = getContext().getContentResolver();
452 updateContentObserver();
453 }
454
455 void updateContentObserver() {
456 mContentResolver.unregisterContentObserver(this);
457 mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
458 false /* notifyForDescendents */,
459 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700460 UserHandle.USER_ALL);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700461 mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
462 false /* notifyForDescendents */,
463 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700464 UserHandle.USER_ALL);
Kevin Chyn31402832019-01-10 15:56:41 -0800465 mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
466 false /* notifyForDescendents */,
467 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700468 UserHandle.USER_ALL);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700469 }
470
471 @Override
Kevin Chyn2170ab72019-04-03 20:28:28 -0700472 public void onChange(boolean selfChange, Uri uri, int userId) {
Kevin Chynb7b54a62018-09-28 18:48:12 -0700473 if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700474 mFaceEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser(
Kevin Chynb7b54a62018-09-28 18:48:12 -0700475 mContentResolver,
476 Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700477 DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */,
478 userId) != 0);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700479
Kevin Chyn750663c2019-05-15 12:17:38 -0700480 if (userId == ActivityManager.getCurrentUser() && !selfChange) {
Kevin Chyn0c000332019-04-04 16:02:37 -0700481 notifyEnabledOnKeyguardCallbacks(userId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700482 }
483 } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700484 mFaceEnabledForApps.put(userId, Settings.Secure.getIntForUser(
Kevin Chynb7b54a62018-09-28 18:48:12 -0700485 mContentResolver,
486 Settings.Secure.FACE_UNLOCK_APP_ENABLED,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700487 DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
488 userId) != 0);
Kevin Chyn31402832019-01-10 15:56:41 -0800489 } else if (FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700490 mFaceAlwaysRequireConfirmation.put(userId, Settings.Secure.getIntForUser(
Kevin Chyn31402832019-01-10 15:56:41 -0800491 mContentResolver,
492 Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700493 DEFAULT_ALWAYS_REQUIRE_CONFIRMATION ? 1 : 0 /* default */,
494 userId) != 0);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700495 }
496 }
497
498 boolean getFaceEnabledOnKeyguard() {
Kevin Chyn750663c2019-05-15 12:17:38 -0700499 final int user = ActivityManager.getCurrentUser();
500 if (!mFaceEnabledOnKeyguard.containsKey(user)) {
501 onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
502 }
503 return mFaceEnabledOnKeyguard.get(user);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700504 }
505
Kevin Chyn2170ab72019-04-03 20:28:28 -0700506 boolean getFaceEnabledForApps(int userId) {
Kevin Chyn750663c2019-05-15 12:17:38 -0700507 if (!mFaceEnabledForApps.containsKey(userId)) {
508 onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
509 }
Kevin Chyn2170ab72019-04-03 20:28:28 -0700510 return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700511 }
Kevin Chyn31402832019-01-10 15:56:41 -0800512
Kevin Chyn2170ab72019-04-03 20:28:28 -0700513 boolean getFaceAlwaysRequireConfirmation(int userId) {
Kevin Chyn750663c2019-05-15 12:17:38 -0700514 if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
515 onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
516 }
517 return mFaceAlwaysRequireConfirmation.get(userId);
Kevin Chyn31402832019-01-10 15:56:41 -0800518 }
Kevin Chyn0c000332019-04-04 16:02:37 -0700519
520 void notifyEnabledOnKeyguardCallbacks(int userId) {
521 List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
522 for (int i = 0; i < callbacks.size(); i++) {
523 callbacks.get(i).notify(BiometricSourceType.FACE,
524 mFaceEnabledOnKeyguard.getOrDefault(userId,
525 DEFAULT_KEYGUARD_ENABLED));
526 }
527 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700528 }
529
530 private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
531
532 private final IBiometricEnabledOnKeyguardCallback mCallback;
533
534 EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
535 mCallback = callback;
536 try {
537 mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
538 } catch (RemoteException e) {
539 Slog.w(TAG, "Unable to linkToDeath", e);
540 }
541 }
542
543 void notify(BiometricSourceType sourceType, boolean enabled) {
544 try {
545 mCallback.onChanged(sourceType, enabled);
546 } catch (DeadObjectException e) {
547 Slog.w(TAG, "Death while invoking notify", e);
548 mEnabledOnKeyguardCallbacks.remove(this);
549 } catch (RemoteException e) {
550 Slog.w(TAG, "Failed to invoke onChanged", e);
551 }
552 }
553
554 @Override
555 public void binderDied() {
556 Slog.e(TAG, "Enabled callback binder died");
557 mEnabledOnKeyguardCallbacks.remove(this);
558 }
559 }
560
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700561 // Wrap the client's receiver so we can do things with the BiometricDialog first
562 private final IBiometricServiceReceiverInternal mInternalReceiver =
563 new IBiometricServiceReceiverInternal.Stub() {
564 @Override
565 public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
566 throws RemoteException {
567 SomeArgs args = SomeArgs.obtain();
568 args.arg1 = requireConfirmation;
569 args.arg2 = token;
570 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
571 }
572
573 @Override
574 public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
575 throws RemoteException {
Kevin Chyne674e852019-04-24 12:39:40 -0700576 String failureReason = getContext().getString(R.string.biometric_not_recognized);
577 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700578 }
579
580 @Override
581 public void onError(int cookie, int error, String message) throws RemoteException {
Kevin Chyne674e852019-04-24 12:39:40 -0700582 // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
583 // soft errors and we should allow the user to try authenticating again instead of
584 // dismissing BiometricPrompt.
585 if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
586 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
587 } else {
588 SomeArgs args = SomeArgs.obtain();
589 args.argi1 = cookie;
590 args.argi2 = error;
591 args.arg1 = message;
592 mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
593 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700594 }
595
596 @Override
597 public void onAcquired(int acquiredInfo, String message) throws RemoteException {
598 SomeArgs args = SomeArgs.obtain();
599 args.argi1 = acquiredInfo;
600 args.arg1 = message;
601 mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget();
602 }
603
604 @Override
605 public void onDialogDismissed(int reason) throws RemoteException {
606 mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget();
607 }
608
609 @Override
610 public void onTryAgainPressed() {
611 mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
612 }
613 };
614
615
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700616 /**
617 * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
618 * should not carry any state. The reality is we need to keep a tiny amount of state so that
619 * cancelAuthentication() can go to the right place.
620 */
Kevin Chynbf830a32018-10-07 15:58:46 -0700621 private final class BiometricServiceWrapper extends IBiometricService.Stub {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800622 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800623 public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800624 checkInternalPermission();
Kevin Chyn87f257a2018-11-27 16:26:07 -0800625
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700626 SomeArgs args = SomeArgs.obtain();
627 args.argi1 = cookie;
628 args.arg1 = requireConfirmation;
629 args.argi2 = userId;
630 mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget();
Kevin Chyne92cdae2018-11-21 16:35:04 -0800631 }
632
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700633 @Override // Binder call
634 public void authenticate(IBinder token, long sessionId, int userId,
Kevin Chyn5a90a652019-03-25 18:11:16 -0700635 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
636 IBiometricConfirmDeviceCredentialCallback callback)
Kevin Chyn23289ef2018-11-28 16:32:36 -0800637 throws RemoteException {
Kevin Chyn067085a2018-11-12 15:49:19 -0800638 final int callingUid = Binder.getCallingUid();
639 final int callingPid = Binder.getCallingPid();
640 final int callingUserId = UserHandle.getCallingUserId();
641
Kevin Chyn5a90a652019-03-25 18:11:16 -0700642 // TODO(b/123378871): Remove when moved.
643 if (callback != null) {
644 checkInternalPermission();
645 }
646
Kevin Chyn067085a2018-11-12 15:49:19 -0800647 // In the BiometricServiceBase, check do the AppOps and foreground check.
648 if (userId == callingUserId) {
649 // Check the USE_BIOMETRIC permission here.
650 checkPermission();
651 } else {
652 // Only allow internal clients to authenticate with a different userId
653 Slog.w(TAG, "User " + callingUserId + " is requesting authentication of userid: "
654 + userId);
655 checkInternalPermission();
656 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700657
Kevin Chyne92cdae2018-11-21 16:35:04 -0800658 if (token == null || receiver == null || opPackageName == null || bundle == null) {
Kevin Chyn7c2371a2018-09-12 01:54:18 -0700659 Slog.e(TAG, "Unable to authenticate, one or more null arguments");
660 return;
661 }
662
Kevin Chyn5a90a652019-03-25 18:11:16 -0700663 final boolean isFromConfirmDeviceCredential =
664 bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
665 if (isFromConfirmDeviceCredential) {
666 checkInternalPermission();
667 }
668
Kevin Chyn3a0187192018-10-08 15:40:05 -0700669 // Check the usage of this in system server. Need to remove this check if it becomes
670 // a public API.
Kevin Chyn87f257a2018-11-27 16:26:07 -0800671 final boolean useDefaultTitle =
672 bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
673 if (useDefaultTitle) {
Kevin Chyn3a0187192018-10-08 15:40:05 -0700674 checkInternalPermission();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800675 // Set the default title if necessary
Kevin Chyn9eed6122019-03-28 21:14:19 -0700676 if (TextUtils.isEmpty(bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
677 bundle.putCharSequence(BiometricPrompt.KEY_TITLE,
678 getContext().getString(R.string.biometric_dialog_default_title));
Kevin Chyn23289ef2018-11-28 16:32:36 -0800679 }
Kevin Chyn3a0187192018-10-08 15:40:05 -0700680 }
681
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800682 // Launch CDC instead if necessary. CDC will return results through an AIDL call, since
683 // we can't get activity results. Store the receiver somewhere so we can forward the
684 // result back to the client.
685 // TODO(b/123378871): Remove when moved.
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800686 if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)) {
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800687 mHandler.post(() -> {
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800688 final KeyguardManager kgm = getContext().getSystemService(
689 KeyguardManager.class);
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800690 if (!kgm.isDeviceSecure()) {
691 try {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700692 receiver.onError(
693 BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800694 getContext().getString(
695 R.string.biometric_error_device_not_secured));
696 } catch (RemoteException e) {
697 Slog.e(TAG, "Remote exception", e);
698 }
699 return;
700 }
701 mConfirmDeviceCredentialReceiver = receiver;
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800702 // Use this so we don't need to duplicate logic..
703 final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
Kevin Chynb1373302019-04-03 19:02:27 -0700704 null /* description */, userId);
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800705 // Then give it the bundle to do magic behavior..
706 intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
joshmccloskey2334f7a2019-03-19 16:31:04 -0700707 // Create a new task with this activity located at the root.
708 intent.setFlags(
709 Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800710 getContext().startActivityAsUser(intent, UserHandle.CURRENT);
711 });
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800712 return;
713 }
714
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700715 SomeArgs args = SomeArgs.obtain();
716 args.arg1 = token;
717 args.arg2 = sessionId;
718 args.argi1 = userId;
719 args.arg3 = receiver;
720 args.arg4 = opPackageName;
721 args.arg5 = bundle;
722 args.argi2 = callingUid;
723 args.argi3 = callingPid;
724 args.argi4 = callingUserId;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700725 args.arg6 = callback;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700726
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700727 mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700728 }
729
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800730 @Override // Binder call
731 public void onConfirmDeviceCredentialSuccess() {
732 checkInternalPermission();
Kevin Chyn5a90a652019-03-25 18:11:16 -0700733
734 mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS);
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800735 }
736
737 @Override // Binder call
738 public void onConfirmDeviceCredentialError(int error, String message) {
739 checkInternalPermission();
Kevin Chyn5a90a652019-03-25 18:11:16 -0700740
741 SomeArgs args = SomeArgs.obtain();
742 args.argi1 = error;
743 args.arg1 = message;
744 mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget();
745 }
746
747 @Override // Binder call
748 public void registerCancellationCallback(
749 IBiometricConfirmDeviceCredentialCallback callback) {
750 // TODO(b/123378871): Remove when moved.
751 // This callback replaces the one stored in the current session. If the session is null
752 // we can ignore this, since it means ConfirmDeviceCredential was launched by something
753 // else (not BiometricPrompt)
754 checkInternalPermission();
755
756 mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800757 }
758
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700759 @Override // Binder call
760 public void cancelAuthentication(IBinder token, String opPackageName)
761 throws RemoteException {
762 checkPermission();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800763
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700764 SomeArgs args = SomeArgs.obtain();
765 args.arg1 = token;
766 args.arg2 = opPackageName;
767 mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700768 }
Kevin Chyn05c21502018-09-18 13:07:19 -0700769
770 @Override // Binder call
Kevin Chyn28623b62019-06-11 11:43:33 -0700771 public int canAuthenticate(String opPackageName, int userId) {
772 Slog.d(TAG, "canAuthenticate: User=" + userId
773 + ", Caller=" + UserHandle.getCallingUserId());
Kevin Chyn69183e52018-09-21 17:04:09 -0700774
Kevin Chyn28623b62019-06-11 11:43:33 -0700775 if (userId != UserHandle.getCallingUserId()) {
776 checkInternalPermission();
777 } else {
778 checkPermission();
779 }
780
Kevin Chyn05c21502018-09-18 13:07:19 -0700781 final long ident = Binder.clearCallingIdentity();
Kevin Chyne7411422018-09-27 17:28:20 -0700782 int error;
Kevin Chyn05c21502018-09-18 13:07:19 -0700783 try {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700784 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
Kevin Chyne7411422018-09-27 17:28:20 -0700785 error = result.second;
Kevin Chyn05c21502018-09-18 13:07:19 -0700786 } finally {
787 Binder.restoreCallingIdentity(ident);
788 }
Kevin Chyne7411422018-09-27 17:28:20 -0700789 return error;
790 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700791
Kevin Chynbf830a32018-10-07 15:58:46 -0700792 @Override // Binder call
Kevin Chynb7b54a62018-09-28 18:48:12 -0700793 public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
794 throws RemoteException {
795 checkInternalPermission();
796 mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
797 try {
798 callback.onChanged(BiometricSourceType.FACE,
799 mSettingObserver.getFaceEnabledOnKeyguard());
800 } catch (RemoteException e) {
801 Slog.w(TAG, "Remote exception", e);
802 }
803 }
Kevin Chynbf830a32018-10-07 15:58:46 -0700804
805 @Override // Binder call
806 public void setActiveUser(int userId) {
807 checkInternalPermission();
808 final long ident = Binder.clearCallingIdentity();
809 try {
810 for (int i = 0; i < mAuthenticators.size(); i++) {
811 mAuthenticators.get(i).getAuthenticator().setActiveUser(userId);
812 }
813 } finally {
814 Binder.restoreCallingIdentity(ident);
815 }
816 }
Kevin Chyne92cdae2018-11-21 16:35:04 -0800817
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800818 @Override // Binder call
Kevin Chyna38653c2019-02-11 17:46:21 -0800819 public void resetLockout(byte[] token) {
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800820 checkInternalPermission();
821 final long ident = Binder.clearCallingIdentity();
822 try {
823 if (mFingerprintService != null) {
824 mFingerprintService.resetTimeout(token);
825 }
826 if (mFaceService != null) {
Kevin Chyna38653c2019-02-11 17:46:21 -0800827 mFaceService.resetLockout(token);
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800828 }
829 } catch (RemoteException e) {
830 Slog.e(TAG, "Remote exception", e);
831 } finally {
832 Binder.restoreCallingIdentity(ident);
833 }
834 }
Kevin Chyne7411422018-09-27 17:28:20 -0700835 }
836
837 private void checkAppOp(String opPackageName, int callingUid) {
838 if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
839 opPackageName) != AppOpsManager.MODE_ALLOWED) {
840 Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
841 throw new SecurityException("Permission denied");
Kevin Chyn05c21502018-09-18 13:07:19 -0700842 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700843 }
844
Kevin Chynb7b54a62018-09-28 18:48:12 -0700845 private void checkInternalPermission() {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800846 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
Kevin Chyn3a0187192018-10-08 15:40:05 -0700847 "Must have USE_BIOMETRIC_INTERNAL permission");
Kevin Chynb7b54a62018-09-28 18:48:12 -0700848 }
849
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700850 private void checkPermission() {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700851 if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700852 != PackageManager.PERMISSION_GRANTED) {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700853 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700854 "Must have USE_BIOMETRIC permission");
855 }
856 }
857
858 /**
859 * Initializes the system service.
860 * <p>
861 * Subclasses must define a single argument constructor that accepts the context
862 * and passes it to super.
863 * </p>
864 *
865 * @param context The system server context.
866 */
Kevin Chyn352adfe2018-09-20 22:28:50 -0700867 public BiometricService(Context context) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700868 super(context);
869
Kevin Chyn69183e52018-09-21 17:04:09 -0700870 mAppOps = context.getSystemService(AppOpsManager.class);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700871 mEnabledOnKeyguardCallbacks = new ArrayList<>();
872 mSettingObserver = new SettingObserver(mHandler);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700873
874 final PackageManager pm = context.getPackageManager();
875 mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
876 mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
877 mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700878
879 try {
880 ActivityManager.getService().registerUserSwitchObserver(
881 new UserSwitchObserver() {
882 @Override
883 public void onUserSwitchComplete(int newUserId) {
884 mSettingObserver.updateContentObserver();
Kevin Chyn0c000332019-04-04 16:02:37 -0700885 mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700886 }
887 }, BiometricService.class.getName()
888 );
889 } catch (RemoteException e) {
890 Slog.e(TAG, "Failed to register user switch observer", e);
891 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700892 }
893
894 @Override
895 public void onStart() {
896 // TODO: maybe get these on-demand
897 if (mHasFeatureFingerprint) {
898 mFingerprintService = IFingerprintService.Stub.asInterface(
899 ServiceManager.getService(Context.FINGERPRINT_SERVICE));
900 }
901 if (mHasFeatureFace) {
902 mFaceService = IFaceService.Stub.asInterface(
903 ServiceManager.getService(Context.FACE_SERVICE));
904 }
905
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700906 mActivityTaskManager = ActivityTaskManager.getService();
907 mStatusBarService = IStatusBarService.Stub.asInterface(
908 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
909
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700910 // Cache the authenticators
911 for (int i = 0; i < FEATURE_ID.length; i++) {
912 if (hasFeature(FEATURE_ID[i])) {
913 Authenticator authenticator =
914 new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
915 mAuthenticators.add(authenticator);
916 }
917 }
918
Kevin Chynbf830a32018-10-07 15:58:46 -0700919 publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700920 }
921
922 /**
923 * Checks if there are any available biometrics, and returns the modality. This method also
924 * returns errors through the callback (no biometric feature, hardware not detected, no
925 * templates enrolled, etc). This service must not start authentication if errors are sent.
Kevin Chyne7411422018-09-27 17:28:20 -0700926 *
Kevin Chyn87f257a2018-11-27 16:26:07 -0800927 * @Returns A pair [Modality, Error] with Modality being one of
928 * {@link BiometricAuthenticator#TYPE_NONE},
929 * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
930 * {@link BiometricAuthenticator#TYPE_IRIS},
931 * {@link BiometricAuthenticator#TYPE_FACE}
Kevin Chyne7411422018-09-27 17:28:20 -0700932 * and the error containing one of the {@link BiometricConstants} errors.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700933 */
Kevin Chyn3b9777b2018-12-19 12:03:36 -0800934 private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800935 int modality = TYPE_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700936
937 // No biometric features, send error
938 if (mAuthenticators.isEmpty()) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800939 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700940 }
941
Kevin Chyne7411422018-09-27 17:28:20 -0700942 // Assuming that authenticators are listed in priority-order, the rest of this function
943 // will go through and find the first authenticator that's available, enrolled, and enabled.
944 // The tricky part is returning the correct error. Error strings that are modality-specific
945 // should also respect the priority-order.
946
947 // Find first authenticator that's detected, enrolled, and enabled.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700948 boolean isHardwareDetected = false;
949 boolean hasTemplatesEnrolled = false;
Kevin Chyne7411422018-09-27 17:28:20 -0700950 boolean enabledForApps = false;
951
Kevin Chyn87f257a2018-11-27 16:26:07 -0800952 int firstHwAvailable = TYPE_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700953 for (int i = 0; i < mAuthenticators.size(); i++) {
Kevin Chyne7411422018-09-27 17:28:20 -0700954 modality = mAuthenticators.get(i).getType();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700955 BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
956 if (authenticator.isHardwareDetected()) {
957 isHardwareDetected = true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800958 if (firstHwAvailable == TYPE_NONE) {
Kevin Chyne7411422018-09-27 17:28:20 -0700959 // Store the first one since we want to return the error in correct priority
960 // order.
961 firstHwAvailable = modality;
962 }
Kevin Chyn3b9777b2018-12-19 12:03:36 -0800963 if (authenticator.hasEnrolledTemplates(userId)) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700964 hasTemplatesEnrolled = true;
Kevin Chyn2170ab72019-04-03 20:28:28 -0700965 if (isEnabledForApp(modality, userId)) {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700966 // TODO(b/110907543): When face settings (and other settings) have both a
967 // user toggle as well as a work profile settings page, this needs to be
968 // updated to reflect the correct setting.
Kevin Chyne7411422018-09-27 17:28:20 -0700969 enabledForApps = true;
970 break;
971 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700972 }
973 }
974 }
975
Kevin Chynbe67ce02019-06-10 16:14:22 -0700976 Slog.d(TAG, "checkAndGetBiometricModality: user=" + userId
977 + " isHardwareDetected=" + isHardwareDetected
978 + " hasTemplatesEnrolled=" + hasTemplatesEnrolled
979 + " enabledForApps=" + enabledForApps);
980
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700981 // Check error conditions
982 if (!isHardwareDetected) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800983 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyne7411422018-09-27 17:28:20 -0700984 } else if (!hasTemplatesEnrolled) {
985 // Return the modality here so the correct error string can be sent. This error is
986 // preferred over !enabledForApps
987 return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
988 } else if (!enabledForApps) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800989 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700990 }
991
Kevin Chyna8b57ef2018-10-25 11:09:23 -0700992 return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
Kevin Chyne7411422018-09-27 17:28:20 -0700993 }
994
Kevin Chyn2170ab72019-04-03 20:28:28 -0700995 private boolean isEnabledForApp(int modality, int userId) {
Kevin Chyne7411422018-09-27 17:28:20 -0700996 switch(modality) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800997 case TYPE_FINGERPRINT:
Kevin Chyne7411422018-09-27 17:28:20 -0700998 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800999 case TYPE_IRIS:
Kevin Chyne7411422018-09-27 17:28:20 -07001000 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001001 case TYPE_FACE:
Kevin Chyn2170ab72019-04-03 20:28:28 -07001002 return mSettingObserver.getFaceEnabledForApps(userId);
Kevin Chyne7411422018-09-27 17:28:20 -07001003 default:
1004 Slog.w(TAG, "Unsupported modality: " + modality);
1005 return false;
1006 }
1007 }
1008
1009 private String getErrorString(int type, int error, int vendorCode) {
1010 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001011 case TYPE_FINGERPRINT:
Kevin Chyne7411422018-09-27 17:28:20 -07001012 return FingerprintManager.getErrorString(getContext(), error, vendorCode);
Kevin Chyn87f257a2018-11-27 16:26:07 -08001013 case TYPE_IRIS:
Kevin Chyne7411422018-09-27 17:28:20 -07001014 Slog.w(TAG, "Modality not supported");
1015 return null; // not supported
Kevin Chyn87f257a2018-11-27 16:26:07 -08001016 case TYPE_FACE:
Kevin Chyne7411422018-09-27 17:28:20 -07001017 return FaceManager.getErrorString(getContext(), error, vendorCode);
1018 default:
1019 Slog.w(TAG, "Unable to get error string for modality: " + type);
1020 return null;
1021 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001022 }
1023
1024 private BiometricAuthenticator getAuthenticator(int type) {
1025 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001026 case TYPE_FINGERPRINT:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001027 return (FingerprintManager)
1028 getContext().getSystemService(Context.FINGERPRINT_SERVICE);
Kevin Chyn87f257a2018-11-27 16:26:07 -08001029 case TYPE_IRIS:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001030 return null;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001031 case TYPE_FACE:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001032 return (FaceManager)
1033 getContext().getSystemService(Context.FACE_SERVICE);
1034 default:
1035 return null;
1036 }
1037 }
1038
1039 private boolean hasFeature(int type) {
1040 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001041 case TYPE_FINGERPRINT:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001042 return mHasFeatureFingerprint;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001043 case TYPE_IRIS:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001044 return mHasFeatureIris;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001045 case TYPE_FACE:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001046 return mHasFeatureFace;
1047 default:
1048 return false;
1049 }
1050 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001051
1052 private void logDialogDismissed(int reason) {
1053 if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1054 // Explicit auth, authentication confirmed.
1055 // Latency in this case is authenticated -> confirmed. <Biometric>Service
1056 // should have the first half (first acquired -> authenticated).
1057 final long latency = System.currentTimeMillis()
1058 - mCurrentAuthSession.mAuthenticatedTimeMs;
1059
1060 if (LoggableMonitor.DEBUG) {
1061 Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
1062 + ", User: " + mCurrentAuthSession.mUserId
1063 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1064 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
1065 + ", RequireConfirmation: "
1066 + mCurrentAuthSession.mRequireConfirmation
1067 + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
1068 + ", Latency: " + latency);
1069 }
1070
1071 StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
1072 statsModality(),
1073 mCurrentAuthSession.mUserId,
1074 mCurrentAuthSession.isCrypto(),
1075 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1076 mCurrentAuthSession.mRequireConfirmation,
1077 StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
Kevin Chyn4858da42019-04-11 13:02:56 -07001078 latency,
1079 Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001080 } else {
Kevin Chyn7fca2362019-06-20 17:20:42 -07001081
1082 final long latency = System.currentTimeMillis() - mCurrentAuthSession.mStartTimeMs;
1083
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001084 int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
1085 ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
1086 : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
1087 ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
1088 : 0;
1089 if (LoggableMonitor.DEBUG) {
1090 Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
1091 + ", User: " + mCurrentAuthSession.mUserId
1092 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1093 + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
1094 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
Kevin Chyn7fca2362019-06-20 17:20:42 -07001095 + ", Error: " + error
1096 + ", Latency: " + latency);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001097 }
1098 // Auth canceled
1099 StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
1100 statsModality(),
1101 mCurrentAuthSession.mUserId,
1102 mCurrentAuthSession.isCrypto(),
1103 BiometricsProtoEnums.ACTION_AUTHENTICATE,
1104 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1105 error,
Kevin Chyn4858da42019-04-11 13:02:56 -07001106 0 /* vendorCode */,
Kevin Chyn7fca2362019-06-20 17:20:42 -07001107 Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
1108 latency);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001109 }
1110 }
1111
1112 private int statsModality() {
1113 int modality = 0;
1114 if (mCurrentAuthSession == null) {
1115 return BiometricsProtoEnums.MODALITY_UNKNOWN;
1116 }
1117 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
1118 != 0) {
1119 modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
1120 }
1121 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
1122 modality |= BiometricsProtoEnums.MODALITY_IRIS;
1123 }
1124 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
1125 modality |= BiometricsProtoEnums.MODALITY_FACE;
1126 }
1127 return modality;
1128 }
1129
1130 private void handleTaskStackChanged() {
1131 try {
1132 final List<ActivityManager.RunningTaskInfo> runningTasks =
1133 mActivityTaskManager.getTasks(1);
1134 if (!runningTasks.isEmpty()) {
1135 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
1136 if (mCurrentAuthSession != null
1137 && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
1138 mStatusBarService.hideBiometricDialog();
1139 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1140 mCurrentAuthSession.mClientReceiver.onError(
1141 BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1142 getContext().getString(
1143 com.android.internal.R.string.biometric_error_canceled)
1144 );
1145 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1146 mCurrentAuthSession = null;
1147 }
1148 }
1149 } catch (RemoteException e) {
1150 Slog.e(TAG, "Unable to get running tasks", e);
1151 }
1152 }
1153
1154 private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
1155
1156 try {
1157 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1158 // after user dismissed/canceled dialog).
1159 if (mCurrentAuthSession == null) {
1160 Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
1161 return;
1162 }
1163
1164 if (!requireConfirmation) {
1165 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1166 KeyStore.getInstance().addAuthToken(token);
1167 mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
1168 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1169 mCurrentAuthSession = null;
1170 } else {
1171 mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
1172 // Store the auth token and submit it to keystore after the confirmation
1173 // button has been pressed.
1174 mCurrentAuthSession.mTokenEscrow = token;
1175 mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
1176 }
1177
1178 // Notify SysUI that the biometric has been authenticated. SysUI already knows
1179 // the implicit/explicit state and will react accordingly.
Kevin Chyne674e852019-04-24 12:39:40 -07001180 mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001181 } catch (RemoteException e) {
1182 Slog.e(TAG, "Remote exception", e);
1183 }
1184 }
1185
Kevin Chyne674e852019-04-24 12:39:40 -07001186 private void handleAuthenticationFailed(String failureReason) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001187 try {
1188 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1189 // after user dismissed/canceled dialog).
1190 if (mCurrentAuthSession == null) {
1191 Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
1192 return;
1193 }
1194
Kevin Chyne674e852019-04-24 12:39:40 -07001195 mStatusBarService.onBiometricAuthenticated(false, failureReason);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001196
1197 // TODO: This logic will need to be updated if BP is multi-modal
1198 if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
1199 // Pause authentication. onBiometricAuthenticated(false) causes the
1200 // dialog to show a "try again" button for passive modalities.
1201 mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
1202 }
1203
1204 mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
1205 } catch (RemoteException e) {
1206 Slog.e(TAG, "Remote exception", e);
1207 }
1208 }
1209
Kevin Chyn5a90a652019-03-25 18:11:16 -07001210 private void handleOnConfirmDeviceCredentialSuccess() {
1211 if (mConfirmDeviceCredentialReceiver == null) {
1212 Slog.w(TAG, "onCDCASuccess null!");
1213 return;
1214 }
1215 try {
1216 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1217 mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
1218 if (mCurrentAuthSession != null) {
1219 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1220 mCurrentAuthSession = null;
1221 }
1222 } catch (RemoteException e) {
1223 Slog.e(TAG, "RemoteException", e);
1224 }
1225 mConfirmDeviceCredentialReceiver = null;
1226 }
1227
1228 private void handleOnConfirmDeviceCredentialError(int error, String message) {
1229 if (mConfirmDeviceCredentialReceiver == null) {
1230 Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
1231 return;
1232 }
1233 try {
1234 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1235 mConfirmDeviceCredentialReceiver.onError(error, message);
1236 if (mCurrentAuthSession != null) {
1237 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1238 mCurrentAuthSession = null;
1239 }
1240 } catch (RemoteException e) {
1241 Slog.e(TAG, "RemoteException", e);
1242 }
1243 mConfirmDeviceCredentialReceiver = null;
1244 }
1245
1246 private void handleRegisterCancellationCallback(
1247 IBiometricConfirmDeviceCredentialCallback callback) {
1248 if (mCurrentAuthSession == null) {
1249 Slog.d(TAG, "Current auth session null");
1250 return;
1251 }
1252 Slog.d(TAG, "Updating cancel callback");
1253 mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback;
1254 }
1255
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001256 private void handleOnError(int cookie, int error, String message) {
1257 Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
1258 // Errors can either be from the current auth session or the pending auth session.
1259 // The pending auth session may receive errors such as ERROR_LOCKOUT before
1260 // it becomes the current auth session. Similarly, the current auth session may
1261 // receive errors such as ERROR_CANCELED while the pending auth session is preparing
1262 // to be started. Thus we must match error messages with their cookies to be sure
1263 // of their intended receivers.
1264 try {
1265 if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
Kevin Chyn5a90a652019-03-25 18:11:16 -07001266
1267 if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
1268 // If we were invoked by ConfirmDeviceCredential, do not delete the current
1269 // auth session since we still need to respond to cancel signal while
1270 if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state");
1271
1272 // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass
1273 // screen
1274 mCurrentAuthSession.mClientReceiver.onError(error, message);
1275 mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
1276 mStatusBarService.hideBiometricDialog();
1277 } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001278 mStatusBarService.onBiometricError(message);
1279 if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
1280 mActivityTaskManager.unregisterTaskStackListener(
1281 mTaskStackListener);
1282 mCurrentAuthSession.mClientReceiver.onError(error, message);
1283 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1284 mCurrentAuthSession = null;
1285 mStatusBarService.hideBiometricDialog();
1286 } else {
1287 // Send errors after the dialog is dismissed.
1288 mHandler.postDelayed(() -> {
1289 try {
1290 if (mCurrentAuthSession != null) {
1291 mActivityTaskManager.unregisterTaskStackListener(
1292 mTaskStackListener);
1293 mCurrentAuthSession.mClientReceiver.onError(error,
1294 message);
1295 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1296 mCurrentAuthSession = null;
1297 }
1298 } catch (RemoteException e) {
1299 Slog.e(TAG, "Remote exception", e);
1300 }
1301 }, BiometricPrompt.HIDE_DIALOG_DELAY);
1302 }
1303 } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
1304 // In the "try again" state, we should forward canceled errors to
1305 // the client and and clean up.
1306 mCurrentAuthSession.mClientReceiver.onError(error, message);
1307 mStatusBarService.onBiometricError(message);
1308 mActivityTaskManager.unregisterTaskStackListener(
1309 mTaskStackListener);
1310 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1311 mCurrentAuthSession = null;
1312 } else {
1313 Slog.e(TAG, "Impossible session error state: "
1314 + mCurrentAuthSession.mState);
1315 }
1316 } else if (mPendingAuthSession != null
1317 && mPendingAuthSession.containsCookie(cookie)) {
1318 if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
1319 mPendingAuthSession.mClientReceiver.onError(error, message);
1320 mPendingAuthSession.mState = STATE_AUTH_IDLE;
1321 mPendingAuthSession = null;
1322 } else {
1323 Slog.e(TAG, "Impossible pending session error state: "
1324 + mPendingAuthSession.mState);
1325 }
1326 }
1327 } catch (RemoteException e) {
1328 Slog.e(TAG, "Remote exception", e);
1329 }
1330 }
1331
1332 private void handleOnAcquired(int acquiredInfo, String message) {
1333 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1334 // after user dismissed/canceled dialog).
1335 if (mCurrentAuthSession == null) {
1336 Slog.e(TAG, "onAcquired(): Auth session is null");
1337 return;
1338 }
1339
1340 if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
Kevin Chyn3b53d6f2019-05-01 11:49:05 -07001341 if (message == null) {
1342 Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
1343 return;
1344 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001345 try {
1346 mStatusBarService.onBiometricHelp(message);
1347 } catch (RemoteException e) {
1348 Slog.e(TAG, "Remote exception", e);
1349 }
1350 }
1351 }
1352
1353 private void handleOnDismissed(int reason) {
1354 if (mCurrentAuthSession == null) {
1355 Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
1356 return;
1357 }
1358
1359 logDialogDismissed(reason);
1360
1361 try {
1362 if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1363 // Positive button is used by passive modalities as a "confirm" button,
1364 // do not send to client
1365 mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
1366 // Cancel authentication. Skip the token/package check since we are cancelling
1367 // from system server. The interface is permission protected so this is fine.
1368 cancelInternal(null /* token */, null /* package */, false /* fromClient */);
1369 }
1370 if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
1371 mCurrentAuthSession.mClientReceiver.onError(
1372 BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
1373 getContext().getString(
1374 com.android.internal.R.string.biometric_error_user_canceled));
1375 } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1376 // Have the service send the token to KeyStore, and send onAuthenticated
1377 // to the application
1378 KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
1379 mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
1380 }
Kevin Chyn5a90a652019-03-25 18:11:16 -07001381
1382 // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the
1383 // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when
1384 // ConfirmDeviceCredential is confirmed or canceled.
1385 // TODO(b/123378871): Remove when moved
1386 if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) {
1387 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1388 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1389 mCurrentAuthSession = null;
1390 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001391 } catch (RemoteException e) {
1392 Slog.e(TAG, "Remote exception", e);
1393 }
1394 }
1395
1396 private void handleOnTryAgainPressed() {
1397 Slog.d(TAG, "onTryAgainPressed");
1398 // No need to check permission, since it can only be invoked by SystemUI
1399 // (or system server itself).
1400 authenticateInternal(mCurrentAuthSession.mToken,
1401 mCurrentAuthSession.mSessionId,
1402 mCurrentAuthSession.mUserId,
1403 mCurrentAuthSession.mClientReceiver,
1404 mCurrentAuthSession.mOpPackageName,
1405 mCurrentAuthSession.mBundle,
1406 mCurrentAuthSession.mCallingUid,
1407 mCurrentAuthSession.mCallingPid,
1408 mCurrentAuthSession.mCallingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001409 mCurrentAuthSession.mModality,
1410 mCurrentAuthSession.mConfirmDeviceCredentialCallback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001411 }
1412
1413 private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation,
1414 int userId) {
1415 Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
1416 while (it.hasNext()) {
1417 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1418 if (pair.getValue() == cookie) {
1419 mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
1420 mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
1421 Slog.d(TAG, "Matched cookie: " + cookie + ", "
1422 + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
1423 break;
1424 }
1425 }
1426
1427 if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
1428 final boolean continuing = mCurrentAuthSession != null
1429 && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
1430
1431 mCurrentAuthSession = mPendingAuthSession;
Kevin Chyn7fca2362019-06-20 17:20:42 -07001432
1433 // Time starts when lower layers are ready to start the client.
1434 mCurrentAuthSession.mStartTimeMs = System.currentTimeMillis();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001435 mPendingAuthSession = null;
1436
1437 mCurrentAuthSession.mState = STATE_AUTH_STARTED;
1438 try {
1439 int modality = TYPE_NONE;
1440 it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
1441 while (it.hasNext()) {
1442 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1443 if (pair.getKey() == TYPE_FINGERPRINT) {
1444 mFingerprintService.startPreparedClient(pair.getValue());
1445 } else if (pair.getKey() == TYPE_IRIS) {
1446 Slog.e(TAG, "Iris unsupported");
1447 } else if (pair.getKey() == TYPE_FACE) {
1448 mFaceService.startPreparedClient(pair.getValue());
1449 } else {
1450 Slog.e(TAG, "Unknown modality: " + pair.getKey());
1451 }
1452 modality |= pair.getKey();
1453 }
1454
1455 if (!continuing) {
1456 mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
1457 mInternalReceiver, modality, requireConfirmation, userId);
1458 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
1459 }
1460 } catch (RemoteException e) {
1461 Slog.e(TAG, "Remote exception", e);
1462 }
1463 }
1464 }
1465
1466 private void handleAuthenticate(IBinder token, long sessionId, int userId,
1467 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001468 int callingUid, int callingPid, int callingUserId,
1469 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001470
1471 mHandler.post(() -> {
1472 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
1473 final int modality = result.first;
1474 final int error = result.second;
1475
1476 // Check for errors, notify callback, and return
1477 if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
1478 try {
1479 final String hardwareUnavailable =
1480 getContext().getString(R.string.biometric_error_hw_unavailable);
1481 switch (error) {
1482 case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
1483 receiver.onError(error, hardwareUnavailable);
1484 break;
1485 case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
1486 receiver.onError(error, hardwareUnavailable);
1487 break;
1488 case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
1489 receiver.onError(error,
1490 getErrorString(modality, error, 0 /* vendorCode */));
1491 break;
1492 default:
1493 Slog.e(TAG, "Unhandled error");
1494 break;
1495 }
1496 } catch (RemoteException e) {
1497 Slog.e(TAG, "Unable to send error", e);
1498 }
1499 return;
1500 }
1501
1502 mCurrentModality = modality;
1503
1504 // Start preparing for authentication. Authentication starts when
1505 // all modalities requested have invoked onReadyForAuthentication.
1506 authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001507 callingUid, callingPid, callingUserId, modality, callback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001508 });
1509 }
1510
1511 /**
1512 * authenticate() (above) which is called from BiometricPrompt determines which
1513 * modality/modalities to start authenticating with. authenticateInternal() should only be
1514 * used for:
1515 * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
1516 * invoked, shortly after which BiometricPrompt is shown and authentication starts
1517 * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
1518 * and the user has pressed "try again"
1519 */
1520 private void authenticateInternal(IBinder token, long sessionId, int userId,
1521 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001522 int callingUid, int callingPid, int callingUserId, int modality,
1523 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001524 try {
1525 boolean requireConfirmation = bundle.getBoolean(
1526 BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
1527 if ((modality & TYPE_FACE) != 0) {
1528 // Check if the user has forced confirmation to be required in Settings.
1529 requireConfirmation = requireConfirmation
Kevin Chyn2170ab72019-04-03 20:28:28 -07001530 || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001531 }
1532 // Generate random cookies to pass to the services that should prepare to start
1533 // authenticating. Store the cookie here and wait for all services to "ack"
1534 // with the cookie. Once all cookies are received, we can show the prompt
1535 // and let the services start authenticating. The cookie should be non-zero.
1536 final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1537 Slog.d(TAG, "Creating auth session. Modality: " + modality
1538 + ", cookie: " + cookie);
1539 final HashMap<Integer, Integer> authenticators = new HashMap<>();
1540 authenticators.put(modality, cookie);
1541 mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
1542 receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001543 modality, requireConfirmation, callback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001544 mPendingAuthSession.mState = STATE_AUTH_CALLED;
1545 // No polymorphism :(
1546 if ((modality & TYPE_FINGERPRINT) != 0) {
1547 mFingerprintService.prepareForAuthentication(token, sessionId, userId,
1548 mInternalReceiver, opPackageName, cookie,
1549 callingUid, callingPid, callingUserId);
1550 }
1551 if ((modality & TYPE_IRIS) != 0) {
1552 Slog.w(TAG, "Iris unsupported");
1553 }
1554 if ((modality & TYPE_FACE) != 0) {
1555 mFaceService.prepareForAuthentication(requireConfirmation,
1556 token, sessionId, userId, mInternalReceiver, opPackageName,
1557 cookie, callingUid, callingPid, callingUserId);
1558 }
1559 } catch (RemoteException e) {
1560 Slog.e(TAG, "Unable to start authentication", e);
1561 }
1562 }
1563
1564 private void handleCancelAuthentication(IBinder token, String opPackageName) {
1565 if (token == null || opPackageName == null) {
1566 Slog.e(TAG, "Unable to cancel, one or more null arguments");
1567 return;
1568 }
1569
Kevin Chyn5a90a652019-03-25 18:11:16 -07001570 if (mCurrentAuthSession != null
1571 && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) {
1572 if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing");
1573 try {
1574 mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel();
1575 } catch (RemoteException e) {
1576 Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e);
1577 }
1578
1579 // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up.
1580 handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1581 getContext().getString(R.string.biometric_error_canceled));
1582 } else if (mCurrentAuthSession != null
1583 && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
1584 // We need to check the current authenticators state. If we're pending confirm
1585 // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
1586 // since we won't be getting an onError from the driver.
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001587 try {
1588 // Send error to client
1589 mCurrentAuthSession.mClientReceiver.onError(
1590 BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1591 getContext().getString(
1592 com.android.internal.R.string.biometric_error_user_canceled)
1593 );
1594
1595 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1596 mCurrentAuthSession = null;
1597 mStatusBarService.hideBiometricDialog();
1598 } catch (RemoteException e) {
1599 Slog.e(TAG, "Remote exception", e);
1600 }
1601 } else {
Kevin Chyn5a90a652019-03-25 18:11:16 -07001602 boolean fromCDC = false;
1603 if (mCurrentAuthSession != null) {
1604 fromCDC = mCurrentAuthSession.mBundle.getBoolean(
1605 BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
1606 }
1607
1608 if (fromCDC) {
1609 if (DEBUG) Slog.d(TAG, "Cancelling from CDC");
1610 cancelInternal(token, opPackageName, false /* fromClient */);
1611 } else {
1612 cancelInternal(token, opPackageName, true /* fromClient */);
1613 }
1614
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001615 }
1616 }
1617
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001618 void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
1619 final int callingUid = Binder.getCallingUid();
1620 final int callingPid = Binder.getCallingPid();
1621 final int callingUserId = UserHandle.getCallingUserId();
1622 mHandler.post(() -> {
1623 try {
1624 // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
1625 // drivers have canceled authentication.
1626 if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
1627 mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
1628 callingUid, callingPid, callingUserId, fromClient);
1629 }
1630 if ((mCurrentModality & TYPE_IRIS) != 0) {
1631 Slog.w(TAG, "Iris unsupported");
1632 }
1633 if ((mCurrentModality & TYPE_FACE) != 0) {
1634 mFaceService.cancelAuthenticationFromService(token, opPackageName,
1635 callingUid, callingPid, callingUserId, fromClient);
1636 }
1637 } catch (RemoteException e) {
1638 Slog.e(TAG, "Unable to cancel authentication");
1639 }
1640 });
1641 }
1642
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001643}