blob: db17f8397ed41a2ec3f755d90ec01ec2bcf00620 [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 Chyn750663c2019-05-15 12:17:38 -0700478 if (userId == ActivityManager.getCurrentUser() && !selfChange) {
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 Chyn750663c2019-05-15 12:17:38 -0700497 final int user = ActivityManager.getCurrentUser();
498 if (!mFaceEnabledOnKeyguard.containsKey(user)) {
499 onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
500 }
501 return mFaceEnabledOnKeyguard.get(user);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700502 }
503
Kevin Chyn2170ab72019-04-03 20:28:28 -0700504 boolean getFaceEnabledForApps(int userId) {
Kevin Chyn750663c2019-05-15 12:17:38 -0700505 if (!mFaceEnabledForApps.containsKey(userId)) {
506 onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
507 }
Kevin Chyn2170ab72019-04-03 20:28:28 -0700508 return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700509 }
Kevin Chyn31402832019-01-10 15:56:41 -0800510
Kevin Chyn2170ab72019-04-03 20:28:28 -0700511 boolean getFaceAlwaysRequireConfirmation(int userId) {
Kevin Chyn750663c2019-05-15 12:17:38 -0700512 if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
513 onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
514 }
515 return mFaceAlwaysRequireConfirmation.get(userId);
Kevin Chyn31402832019-01-10 15:56:41 -0800516 }
Kevin Chyn0c000332019-04-04 16:02:37 -0700517
518 void notifyEnabledOnKeyguardCallbacks(int userId) {
519 List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
520 for (int i = 0; i < callbacks.size(); i++) {
521 callbacks.get(i).notify(BiometricSourceType.FACE,
522 mFaceEnabledOnKeyguard.getOrDefault(userId,
523 DEFAULT_KEYGUARD_ENABLED));
524 }
525 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700526 }
527
528 private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
529
530 private final IBiometricEnabledOnKeyguardCallback mCallback;
531
532 EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
533 mCallback = callback;
534 try {
535 mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
536 } catch (RemoteException e) {
537 Slog.w(TAG, "Unable to linkToDeath", e);
538 }
539 }
540
541 void notify(BiometricSourceType sourceType, boolean enabled) {
542 try {
543 mCallback.onChanged(sourceType, enabled);
544 } catch (DeadObjectException e) {
545 Slog.w(TAG, "Death while invoking notify", e);
546 mEnabledOnKeyguardCallbacks.remove(this);
547 } catch (RemoteException e) {
548 Slog.w(TAG, "Failed to invoke onChanged", e);
549 }
550 }
551
552 @Override
553 public void binderDied() {
554 Slog.e(TAG, "Enabled callback binder died");
555 mEnabledOnKeyguardCallbacks.remove(this);
556 }
557 }
558
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700559 // Wrap the client's receiver so we can do things with the BiometricDialog first
560 private final IBiometricServiceReceiverInternal mInternalReceiver =
561 new IBiometricServiceReceiverInternal.Stub() {
562 @Override
563 public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
564 throws RemoteException {
565 SomeArgs args = SomeArgs.obtain();
566 args.arg1 = requireConfirmation;
567 args.arg2 = token;
568 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
569 }
570
571 @Override
572 public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
573 throws RemoteException {
Kevin Chyne674e852019-04-24 12:39:40 -0700574 String failureReason = getContext().getString(R.string.biometric_not_recognized);
575 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700576 }
577
578 @Override
579 public void onError(int cookie, int error, String message) throws RemoteException {
Kevin Chyne674e852019-04-24 12:39:40 -0700580 // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
581 // soft errors and we should allow the user to try authenticating again instead of
582 // dismissing BiometricPrompt.
583 if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
584 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
585 } else {
586 SomeArgs args = SomeArgs.obtain();
587 args.argi1 = cookie;
588 args.argi2 = error;
589 args.arg1 = message;
590 mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
591 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700592 }
593
594 @Override
595 public void onAcquired(int acquiredInfo, String message) throws RemoteException {
596 SomeArgs args = SomeArgs.obtain();
597 args.argi1 = acquiredInfo;
598 args.arg1 = message;
599 mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget();
600 }
601
602 @Override
603 public void onDialogDismissed(int reason) throws RemoteException {
604 mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget();
605 }
606
607 @Override
608 public void onTryAgainPressed() {
609 mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
610 }
611 };
612
613
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700614 /**
615 * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
616 * should not carry any state. The reality is we need to keep a tiny amount of state so that
617 * cancelAuthentication() can go to the right place.
618 */
Kevin Chynbf830a32018-10-07 15:58:46 -0700619 private final class BiometricServiceWrapper extends IBiometricService.Stub {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800620 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800621 public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800622 checkInternalPermission();
Kevin Chyn87f257a2018-11-27 16:26:07 -0800623
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700624 SomeArgs args = SomeArgs.obtain();
625 args.argi1 = cookie;
626 args.arg1 = requireConfirmation;
627 args.argi2 = userId;
628 mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget();
Kevin Chyne92cdae2018-11-21 16:35:04 -0800629 }
630
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700631 @Override // Binder call
632 public void authenticate(IBinder token, long sessionId, int userId,
Kevin Chyn5a90a652019-03-25 18:11:16 -0700633 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
634 IBiometricConfirmDeviceCredentialCallback callback)
Kevin Chyn23289ef2018-11-28 16:32:36 -0800635 throws RemoteException {
Kevin Chyn067085a2018-11-12 15:49:19 -0800636 final int callingUid = Binder.getCallingUid();
637 final int callingPid = Binder.getCallingPid();
638 final int callingUserId = UserHandle.getCallingUserId();
639
Kevin Chyn5a90a652019-03-25 18:11:16 -0700640 // TODO(b/123378871): Remove when moved.
641 if (callback != null) {
642 checkInternalPermission();
643 }
644
Kevin Chyn067085a2018-11-12 15:49:19 -0800645 // In the BiometricServiceBase, check do the AppOps and foreground check.
646 if (userId == callingUserId) {
647 // Check the USE_BIOMETRIC permission here.
648 checkPermission();
649 } else {
650 // Only allow internal clients to authenticate with a different userId
651 Slog.w(TAG, "User " + callingUserId + " is requesting authentication of userid: "
652 + userId);
653 checkInternalPermission();
654 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700655
Kevin Chyne92cdae2018-11-21 16:35:04 -0800656 if (token == null || receiver == null || opPackageName == null || bundle == null) {
Kevin Chyn7c2371a2018-09-12 01:54:18 -0700657 Slog.e(TAG, "Unable to authenticate, one or more null arguments");
658 return;
659 }
660
Kevin Chyn5a90a652019-03-25 18:11:16 -0700661 final boolean isFromConfirmDeviceCredential =
662 bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
663 if (isFromConfirmDeviceCredential) {
664 checkInternalPermission();
665 }
666
Kevin Chyn3a0187192018-10-08 15:40:05 -0700667 // Check the usage of this in system server. Need to remove this check if it becomes
668 // a public API.
Kevin Chyn87f257a2018-11-27 16:26:07 -0800669 final boolean useDefaultTitle =
670 bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
671 if (useDefaultTitle) {
Kevin Chyn3a0187192018-10-08 15:40:05 -0700672 checkInternalPermission();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800673 // Set the default title if necessary
Kevin Chyn9eed6122019-03-28 21:14:19 -0700674 if (TextUtils.isEmpty(bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
675 bundle.putCharSequence(BiometricPrompt.KEY_TITLE,
676 getContext().getString(R.string.biometric_dialog_default_title));
Kevin Chyn23289ef2018-11-28 16:32:36 -0800677 }
Kevin Chyn3a0187192018-10-08 15:40:05 -0700678 }
679
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800680 // Launch CDC instead if necessary. CDC will return results through an AIDL call, since
681 // we can't get activity results. Store the receiver somewhere so we can forward the
682 // result back to the client.
683 // TODO(b/123378871): Remove when moved.
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800684 if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)) {
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800685 mHandler.post(() -> {
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800686 final KeyguardManager kgm = getContext().getSystemService(
687 KeyguardManager.class);
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800688 if (!kgm.isDeviceSecure()) {
689 try {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700690 receiver.onError(
691 BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
Kevin Chyn45d1f9d2019-02-07 13:38:04 -0800692 getContext().getString(
693 R.string.biometric_error_device_not_secured));
694 } catch (RemoteException e) {
695 Slog.e(TAG, "Remote exception", e);
696 }
697 return;
698 }
699 mConfirmDeviceCredentialReceiver = receiver;
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800700 // Use this so we don't need to duplicate logic..
701 final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
Kevin Chynb1373302019-04-03 19:02:27 -0700702 null /* description */, userId);
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800703 // Then give it the bundle to do magic behavior..
704 intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
joshmccloskey2334f7a2019-03-19 16:31:04 -0700705 // Create a new task with this activity located at the root.
706 intent.setFlags(
707 Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
joshmccloskeyb5d54fd2019-01-30 19:23:35 -0800708 getContext().startActivityAsUser(intent, UserHandle.CURRENT);
709 });
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800710 return;
711 }
712
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700713 SomeArgs args = SomeArgs.obtain();
714 args.arg1 = token;
715 args.arg2 = sessionId;
716 args.argi1 = userId;
717 args.arg3 = receiver;
718 args.arg4 = opPackageName;
719 args.arg5 = bundle;
720 args.argi2 = callingUid;
721 args.argi3 = callingPid;
722 args.argi4 = callingUserId;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700723 args.arg6 = callback;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700724
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700725 mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700726 }
727
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800728 @Override // Binder call
729 public void onConfirmDeviceCredentialSuccess() {
730 checkInternalPermission();
Kevin Chyn5a90a652019-03-25 18:11:16 -0700731
732 mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS);
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800733 }
734
735 @Override // Binder call
736 public void onConfirmDeviceCredentialError(int error, String message) {
737 checkInternalPermission();
Kevin Chyn5a90a652019-03-25 18:11:16 -0700738
739 SomeArgs args = SomeArgs.obtain();
740 args.argi1 = error;
741 args.arg1 = message;
742 mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget();
743 }
744
745 @Override // Binder call
746 public void registerCancellationCallback(
747 IBiometricConfirmDeviceCredentialCallback callback) {
748 // TODO(b/123378871): Remove when moved.
749 // This callback replaces the one stored in the current session. If the session is null
750 // we can ignore this, since it means ConfirmDeviceCredential was launched by something
751 // else (not BiometricPrompt)
752 checkInternalPermission();
753
754 mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800755 }
756
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700757 @Override // Binder call
758 public void cancelAuthentication(IBinder token, String opPackageName)
759 throws RemoteException {
760 checkPermission();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800761
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700762 SomeArgs args = SomeArgs.obtain();
763 args.arg1 = token;
764 args.arg2 = opPackageName;
765 mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700766 }
Kevin Chyn05c21502018-09-18 13:07:19 -0700767
768 @Override // Binder call
Kevin Chyne7411422018-09-27 17:28:20 -0700769 public int canAuthenticate(String opPackageName) {
Kevin Chyn05c21502018-09-18 13:07:19 -0700770 checkPermission();
Kevin Chyn69183e52018-09-21 17:04:09 -0700771
Kevin Chyn75dbb832018-10-05 17:32:57 -0700772 final int userId = UserHandle.getCallingUserId();
Kevin Chyn05c21502018-09-18 13:07:19 -0700773 final long ident = Binder.clearCallingIdentity();
Kevin Chyne7411422018-09-27 17:28:20 -0700774 int error;
Kevin Chyn05c21502018-09-18 13:07:19 -0700775 try {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700776 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
Kevin Chyne7411422018-09-27 17:28:20 -0700777 error = result.second;
Kevin Chyn05c21502018-09-18 13:07:19 -0700778 } finally {
779 Binder.restoreCallingIdentity(ident);
780 }
Kevin Chyne7411422018-09-27 17:28:20 -0700781 return error;
782 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700783
Kevin Chynbf830a32018-10-07 15:58:46 -0700784 @Override // Binder call
Kevin Chynb7b54a62018-09-28 18:48:12 -0700785 public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
786 throws RemoteException {
787 checkInternalPermission();
788 mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
789 try {
790 callback.onChanged(BiometricSourceType.FACE,
791 mSettingObserver.getFaceEnabledOnKeyguard());
792 } catch (RemoteException e) {
793 Slog.w(TAG, "Remote exception", e);
794 }
795 }
Kevin Chynbf830a32018-10-07 15:58:46 -0700796
797 @Override // Binder call
798 public void setActiveUser(int userId) {
799 checkInternalPermission();
800 final long ident = Binder.clearCallingIdentity();
801 try {
802 for (int i = 0; i < mAuthenticators.size(); i++) {
803 mAuthenticators.get(i).getAuthenticator().setActiveUser(userId);
804 }
805 } finally {
806 Binder.restoreCallingIdentity(ident);
807 }
808 }
Kevin Chyne92cdae2018-11-21 16:35:04 -0800809
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800810 @Override // Binder call
Kevin Chyna38653c2019-02-11 17:46:21 -0800811 public void resetLockout(byte[] token) {
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800812 checkInternalPermission();
813 final long ident = Binder.clearCallingIdentity();
814 try {
815 if (mFingerprintService != null) {
816 mFingerprintService.resetTimeout(token);
817 }
818 if (mFaceService != null) {
Kevin Chyna38653c2019-02-11 17:46:21 -0800819 mFaceService.resetLockout(token);
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800820 }
821 } catch (RemoteException e) {
822 Slog.e(TAG, "Remote exception", e);
823 } finally {
824 Binder.restoreCallingIdentity(ident);
825 }
826 }
Kevin Chyne7411422018-09-27 17:28:20 -0700827 }
828
829 private void checkAppOp(String opPackageName, int callingUid) {
830 if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
831 opPackageName) != AppOpsManager.MODE_ALLOWED) {
832 Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
833 throw new SecurityException("Permission denied");
Kevin Chyn05c21502018-09-18 13:07:19 -0700834 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700835 }
836
Kevin Chynb7b54a62018-09-28 18:48:12 -0700837 private void checkInternalPermission() {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800838 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
Kevin Chyn3a0187192018-10-08 15:40:05 -0700839 "Must have USE_BIOMETRIC_INTERNAL permission");
Kevin Chynb7b54a62018-09-28 18:48:12 -0700840 }
841
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700842 private void checkPermission() {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700843 if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700844 != PackageManager.PERMISSION_GRANTED) {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700845 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700846 "Must have USE_BIOMETRIC permission");
847 }
848 }
849
850 /**
851 * Initializes the system service.
852 * <p>
853 * Subclasses must define a single argument constructor that accepts the context
854 * and passes it to super.
855 * </p>
856 *
857 * @param context The system server context.
858 */
Kevin Chyn352adfe2018-09-20 22:28:50 -0700859 public BiometricService(Context context) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700860 super(context);
861
Kevin Chyn69183e52018-09-21 17:04:09 -0700862 mAppOps = context.getSystemService(AppOpsManager.class);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700863 mEnabledOnKeyguardCallbacks = new ArrayList<>();
864 mSettingObserver = new SettingObserver(mHandler);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700865
866 final PackageManager pm = context.getPackageManager();
867 mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
868 mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
869 mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700870
871 try {
872 ActivityManager.getService().registerUserSwitchObserver(
873 new UserSwitchObserver() {
874 @Override
875 public void onUserSwitchComplete(int newUserId) {
876 mSettingObserver.updateContentObserver();
Kevin Chyn0c000332019-04-04 16:02:37 -0700877 mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700878 }
879 }, BiometricService.class.getName()
880 );
881 } catch (RemoteException e) {
882 Slog.e(TAG, "Failed to register user switch observer", e);
883 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700884 }
885
886 @Override
887 public void onStart() {
888 // TODO: maybe get these on-demand
889 if (mHasFeatureFingerprint) {
890 mFingerprintService = IFingerprintService.Stub.asInterface(
891 ServiceManager.getService(Context.FINGERPRINT_SERVICE));
892 }
893 if (mHasFeatureFace) {
894 mFaceService = IFaceService.Stub.asInterface(
895 ServiceManager.getService(Context.FACE_SERVICE));
896 }
897
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700898 mActivityTaskManager = ActivityTaskManager.getService();
899 mStatusBarService = IStatusBarService.Stub.asInterface(
900 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
901
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700902 // Cache the authenticators
903 for (int i = 0; i < FEATURE_ID.length; i++) {
904 if (hasFeature(FEATURE_ID[i])) {
905 Authenticator authenticator =
906 new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
907 mAuthenticators.add(authenticator);
908 }
909 }
910
Kevin Chynbf830a32018-10-07 15:58:46 -0700911 publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700912 }
913
914 /**
915 * Checks if there are any available biometrics, and returns the modality. This method also
916 * returns errors through the callback (no biometric feature, hardware not detected, no
917 * templates enrolled, etc). This service must not start authentication if errors are sent.
Kevin Chyne7411422018-09-27 17:28:20 -0700918 *
Kevin Chyn87f257a2018-11-27 16:26:07 -0800919 * @Returns A pair [Modality, Error] with Modality being one of
920 * {@link BiometricAuthenticator#TYPE_NONE},
921 * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
922 * {@link BiometricAuthenticator#TYPE_IRIS},
923 * {@link BiometricAuthenticator#TYPE_FACE}
Kevin Chyne7411422018-09-27 17:28:20 -0700924 * and the error containing one of the {@link BiometricConstants} errors.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700925 */
Kevin Chyn3b9777b2018-12-19 12:03:36 -0800926 private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800927 int modality = TYPE_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700928
929 // No biometric features, send error
930 if (mAuthenticators.isEmpty()) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800931 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700932 }
933
Kevin Chyne7411422018-09-27 17:28:20 -0700934 // Assuming that authenticators are listed in priority-order, the rest of this function
935 // will go through and find the first authenticator that's available, enrolled, and enabled.
936 // The tricky part is returning the correct error. Error strings that are modality-specific
937 // should also respect the priority-order.
938
939 // Find first authenticator that's detected, enrolled, and enabled.
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700940 boolean isHardwareDetected = false;
941 boolean hasTemplatesEnrolled = false;
Kevin Chyne7411422018-09-27 17:28:20 -0700942 boolean enabledForApps = false;
943
Kevin Chyn87f257a2018-11-27 16:26:07 -0800944 int firstHwAvailable = TYPE_NONE;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700945 for (int i = 0; i < mAuthenticators.size(); i++) {
Kevin Chyne7411422018-09-27 17:28:20 -0700946 modality = mAuthenticators.get(i).getType();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700947 BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
948 if (authenticator.isHardwareDetected()) {
949 isHardwareDetected = true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800950 if (firstHwAvailable == TYPE_NONE) {
Kevin Chyne7411422018-09-27 17:28:20 -0700951 // Store the first one since we want to return the error in correct priority
952 // order.
953 firstHwAvailable = modality;
954 }
Kevin Chyn3b9777b2018-12-19 12:03:36 -0800955 if (authenticator.hasEnrolledTemplates(userId)) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700956 hasTemplatesEnrolled = true;
Kevin Chyn2170ab72019-04-03 20:28:28 -0700957 if (isEnabledForApp(modality, userId)) {
Kevin Chyn75dbb832018-10-05 17:32:57 -0700958 // TODO(b/110907543): When face settings (and other settings) have both a
959 // user toggle as well as a work profile settings page, this needs to be
960 // updated to reflect the correct setting.
Kevin Chyne7411422018-09-27 17:28:20 -0700961 enabledForApps = true;
962 break;
963 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700964 }
965 }
966 }
967
968 // Check error conditions
969 if (!isHardwareDetected) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800970 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyne7411422018-09-27 17:28:20 -0700971 } else if (!hasTemplatesEnrolled) {
972 // Return the modality here so the correct error string can be sent. This error is
973 // preferred over !enabledForApps
974 return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
975 } else if (!enabledForApps) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800976 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700977 }
978
Kevin Chyna8b57ef2018-10-25 11:09:23 -0700979 return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
Kevin Chyne7411422018-09-27 17:28:20 -0700980 }
981
Kevin Chyn2170ab72019-04-03 20:28:28 -0700982 private boolean isEnabledForApp(int modality, int userId) {
Kevin Chyne7411422018-09-27 17:28:20 -0700983 switch(modality) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800984 case TYPE_FINGERPRINT:
Kevin Chyne7411422018-09-27 17:28:20 -0700985 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800986 case TYPE_IRIS:
Kevin Chyne7411422018-09-27 17:28:20 -0700987 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -0800988 case TYPE_FACE:
Kevin Chyn2170ab72019-04-03 20:28:28 -0700989 return mSettingObserver.getFaceEnabledForApps(userId);
Kevin Chyne7411422018-09-27 17:28:20 -0700990 default:
991 Slog.w(TAG, "Unsupported modality: " + modality);
992 return false;
993 }
994 }
995
996 private String getErrorString(int type, int error, int vendorCode) {
997 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -0800998 case TYPE_FINGERPRINT:
Kevin Chyne7411422018-09-27 17:28:20 -0700999 return FingerprintManager.getErrorString(getContext(), error, vendorCode);
Kevin Chyn87f257a2018-11-27 16:26:07 -08001000 case TYPE_IRIS:
Kevin Chyne7411422018-09-27 17:28:20 -07001001 Slog.w(TAG, "Modality not supported");
1002 return null; // not supported
Kevin Chyn87f257a2018-11-27 16:26:07 -08001003 case TYPE_FACE:
Kevin Chyne7411422018-09-27 17:28:20 -07001004 return FaceManager.getErrorString(getContext(), error, vendorCode);
1005 default:
1006 Slog.w(TAG, "Unable to get error string for modality: " + type);
1007 return null;
1008 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001009 }
1010
1011 private BiometricAuthenticator getAuthenticator(int type) {
1012 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001013 case TYPE_FINGERPRINT:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001014 return (FingerprintManager)
1015 getContext().getSystemService(Context.FINGERPRINT_SERVICE);
Kevin Chyn87f257a2018-11-27 16:26:07 -08001016 case TYPE_IRIS:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001017 return null;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001018 case TYPE_FACE:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001019 return (FaceManager)
1020 getContext().getSystemService(Context.FACE_SERVICE);
1021 default:
1022 return null;
1023 }
1024 }
1025
1026 private boolean hasFeature(int type) {
1027 switch (type) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001028 case TYPE_FINGERPRINT:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001029 return mHasFeatureFingerprint;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001030 case TYPE_IRIS:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001031 return mHasFeatureIris;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001032 case TYPE_FACE:
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001033 return mHasFeatureFace;
1034 default:
1035 return false;
1036 }
1037 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001038
1039 private void logDialogDismissed(int reason) {
1040 if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1041 // Explicit auth, authentication confirmed.
1042 // Latency in this case is authenticated -> confirmed. <Biometric>Service
1043 // should have the first half (first acquired -> authenticated).
1044 final long latency = System.currentTimeMillis()
1045 - mCurrentAuthSession.mAuthenticatedTimeMs;
1046
1047 if (LoggableMonitor.DEBUG) {
1048 Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
1049 + ", User: " + mCurrentAuthSession.mUserId
1050 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1051 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
1052 + ", RequireConfirmation: "
1053 + mCurrentAuthSession.mRequireConfirmation
1054 + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
1055 + ", Latency: " + latency);
1056 }
1057
1058 StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
1059 statsModality(),
1060 mCurrentAuthSession.mUserId,
1061 mCurrentAuthSession.isCrypto(),
1062 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1063 mCurrentAuthSession.mRequireConfirmation,
1064 StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
Kevin Chyn4858da42019-04-11 13:02:56 -07001065 latency,
1066 Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001067 } else {
1068 int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
1069 ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
1070 : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
1071 ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
1072 : 0;
1073 if (LoggableMonitor.DEBUG) {
1074 Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
1075 + ", User: " + mCurrentAuthSession.mUserId
1076 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1077 + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
1078 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
1079 + ", Error: " + error);
1080 }
1081 // Auth canceled
1082 StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
1083 statsModality(),
1084 mCurrentAuthSession.mUserId,
1085 mCurrentAuthSession.isCrypto(),
1086 BiometricsProtoEnums.ACTION_AUTHENTICATE,
1087 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1088 error,
Kevin Chyn4858da42019-04-11 13:02:56 -07001089 0 /* vendorCode */,
1090 Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001091 }
1092 }
1093
1094 private int statsModality() {
1095 int modality = 0;
1096 if (mCurrentAuthSession == null) {
1097 return BiometricsProtoEnums.MODALITY_UNKNOWN;
1098 }
1099 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
1100 != 0) {
1101 modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
1102 }
1103 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
1104 modality |= BiometricsProtoEnums.MODALITY_IRIS;
1105 }
1106 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
1107 modality |= BiometricsProtoEnums.MODALITY_FACE;
1108 }
1109 return modality;
1110 }
1111
1112 private void handleTaskStackChanged() {
1113 try {
1114 final List<ActivityManager.RunningTaskInfo> runningTasks =
1115 mActivityTaskManager.getTasks(1);
1116 if (!runningTasks.isEmpty()) {
1117 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
1118 if (mCurrentAuthSession != null
1119 && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
1120 mStatusBarService.hideBiometricDialog();
1121 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1122 mCurrentAuthSession.mClientReceiver.onError(
1123 BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1124 getContext().getString(
1125 com.android.internal.R.string.biometric_error_canceled)
1126 );
1127 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1128 mCurrentAuthSession = null;
1129 }
1130 }
1131 } catch (RemoteException e) {
1132 Slog.e(TAG, "Unable to get running tasks", e);
1133 }
1134 }
1135
1136 private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
1137
1138 try {
1139 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1140 // after user dismissed/canceled dialog).
1141 if (mCurrentAuthSession == null) {
1142 Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
1143 return;
1144 }
1145
1146 if (!requireConfirmation) {
1147 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1148 KeyStore.getInstance().addAuthToken(token);
1149 mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
1150 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1151 mCurrentAuthSession = null;
1152 } else {
1153 mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
1154 // Store the auth token and submit it to keystore after the confirmation
1155 // button has been pressed.
1156 mCurrentAuthSession.mTokenEscrow = token;
1157 mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
1158 }
1159
1160 // Notify SysUI that the biometric has been authenticated. SysUI already knows
1161 // the implicit/explicit state and will react accordingly.
Kevin Chyne674e852019-04-24 12:39:40 -07001162 mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001163 } catch (RemoteException e) {
1164 Slog.e(TAG, "Remote exception", e);
1165 }
1166 }
1167
Kevin Chyne674e852019-04-24 12:39:40 -07001168 private void handleAuthenticationFailed(String failureReason) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001169 try {
1170 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1171 // after user dismissed/canceled dialog).
1172 if (mCurrentAuthSession == null) {
1173 Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
1174 return;
1175 }
1176
Kevin Chyne674e852019-04-24 12:39:40 -07001177 mStatusBarService.onBiometricAuthenticated(false, failureReason);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001178
1179 // TODO: This logic will need to be updated if BP is multi-modal
1180 if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
1181 // Pause authentication. onBiometricAuthenticated(false) causes the
1182 // dialog to show a "try again" button for passive modalities.
1183 mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
1184 }
1185
1186 mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
1187 } catch (RemoteException e) {
1188 Slog.e(TAG, "Remote exception", e);
1189 }
1190 }
1191
Kevin Chyn5a90a652019-03-25 18:11:16 -07001192 private void handleOnConfirmDeviceCredentialSuccess() {
1193 if (mConfirmDeviceCredentialReceiver == null) {
1194 Slog.w(TAG, "onCDCASuccess null!");
1195 return;
1196 }
1197 try {
1198 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1199 mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
1200 if (mCurrentAuthSession != null) {
1201 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1202 mCurrentAuthSession = null;
1203 }
1204 } catch (RemoteException e) {
1205 Slog.e(TAG, "RemoteException", e);
1206 }
1207 mConfirmDeviceCredentialReceiver = null;
1208 }
1209
1210 private void handleOnConfirmDeviceCredentialError(int error, String message) {
1211 if (mConfirmDeviceCredentialReceiver == null) {
1212 Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
1213 return;
1214 }
1215 try {
1216 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1217 mConfirmDeviceCredentialReceiver.onError(error, message);
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 handleRegisterCancellationCallback(
1229 IBiometricConfirmDeviceCredentialCallback callback) {
1230 if (mCurrentAuthSession == null) {
1231 Slog.d(TAG, "Current auth session null");
1232 return;
1233 }
1234 Slog.d(TAG, "Updating cancel callback");
1235 mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback;
1236 }
1237
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001238 private void handleOnError(int cookie, int error, String message) {
1239 Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
1240 // Errors can either be from the current auth session or the pending auth session.
1241 // The pending auth session may receive errors such as ERROR_LOCKOUT before
1242 // it becomes the current auth session. Similarly, the current auth session may
1243 // receive errors such as ERROR_CANCELED while the pending auth session is preparing
1244 // to be started. Thus we must match error messages with their cookies to be sure
1245 // of their intended receivers.
1246 try {
1247 if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
Kevin Chyn5a90a652019-03-25 18:11:16 -07001248
1249 if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
1250 // If we were invoked by ConfirmDeviceCredential, do not delete the current
1251 // auth session since we still need to respond to cancel signal while
1252 if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state");
1253
1254 // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass
1255 // screen
1256 mCurrentAuthSession.mClientReceiver.onError(error, message);
1257 mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
1258 mStatusBarService.hideBiometricDialog();
1259 } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001260 mStatusBarService.onBiometricError(message);
1261 if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
1262 mActivityTaskManager.unregisterTaskStackListener(
1263 mTaskStackListener);
1264 mCurrentAuthSession.mClientReceiver.onError(error, message);
1265 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1266 mCurrentAuthSession = null;
1267 mStatusBarService.hideBiometricDialog();
1268 } else {
1269 // Send errors after the dialog is dismissed.
1270 mHandler.postDelayed(() -> {
1271 try {
1272 if (mCurrentAuthSession != null) {
1273 mActivityTaskManager.unregisterTaskStackListener(
1274 mTaskStackListener);
1275 mCurrentAuthSession.mClientReceiver.onError(error,
1276 message);
1277 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1278 mCurrentAuthSession = null;
1279 }
1280 } catch (RemoteException e) {
1281 Slog.e(TAG, "Remote exception", e);
1282 }
1283 }, BiometricPrompt.HIDE_DIALOG_DELAY);
1284 }
1285 } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
1286 // In the "try again" state, we should forward canceled errors to
1287 // the client and and clean up.
1288 mCurrentAuthSession.mClientReceiver.onError(error, message);
1289 mStatusBarService.onBiometricError(message);
1290 mActivityTaskManager.unregisterTaskStackListener(
1291 mTaskStackListener);
1292 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1293 mCurrentAuthSession = null;
1294 } else {
1295 Slog.e(TAG, "Impossible session error state: "
1296 + mCurrentAuthSession.mState);
1297 }
1298 } else if (mPendingAuthSession != null
1299 && mPendingAuthSession.containsCookie(cookie)) {
1300 if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
1301 mPendingAuthSession.mClientReceiver.onError(error, message);
1302 mPendingAuthSession.mState = STATE_AUTH_IDLE;
1303 mPendingAuthSession = null;
1304 } else {
1305 Slog.e(TAG, "Impossible pending session error state: "
1306 + mPendingAuthSession.mState);
1307 }
1308 }
1309 } catch (RemoteException e) {
1310 Slog.e(TAG, "Remote exception", e);
1311 }
1312 }
1313
1314 private void handleOnAcquired(int acquiredInfo, String message) {
1315 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1316 // after user dismissed/canceled dialog).
1317 if (mCurrentAuthSession == null) {
1318 Slog.e(TAG, "onAcquired(): Auth session is null");
1319 return;
1320 }
1321
1322 if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
Kevin Chyn3b53d6f2019-05-01 11:49:05 -07001323 if (message == null) {
1324 Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
1325 return;
1326 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001327 try {
1328 mStatusBarService.onBiometricHelp(message);
1329 } catch (RemoteException e) {
1330 Slog.e(TAG, "Remote exception", e);
1331 }
1332 }
1333 }
1334
1335 private void handleOnDismissed(int reason) {
1336 if (mCurrentAuthSession == null) {
1337 Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
1338 return;
1339 }
1340
1341 logDialogDismissed(reason);
1342
1343 try {
1344 if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1345 // Positive button is used by passive modalities as a "confirm" button,
1346 // do not send to client
1347 mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
1348 // Cancel authentication. Skip the token/package check since we are cancelling
1349 // from system server. The interface is permission protected so this is fine.
1350 cancelInternal(null /* token */, null /* package */, false /* fromClient */);
1351 }
1352 if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
1353 mCurrentAuthSession.mClientReceiver.onError(
1354 BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
1355 getContext().getString(
1356 com.android.internal.R.string.biometric_error_user_canceled));
1357 } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
1358 // Have the service send the token to KeyStore, and send onAuthenticated
1359 // to the application
1360 KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
1361 mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
1362 }
Kevin Chyn5a90a652019-03-25 18:11:16 -07001363
1364 // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the
1365 // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when
1366 // ConfirmDeviceCredential is confirmed or canceled.
1367 // TODO(b/123378871): Remove when moved
1368 if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) {
1369 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
1370 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1371 mCurrentAuthSession = null;
1372 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001373 } catch (RemoteException e) {
1374 Slog.e(TAG, "Remote exception", e);
1375 }
1376 }
1377
1378 private void handleOnTryAgainPressed() {
1379 Slog.d(TAG, "onTryAgainPressed");
1380 // No need to check permission, since it can only be invoked by SystemUI
1381 // (or system server itself).
1382 authenticateInternal(mCurrentAuthSession.mToken,
1383 mCurrentAuthSession.mSessionId,
1384 mCurrentAuthSession.mUserId,
1385 mCurrentAuthSession.mClientReceiver,
1386 mCurrentAuthSession.mOpPackageName,
1387 mCurrentAuthSession.mBundle,
1388 mCurrentAuthSession.mCallingUid,
1389 mCurrentAuthSession.mCallingPid,
1390 mCurrentAuthSession.mCallingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001391 mCurrentAuthSession.mModality,
1392 mCurrentAuthSession.mConfirmDeviceCredentialCallback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001393 }
1394
1395 private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation,
1396 int userId) {
1397 Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
1398 while (it.hasNext()) {
1399 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1400 if (pair.getValue() == cookie) {
1401 mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
1402 mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
1403 Slog.d(TAG, "Matched cookie: " + cookie + ", "
1404 + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
1405 break;
1406 }
1407 }
1408
1409 if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
1410 final boolean continuing = mCurrentAuthSession != null
1411 && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
1412
1413 mCurrentAuthSession = mPendingAuthSession;
1414 mPendingAuthSession = null;
1415
1416 mCurrentAuthSession.mState = STATE_AUTH_STARTED;
1417 try {
1418 int modality = TYPE_NONE;
1419 it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
1420 while (it.hasNext()) {
1421 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1422 if (pair.getKey() == TYPE_FINGERPRINT) {
1423 mFingerprintService.startPreparedClient(pair.getValue());
1424 } else if (pair.getKey() == TYPE_IRIS) {
1425 Slog.e(TAG, "Iris unsupported");
1426 } else if (pair.getKey() == TYPE_FACE) {
1427 mFaceService.startPreparedClient(pair.getValue());
1428 } else {
1429 Slog.e(TAG, "Unknown modality: " + pair.getKey());
1430 }
1431 modality |= pair.getKey();
1432 }
1433
1434 if (!continuing) {
1435 mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
1436 mInternalReceiver, modality, requireConfirmation, userId);
1437 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
1438 }
1439 } catch (RemoteException e) {
1440 Slog.e(TAG, "Remote exception", e);
1441 }
1442 }
1443 }
1444
1445 private void handleAuthenticate(IBinder token, long sessionId, int userId,
1446 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001447 int callingUid, int callingPid, int callingUserId,
1448 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001449
1450 mHandler.post(() -> {
1451 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
1452 final int modality = result.first;
1453 final int error = result.second;
1454
1455 // Check for errors, notify callback, and return
1456 if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
1457 try {
1458 final String hardwareUnavailable =
1459 getContext().getString(R.string.biometric_error_hw_unavailable);
1460 switch (error) {
1461 case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
1462 receiver.onError(error, hardwareUnavailable);
1463 break;
1464 case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
1465 receiver.onError(error, hardwareUnavailable);
1466 break;
1467 case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
1468 receiver.onError(error,
1469 getErrorString(modality, error, 0 /* vendorCode */));
1470 break;
1471 default:
1472 Slog.e(TAG, "Unhandled error");
1473 break;
1474 }
1475 } catch (RemoteException e) {
1476 Slog.e(TAG, "Unable to send error", e);
1477 }
1478 return;
1479 }
1480
1481 mCurrentModality = modality;
1482
1483 // Start preparing for authentication. Authentication starts when
1484 // all modalities requested have invoked onReadyForAuthentication.
1485 authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001486 callingUid, callingPid, callingUserId, modality, callback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001487 });
1488 }
1489
1490 /**
1491 * authenticate() (above) which is called from BiometricPrompt determines which
1492 * modality/modalities to start authenticating with. authenticateInternal() should only be
1493 * used for:
1494 * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
1495 * invoked, shortly after which BiometricPrompt is shown and authentication starts
1496 * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
1497 * and the user has pressed "try again"
1498 */
1499 private void authenticateInternal(IBinder token, long sessionId, int userId,
1500 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001501 int callingUid, int callingPid, int callingUserId, int modality,
1502 IBiometricConfirmDeviceCredentialCallback callback) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001503 try {
1504 boolean requireConfirmation = bundle.getBoolean(
1505 BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
1506 if ((modality & TYPE_FACE) != 0) {
1507 // Check if the user has forced confirmation to be required in Settings.
1508 requireConfirmation = requireConfirmation
Kevin Chyn2170ab72019-04-03 20:28:28 -07001509 || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001510 }
1511 // Generate random cookies to pass to the services that should prepare to start
1512 // authenticating. Store the cookie here and wait for all services to "ack"
1513 // with the cookie. Once all cookies are received, we can show the prompt
1514 // and let the services start authenticating. The cookie should be non-zero.
1515 final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1516 Slog.d(TAG, "Creating auth session. Modality: " + modality
1517 + ", cookie: " + cookie);
1518 final HashMap<Integer, Integer> authenticators = new HashMap<>();
1519 authenticators.put(modality, cookie);
1520 mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
1521 receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
Kevin Chyn5a90a652019-03-25 18:11:16 -07001522 modality, requireConfirmation, callback);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001523 mPendingAuthSession.mState = STATE_AUTH_CALLED;
1524 // No polymorphism :(
1525 if ((modality & TYPE_FINGERPRINT) != 0) {
1526 mFingerprintService.prepareForAuthentication(token, sessionId, userId,
1527 mInternalReceiver, opPackageName, cookie,
1528 callingUid, callingPid, callingUserId);
1529 }
1530 if ((modality & TYPE_IRIS) != 0) {
1531 Slog.w(TAG, "Iris unsupported");
1532 }
1533 if ((modality & TYPE_FACE) != 0) {
1534 mFaceService.prepareForAuthentication(requireConfirmation,
1535 token, sessionId, userId, mInternalReceiver, opPackageName,
1536 cookie, callingUid, callingPid, callingUserId);
1537 }
1538 } catch (RemoteException e) {
1539 Slog.e(TAG, "Unable to start authentication", e);
1540 }
1541 }
1542
1543 private void handleCancelAuthentication(IBinder token, String opPackageName) {
1544 if (token == null || opPackageName == null) {
1545 Slog.e(TAG, "Unable to cancel, one or more null arguments");
1546 return;
1547 }
1548
Kevin Chyn5a90a652019-03-25 18:11:16 -07001549 if (mCurrentAuthSession != null
1550 && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) {
1551 if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing");
1552 try {
1553 mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel();
1554 } catch (RemoteException e) {
1555 Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e);
1556 }
1557
1558 // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up.
1559 handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1560 getContext().getString(R.string.biometric_error_canceled));
1561 } else if (mCurrentAuthSession != null
1562 && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
1563 // We need to check the current authenticators state. If we're pending confirm
1564 // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
1565 // since we won't be getting an onError from the driver.
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001566 try {
1567 // Send error to client
1568 mCurrentAuthSession.mClientReceiver.onError(
1569 BiometricConstants.BIOMETRIC_ERROR_CANCELED,
1570 getContext().getString(
1571 com.android.internal.R.string.biometric_error_user_canceled)
1572 );
1573
1574 mCurrentAuthSession.mState = STATE_AUTH_IDLE;
1575 mCurrentAuthSession = null;
1576 mStatusBarService.hideBiometricDialog();
1577 } catch (RemoteException e) {
1578 Slog.e(TAG, "Remote exception", e);
1579 }
1580 } else {
Kevin Chyn5a90a652019-03-25 18:11:16 -07001581 boolean fromCDC = false;
1582 if (mCurrentAuthSession != null) {
1583 fromCDC = mCurrentAuthSession.mBundle.getBoolean(
1584 BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
1585 }
1586
1587 if (fromCDC) {
1588 if (DEBUG) Slog.d(TAG, "Cancelling from CDC");
1589 cancelInternal(token, opPackageName, false /* fromClient */);
1590 } else {
1591 cancelInternal(token, opPackageName, true /* fromClient */);
1592 }
1593
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001594 }
1595 }
1596
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001597 void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
1598 final int callingUid = Binder.getCallingUid();
1599 final int callingPid = Binder.getCallingPid();
1600 final int callingUserId = UserHandle.getCallingUserId();
1601 mHandler.post(() -> {
1602 try {
1603 // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
1604 // drivers have canceled authentication.
1605 if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
1606 mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
1607 callingUid, callingPid, callingUserId, fromClient);
1608 }
1609 if ((mCurrentModality & TYPE_IRIS) != 0) {
1610 Slog.w(TAG, "Iris unsupported");
1611 }
1612 if ((mCurrentModality & TYPE_FACE) != 0) {
1613 mFaceService.cancelAuthenticationFromService(token, opPackageName,
1614 callingUid, callingPid, callingUserId, fromClient);
1615 }
1616 } catch (RemoteException e) {
1617 Slog.e(TAG, "Unable to cancel authentication");
1618 }
1619 });
1620 }
1621
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001622}