blob: b9a6a1020a77fa3ac105928281ff2614d6f2d4e1 [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
173 // Timestamp when hardware authentication occurred
174 private long mAuthenticatedTimeMs;
175
Kevin Chyn5a90a652019-03-25 18:11:16 -0700176 // TODO(b/123378871): Remove when moved.
177 private IBiometricConfirmDeviceCredentialCallback mConfirmDeviceCredentialCallback;
178
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700179 AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
180 int userId, IBiometricServiceReceiver receiver, String opPackageName,
181 Bundle bundle, int callingUid, int callingPid, int callingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -0700182 int modality, boolean requireConfirmation,
183 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700184 mModalitiesWaiting = modalities;
185 mToken = token;
186 mSessionId = sessionId;
187 mUserId = userId;
188 mClientReceiver = receiver;
189 mOpPackageName = opPackageName;
190 mBundle = bundle;
191 mCallingUid = callingUid;
192 mCallingPid = callingPid;
193 mCallingUserId = callingUserId;
194 mModality = modality;
195 mRequireConfirmation = requireConfirmation;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700196 mConfirmDeviceCredentialCallback = callback;
197
198 if (isFromConfirmDeviceCredential()) {
199 try {
200 token.linkToDeath(this, 0 /* flags */);
201 } catch (RemoteException e) {
202 Slog.e(TAG, "Unable to link to death", e);
203 }
204 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700205 }
206
207 boolean isCrypto() {
208 return mSessionId != 0;
209 }
210
Kevin Chyn5a90a652019-03-25 18:11:16 -0700211 boolean isFromConfirmDeviceCredential() {
212 return mBundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
213 }
214
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700215 boolean containsCookie(int cookie) {
216 if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
217 return true;
218 }
219 if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
220 return true;
221 }
222 return false;
223 }
Kevin Chyn5a90a652019-03-25 18:11:16 -0700224
225 // TODO(b/123378871): Remove when moved.
226 @Override
227 public void binderDied() {
228 mHandler.post(() -> {
229 Slog.e(TAG, "Binder died, killing ConfirmDeviceCredential");
230 if (mConfirmDeviceCredentialCallback == null) {
231 Slog.e(TAG, "Callback is null");
232 return;
233 }
234
235 try {
236 mConfirmDeviceCredentialCallback.cancel();
237 mConfirmDeviceCredentialCallback = null;
238 } catch (RemoteException e) {
239 Slog.e(TAG, "Unable to send cancel", e);
240 }
241 });
242 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700243 }
244
245 private final class BiometricTaskStackListener extends TaskStackListener {
246 @Override
247 public void onTaskStackChanged() {
248 mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED);
249 }
250 }
251
Kevin Chyn69183e52018-09-21 17:04:09 -0700252 private final AppOpsManager mAppOps;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700253 private final boolean mHasFeatureFingerprint;
254 private final boolean mHasFeatureIris;
255 private final boolean mHasFeatureFace;
Kevin Chynb7b54a62018-09-28 18:48:12 -0700256 private final SettingObserver mSettingObserver;
257 private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700258 private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
259 private final Random mRandom = new Random();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700260
261 private IFingerprintService mFingerprintService;
262 private IFaceService mFaceService;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700263 private IActivityTaskManager mActivityTaskManager;
264 private IStatusBarService mStatusBarService;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700265
266 // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
267 // polymorphism :/
268 final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
269
270 // Cache the current service that's being used. This is the service which
271 // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
272 // check (is caller the current client) is done in the <Biometric>Service.
273 // Since Settings/System (not application) is responsible for changing preference, this
274 // should be safe.
275 private int mCurrentModality;
276
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700277 // The current authentication session, null if idle/done. We need to track both the current
278 // and pending sessions since errors may be sent to either.
279 private AuthSession mCurrentAuthSession;
280 private AuthSession mPendingAuthSession;
281
Kevin Chyn5a90a652019-03-25 18:11:16 -0700282 // TODO(b/123378871): Remove when moved.
283 // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
284 // client (app) receiver. BiometricService internally launches CDCA which invokes
285 // BiometricService to start authentication (normal path). When auth is success/rejected,
286 // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded
287 // to this receiver.
288 private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
289
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700290 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
291 @Override
292 public void handleMessage(Message msg) {
293 switch (msg.what) {
294 case MSG_ON_TASK_STACK_CHANGED: {
295 handleTaskStackChanged();
296 break;
297 }
298
299 case MSG_ON_AUTHENTICATION_SUCCEEDED: {
300 SomeArgs args = (SomeArgs) msg.obj;
301 handleAuthenticationSucceeded(
302 (boolean) args.arg1 /* requireConfirmation */,
303 (byte[]) args.arg2 /* token */);
304 args.recycle();
305 break;
306 }
307
308 case MSG_ON_AUTHENTICATION_FAILED: {
Kevin Chyne674e852019-04-24 12:39:40 -0700309 handleAuthenticationFailed((String) msg.obj /* failureReason */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700310 break;
311 }
312
313 case MSG_ON_ERROR: {
314 SomeArgs args = (SomeArgs) msg.obj;
315 handleOnError(
316 args.argi1 /* cookie */,
317 args.argi2 /* error */,
318 (String) args.arg1 /* message */);
319 args.recycle();
320 break;
321 }
322
323 case MSG_ON_ACQUIRED: {
324 SomeArgs args = (SomeArgs) msg.obj;
325 handleOnAcquired(
326 args.argi1 /* acquiredInfo */,
327 (String) args.arg1 /* message */);
328 args.recycle();
329 break;
330 }
331
332 case MSG_ON_DISMISSED: {
333 handleOnDismissed(msg.arg1);
334 break;
335 }
336
337 case MSG_ON_TRY_AGAIN_PRESSED: {
338 handleOnTryAgainPressed();
339 break;
340 }
341
342 case MSG_ON_READY_FOR_AUTHENTICATION: {
343 SomeArgs args = (SomeArgs) msg.obj;
344 handleOnReadyForAuthentication(
345 args.argi1 /* cookie */,
346 (boolean) args.arg1 /* requireConfirmation */,
347 args.argi2 /* userId */);
348 args.recycle();
349 break;
350 }
351
352 case MSG_AUTHENTICATE: {
353 SomeArgs args = (SomeArgs) msg.obj;
354 handleAuthenticate(
355 (IBinder) args.arg1 /* token */,
356 (long) args.arg2 /* sessionId */,
357 args.argi1 /* userid */,
358 (IBiometricServiceReceiver) args.arg3 /* receiver */,
359 (String) args.arg4 /* opPackageName */,
360 (Bundle) args.arg5 /* bundle */,
361 args.argi2 /* callingUid */,
362 args.argi3 /* callingPid */,
Kevin Chyn5a90a652019-03-25 18:11:16 -0700363 args.argi4 /* callingUserId */,
364 (IBiometricConfirmDeviceCredentialCallback) args.arg6 /* callback */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700365 args.recycle();
366 break;
367 }
368
369 case MSG_CANCEL_AUTHENTICATION: {
370 SomeArgs args = (SomeArgs) msg.obj;
371 handleCancelAuthentication(
372 (IBinder) args.arg1 /* token */,
373 (String) args.arg2 /* opPackageName */);
374 args.recycle();
375 break;
376 }
377
Kevin Chyn5a90a652019-03-25 18:11:16 -0700378 case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS: {
379 handleOnConfirmDeviceCredentialSuccess();
380 break;
381 }
382
383 case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR: {
384 SomeArgs args = (SomeArgs) msg.obj;
385 handleOnConfirmDeviceCredentialError(
386 args.argi1 /* error */,
387 (String) args.arg1 /* errorMsg */);
388 args.recycle();
389 break;
390 }
391
392 case MSG_REGISTER_CANCELLATION_CALLBACK: {
393 handleRegisterCancellationCallback(
394 (IBiometricConfirmDeviceCredentialCallback) msg.obj /* callback */);
395 break;
396 }
397
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700398 default:
Kevin Chyn5a90a652019-03-25 18:11:16 -0700399 Slog.e(TAG, "Unknown message: " + msg);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700400 break;
401 }
402 }
403 };
404
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700405 private final class Authenticator {
406 int mType;
407 BiometricAuthenticator mAuthenticator;
408
409 Authenticator(int type, BiometricAuthenticator authenticator) {
410 mType = type;
411 mAuthenticator = authenticator;
412 }
413
414 int getType() {
415 return mType;
416 }
417
418 BiometricAuthenticator getAuthenticator() {
419 return mAuthenticator;
420 }
421 }
422
Kevin Chynb7b54a62018-09-28 18:48:12 -0700423 private final class SettingObserver extends ContentObserver {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700424
425 private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
426 private static final boolean DEFAULT_APP_ENABLED = true;
427 private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
428
Kevin Chynb7b54a62018-09-28 18:48:12 -0700429 private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
430 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
431 private final Uri FACE_UNLOCK_APP_ENABLED =
432 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
Kevin Chyn31402832019-01-10 15:56:41 -0800433 private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
434 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700435
436 private final ContentResolver mContentResolver;
Kevin Chyn2170ab72019-04-03 20:28:28 -0700437
438 private Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
439 private Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
440 private Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
Kevin Chynb7b54a62018-09-28 18:48:12 -0700441
442 /**
443 * Creates a content observer.
444 *
445 * @param handler The handler to run {@link #onChange} on, or null if none.
446 */
447 SettingObserver(Handler handler) {
448 super(handler);
449 mContentResolver = getContext().getContentResolver();
450 updateContentObserver();
451 }
452
453 void updateContentObserver() {
454 mContentResolver.unregisterContentObserver(this);
455 mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
456 false /* notifyForDescendents */,
457 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700458 UserHandle.USER_ALL);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700459 mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
460 false /* notifyForDescendents */,
461 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700462 UserHandle.USER_ALL);
Kevin Chyn31402832019-01-10 15:56:41 -0800463 mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
464 false /* notifyForDescendents */,
465 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700466 UserHandle.USER_ALL);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700467 }
468
469 @Override
Kevin Chyn2170ab72019-04-03 20:28:28 -0700470 public void onChange(boolean selfChange, Uri uri, int userId) {
Kevin Chynb7b54a62018-09-28 18:48:12 -0700471 if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700472 mFaceEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser(
Kevin Chynb7b54a62018-09-28 18:48:12 -0700473 mContentResolver,
474 Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700475 DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */,
476 userId) != 0);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700477
Kevin Chyn2170ab72019-04-03 20:28:28 -0700478 if (userId == ActivityManager.getCurrentUser()) {
Kevin Chyn0c000332019-04-04 16:02:37 -0700479 notifyEnabledOnKeyguardCallbacks(userId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700480 }
481 } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700482 mFaceEnabledForApps.put(userId, Settings.Secure.getIntForUser(
Kevin Chynb7b54a62018-09-28 18:48:12 -0700483 mContentResolver,
484 Settings.Secure.FACE_UNLOCK_APP_ENABLED,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700485 DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
486 userId) != 0);
Kevin Chyn31402832019-01-10 15:56:41 -0800487 } else if (FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700488 mFaceAlwaysRequireConfirmation.put(userId, Settings.Secure.getIntForUser(
Kevin Chyn31402832019-01-10 15:56:41 -0800489 mContentResolver,
490 Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700491 DEFAULT_ALWAYS_REQUIRE_CONFIRMATION ? 1 : 0 /* default */,
492 userId) != 0);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700493 }
494 }
495
496 boolean getFaceEnabledOnKeyguard() {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700497 return mFaceEnabledOnKeyguard.getOrDefault(
498 ActivityManager.getCurrentUser(), DEFAULT_KEYGUARD_ENABLED);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700499 }
500
Kevin Chyn2170ab72019-04-03 20:28:28 -0700501 boolean getFaceEnabledForApps(int userId) {
502 return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700503 }
Kevin Chyn31402832019-01-10 15:56:41 -0800504
Kevin Chyn2170ab72019-04-03 20:28:28 -0700505 boolean getFaceAlwaysRequireConfirmation(int userId) {
506 return mFaceAlwaysRequireConfirmation
507 .getOrDefault(userId, DEFAULT_ALWAYS_REQUIRE_CONFIRMATION);
Kevin Chyn31402832019-01-10 15:56:41 -0800508 }
Kevin Chyn0c000332019-04-04 16:02:37 -0700509
510 void notifyEnabledOnKeyguardCallbacks(int userId) {
511 List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
512 for (int i = 0; i < callbacks.size(); i++) {
513 callbacks.get(i).notify(BiometricSourceType.FACE,
514 mFaceEnabledOnKeyguard.getOrDefault(userId,
515 DEFAULT_KEYGUARD_ENABLED));
516 }
517 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700518 }
519
520 private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
521
522 private final IBiometricEnabledOnKeyguardCallback mCallback;
523
524 EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
525 mCallback = callback;
526 try {
527 mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
528 } catch (RemoteException e) {
529 Slog.w(TAG, "Unable to linkToDeath", e);
530 }
531 }
532
533 void notify(BiometricSourceType sourceType, boolean enabled) {
534 try {
535 mCallback.onChanged(sourceType, enabled);
536 } catch (DeadObjectException e) {
537 Slog.w(TAG, "Death while invoking notify", e);
538 mEnabledOnKeyguardCallbacks.remove(this);
539 } catch (RemoteException e) {
540 Slog.w(TAG, "Failed to invoke onChanged", e);
541 }
542 }
543
544 @Override
545 public void binderDied() {
546 Slog.e(TAG, "Enabled callback binder died");
547 mEnabledOnKeyguardCallbacks.remove(this);
548 }
549 }
550
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700551 // Wrap the client's receiver so we can do things with the BiometricDialog first
552 private final IBiometricServiceReceiverInternal mInternalReceiver =
553 new IBiometricServiceReceiverInternal.Stub() {
554 @Override
555 public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
556 throws RemoteException {
557 SomeArgs args = SomeArgs.obtain();
558 args.arg1 = requireConfirmation;
559 args.arg2 = token;
560 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
561 }
562
563 @Override
564 public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
565 throws RemoteException {
Kevin Chyne674e852019-04-24 12:39:40 -0700566 String failureReason = getContext().getString(R.string.biometric_not_recognized);
567 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700568 }
569
570 @Override
571 public void onError(int cookie, int error, String message) throws RemoteException {
Kevin Chyne674e852019-04-24 12:39:40 -0700572 // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
573 // soft errors and we should allow the user to try authenticating again instead of
574 // dismissing BiometricPrompt.
575 if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
576 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
577 } else {
578 SomeArgs args = SomeArgs.obtain();
579 args.argi1 = cookie;
580 args.argi2 = error;
581 args.arg1 = message;
582 mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
583 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700584 }
585
586 @Override
587 public void onAcquired(int acquiredInfo, String message) throws RemoteException {
588 SomeArgs args = SomeArgs.obtain();
589 args.argi1 = acquiredInfo;
590 args.arg1 = message;
591 mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget();
592 }
593
594 @Override
595 public void onDialogDismissed(int reason) throws RemoteException {
596 mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget();
597 }
598
599 @Override
600 public void onTryAgainPressed() {
601 mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
602 }
603 };
604
605
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700606 /**
607 * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
608 * should not carry any state. The reality is we need to keep a tiny amount of state so that
609 * cancelAuthentication() can go to the right place.
610 */
Kevin Chynbf830a32018-10-07 15:58:46 -0700611 private final class BiometricServiceWrapper extends IBiometricService.Stub {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800612 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800613 public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800614 checkInternalPermission();
Kevin Chyn87f257a2018-11-27 16:26:07 -0800615
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700616 SomeArgs args = SomeArgs.obtain();
617 args.argi1 = cookie;
618 args.arg1 = requireConfirmation;
619 args.argi2 = userId;
620 mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget();
Kevin Chyne92cdae2018-11-21 16:35:04 -0800621 }
622
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700623 @Override // Binder call
624 public void authenticate(IBinder token, long sessionId, int userId,
Kevin Chyn5a90a652019-03-25 18:11:16 -0700625 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
626 IBiometricConfirmDeviceCredentialCallback callback)
Kevin Chyn23289ef2018-11-28 16:32:36 -0800627 throws RemoteException {
Kevin Chyn067085a2018-11-12 15:49:19 -0800628 final int callingUid = Binder.getCallingUid();
629 final int callingPid = Binder.getCallingPid();
630 final int callingUserId = UserHandle.getCallingUserId();
631
Kevin Chyn5a90a652019-03-25 18:11:16 -0700632 // TODO(b/123378871): Remove when moved.
633 if (callback != null) {
634 checkInternalPermission();
635 }
636
Kevin Chyn067085a2018-11-12 15:49:19 -0800637 // In the BiometricServiceBase, check do the AppOps and foreground check.
638 if (userId == callingUserId) {
639 // Check the USE_BIOMETRIC permission here.
640 checkPermission();
641 } else {
642 // Only allow internal clients to authenticate with a different userId
643 Slog.w(TAG, "User " + callingUserId + " is requesting authentication of userid: "
644 + userId);
645 checkInternalPermission();
646 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700647
Kevin Chyne92cdae2018-11-21 16:35:04 -0800648 if (token == null || receiver == null || opPackageName == null || bundle == null) {
Kevin Chyn7c2371a2018-09-12 01:54:18 -0700649 Slog.e(TAG, "Unable to authenticate, one or more null arguments");
650 return;
651 }
652
Kevin Chyn5a90a652019-03-25 18:11:16 -0700653 final boolean isFromConfirmDeviceCredential =
654 bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
655 if (isFromConfirmDeviceCredential) {
656 checkInternalPermission();
657 }
658
Kevin Chyn3a0187192018-10-08 15:40:05 -0700659 // Check the usage of this in system server. Need to remove this check if it becomes
660 // a public API.
Kevin Chyn87f257a2018-11-27 16:26:07 -0800661 final boolean useDefaultTitle =
662 bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
663 if (useDefaultTitle) {
Kevin Chyn3a0187192018-10-08 15:40:05 -0700664 checkInternalPermission();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800665 // Set the default title if necessary
Kevin Chyn9eed6122019-03-28 21:14:19 -0700666 if (TextUtils.isEmpty(bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
667 bundle.putCharSequence(BiometricPrompt.KEY_TITLE,
668 getContext().getString(R.string.biometric_dialog_default_title));
Kevin Chyn23289ef2018-11-28 16:32:36 -0800669 }
Kevin Chyn3a0187192018-10-08 15:40:05 -0700670 }
671
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800672 // Launch CDC instead if necessary. CDC will return results through an AIDL call, since
673 // we can't get activity results. Store the receiver somewhere so we can forward the
674 // result back to the client.
675 // TODO(b/123378871): Remove when moved.
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800676 if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)) {
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800677 mHandler.post(() -> {
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800678 final KeyguardManager kgm = getContext().getSystemService(
679 KeyguardManager.class);
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800680 if (!kgm.isDeviceSecure()) {
681 try {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700682 receiver.onError(
683 BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800684 getContext().getString(
685 R.string.biometric_error_device_not_secured));
686 } catch (RemoteException e) {
687 Slog.e(TAG, "Remote exception", e);
688 }
689 return;
690 }
691 mConfirmDeviceCredentialReceiver = receiver;
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800692 // Use this so we don't need to duplicate logic..
693 final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
Kevin Chynb1373302019-04-03 19:02:27 -0700694 null /* description */, userId);
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800695 // Then give it the bundle to do magic behavior..
696 intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
joshmccloskey2334f7a2019-03-19 16:31:04 -0700697 // Create a new task with this activity located at the root.
698 intent.setFlags(
699 Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800700 getContext().startActivityAsUser(intent, UserHandle.CURRENT);
701 });
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800702 return;
703 }
704
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700705 SomeArgs args = SomeArgs.obtain();
706 args.arg1 = token;
707 args.arg2 = sessionId;
708 args.argi1 = userId;
709 args.arg3 = receiver;
710 args.arg4 = opPackageName;
711 args.arg5 = bundle;
712 args.argi2 = callingUid;
713 args.argi3 = callingPid;
714 args.argi4 = callingUserId;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700715 args.arg6 = callback;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700716
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700717 mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700718 }
719
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800720 @Override // Binder call
721 public void onConfirmDeviceCredentialSuccess() {
722 checkInternalPermission();
Kevin Chyn5a90a652019-03-25 18:11:16 -0700723
724 mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS);
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800725 }
726
727 @Override // Binder call
728 public void onConfirmDeviceCredentialError(int error, String message) {
729 checkInternalPermission();
Kevin Chyn5a90a652019-03-25 18:11:16 -0700730
731 SomeArgs args = SomeArgs.obtain();
732 args.argi1 = error;
733 args.arg1 = message;
734 mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget();
735 }
736
737 @Override // Binder call
738 public void registerCancellationCallback(
739 IBiometricConfirmDeviceCredentialCallback callback) {
740 // TODO(b/123378871): Remove when moved.
741 // This callback replaces the one stored in the current session. If the session is null
742 // we can ignore this, since it means ConfirmDeviceCredential was launched by something
743 // else (not BiometricPrompt)
744 checkInternalPermission();
745
746 mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800747 }
748
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700749 @Override // Binder call
750 public void cancelAuthentication(IBinder token, String opPackageName)
751 throws RemoteException {
752 checkPermission();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800753
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700754 SomeArgs args = SomeArgs.obtain();
755 args.arg1 = token;
756 args.arg2 = opPackageName;
757 mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700758 }
Kevin Chyn05c21502018-09-18 13:07:19 -0700759
760 @Override // Binder call
Kevin Chyne7411422018-09-27 17:28:20 -0700761 public int canAuthenticate(String opPackageName) {
Kevin Chyn05c21502018-09-18 13:07:19 -0700762 checkPermission();
Kevin Chyn69183e52018-09-21 17:04:09 -0700763
Kevin Chyn75dbb832018-10-05 17:32:57 -0700764 final int userId = UserHandle.getCallingUserId();
Kevin Chyn05c21502018-09-18 13:07:19 -0700765 final long ident = Binder.clearCallingIdentity();
Kevin Chyne7411422018-09-27 17:28:20 -0700766 int error;
Kevin Chyn05c21502018-09-18 13:07:19 -0700767 try {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700768 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
Kevin Chyne7411422018-09-27 17:28:20 -0700769 error = result.second;
Kevin Chyn05c21502018-09-18 13:07:19 -0700770 } finally {
771 Binder.restoreCallingIdentity(ident);
772 }
Kevin Chyne7411422018-09-27 17:28:20 -0700773 return error;
774 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700775
Kevin Chynbf830a32018-10-07 15:58:46 -0700776 @Override // Binder call
Kevin Chynb7b54a62018-09-28 18:48:12 -0700777 public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
778 throws RemoteException {
779 checkInternalPermission();
780 mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
781 try {
782 callback.onChanged(BiometricSourceType.FACE,
783 mSettingObserver.getFaceEnabledOnKeyguard());
784 } catch (RemoteException e) {
785 Slog.w(TAG, "Remote exception", e);
786 }
787 }
Kevin Chynbf830a32018-10-07 15:58:46 -0700788
789 @Override // Binder call
790 public void setActiveUser(int userId) {
791 checkInternalPermission();
792 final long ident = Binder.clearCallingIdentity();
793 try {
794 for (int i = 0; i < mAuthenticators.size(); i++) {
795 mAuthenticators.get(i).getAuthenticator().setActiveUser(userId);
796 }
797 } finally {
798 Binder.restoreCallingIdentity(ident);
799 }
800 }
Kevin Chyne92cdae2018-11-21 16:35:04 -0800801
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800802 @Override // Binder call
Kevin Chyna38653c2019-02-11 17:46:21 -0800803 public void resetLockout(byte[] token) {
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800804 checkInternalPermission();
805 final long ident = Binder.clearCallingIdentity();
806 try {
807 if (mFingerprintService != null) {
808 mFingerprintService.resetTimeout(token);
809 }
810 if (mFaceService != null) {
Kevin Chyna38653c2019-02-11 17:46:21 -0800811 mFaceService.resetLockout(token);
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800812 }
813 } catch (RemoteException e) {
814 Slog.e(TAG, "Remote exception", e);
815 } finally {
816 Binder.restoreCallingIdentity(ident);
817 }
818 }
Kevin Chyne7411422018-09-27 17:28:20 -0700819 }
820
821 private void checkAppOp(String opPackageName, int callingUid) {
822 if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
823 opPackageName) != AppOpsManager.MODE_ALLOWED) {
824 Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
825 throw new SecurityException("Permission denied");
Kevin Chyn05c21502018-09-18 13:07:19 -0700826 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700827 }
828
Kevin Chynb7b54a62018-09-28 18:48:12 -0700829 private void checkInternalPermission() {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800830 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
Kevin Chyn3a0187192018-10-08 15:40:05 -0700831 "Must have USE_BIOMETRIC_INTERNAL permission");
Kevin Chynb7b54a62018-09-28 18:48:12 -0700832 }
833
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700834 private void checkPermission() {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700835 if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700836 != PackageManager.PERMISSION_GRANTED) {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700837 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700838 "Must have USE_BIOMETRIC permission");
839 }
840 }
841
842 /**
843 * Initializes the system service.
844 * <p>
845 * Subclasses must define a single argument constructor that accepts the context
846 * and passes it to super.
847 * </p>
848 *
849 * @param context The system server context.
850 */
Kevin Chyn352adfe2018-09-20 22:28:50 -0700851 public BiometricService(Context context) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700852 super(context);
853
Kevin Chyn69183e52018-09-21 17:04:09 -0700854 mAppOps = context.getSystemService(AppOpsManager.class);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700855 mEnabledOnKeyguardCallbacks = new ArrayList<>();
856 mSettingObserver = new SettingObserver(mHandler);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700857
858 final PackageManager pm = context.getPackageManager();
859 mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
860 mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
861 mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700862
863 try {
864 ActivityManager.getService().registerUserSwitchObserver(
865 new UserSwitchObserver() {
866 @Override
867 public void onUserSwitchComplete(int newUserId) {
868 mSettingObserver.updateContentObserver();
Kevin Chyn0c000332019-04-04 16:02:37 -0700869 mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700870 }
871 }, BiometricService.class.getName()
872 );
873 } catch (RemoteException e) {
874 Slog.e(TAG, "Failed to register user switch observer", e);
875 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700876 }
877
878 @Override
879 public void onStart() {
880 // TODO: maybe get these on-demand
881 if (mHasFeatureFingerprint) {
882 mFingerprintService = IFingerprintService.Stub.asInterface(
883 ServiceManager.getService(Context.FINGERPRINT_SERVICE));
884 }
885 if (mHasFeatureFace) {
886 mFaceService = IFaceService.Stub.asInterface(
887 ServiceManager.getService(Context.FACE_SERVICE));
888 }
889
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700890 mActivityTaskManager = ActivityTaskManager.getService();
891 mStatusBarService = IStatusBarService.Stub.asInterface(
892 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
893
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700894 // Cache the authenticators
895 for (int i = 0; i < FEATURE_ID.length; i++) {
896 if (hasFeature(FEATURE_ID[i])) {
897 Authenticator authenticator =
898 new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
899 mAuthenticators.add(authenticator);
900 }
901 }
902
Kevin Chynbf830a32018-10-07 15:58:46 -0700903 publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700904 }
905
906 /**
907 * Checks if there are any available biometrics, and returns the modality. This method also
908 * returns errors through the callback (no biometric feature, hardware not detected, no
909 * templates enrolled, etc). This service must not start authentication if errors are sent.
Kevin Chyne7411422018-09-27 17:28:20 -0700910 *
Kevin Chyn87f257a2018-11-27 16:26:07 -0800911 * @Returns A pair [Modality, Error] with Modality being one of
912 * {@link BiometricAuthenticator#TYPE_NONE},
913 * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
914 * {@link BiometricAuthenticator#TYPE_IRIS},
915 * {@link BiometricAuthenticator#TYPE_FACE}
Kevin Chyne7411422018-09-27 17:28:20 -0700916 * and the error containing one of the {@link BiometricConstants} errors.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700917 */
Kevin Chyn3b9777b2018-12-19 12:03:36 -0800918 private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800919 int modality = TYPE_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700920
921 // No biometric features, send error
922 if (mAuthenticators.isEmpty()) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800923 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700924 }
925
Kevin Chyne7411422018-09-27 17:28:20 -0700926 // Assuming that authenticators are listed in priority-order, the rest of this function
927 // will go through and find the first authenticator that's available, enrolled, and enabled.
928 // The tricky part is returning the correct error. Error strings that are modality-specific
929 // should also respect the priority-order.
930
931 // Find first authenticator that's detected, enrolled, and enabled.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700932 boolean isHardwareDetected = false;
933 boolean hasTemplatesEnrolled = false;
Kevin Chyne7411422018-09-27 17:28:20 -0700934 boolean enabledForApps = false;
935
Kevin Chyn87f257a2018-11-27 16:26:07 -0800936 int firstHwAvailable = TYPE_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700937 for (int i = 0; i < mAuthenticators.size(); i++) {
Kevin Chyne7411422018-09-27 17:28:20 -0700938 modality = mAuthenticators.get(i).getType();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700939 BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
940 if (authenticator.isHardwareDetected()) {
941 isHardwareDetected = true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800942 if (firstHwAvailable == TYPE_NONE) {
Kevin Chyne7411422018-09-27 17:28:20 -0700943 // Store the first one since we want to return the error in correct priority
944 // order.
945 firstHwAvailable = modality;
946 }
Kevin Chyn3b9777b2018-12-19 12:03:36 -0800947 if (authenticator.hasEnrolledTemplates(userId)) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700948 hasTemplatesEnrolled = true;
Kevin Chyn2170ab72019-04-03 20:28:28 -0700949 if (isEnabledForApp(modality, userId)) {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700950 // TODO(b/110907543): When face settings (and other settings) have both a
951 // user toggle as well as a work profile settings page, this needs to be
952 // updated to reflect the correct setting.
Kevin Chyne7411422018-09-27 17:28:20 -0700953 enabledForApps = true;
954 break;
955 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700956 }
957 }
958 }
959
960 // Check error conditions
961 if (!isHardwareDetected) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800962 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyne7411422018-09-27 17:28:20 -0700963 } else if (!hasTemplatesEnrolled) {
964 // Return the modality here so the correct error string can be sent. This error is
965 // preferred over !enabledForApps
966 return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
967 } else if (!enabledForApps) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800968 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700969 }
970
Kevin Chyna8b57ef2018-10-25 11:09:23 -0700971 return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
Kevin Chyne7411422018-09-27 17:28:20 -0700972 }
973
Kevin Chyn2170ab72019-04-03 20:28:28 -0700974 private boolean isEnabledForApp(int modality, int userId) {
Kevin Chyne7411422018-09-27 17:28:20 -0700975 switch(modality) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800976 case TYPE_FINGERPRINT:
Kevin Chyne7411422018-09-27 17:28:20 -0700977 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800978 case TYPE_IRIS:
Kevin Chyne7411422018-09-27 17:28:20 -0700979 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800980 case TYPE_FACE:
Kevin Chyn2170ab72019-04-03 20:28:28 -0700981 return mSettingObserver.getFaceEnabledForApps(userId);
Kevin Chyne7411422018-09-27 17:28:20 -0700982 default:
983 Slog.w(TAG, "Unsupported modality: " + modality);
984 return false;
985 }
986 }
987
988 private String getErrorString(int type, int error, int vendorCode) {
989 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800990 case TYPE_FINGERPRINT:
Kevin Chyne7411422018-09-27 17:28:20 -0700991 return FingerprintManager.getErrorString(getContext(), error, vendorCode);
Kevin Chyn87f257a2018-11-27 16:26:07 -0800992 case TYPE_IRIS:
Kevin Chyne7411422018-09-27 17:28:20 -0700993 Slog.w(TAG, "Modality not supported");
994 return null; // not supported
Kevin Chyn87f257a2018-11-27 16:26:07 -0800995 case TYPE_FACE:
Kevin Chyne7411422018-09-27 17:28:20 -0700996 return FaceManager.getErrorString(getContext(), error, vendorCode);
997 default:
998 Slog.w(TAG, "Unable to get error string for modality: " + type);
999 return null;
1000 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001001 }
1002
1003 private BiometricAuthenticator getAuthenticator(int type) {
1004 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001005 case TYPE_FINGERPRINT:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001006 return (FingerprintManager)
1007 getContext().getSystemService(Context.FINGERPRINT_SERVICE);
Kevin Chyn87f257a2018-11-27 16:26:07 -08001008 case TYPE_IRIS:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001009 return null;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001010 case TYPE_FACE:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001011 return (FaceManager)
1012 getContext().getSystemService(Context.FACE_SERVICE);
1013 default:
1014 return null;
1015 }
1016 }
1017
1018 private boolean hasFeature(int type) {
1019 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001020 case TYPE_FINGERPRINT:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001021 return mHasFeatureFingerprint;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001022 case TYPE_IRIS:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001023 return mHasFeatureIris;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001024 case TYPE_FACE:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001025 return mHasFeatureFace;
1026 default:
1027 return false;
1028 }
1029 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001030
1031 private void logDialogDismissed(int reason) {
1032 if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1033 // Explicit auth, authentication confirmed.
1034 // Latency in this case is authenticated -> confirmed. <Biometric>Service
1035 // should have the first half (first acquired -> authenticated).
1036 final long latency = System.currentTimeMillis()
1037 - mCurrentAuthSession.mAuthenticatedTimeMs;
1038
1039 if (LoggableMonitor.DEBUG) {
1040 Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
1041 + ", User: " + mCurrentAuthSession.mUserId
1042 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1043 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
1044 + ", RequireConfirmation: "
1045 + mCurrentAuthSession.mRequireConfirmation
1046 + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
1047 + ", Latency: " + latency);
1048 }
1049
1050 StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
1051 statsModality(),
1052 mCurrentAuthSession.mUserId,
1053 mCurrentAuthSession.isCrypto(),
1054 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1055 mCurrentAuthSession.mRequireConfirmation,
1056 StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
Kevin Chyn4858da42019-04-11 13:02:56 -07001057 latency,
1058 Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001059 } else {
1060 int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
1061 ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
1062 : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
1063 ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
1064 : 0;
1065 if (LoggableMonitor.DEBUG) {
1066 Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
1067 + ", User: " + mCurrentAuthSession.mUserId
1068 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1069 + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
1070 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
1071 + ", Error: " + error);
1072 }
1073 // Auth canceled
1074 StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
1075 statsModality(),
1076 mCurrentAuthSession.mUserId,
1077 mCurrentAuthSession.isCrypto(),
1078 BiometricsProtoEnums.ACTION_AUTHENTICATE,
1079 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1080 error,
Kevin Chyn4858da42019-04-11 13:02:56 -07001081 0 /* vendorCode */,
1082 Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001083 }
1084 }
1085
1086 private int statsModality() {
1087 int modality = 0;
1088 if (mCurrentAuthSession == null) {
1089 return BiometricsProtoEnums.MODALITY_UNKNOWN;
1090 }
1091 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
1092 != 0) {
1093 modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
1094 }
1095 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
1096 modality |= BiometricsProtoEnums.MODALITY_IRIS;
1097 }
1098 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
1099 modality |= BiometricsProtoEnums.MODALITY_FACE;
1100 }
1101 return modality;
1102 }
1103
1104 private void handleTaskStackChanged() {
1105 try {
1106 final List<ActivityManager.RunningTaskInfo> runningTasks =
1107 mActivityTaskManager.getTasks(1);
1108 if (!runningTasks.isEmpty()) {
1109 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
1110 if (mCurrentAuthSession != null
1111 && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
1112 mStatusBarService.hideBiometricDialog();
1113 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1114 mCurrentAuthSession.mClientReceiver.onError(
1115 BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1116 getContext().getString(
1117 com.android.internal.R.string.biometric_error_canceled)
1118 );
1119 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1120 mCurrentAuthSession = null;
1121 }
1122 }
1123 } catch (RemoteException e) {
1124 Slog.e(TAG, "Unable to get running tasks", e);
1125 }
1126 }
1127
1128 private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
1129
1130 try {
1131 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1132 // after user dismissed/canceled dialog).
1133 if (mCurrentAuthSession == null) {
1134 Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
1135 return;
1136 }
1137
1138 if (!requireConfirmation) {
1139 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1140 KeyStore.getInstance().addAuthToken(token);
1141 mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
1142 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1143 mCurrentAuthSession = null;
1144 } else {
1145 mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
1146 // Store the auth token and submit it to keystore after the confirmation
1147 // button has been pressed.
1148 mCurrentAuthSession.mTokenEscrow = token;
1149 mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
1150 }
1151
1152 // Notify SysUI that the biometric has been authenticated. SysUI already knows
1153 // the implicit/explicit state and will react accordingly.
Kevin Chyne674e852019-04-24 12:39:40 -07001154 mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001155 } catch (RemoteException e) {
1156 Slog.e(TAG, "Remote exception", e);
1157 }
1158 }
1159
Kevin Chyne674e852019-04-24 12:39:40 -07001160 private void handleAuthenticationFailed(String failureReason) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001161 try {
1162 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1163 // after user dismissed/canceled dialog).
1164 if (mCurrentAuthSession == null) {
1165 Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
1166 return;
1167 }
1168
Kevin Chyne674e852019-04-24 12:39:40 -07001169 mStatusBarService.onBiometricAuthenticated(false, failureReason);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001170
1171 // TODO: This logic will need to be updated if BP is multi-modal
1172 if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
1173 // Pause authentication. onBiometricAuthenticated(false) causes the
1174 // dialog to show a "try again" button for passive modalities.
1175 mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
1176 }
1177
1178 mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
1179 } catch (RemoteException e) {
1180 Slog.e(TAG, "Remote exception", e);
1181 }
1182 }
1183
Kevin Chyn5a90a652019-03-25 18:11:16 -07001184 private void handleOnConfirmDeviceCredentialSuccess() {
1185 if (mConfirmDeviceCredentialReceiver == null) {
1186 Slog.w(TAG, "onCDCASuccess null!");
1187 return;
1188 }
1189 try {
1190 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1191 mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
1192 if (mCurrentAuthSession != null) {
1193 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1194 mCurrentAuthSession = null;
1195 }
1196 } catch (RemoteException e) {
1197 Slog.e(TAG, "RemoteException", e);
1198 }
1199 mConfirmDeviceCredentialReceiver = null;
1200 }
1201
1202 private void handleOnConfirmDeviceCredentialError(int error, String message) {
1203 if (mConfirmDeviceCredentialReceiver == null) {
1204 Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
1205 return;
1206 }
1207 try {
1208 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1209 mConfirmDeviceCredentialReceiver.onError(error, message);
1210 if (mCurrentAuthSession != null) {
1211 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1212 mCurrentAuthSession = null;
1213 }
1214 } catch (RemoteException e) {
1215 Slog.e(TAG, "RemoteException", e);
1216 }
1217 mConfirmDeviceCredentialReceiver = null;
1218 }
1219
1220 private void handleRegisterCancellationCallback(
1221 IBiometricConfirmDeviceCredentialCallback callback) {
1222 if (mCurrentAuthSession == null) {
1223 Slog.d(TAG, "Current auth session null");
1224 return;
1225 }
1226 Slog.d(TAG, "Updating cancel callback");
1227 mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback;
1228 }
1229
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001230 private void handleOnError(int cookie, int error, String message) {
1231 Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
1232 // Errors can either be from the current auth session or the pending auth session.
1233 // The pending auth session may receive errors such as ERROR_LOCKOUT before
1234 // it becomes the current auth session. Similarly, the current auth session may
1235 // receive errors such as ERROR_CANCELED while the pending auth session is preparing
1236 // to be started. Thus we must match error messages with their cookies to be sure
1237 // of their intended receivers.
1238 try {
1239 if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
Kevin Chyn5a90a652019-03-25 18:11:16 -07001240
1241 if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
1242 // If we were invoked by ConfirmDeviceCredential, do not delete the current
1243 // auth session since we still need to respond to cancel signal while
1244 if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state");
1245
1246 // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass
1247 // screen
1248 mCurrentAuthSession.mClientReceiver.onError(error, message);
1249 mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
1250 mStatusBarService.hideBiometricDialog();
1251 } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001252 mStatusBarService.onBiometricError(message);
1253 if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
1254 mActivityTaskManager.unregisterTaskStackListener(
1255 mTaskStackListener);
1256 mCurrentAuthSession.mClientReceiver.onError(error, message);
1257 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1258 mCurrentAuthSession = null;
1259 mStatusBarService.hideBiometricDialog();
1260 } else {
1261 // Send errors after the dialog is dismissed.
1262 mHandler.postDelayed(() -> {
1263 try {
1264 if (mCurrentAuthSession != null) {
1265 mActivityTaskManager.unregisterTaskStackListener(
1266 mTaskStackListener);
1267 mCurrentAuthSession.mClientReceiver.onError(error,
1268 message);
1269 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1270 mCurrentAuthSession = null;
1271 }
1272 } catch (RemoteException e) {
1273 Slog.e(TAG, "Remote exception", e);
1274 }
1275 }, BiometricPrompt.HIDE_DIALOG_DELAY);
1276 }
1277 } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
1278 // In the "try again" state, we should forward canceled errors to
1279 // the client and and clean up.
1280 mCurrentAuthSession.mClientReceiver.onError(error, message);
1281 mStatusBarService.onBiometricError(message);
1282 mActivityTaskManager.unregisterTaskStackListener(
1283 mTaskStackListener);
1284 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1285 mCurrentAuthSession = null;
1286 } else {
1287 Slog.e(TAG, "Impossible session error state: "
1288 + mCurrentAuthSession.mState);
1289 }
1290 } else if (mPendingAuthSession != null
1291 && mPendingAuthSession.containsCookie(cookie)) {
1292 if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
1293 mPendingAuthSession.mClientReceiver.onError(error, message);
1294 mPendingAuthSession.mState = STATE_AUTH_IDLE;
1295 mPendingAuthSession = null;
1296 } else {
1297 Slog.e(TAG, "Impossible pending session error state: "
1298 + mPendingAuthSession.mState);
1299 }
1300 }
1301 } catch (RemoteException e) {
1302 Slog.e(TAG, "Remote exception", e);
1303 }
1304 }
1305
1306 private void handleOnAcquired(int acquiredInfo, String message) {
1307 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1308 // after user dismissed/canceled dialog).
1309 if (mCurrentAuthSession == null) {
1310 Slog.e(TAG, "onAcquired(): Auth session is null");
1311 return;
1312 }
1313
1314 if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
Kevin Chyn3b53d6f2019-05-01 11:49:05 -07001315 if (message == null) {
1316 Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
1317 return;
1318 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001319 try {
1320 mStatusBarService.onBiometricHelp(message);
1321 } catch (RemoteException e) {
1322 Slog.e(TAG, "Remote exception", e);
1323 }
1324 }
1325 }
1326
1327 private void handleOnDismissed(int reason) {
1328 if (mCurrentAuthSession == null) {
1329 Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
1330 return;
1331 }
1332
1333 logDialogDismissed(reason);
1334
1335 try {
1336 if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1337 // Positive button is used by passive modalities as a "confirm" button,
1338 // do not send to client
1339 mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
1340 // Cancel authentication. Skip the token/package check since we are cancelling
1341 // from system server. The interface is permission protected so this is fine.
1342 cancelInternal(null /* token */, null /* package */, false /* fromClient */);
1343 }
1344 if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
1345 mCurrentAuthSession.mClientReceiver.onError(
1346 BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
1347 getContext().getString(
1348 com.android.internal.R.string.biometric_error_user_canceled));
1349 } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1350 // Have the service send the token to KeyStore, and send onAuthenticated
1351 // to the application
1352 KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
1353 mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
1354 }
Kevin Chyn5a90a652019-03-25 18:11:16 -07001355
1356 // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the
1357 // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when
1358 // ConfirmDeviceCredential is confirmed or canceled.
1359 // TODO(b/123378871): Remove when moved
1360 if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) {
1361 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1362 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1363 mCurrentAuthSession = null;
1364 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001365 } catch (RemoteException e) {
1366 Slog.e(TAG, "Remote exception", e);
1367 }
1368 }
1369
1370 private void handleOnTryAgainPressed() {
1371 Slog.d(TAG, "onTryAgainPressed");
1372 // No need to check permission, since it can only be invoked by SystemUI
1373 // (or system server itself).
1374 authenticateInternal(mCurrentAuthSession.mToken,
1375 mCurrentAuthSession.mSessionId,
1376 mCurrentAuthSession.mUserId,
1377 mCurrentAuthSession.mClientReceiver,
1378 mCurrentAuthSession.mOpPackageName,
1379 mCurrentAuthSession.mBundle,
1380 mCurrentAuthSession.mCallingUid,
1381 mCurrentAuthSession.mCallingPid,
1382 mCurrentAuthSession.mCallingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001383 mCurrentAuthSession.mModality,
1384 mCurrentAuthSession.mConfirmDeviceCredentialCallback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001385 }
1386
1387 private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation,
1388 int userId) {
1389 Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
1390 while (it.hasNext()) {
1391 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1392 if (pair.getValue() == cookie) {
1393 mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
1394 mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
1395 Slog.d(TAG, "Matched cookie: " + cookie + ", "
1396 + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
1397 break;
1398 }
1399 }
1400
1401 if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
1402 final boolean continuing = mCurrentAuthSession != null
1403 && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
1404
1405 mCurrentAuthSession = mPendingAuthSession;
1406 mPendingAuthSession = null;
1407
1408 mCurrentAuthSession.mState = STATE_AUTH_STARTED;
1409 try {
1410 int modality = TYPE_NONE;
1411 it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
1412 while (it.hasNext()) {
1413 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1414 if (pair.getKey() == TYPE_FINGERPRINT) {
1415 mFingerprintService.startPreparedClient(pair.getValue());
1416 } else if (pair.getKey() == TYPE_IRIS) {
1417 Slog.e(TAG, "Iris unsupported");
1418 } else if (pair.getKey() == TYPE_FACE) {
1419 mFaceService.startPreparedClient(pair.getValue());
1420 } else {
1421 Slog.e(TAG, "Unknown modality: " + pair.getKey());
1422 }
1423 modality |= pair.getKey();
1424 }
1425
1426 if (!continuing) {
1427 mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
1428 mInternalReceiver, modality, requireConfirmation, userId);
1429 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
1430 }
1431 } catch (RemoteException e) {
1432 Slog.e(TAG, "Remote exception", e);
1433 }
1434 }
1435 }
1436
1437 private void handleAuthenticate(IBinder token, long sessionId, int userId,
1438 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001439 int callingUid, int callingPid, int callingUserId,
1440 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001441
1442 mHandler.post(() -> {
1443 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
1444 final int modality = result.first;
1445 final int error = result.second;
1446
1447 // Check for errors, notify callback, and return
1448 if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
1449 try {
1450 final String hardwareUnavailable =
1451 getContext().getString(R.string.biometric_error_hw_unavailable);
1452 switch (error) {
1453 case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
1454 receiver.onError(error, hardwareUnavailable);
1455 break;
1456 case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
1457 receiver.onError(error, hardwareUnavailable);
1458 break;
1459 case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
1460 receiver.onError(error,
1461 getErrorString(modality, error, 0 /* vendorCode */));
1462 break;
1463 default:
1464 Slog.e(TAG, "Unhandled error");
1465 break;
1466 }
1467 } catch (RemoteException e) {
1468 Slog.e(TAG, "Unable to send error", e);
1469 }
1470 return;
1471 }
1472
1473 mCurrentModality = modality;
1474
1475 // Start preparing for authentication. Authentication starts when
1476 // all modalities requested have invoked onReadyForAuthentication.
1477 authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001478 callingUid, callingPid, callingUserId, modality, callback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001479 });
1480 }
1481
1482 /**
1483 * authenticate() (above) which is called from BiometricPrompt determines which
1484 * modality/modalities to start authenticating with. authenticateInternal() should only be
1485 * used for:
1486 * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
1487 * invoked, shortly after which BiometricPrompt is shown and authentication starts
1488 * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
1489 * and the user has pressed "try again"
1490 */
1491 private void authenticateInternal(IBinder token, long sessionId, int userId,
1492 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001493 int callingUid, int callingPid, int callingUserId, int modality,
1494 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001495 try {
1496 boolean requireConfirmation = bundle.getBoolean(
1497 BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
1498 if ((modality & TYPE_FACE) != 0) {
1499 // Check if the user has forced confirmation to be required in Settings.
1500 requireConfirmation = requireConfirmation
Kevin Chyn2170ab72019-04-03 20:28:28 -07001501 || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001502 }
1503 // Generate random cookies to pass to the services that should prepare to start
1504 // authenticating. Store the cookie here and wait for all services to "ack"
1505 // with the cookie. Once all cookies are received, we can show the prompt
1506 // and let the services start authenticating. The cookie should be non-zero.
1507 final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1508 Slog.d(TAG, "Creating auth session. Modality: " + modality
1509 + ", cookie: " + cookie);
1510 final HashMap<Integer, Integer> authenticators = new HashMap<>();
1511 authenticators.put(modality, cookie);
1512 mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
1513 receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001514 modality, requireConfirmation, callback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001515 mPendingAuthSession.mState = STATE_AUTH_CALLED;
1516 // No polymorphism :(
1517 if ((modality & TYPE_FINGERPRINT) != 0) {
1518 mFingerprintService.prepareForAuthentication(token, sessionId, userId,
1519 mInternalReceiver, opPackageName, cookie,
1520 callingUid, callingPid, callingUserId);
1521 }
1522 if ((modality & TYPE_IRIS) != 0) {
1523 Slog.w(TAG, "Iris unsupported");
1524 }
1525 if ((modality & TYPE_FACE) != 0) {
1526 mFaceService.prepareForAuthentication(requireConfirmation,
1527 token, sessionId, userId, mInternalReceiver, opPackageName,
1528 cookie, callingUid, callingPid, callingUserId);
1529 }
1530 } catch (RemoteException e) {
1531 Slog.e(TAG, "Unable to start authentication", e);
1532 }
1533 }
1534
1535 private void handleCancelAuthentication(IBinder token, String opPackageName) {
1536 if (token == null || opPackageName == null) {
1537 Slog.e(TAG, "Unable to cancel, one or more null arguments");
1538 return;
1539 }
1540
Kevin Chyn5a90a652019-03-25 18:11:16 -07001541 if (mCurrentAuthSession != null
1542 && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) {
1543 if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing");
1544 try {
1545 mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel();
1546 } catch (RemoteException e) {
1547 Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e);
1548 }
1549
1550 // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up.
1551 handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1552 getContext().getString(R.string.biometric_error_canceled));
1553 } else if (mCurrentAuthSession != null
1554 && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
1555 // We need to check the current authenticators state. If we're pending confirm
1556 // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
1557 // since we won't be getting an onError from the driver.
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001558 try {
1559 // Send error to client
1560 mCurrentAuthSession.mClientReceiver.onError(
1561 BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1562 getContext().getString(
1563 com.android.internal.R.string.biometric_error_user_canceled)
1564 );
1565
1566 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1567 mCurrentAuthSession = null;
1568 mStatusBarService.hideBiometricDialog();
1569 } catch (RemoteException e) {
1570 Slog.e(TAG, "Remote exception", e);
1571 }
1572 } else {
Kevin Chyn5a90a652019-03-25 18:11:16 -07001573 boolean fromCDC = false;
1574 if (mCurrentAuthSession != null) {
1575 fromCDC = mCurrentAuthSession.mBundle.getBoolean(
1576 BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
1577 }
1578
1579 if (fromCDC) {
1580 if (DEBUG) Slog.d(TAG, "Cancelling from CDC");
1581 cancelInternal(token, opPackageName, false /* fromClient */);
1582 } else {
1583 cancelInternal(token, opPackageName, true /* fromClient */);
1584 }
1585
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001586 }
1587 }
1588
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001589 void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
1590 final int callingUid = Binder.getCallingUid();
1591 final int callingPid = Binder.getCallingPid();
1592 final int callingUserId = UserHandle.getCallingUserId();
1593 mHandler.post(() -> {
1594 try {
1595 // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
1596 // drivers have canceled authentication.
1597 if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
1598 mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
1599 callingUid, callingPid, callingUserId, fromClient);
1600 }
1601 if ((mCurrentModality & TYPE_IRIS) != 0) {
1602 Slog.w(TAG, "Iris unsupported");
1603 }
1604 if ((mCurrentModality & TYPE_FACE) != 0) {
1605 mFaceService.cancelAuthenticationFromService(token, opPackageName,
1606 callingUid, callingPid, callingUserId, fromClient);
1607 }
1608 } catch (RemoteException e) {
1609 Slog.e(TAG, "Unable to cancel authentication");
1610 }
1611 });
1612 }
1613
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001614}