blob: 8dd3242c947a589e4e64908a9cf737a95a17abe7 [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;
Curtis Belmonte13eb5812019-10-22 14:17:30 -070026import static android.hardware.biometrics.BiometricManager.Authenticators;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070027
Kevin Chynb7b54a62018-09-28 18:48:12 -070028import android.app.ActivityManager;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -070029import android.app.IActivityManager;
Kevin Chynb7b54a62018-09-28 18:48:12 -070030import android.app.UserSwitchObserver;
joshmccloskey15c0a442019-10-18 16:09:06 -070031import android.app.admin.DevicePolicyManager;
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -080032import android.app.trust.ITrustManager;
Kevin Chynb7b54a62018-09-28 18:48:12 -070033import android.content.ContentResolver;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070034import android.content.Context;
35import android.content.pm.PackageManager;
Kevin Chynb7b54a62018-09-28 18:48:12 -070036import android.database.ContentObserver;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070037import android.hardware.biometrics.BiometricAuthenticator;
38import android.hardware.biometrics.BiometricConstants;
Kevin Chyn3a0187192018-10-08 15:40:05 -070039import android.hardware.biometrics.BiometricPrompt;
Kevin Chynb7b54a62018-09-28 18:48:12 -070040import android.hardware.biometrics.BiometricSourceType;
Kevin Chyn7782d142019-01-18 12:51:33 -080041import android.hardware.biometrics.BiometricsProtoEnums;
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -070042import android.hardware.biometrics.IBiometricAuthenticator;
Kevin Chynb7b54a62018-09-28 18:48:12 -070043import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
Kevin Chyn352adfe2018-09-20 22:28:50 -070044import android.hardware.biometrics.IBiometricService;
45import android.hardware.biometrics.IBiometricServiceReceiver;
Kevin Chyn23289ef2018-11-28 16:32:36 -080046import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Kevin Chynb7b54a62018-09-28 18:48:12 -070047import android.net.Uri;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070048import android.os.Binder;
49import android.os.Bundle;
Kevin Chynb7b54a62018-09-28 18:48:12 -070050import android.os.DeadObjectException;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070051import android.os.Handler;
52import android.os.IBinder;
53import android.os.Looper;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070054import android.os.Message;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070055import android.os.RemoteException;
56import android.os.ServiceManager;
57import android.os.UserHandle;
Kevin Chynd79e24e2018-09-25 12:06:59 -070058import android.provider.Settings;
Kevin Chyne92cdae2018-11-21 16:35:04 -080059import android.security.KeyStore;
Kevin Chyn87f257a2018-11-27 16:26:07 -080060import android.text.TextUtils;
Kevin Chyne7411422018-09-27 17:28:20 -070061import android.util.Pair;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070062import android.util.Slog;
Kevin Chyn7782d142019-01-18 12:51:33 -080063import android.util.StatsLog;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070064
65import com.android.internal.R;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -070066import com.android.internal.annotations.VisibleForTesting;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070067import com.android.internal.os.SomeArgs;
Kevin Chyne92cdae2018-11-21 16:35:04 -080068import com.android.internal.statusbar.IStatusBarService;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070069import com.android.server.SystemService;
70
71import java.util.ArrayList;
Kevin Chyn87f257a2018-11-27 16:26:07 -080072import java.util.HashMap;
73import java.util.Iterator;
Kevin Chynb7b54a62018-09-28 18:48:12 -070074import java.util.List;
Kevin Chyn87f257a2018-11-27 16:26:07 -080075import java.util.Map;
76import java.util.Random;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070077
78/**
79 * System service that arbitrates the modality for BiometricPrompt to use.
80 */
Kevin Chyn352adfe2018-09-20 22:28:50 -070081public class BiometricService extends SystemService {
Kevin Chyna24e9fd2018-08-27 12:39:17 -070082
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -080083 static final String TAG = "BiometricService";
Kevin Chyn5a90a652019-03-25 18:11:16 -070084 private static final boolean DEBUG = true;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070085
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070086 private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -070087 private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070088 private static final int MSG_ON_ERROR = 4;
89 private static final int MSG_ON_ACQUIRED = 5;
90 private static final int MSG_ON_DISMISSED = 6;
91 private static final int MSG_ON_TRY_AGAIN_PRESSED = 7;
92 private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8;
93 private static final int MSG_AUTHENTICATE = 9;
94 private static final int MSG_CANCEL_AUTHENTICATION = 10;
joshmccloskey568d3292019-09-17 14:02:23 -070095 private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11;
Kevin Chynff168dc2019-09-16 16:04:38 -070096 private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
Kevin Chyna24e9fd2018-08-27 12:39:17 -070097
Kevin Chyn84c2f5e2019-03-16 13:05:16 -070098 /**
99 * Authentication either just called and we have not transitioned to the CALLED state, or
100 * authentication terminated (success or error).
101 */
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700102 static final int STATE_AUTH_IDLE = 0;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700103 /**
104 * Authentication was called and we are waiting for the <Biometric>Services to return their
105 * cookies before starting the hardware and showing the BiometricPrompt.
106 */
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700107 static final int STATE_AUTH_CALLED = 1;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700108 /**
109 * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
110 */
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700111 static final int STATE_AUTH_STARTED = 2;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700112 /**
113 * Authentication is paused, waiting for the user to press "try again" button. Only
114 * passive modalities such as Face or Iris should have this state. Note that for passive
115 * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
116 * fingerprint.
117 */
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700118 static final int STATE_AUTH_PAUSED = 3;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700119 /**
120 * Authentication is successful, but we're waiting for the user to press "confirm" button.
121 */
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700122 static final int STATE_AUTH_PENDING_CONFIRM = 5;
Kevin Chyn5a90a652019-03-25 18:11:16 -0700123 /**
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700124 * Biometric authenticated, waiting for SysUI to finish animation
125 */
joshmccloskey568d3292019-09-17 14:02:23 -0700126 static final int STATE_AUTHENTICATED_PENDING_SYSUI = 6;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700127 /**
128 * Biometric error, waiting for SysUI to finish animation
129 */
joshmccloskey568d3292019-09-17 14:02:23 -0700130 static final int STATE_ERROR_PENDING_SYSUI = 7;
Kevin Chynff168dc2019-09-16 16:04:38 -0700131 /**
132 * Device credential in AuthController is showing
133 */
134 static final int STATE_SHOWING_DEVICE_CREDENTIAL = 8;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700135
joshmccloskey568d3292019-09-17 14:02:23 -0700136 final class AuthSession {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700137 // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
138 // <Biometric>Services before we can start authenticating. Pairs that have been returned
139 // are moved to mModalitiesMatched.
140 final HashMap<Integer, Integer> mModalitiesWaiting;
141 // Pairs that have been matched.
142 final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
143
144 // The following variables are passed to authenticateInternal, which initiates the
145 // appropriate <Biometric>Services.
146 final IBinder mToken;
147 final long mSessionId;
148 final int mUserId;
149 // Original receiver from BiometricPrompt.
150 final IBiometricServiceReceiver mClientReceiver;
151 final String mOpPackageName;
152 // Info to be shown on BiometricDialog when all cookies are returned.
153 final Bundle mBundle;
154 final int mCallingUid;
155 final int mCallingPid;
156 final int mCallingUserId;
157 // Continue authentication with the same modality/modalities after "try again" is
158 // pressed
159 final int mModality;
160 final boolean mRequireConfirmation;
161
162 // The current state, which can be either idle, called, or started
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700163 int mState = STATE_AUTH_IDLE;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700164 // For explicit confirmation, do not send to keystore until the user has confirmed
165 // the authentication.
166 byte[] mTokenEscrow;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700167 // Waiting for SystemUI to complete animation
168 int mErrorEscrow;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700169 int mVendorCodeEscrow;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700170
Kevin Chyn7fca2362019-06-20 17:20:42 -0700171 // Timestamp when authentication started
172 private long mStartTimeMs;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700173 // Timestamp when hardware authentication occurred
174 private long mAuthenticatedTimeMs;
175
176 AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
177 int userId, IBiometricServiceReceiver receiver, String opPackageName,
178 Bundle bundle, int callingUid, int callingPid, int callingUserId,
joshmccloskey568d3292019-09-17 14:02:23 -0700179 int modality, boolean requireConfirmation) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700180 mModalitiesWaiting = modalities;
181 mToken = token;
182 mSessionId = sessionId;
183 mUserId = userId;
184 mClientReceiver = receiver;
185 mOpPackageName = opPackageName;
186 mBundle = bundle;
187 mCallingUid = callingUid;
188 mCallingPid = callingPid;
189 mCallingUserId = callingUserId;
190 mModality = modality;
191 mRequireConfirmation = requireConfirmation;
192 }
193
194 boolean isCrypto() {
195 return mSessionId != 0;
196 }
197
198 boolean containsCookie(int cookie) {
199 if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
200 return true;
201 }
202 if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
203 return true;
204 }
205 return false;
206 }
Kevin Chyn5a90a652019-03-25 18:11:16 -0700207
Kevin Chyn8429da22019-09-24 12:42:35 -0700208 boolean isAllowDeviceCredential() {
Kevin Chync70d6b82019-10-03 15:32:37 -0700209 return Utils.isDeviceCredentialAllowed(mBundle);
Kevin Chyn8429da22019-09-24 12:42:35 -0700210 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700211 }
212
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700213 private final Injector mInjector;
joshmccloskey15c0a442019-10-18 16:09:06 -0700214 private final DevicePolicyManager mDevicePolicyManager;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700215 @VisibleForTesting
216 final IBiometricService.Stub mImpl;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700217 @VisibleForTesting
Ilya Matyukhina610efb2019-10-02 12:46:27 -0700218 final SettingObserver mSettingObserver;
Kevin Chynb7b54a62018-09-28 18:48:12 -0700219 private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700220 private final Random mRandom = new Random();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700221
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700222 @VisibleForTesting
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700223 IStatusBarService mStatusBarService;
224 @VisibleForTesting
225 KeyStore mKeyStore;
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -0800226 @VisibleForTesting
227 ITrustManager mTrustManager;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700228
229 // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
230 // polymorphism :/
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700231 final ArrayList<AuthenticatorWrapper> mAuthenticators = new ArrayList<>();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700232
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700233 BiometricStrengthController mBiometricStrengthController;
234
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700235 // The current authentication session, null if idle/done. We need to track both the current
236 // and pending sessions since errors may be sent to either.
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700237 @VisibleForTesting
238 AuthSession mCurrentAuthSession;
239 @VisibleForTesting
240 AuthSession mPendingAuthSession;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700241
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700242 @VisibleForTesting
243 final Handler mHandler = new Handler(Looper.getMainLooper()) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700244 @Override
245 public void handleMessage(Message msg) {
246 switch (msg.what) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700247 case MSG_ON_AUTHENTICATION_SUCCEEDED: {
248 SomeArgs args = (SomeArgs) msg.obj;
249 handleAuthenticationSucceeded(
250 (boolean) args.arg1 /* requireConfirmation */,
251 (byte[]) args.arg2 /* token */);
252 args.recycle();
253 break;
254 }
255
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700256 case MSG_ON_AUTHENTICATION_REJECTED: {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700257 handleAuthenticationRejected();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700258 break;
259 }
260
261 case MSG_ON_ERROR: {
262 SomeArgs args = (SomeArgs) msg.obj;
263 handleOnError(
264 args.argi1 /* cookie */,
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700265 args.argi2 /* modality */,
266 args.argi3 /* error */,
267 args.argi4 /* vendorCode */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700268 args.recycle();
269 break;
270 }
271
272 case MSG_ON_ACQUIRED: {
273 SomeArgs args = (SomeArgs) msg.obj;
274 handleOnAcquired(
275 args.argi1 /* acquiredInfo */,
276 (String) args.arg1 /* message */);
277 args.recycle();
278 break;
279 }
280
281 case MSG_ON_DISMISSED: {
282 handleOnDismissed(msg.arg1);
283 break;
284 }
285
286 case MSG_ON_TRY_AGAIN_PRESSED: {
287 handleOnTryAgainPressed();
288 break;
289 }
290
291 case MSG_ON_READY_FOR_AUTHENTICATION: {
292 SomeArgs args = (SomeArgs) msg.obj;
293 handleOnReadyForAuthentication(
294 args.argi1 /* cookie */,
295 (boolean) args.arg1 /* requireConfirmation */,
296 args.argi2 /* userId */);
297 args.recycle();
298 break;
299 }
300
301 case MSG_AUTHENTICATE: {
302 SomeArgs args = (SomeArgs) msg.obj;
303 handleAuthenticate(
304 (IBinder) args.arg1 /* token */,
305 (long) args.arg2 /* sessionId */,
306 args.argi1 /* userid */,
307 (IBiometricServiceReceiver) args.arg3 /* receiver */,
308 (String) args.arg4 /* opPackageName */,
309 (Bundle) args.arg5 /* bundle */,
310 args.argi2 /* callingUid */,
311 args.argi3 /* callingPid */,
joshmccloskey568d3292019-09-17 14:02:23 -0700312 args.argi4 /* callingUserId */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700313 args.recycle();
314 break;
315 }
316
317 case MSG_CANCEL_AUTHENTICATION: {
318 SomeArgs args = (SomeArgs) msg.obj;
319 handleCancelAuthentication(
320 (IBinder) args.arg1 /* token */,
321 (String) args.arg2 /* opPackageName */);
322 args.recycle();
323 break;
324 }
325
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700326 case MSG_ON_AUTHENTICATION_TIMED_OUT: {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700327 SomeArgs args = (SomeArgs) msg.obj;
328 handleAuthenticationTimedOut(
329 args.argi1 /* modality */,
330 args.argi2 /* error */,
331 args.argi3 /* vendorCode */);
332 args.recycle();
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700333 break;
334 }
335
Kevin Chynff168dc2019-09-16 16:04:38 -0700336 case MSG_ON_DEVICE_CREDENTIAL_PRESSED: {
337 handleOnDeviceCredentialPressed();
338 break;
339 }
340
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700341 default:
Kevin Chyn5a90a652019-03-25 18:11:16 -0700342 Slog.e(TAG, "Unknown message: " + msg);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700343 break;
344 }
345 }
346 };
347
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700348 /**
349 * Wraps IBiometricAuthenticator implementation and stores information about the authenticator.
350 * TODO(b/141025588): Consider refactoring the tests to not rely on this implementation detail.
351 */
352 @VisibleForTesting
353 public static final class AuthenticatorWrapper {
354 public final int id;
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700355 public final int OEMStrength; // strength as configured by the OEM
356 private int updatedStrength; // strength updated by BiometricStrengthController
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700357 public final int modality;
358 public final IBiometricAuthenticator impl;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700359
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700360 AuthenticatorWrapper(int id, int modality, int strength,
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700361 IBiometricAuthenticator impl) {
362 this.id = id;
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700363 this.modality = modality;
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700364 this.OEMStrength = strength;
365 this.updatedStrength = strength;
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700366 this.impl = impl;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700367 }
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700368
369 /**
370 * Returns the actual strength, taking any updated strengths into effect. Since more bits
371 * means lower strength, the resulting strength is never stronger than the OEM's configured
372 * strength.
373 * @return a bitfield, see {@link Authenticators}
374 */
375 public int getActualStrength() {
376 return OEMStrength | updatedStrength;
377 }
378
379 /**
380 * Stores the updated strength, which takes effect whenever {@link #getActualStrength()}
381 * is checked.
382 * @param newStrength
383 */
384 public void updateStrength(int newStrength) {
385 String log = "updateStrength: Before(" + toString() + ")";
386 updatedStrength = newStrength;
387 log += " After(" + toString() + ")";
388 Slog.d(TAG, log);
389 }
390
391 @Override
392 public String toString() {
393 return "ID(" + id + ")"
394 + " OEMStrength: " + OEMStrength
395 + " updatedStrength: " + updatedStrength
396 + " modality " + modality
397 + " authenticator: " + impl;
398 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700399 }
400
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700401 @VisibleForTesting
402 public static class SettingObserver extends ContentObserver {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700403
404 private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
405 private static final boolean DEFAULT_APP_ENABLED = true;
406 private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
407
Kevin Chynb7b54a62018-09-28 18:48:12 -0700408 private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
409 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
410 private final Uri FACE_UNLOCK_APP_ENABLED =
411 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
Kevin Chyn31402832019-01-10 15:56:41 -0800412 private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
413 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700414
415 private final ContentResolver mContentResolver;
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700416 private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
Kevin Chyn2170ab72019-04-03 20:28:28 -0700417
Ilya Matyukhina610efb2019-10-02 12:46:27 -0700418 private final Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
419 private final Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
420 private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
Kevin Chynb7b54a62018-09-28 18:48:12 -0700421
422 /**
423 * Creates a content observer.
424 *
425 * @param handler The handler to run {@link #onChange} on, or null if none.
426 */
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700427 public SettingObserver(Context context, Handler handler,
428 List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
Kevin Chynb7b54a62018-09-28 18:48:12 -0700429 super(handler);
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700430 mContentResolver = context.getContentResolver();
431 mCallbacks = callbacks;
Kevin Chynb7b54a62018-09-28 18:48:12 -0700432 updateContentObserver();
433 }
434
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700435 public void updateContentObserver() {
Kevin Chynb7b54a62018-09-28 18:48:12 -0700436 mContentResolver.unregisterContentObserver(this);
437 mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
438 false /* notifyForDescendents */,
439 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700440 UserHandle.USER_ALL);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700441 mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
442 false /* notifyForDescendents */,
443 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700444 UserHandle.USER_ALL);
Kevin Chyn31402832019-01-10 15:56:41 -0800445 mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
446 false /* notifyForDescendents */,
447 this /* observer */,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700448 UserHandle.USER_ALL);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700449 }
450
451 @Override
Kevin Chyn2170ab72019-04-03 20:28:28 -0700452 public void onChange(boolean selfChange, Uri uri, int userId) {
Kevin Chynb7b54a62018-09-28 18:48:12 -0700453 if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700454 mFaceEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser(
Kevin Chynb7b54a62018-09-28 18:48:12 -0700455 mContentResolver,
456 Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700457 DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */,
458 userId) != 0);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700459
Kevin Chyn750663c2019-05-15 12:17:38 -0700460 if (userId == ActivityManager.getCurrentUser() && !selfChange) {
Kevin Chyn0c000332019-04-04 16:02:37 -0700461 notifyEnabledOnKeyguardCallbacks(userId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700462 }
463 } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700464 mFaceEnabledForApps.put(userId, Settings.Secure.getIntForUser(
Kevin Chynb7b54a62018-09-28 18:48:12 -0700465 mContentResolver,
466 Settings.Secure.FACE_UNLOCK_APP_ENABLED,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700467 DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
468 userId) != 0);
Kevin Chyn31402832019-01-10 15:56:41 -0800469 } else if (FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION.equals(uri)) {
Kevin Chyn2170ab72019-04-03 20:28:28 -0700470 mFaceAlwaysRequireConfirmation.put(userId, Settings.Secure.getIntForUser(
Kevin Chyn31402832019-01-10 15:56:41 -0800471 mContentResolver,
472 Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
Kevin Chyn2170ab72019-04-03 20:28:28 -0700473 DEFAULT_ALWAYS_REQUIRE_CONFIRMATION ? 1 : 0 /* default */,
474 userId) != 0);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700475 }
476 }
477
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700478 public boolean getFaceEnabledOnKeyguard() {
Kevin Chyn750663c2019-05-15 12:17:38 -0700479 final int user = ActivityManager.getCurrentUser();
480 if (!mFaceEnabledOnKeyguard.containsKey(user)) {
481 onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
482 }
483 return mFaceEnabledOnKeyguard.get(user);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700484 }
485
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700486 public boolean getFaceEnabledForApps(int userId) {
Kevin Chyn750663c2019-05-15 12:17:38 -0700487 if (!mFaceEnabledForApps.containsKey(userId)) {
488 onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
489 }
Kevin Chyn2170ab72019-04-03 20:28:28 -0700490 return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700491 }
Kevin Chyn31402832019-01-10 15:56:41 -0800492
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700493 public boolean getFaceAlwaysRequireConfirmation(int userId) {
Kevin Chyn750663c2019-05-15 12:17:38 -0700494 if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
495 onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
496 }
497 return mFaceAlwaysRequireConfirmation.get(userId);
Kevin Chyn31402832019-01-10 15:56:41 -0800498 }
Kevin Chyn0c000332019-04-04 16:02:37 -0700499
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700500 public void notifyEnabledOnKeyguardCallbacks(int userId) {
501 List<EnabledOnKeyguardCallback> callbacks = mCallbacks;
Kevin Chyn0c000332019-04-04 16:02:37 -0700502 for (int i = 0; i < callbacks.size(); i++) {
503 callbacks.get(i).notify(BiometricSourceType.FACE,
Lucas Dupin7d95f152019-07-17 16:25:54 -0700504 mFaceEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED),
505 userId);
Kevin Chyn0c000332019-04-04 16:02:37 -0700506 }
507 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700508 }
509
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700510 final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
Kevin Chynb7b54a62018-09-28 18:48:12 -0700511
512 private final IBiometricEnabledOnKeyguardCallback mCallback;
513
514 EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
515 mCallback = callback;
516 try {
517 mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
518 } catch (RemoteException e) {
519 Slog.w(TAG, "Unable to linkToDeath", e);
520 }
521 }
522
Lucas Dupin7d95f152019-07-17 16:25:54 -0700523 void notify(BiometricSourceType sourceType, boolean enabled, int userId) {
Kevin Chynb7b54a62018-09-28 18:48:12 -0700524 try {
Lucas Dupin7d95f152019-07-17 16:25:54 -0700525 mCallback.onChanged(sourceType, enabled, userId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700526 } catch (DeadObjectException e) {
527 Slog.w(TAG, "Death while invoking notify", e);
528 mEnabledOnKeyguardCallbacks.remove(this);
529 } catch (RemoteException e) {
530 Slog.w(TAG, "Failed to invoke onChanged", e);
531 }
532 }
533
534 @Override
535 public void binderDied() {
536 Slog.e(TAG, "Enabled callback binder died");
537 mEnabledOnKeyguardCallbacks.remove(this);
538 }
539 }
540
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700541 // Wrap the client's receiver so we can do things with the BiometricDialog first
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700542 @VisibleForTesting
543 final IBiometricServiceReceiverInternal mInternalReceiver =
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700544 new IBiometricServiceReceiverInternal.Stub() {
545 @Override
546 public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
547 throws RemoteException {
548 SomeArgs args = SomeArgs.obtain();
549 args.arg1 = requireConfirmation;
550 args.arg2 = token;
551 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
552 }
553
554 @Override
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700555 public void onAuthenticationFailed()
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700556 throws RemoteException {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700557 Slog.v(TAG, "onAuthenticationFailed");
558 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED).sendToTarget();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700559 }
560
561 @Override
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700562 public void onError(int cookie, int modality, int error, int vendorCode)
563 throws RemoteException {
Kevin Chyne674e852019-04-24 12:39:40 -0700564 // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
565 // soft errors and we should allow the user to try authenticating again instead of
566 // dismissing BiometricPrompt.
567 if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700568 SomeArgs args = SomeArgs.obtain();
569 args.argi1 = modality;
570 args.argi2 = error;
571 args.argi3 = vendorCode;
572 mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, args).sendToTarget();
Kevin Chyne674e852019-04-24 12:39:40 -0700573 } else {
574 SomeArgs args = SomeArgs.obtain();
575 args.argi1 = cookie;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700576 args.argi2 = modality;
577 args.argi3 = error;
578 args.argi4 = vendorCode;
Kevin Chyne674e852019-04-24 12:39:40 -0700579 mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
580 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700581 }
582
583 @Override
584 public void onAcquired(int acquiredInfo, String message) throws RemoteException {
585 SomeArgs args = SomeArgs.obtain();
586 args.argi1 = acquiredInfo;
587 args.arg1 = message;
588 mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget();
589 }
590
591 @Override
592 public void onDialogDismissed(int reason) throws RemoteException {
593 mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget();
594 }
595
596 @Override
597 public void onTryAgainPressed() {
598 mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
599 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700600
601 @Override
602 public void onDeviceCredentialPressed() {
603 mHandler.sendEmptyMessage(MSG_ON_DEVICE_CREDENTIAL_PRESSED);
604 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700605 };
606
607
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700608 /**
609 * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
610 * should not carry any state. The reality is we need to keep a tiny amount of state so that
611 * cancelAuthentication() can go to the right place.
612 */
Kevin Chynbf830a32018-10-07 15:58:46 -0700613 private final class BiometricServiceWrapper extends IBiometricService.Stub {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800614 @Override // Binder call
Kevin Chyn87f257a2018-11-27 16:26:07 -0800615 public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800616 checkInternalPermission();
Kevin Chyn87f257a2018-11-27 16:26:07 -0800617
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700618 SomeArgs args = SomeArgs.obtain();
619 args.argi1 = cookie;
620 args.arg1 = requireConfirmation;
621 args.argi2 = userId;
622 mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget();
Kevin Chyne92cdae2018-11-21 16:35:04 -0800623 }
624
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700625 @Override // Binder call
626 public void authenticate(IBinder token, long sessionId, int userId,
joshmccloskey568d3292019-09-17 14:02:23 -0700627 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
Kevin Chyn23289ef2018-11-28 16:32:36 -0800628 throws RemoteException {
Kevin Chyn067085a2018-11-12 15:49:19 -0800629 final int callingUid = Binder.getCallingUid();
630 final int callingPid = Binder.getCallingPid();
631 final int callingUserId = UserHandle.getCallingUserId();
632
633 // In the BiometricServiceBase, check do the AppOps and foreground check.
634 if (userId == callingUserId) {
635 // Check the USE_BIOMETRIC permission here.
636 checkPermission();
637 } else {
638 // Only allow internal clients to authenticate with a different userId
639 Slog.w(TAG, "User " + callingUserId + " is requesting authentication of userid: "
640 + userId);
641 checkInternalPermission();
642 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700643
Kevin Chyne92cdae2018-11-21 16:35:04 -0800644 if (token == null || receiver == null || opPackageName == null || bundle == null) {
Kevin Chyn7c2371a2018-09-12 01:54:18 -0700645 Slog.e(TAG, "Unable to authenticate, one or more null arguments");
646 return;
647 }
648
Kevin Chynd04b43d2019-12-13 12:56:41 -0800649 if (!Utils.isValidAuthenticatorConfig(bundle)) {
650 throw new SecurityException("Invalid authenticator configuration");
651 }
652
joshmccloskey15c0a442019-10-18 16:09:06 -0700653 if (bundle.getBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS)) {
654 checkInternalPermission();
655 }
656
Kevin Chync70d6b82019-10-03 15:32:37 -0700657 Utils.combineAuthenticatorBundles(bundle);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700658
Curtis Belmonte13eb5812019-10-22 14:17:30 -0700659 // Check the usage of this in system server. Need to remove this check if it becomes a
660 // 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 Chyn84c2f5e2019-03-16 13:05:16 -0700672 SomeArgs args = SomeArgs.obtain();
673 args.arg1 = token;
674 args.arg2 = sessionId;
675 args.argi1 = userId;
676 args.arg3 = receiver;
677 args.arg4 = opPackageName;
678 args.arg5 = bundle;
679 args.argi2 = callingUid;
680 args.argi3 = callingPid;
681 args.argi4 = callingUserId;
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700682
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700683 mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700684 }
685
Kevin Chyn1b2137c2019-01-24 16:32:38 -0800686 @Override // Binder call
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700687 public void cancelAuthentication(IBinder token, String opPackageName)
688 throws RemoteException {
689 checkPermission();
Kevin Chyn23289ef2018-11-28 16:32:36 -0800690
Kevin Chyn84c2f5e2019-03-16 13:05:16 -0700691 SomeArgs args = SomeArgs.obtain();
692 args.arg1 = token;
693 args.arg2 = opPackageName;
694 mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700695 }
Kevin Chyn05c21502018-09-18 13:07:19 -0700696
697 @Override // Binder call
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -0800698 public int canAuthenticate(String opPackageName, int userId,
699 @Authenticators.Types int authenticators) {
Kevin Chyn28623b62019-06-11 11:43:33 -0700700 Slog.d(TAG, "canAuthenticate: User=" + userId
Kevin Chynd04b43d2019-12-13 12:56:41 -0800701 + ", Caller=" + UserHandle.getCallingUserId()
702 + ", Authenticators=" + authenticators);
Kevin Chyn69183e52018-09-21 17:04:09 -0700703
Kevin Chyn28623b62019-06-11 11:43:33 -0700704 if (userId != UserHandle.getCallingUserId()) {
705 checkInternalPermission();
706 } else {
707 checkPermission();
708 }
709
Kevin Chynd04b43d2019-12-13 12:56:41 -0800710
711 if (!Utils.isValidAuthenticatorConfig(authenticators)) {
712 throw new SecurityException("Invalid authenticator configuration");
713 }
714
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -0800715 final Bundle bundle = new Bundle();
716 bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
717
718 int biometricConstantsResult = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
Kevin Chyn05c21502018-09-18 13:07:19 -0700719 final long ident = Binder.clearCallingIdentity();
720 try {
joshmccloskey15c0a442019-10-18 16:09:06 -0700721 biometricConstantsResult = checkAndGetAuthenticators(userId, bundle, opPackageName,
722 false /* checkDevicePolicyManager */).second;
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -0800723 if (biometricConstantsResult != BiometricConstants.BIOMETRIC_SUCCESS
724 && Utils.isDeviceCredentialAllowed(bundle)) {
725 // If there's an issue with biometrics, but device credential is allowed and
726 // set up, return SUCCESS. If device credential isn't set up either, return
727 // ERROR_NO_DEVICE_CREDENTIAL.
728 if (mTrustManager.isDeviceSecure(userId)) {
729 biometricConstantsResult = BiometricConstants.BIOMETRIC_SUCCESS;
730 } else {
731 biometricConstantsResult =
732 BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL;
733 }
734 }
735
736 } catch (RemoteException e) {
737 Slog.e(TAG, "Remote exception", e);
Kevin Chyn05c21502018-09-18 13:07:19 -0700738 } finally {
739 Binder.restoreCallingIdentity(ident);
740 }
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -0800741
742 return Utils.biometricConstantsToBiometricManager(biometricConstantsResult);
Kevin Chyne7411422018-09-27 17:28:20 -0700743 }
Kevin Chynb7b54a62018-09-28 18:48:12 -0700744
Kevin Chyn27f92312019-07-26 12:41:33 -0700745 @Override
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700746 public boolean hasEnrolledBiometrics(int userId, String opPackageName) {
Kevin Chyn27f92312019-07-26 12:41:33 -0700747 checkInternalPermission();
748
749 final long ident = Binder.clearCallingIdentity();
750 try {
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700751 for (AuthenticatorWrapper authenticator : mAuthenticators) {
752 if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
Kevin Chyn27f92312019-07-26 12:41:33 -0700753 return true;
754 }
755 }
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700756 } catch (RemoteException e) {
757 Slog.e(TAG, "Remote exception", e);
Kevin Chyn27f92312019-07-26 12:41:33 -0700758 } finally {
759 Binder.restoreCallingIdentity(ident);
760 }
761 return false;
762 }
763
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700764 @Override
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700765 public void registerAuthenticator(int id, int modality, int strength,
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700766 IBiometricAuthenticator authenticator) {
Ilya Matyukhine4675b32019-11-07 16:07:19 -0800767 checkInternalPermission();
768
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700769 Slog.d(TAG, "Registering ID: " + id
770 + " Modality: " + modality
771 + " Strength: " + strength);
772
Kevin Chyn9cef3632019-12-15 18:21:46 -0800773 if (authenticator == null) {
774 throw new IllegalArgumentException("Authenticator must not be null."
775 + " Did you forget to modify the core/res/res/values/xml overlay for"
776 + " config_biometric_sensors?");
777 }
778
Kevin Chyne8d2d1f2019-12-13 15:36:49 -0800779 if (strength != Authenticators.BIOMETRIC_STRONG
780 && strength != Authenticators.BIOMETRIC_WEAK) {
781 throw new IllegalStateException("Unsupported strength");
782 }
783
784 for (AuthenticatorWrapper wrapper : mAuthenticators) {
785 if (wrapper.id == id) {
786 throw new IllegalStateException("Cannot register duplicate authenticator");
787 }
788 }
789
790 // This happens infrequently enough, not worth caching.
791 final String[] configs = mInjector.getConfiguration(getContext());
792 boolean idFound = false;
793 for (int i = 0; i < configs.length; i++) {
794 SensorConfig config = new SensorConfig(configs[i]);
795 if (config.mId == id) {
796 idFound = true;
797 break;
798 }
799 }
800 if (!idFound) {
801 throw new IllegalStateException("Cannot register unknown id");
802 }
803
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700804 mAuthenticators.add(new AuthenticatorWrapper(id, modality, strength, authenticator));
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700805 }
806
Kevin Chynbf830a32018-10-07 15:58:46 -0700807 @Override // Binder call
Kevin Chynb7b54a62018-09-28 18:48:12 -0700808 public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
809 throws RemoteException {
810 checkInternalPermission();
811 mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
812 try {
813 callback.onChanged(BiometricSourceType.FACE,
Lucas Dupin7d95f152019-07-17 16:25:54 -0700814 mSettingObserver.getFaceEnabledOnKeyguard(),
815 UserHandle.getCallingUserId());
Kevin Chynb7b54a62018-09-28 18:48:12 -0700816 } catch (RemoteException e) {
817 Slog.w(TAG, "Remote exception", e);
818 }
819 }
Kevin Chynbf830a32018-10-07 15:58:46 -0700820
821 @Override // Binder call
822 public void setActiveUser(int userId) {
823 checkInternalPermission();
824 final long ident = Binder.clearCallingIdentity();
825 try {
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700826 for (AuthenticatorWrapper authenticator : mAuthenticators) {
827 authenticator.impl.setActiveUser(userId);
Kevin Chynbf830a32018-10-07 15:58:46 -0700828 }
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700829 } catch (RemoteException e) {
830 Slog.e(TAG, "Remote exception", e);
Kevin Chynbf830a32018-10-07 15:58:46 -0700831 } finally {
832 Binder.restoreCallingIdentity(ident);
833 }
834 }
Kevin Chyne92cdae2018-11-21 16:35:04 -0800835
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800836 @Override // Binder call
Kevin Chyna38653c2019-02-11 17:46:21 -0800837 public void resetLockout(byte[] token) {
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800838 checkInternalPermission();
839 final long ident = Binder.clearCallingIdentity();
840 try {
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700841 for (AuthenticatorWrapper authenticator : mAuthenticators) {
842 authenticator.impl.resetLockout(token);
Kevin Chyn6adb3a12018-12-05 19:40:31 -0800843 }
844 } catch (RemoteException e) {
845 Slog.e(TAG, "Remote exception", e);
846 } finally {
847 Binder.restoreCallingIdentity(ident);
848 }
849 }
Kevin Chyne7411422018-09-27 17:28:20 -0700850 }
851
Kevin Chynb7b54a62018-09-28 18:48:12 -0700852 private void checkInternalPermission() {
Kevin Chyne92cdae2018-11-21 16:35:04 -0800853 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
Kevin Chyn3a0187192018-10-08 15:40:05 -0700854 "Must have USE_BIOMETRIC_INTERNAL permission");
Kevin Chynb7b54a62018-09-28 18:48:12 -0700855 }
856
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700857 private void checkPermission() {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700858 if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700859 != PackageManager.PERMISSION_GRANTED) {
Kevin Chyn71db85f2019-05-14 15:32:47 -0700860 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700861 "Must have USE_BIOMETRIC permission");
862 }
863 }
864
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700865 /**
866 * Class for injecting dependencies into BiometricService.
867 * TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger).
868 */
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700869 @VisibleForTesting
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700870 public static class Injector {
871
872 @VisibleForTesting
873 public IActivityManager getActivityManagerService() {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700874 return ActivityManager.getService();
875 }
876
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700877 @VisibleForTesting
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -0800878 public ITrustManager getTrustManager() {
879 return ITrustManager.Stub.asInterface(ServiceManager.getService(Context.TRUST_SERVICE));
880 }
881
882 @VisibleForTesting
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700883 public IStatusBarService getStatusBarService() {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700884 return IStatusBarService.Stub.asInterface(
885 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
886 }
887
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700888 /**
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700889 * Allows to mock SettingObserver for testing.
890 */
891 @VisibleForTesting
892 public SettingObserver getSettingObserver(Context context, Handler handler,
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700893 List<EnabledOnKeyguardCallback> callbacks) {
894 return new SettingObserver(context, handler, callbacks);
895 }
896
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700897 @VisibleForTesting
898 public KeyStore getKeyStore() {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700899 return KeyStore.getInstance();
900 }
901
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700902 /**
903 * Allows to enable/disable debug logs.
904 */
905 @VisibleForTesting
906 public boolean isDebugEnabled(Context context, int userId) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700907 return Utils.isDebugEnabled(context, userId);
908 }
909
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -0700910 /**
911 * Allows to stub publishBinderService(...) for testing.
912 */
913 @VisibleForTesting
914 public void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700915 service.publishBinderService(Context.BIOMETRIC_SERVICE, impl);
916 }
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700917
918 /**
919 * Allows to mock BiometricStrengthController for testing.
920 */
921 @VisibleForTesting
922 public BiometricStrengthController getBiometricStrengthController(
923 BiometricService service) {
924 return new BiometricStrengthController(service);
925 }
Kevin Chyne8d2d1f2019-12-13 15:36:49 -0800926
927 /**
928 * Allows to test with various device sensor configurations.
929 * @param context System Server context
930 * @return the sensor configuration from core/res/res/values/config.xml
931 */
932 @VisibleForTesting
933 public String[] getConfiguration(Context context) {
934 return context.getResources().getStringArray(R.array.config_biometric_sensors);
935 }
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700936 }
937
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700938 /**
939 * Initializes the system service.
940 * <p>
941 * Subclasses must define a single argument constructor that accepts the context
942 * and passes it to super.
943 * </p>
944 *
945 * @param context The system server context.
946 */
Kevin Chyn352adfe2018-09-20 22:28:50 -0700947 public BiometricService(Context context) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700948 this(context, new Injector());
949 }
950
951 @VisibleForTesting
952 BiometricService(Context context, Injector injector) {
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700953 super(context);
954
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700955 mInjector = injector;
joshmccloskey15c0a442019-10-18 16:09:06 -0700956 mDevicePolicyManager = (DevicePolicyManager) context
957 .getSystemService(context.DEVICE_POLICY_SERVICE);
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700958 mImpl = new BiometricServiceWrapper();
Kevin Chynb7b54a62018-09-28 18:48:12 -0700959 mEnabledOnKeyguardCallbacks = new ArrayList<>();
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700960 mSettingObserver = mInjector.getSettingObserver(context, mHandler,
961 mEnabledOnKeyguardCallbacks);
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700962
Kevin Chynb7b54a62018-09-28 18:48:12 -0700963 try {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700964 injector.getActivityManagerService().registerUserSwitchObserver(
Kevin Chynb7b54a62018-09-28 18:48:12 -0700965 new UserSwitchObserver() {
966 @Override
967 public void onUserSwitchComplete(int newUserId) {
968 mSettingObserver.updateContentObserver();
Kevin Chyn0c000332019-04-04 16:02:37 -0700969 mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId);
Kevin Chynb7b54a62018-09-28 18:48:12 -0700970 }
971 }, BiometricService.class.getName()
972 );
973 } catch (RemoteException e) {
974 Slog.e(TAG, "Failed to register user switch observer", e);
975 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700976 }
977
978 @Override
979 public void onStart() {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700980 mKeyStore = mInjector.getKeyStore();
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700981 mStatusBarService = mInjector.getStatusBarService();
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -0800982 mTrustManager = mInjector.getTrustManager();
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -0700983 mInjector.publishBinderService(this, mImpl);
Haining Chen5c7ed9a2019-10-29 11:17:48 -0700984 mBiometricStrengthController = mInjector.getBiometricStrengthController(this);
985 mBiometricStrengthController.startListening();
Kevin Chyna24e9fd2018-08-27 12:39:17 -0700986 }
987
988 /**
joshmccloskey15c0a442019-10-18 16:09:06 -0700989 * @param modality one of {@link BiometricAuthenticator#TYPE_FINGERPRINT},
990 * {@link BiometricAuthenticator#TYPE_IRIS} or {@link BiometricAuthenticator#TYPE_FACE}
991 * @return
992 */
993 private int mapModalityToDevicePolicyType(int modality) {
994 switch (modality) {
995 case TYPE_FINGERPRINT:
996 return DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
997 case TYPE_IRIS:
998 return DevicePolicyManager.KEYGUARD_DISABLE_IRIS;
999 case TYPE_FACE:
1000 return DevicePolicyManager.KEYGUARD_DISABLE_FACE;
1001 default:
1002 Slog.e(TAG, "Error modality=" + modality);
1003 return DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
1004 }
1005 }
1006
1007 // TODO(joshmccloskey): Update this to throw an error if a new modality is added and this
1008 // logic is not updated.
1009 private boolean isBiometricDisabledByDevicePolicy(int modality, int effectiveUserId) {
1010 final int biometricToCheck = mapModalityToDevicePolicyType(modality);
1011 if (biometricToCheck == DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE) {
1012 Slog.e(TAG, "Allowing unknown modality " + modality + " to pass Device Policy check");
1013 return false;
1014 }
1015 final int devicePolicyDisabledFeatures =
1016 mDevicePolicyManager.getKeyguardDisabledFeatures(null, effectiveUserId);
1017 final boolean isBiometricDisabled =
1018 (biometricToCheck & devicePolicyDisabledFeatures) != 0;
1019 Slog.w(TAG, "isBiometricDisabledByDevicePolicy(" + modality + "," + effectiveUserId
1020 + ")=" + isBiometricDisabled);
1021 return isBiometricDisabled;
1022 }
1023
1024 /**
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001025 * Checks if there are any available biometrics, and returns the modality. This method also
1026 * returns errors through the callback (no biometric feature, hardware not detected, no
1027 * templates enrolled, etc). This service must not start authentication if errors are sent.
Kevin Chyne7411422018-09-27 17:28:20 -07001028 *
Haining Chen5c7ed9a2019-10-29 11:17:48 -07001029 * @param userId the user to check for
1030 * @param bundle passed from {@link BiometricPrompt}
1031 * @param opPackageName see {@link android.app.AppOpsManager}
1032 *
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001033 * @return A pair [Modality, Error] with Modality being one of
Kevin Chyn87f257a2018-11-27 16:26:07 -08001034 * {@link BiometricAuthenticator#TYPE_NONE},
1035 * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
1036 * {@link BiometricAuthenticator#TYPE_IRIS},
1037 * {@link BiometricAuthenticator#TYPE_FACE}
Kevin Chyne7411422018-09-27 17:28:20 -07001038 * and the error containing one of the {@link BiometricConstants} errors.
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001039 *
1040 * TODO(kchyn): Update this to handle DEVICE_CREDENTIAL better, reduce duplicate code in callers
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001041 */
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001042 private Pair<Integer, Integer> checkAndGetAuthenticators(int userId, Bundle bundle,
joshmccloskey15c0a442019-10-18 16:09:06 -07001043 String opPackageName, boolean checkDevicePolicyManager) throws RemoteException {
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001044 if (!Utils.isBiometricAllowed(bundle)
1045 && Utils.isDeviceCredentialAllowed(bundle)
1046 && !mTrustManager.isDeviceSecure(userId)) {
1047 // If only device credential is being checked, and the user doesn't have one set up
1048 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL);
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001049 }
1050
Kevin Chyne7411422018-09-27 17:28:20 -07001051 // Assuming that authenticators are listed in priority-order, the rest of this function
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001052 // will attempt to find the first authenticator that's as strong or stronger than the
1053 // requested strength, available, enrolled, and enabled. The tricky part is returning the
1054 // correct error. Error strings that are modality-specific should also respect the
1055 // priority-order.
Kevin Chyne7411422018-09-27 17:28:20 -07001056
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001057 // Find first authenticator that's strong enough, detected, enrolled, and enabled.
1058 boolean hasSufficientStrength = false;
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001059 boolean isHardwareDetected = false;
1060 boolean hasTemplatesEnrolled = false;
Kevin Chyne7411422018-09-27 17:28:20 -07001061 boolean enabledForApps = false;
1062
Ilya Matyukhin8344b1bf2019-09-19 16:46:22 -07001063 int modality = TYPE_NONE;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001064 int firstHwAvailable = TYPE_NONE;
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001065 for (AuthenticatorWrapper authenticator : mAuthenticators) {
Haining Chen5c7ed9a2019-10-29 11:17:48 -07001066 final int actualStrength = authenticator.getActualStrength();
Kevin Chynd04b43d2019-12-13 12:56:41 -08001067 final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001068 if (Utils.isAtLeastStrength(actualStrength, requestedStrength)) {
1069 hasSufficientStrength = true;
1070 modality = authenticator.modality;
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001071 if (authenticator.impl.isHardwareDetected(opPackageName)) {
1072 isHardwareDetected = true;
1073 if (firstHwAvailable == TYPE_NONE) {
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001074 // Store the first one since we want to return the error in correct
1075 // priority order.
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001076 firstHwAvailable = modality;
1077 }
1078 if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
1079 hasTemplatesEnrolled = true;
joshmccloskey15c0a442019-10-18 16:09:06 -07001080 // If the device policy manager disables a specific biometric, skip it.
1081 if (checkDevicePolicyManager &&
1082 isBiometricDisabledByDevicePolicy(modality, userId)) {
1083 continue;
1084 }
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001085 if (isEnabledForApp(modality, userId)) {
1086 enabledForApps = true;
1087 break;
1088 }
Kevin Chyne7411422018-09-27 17:28:20 -07001089 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001090 }
1091 }
1092 }
1093
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001094 Slog.d(TAG, "checkAndGetAuthenticators: user=" + userId
joshmccloskey15c0a442019-10-18 16:09:06 -07001095 + " checkDevicePolicyManager=" + checkDevicePolicyManager
Kevin Chynbe67ce02019-06-10 16:14:22 -07001096 + " isHardwareDetected=" + isHardwareDetected
1097 + " hasTemplatesEnrolled=" + hasTemplatesEnrolled
1098 + " enabledForApps=" + enabledForApps);
1099
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001100 // Check error conditions
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001101 if (!hasSufficientStrength) {
1102 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
1103 } else if (!isHardwareDetected) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001104 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyne7411422018-09-27 17:28:20 -07001105 } else if (!hasTemplatesEnrolled) {
1106 // Return the modality here so the correct error string can be sent. This error is
1107 // preferred over !enabledForApps
1108 return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
1109 } else if (!enabledForApps) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001110 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001111 }
1112
Kevin Chyna8b57ef2018-10-25 11:09:23 -07001113 return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
Kevin Chyne7411422018-09-27 17:28:20 -07001114 }
1115
Kevin Chyn2170ab72019-04-03 20:28:28 -07001116 private boolean isEnabledForApp(int modality, int userId) {
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001117 switch (modality) {
Kevin Chyn87f257a2018-11-27 16:26:07 -08001118 case TYPE_FINGERPRINT:
Kevin Chyne7411422018-09-27 17:28:20 -07001119 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001120 case TYPE_IRIS:
Kevin Chyne7411422018-09-27 17:28:20 -07001121 return true;
Kevin Chyn87f257a2018-11-27 16:26:07 -08001122 case TYPE_FACE:
Kevin Chyn2170ab72019-04-03 20:28:28 -07001123 return mSettingObserver.getFaceEnabledForApps(userId);
Kevin Chyne7411422018-09-27 17:28:20 -07001124 default:
1125 Slog.w(TAG, "Unsupported modality: " + modality);
1126 return false;
1127 }
1128 }
1129
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001130 private void logDialogDismissed(int reason) {
Kevin Chynff168dc2019-09-16 16:04:38 -07001131 if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001132 // Explicit auth, authentication confirmed.
1133 // Latency in this case is authenticated -> confirmed. <Biometric>Service
1134 // should have the first half (first acquired -> authenticated).
1135 final long latency = System.currentTimeMillis()
1136 - mCurrentAuthSession.mAuthenticatedTimeMs;
1137
1138 if (LoggableMonitor.DEBUG) {
1139 Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
1140 + ", User: " + mCurrentAuthSession.mUserId
1141 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1142 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
1143 + ", RequireConfirmation: "
1144 + mCurrentAuthSession.mRequireConfirmation
1145 + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
1146 + ", Latency: " + latency);
1147 }
1148
1149 StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
1150 statsModality(),
1151 mCurrentAuthSession.mUserId,
1152 mCurrentAuthSession.isCrypto(),
1153 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1154 mCurrentAuthSession.mRequireConfirmation,
1155 StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
Kevin Chyn4858da42019-04-11 13:02:56 -07001156 latency,
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001157 mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001158 } else {
Kevin Chyn7fca2362019-06-20 17:20:42 -07001159
1160 final long latency = System.currentTimeMillis() - mCurrentAuthSession.mStartTimeMs;
1161
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001162 int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
1163 ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
1164 : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
1165 ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
1166 : 0;
1167 if (LoggableMonitor.DEBUG) {
1168 Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
1169 + ", User: " + mCurrentAuthSession.mUserId
1170 + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
1171 + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
1172 + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
Kevin Chyn7fca2362019-06-20 17:20:42 -07001173 + ", Error: " + error
1174 + ", Latency: " + latency);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001175 }
1176 // Auth canceled
1177 StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
1178 statsModality(),
1179 mCurrentAuthSession.mUserId,
1180 mCurrentAuthSession.isCrypto(),
1181 BiometricsProtoEnums.ACTION_AUTHENTICATE,
1182 BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
1183 error,
Kevin Chyn4858da42019-04-11 13:02:56 -07001184 0 /* vendorCode */,
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001185 mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
Kevin Chyn7fca2362019-06-20 17:20:42 -07001186 latency);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001187 }
1188 }
1189
1190 private int statsModality() {
1191 int modality = 0;
1192 if (mCurrentAuthSession == null) {
1193 return BiometricsProtoEnums.MODALITY_UNKNOWN;
1194 }
1195 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
1196 != 0) {
1197 modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
1198 }
1199 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
1200 modality |= BiometricsProtoEnums.MODALITY_IRIS;
1201 }
1202 if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
1203 modality |= BiometricsProtoEnums.MODALITY_FACE;
1204 }
1205 return modality;
1206 }
1207
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001208 private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001209 try {
1210 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1211 // after user dismissed/canceled dialog).
1212 if (mCurrentAuthSession == null) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001213 Slog.e(TAG, "handleAuthenticationSucceeded: Auth session is null");
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001214 return;
1215 }
1216
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001217 // Store the auth token and submit it to keystore after the dialog is confirmed /
1218 // animating away.
1219 mCurrentAuthSession.mTokenEscrow = token;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001220 if (!requireConfirmation) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001221 mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001222 } else {
1223 mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001224 mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
1225 }
1226
1227 // Notify SysUI that the biometric has been authenticated. SysUI already knows
1228 // the implicit/explicit state and will react accordingly.
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001229 mStatusBarService.onBiometricAuthenticated();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001230 } catch (RemoteException e) {
1231 Slog.e(TAG, "Remote exception", e);
1232 }
1233 }
1234
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001235 private void handleAuthenticationRejected() {
1236 Slog.v(TAG, "handleAuthenticationRejected()");
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001237 try {
1238 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1239 // after user dismissed/canceled dialog).
1240 if (mCurrentAuthSession == null) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001241 Slog.e(TAG, "handleAuthenticationRejected: Auth session is null");
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001242 return;
1243 }
1244
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001245 mStatusBarService.onBiometricError(TYPE_NONE,
1246 BiometricConstants.BIOMETRIC_PAUSED_REJECTED, 0 /* vendorCode */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001247
1248 // TODO: This logic will need to be updated if BP is multi-modal
1249 if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
1250 // Pause authentication. onBiometricAuthenticated(false) causes the
1251 // dialog to show a "try again" button for passive modalities.
1252 mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
1253 }
1254
1255 mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
1256 } catch (RemoteException e) {
1257 Slog.e(TAG, "Remote exception", e);
1258 }
1259 }
1260
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001261 private void handleAuthenticationTimedOut(int modality, int error, int vendorCode) {
1262 Slog.v(TAG, String.format("handleAuthenticationTimedOut(%d, %d, %d)", modality, error,
1263 vendorCode));
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001264 try {
1265 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1266 // after user dismissed/canceled dialog).
1267 if (mCurrentAuthSession == null) {
1268 Slog.e(TAG, "handleAuthenticationTimedOut: Auth session is null");
1269 return;
1270 }
1271
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001272 mStatusBarService.onBiometricError(modality, error, vendorCode);
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001273 mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
1274 } catch (RemoteException e) {
1275 Slog.e(TAG, "Remote exception", e);
1276 }
1277 }
1278
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001279 private void handleOnError(int cookie, int modality, int error, int vendorCode) {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001280 Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001281 // Errors can either be from the current auth session or the pending auth session.
1282 // The pending auth session may receive errors such as ERROR_LOCKOUT before
1283 // it becomes the current auth session. Similarly, the current auth session may
1284 // receive errors such as ERROR_CANCELED while the pending auth session is preparing
1285 // to be started. Thus we must match error messages with their cookies to be sure
1286 // of their intended receivers.
1287 try {
1288 if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
Kevin Chyn050315f2019-08-08 14:22:54 -07001289 mCurrentAuthSession.mErrorEscrow = error;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001290 mCurrentAuthSession.mVendorCodeEscrow = vendorCode;
Kevin Chyn050315f2019-08-08 14:22:54 -07001291
joshmccloskey568d3292019-09-17 14:02:23 -07001292 if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
Kevin Chyn8429da22019-09-24 12:42:35 -07001293 final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
1294 || error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
1295 if (mCurrentAuthSession.isAllowDeviceCredential() && errorLockout) {
1296 // SystemUI handles transition from biometric to device credential.
1297 mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001298 mStatusBarService.onBiometricError(modality, error, vendorCode);
Kevin Chyn22910722019-12-13 16:57:51 -08001299 } else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
1300 mStatusBarService.hideAuthenticationDialog();
1301 // TODO: If multiple authenticators are simultaneously running, this will
1302 // need to be modified. Send the error to the client here, instead of doing
1303 // a round trip to SystemUI.
1304 mCurrentAuthSession.mClientReceiver.onError(modality, error, vendorCode);
1305 mCurrentAuthSession = null;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001306 } else {
Kevin Chyn8429da22019-09-24 12:42:35 -07001307 mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
Kevin Chyn22910722019-12-13 16:57:51 -08001308 mStatusBarService.onBiometricError(modality, error, vendorCode);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001309 }
1310 } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
1311 // In the "try again" state, we should forward canceled errors to
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001312 // the client and and clean up. The only error we should get here is
1313 // ERROR_CANCELED due to another client kicking us out.
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001314 mCurrentAuthSession.mClientReceiver.onError(modality, error, vendorCode);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001315 mStatusBarService.hideAuthenticationDialog();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001316 mCurrentAuthSession = null;
Kevin Chynff168dc2019-09-16 16:04:38 -07001317 } else if (mCurrentAuthSession.mState == STATE_SHOWING_DEVICE_CREDENTIAL) {
1318 Slog.d(TAG, "Biometric canceled, ignoring from state: "
1319 + mCurrentAuthSession.mState);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001320 } else {
1321 Slog.e(TAG, "Impossible session error state: "
1322 + mCurrentAuthSession.mState);
1323 }
1324 } else if (mPendingAuthSession != null
1325 && mPendingAuthSession.containsCookie(cookie)) {
1326 if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001327 // If any error is received while preparing the auth session (lockout, etc),
1328 // and if device credential is allowed, just show the credential UI.
1329 if (mPendingAuthSession.isAllowDeviceCredential()) {
Curtis Belmonte13eb5812019-10-22 14:17:30 -07001330 @Authenticators.Types int authenticators =
1331 mPendingAuthSession.mBundle.getInt(
1332 BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001333 // Disallow biometric and notify SystemUI to show the authentication prompt.
Curtis Belmonte13eb5812019-10-22 14:17:30 -07001334 authenticators &= ~Authenticators.BIOMETRIC_WEAK;
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001335 mPendingAuthSession.mBundle.putInt(
1336 BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
1337 authenticators);
1338
1339 mCurrentAuthSession = mPendingAuthSession;
1340 mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
1341 mPendingAuthSession = null;
1342
1343 mStatusBarService.showAuthenticationDialog(
1344 mCurrentAuthSession.mBundle,
1345 mInternalReceiver,
1346 0 /* biometricModality */,
1347 false /* requireConfirmation */,
1348 mCurrentAuthSession.mUserId,
1349 mCurrentAuthSession.mOpPackageName);
1350 } else {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001351 mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001352 mPendingAuthSession = null;
1353 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001354 } else {
1355 Slog.e(TAG, "Impossible pending session error state: "
1356 + mPendingAuthSession.mState);
1357 }
Kevin Chyn8429da22019-09-24 12:42:35 -07001358 } else {
1359 Slog.e(TAG, "Unknown cookie: " + cookie);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001360 }
1361 } catch (RemoteException e) {
1362 Slog.e(TAG, "Remote exception", e);
1363 }
1364 }
1365
1366 private void handleOnAcquired(int acquiredInfo, String message) {
1367 // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
1368 // after user dismissed/canceled dialog).
1369 if (mCurrentAuthSession == null) {
1370 Slog.e(TAG, "onAcquired(): Auth session is null");
1371 return;
1372 }
1373
1374 if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
Kevin Chyn3b53d6f2019-05-01 11:49:05 -07001375 if (message == null) {
1376 Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
1377 return;
1378 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001379 try {
1380 mStatusBarService.onBiometricHelp(message);
1381 } catch (RemoteException e) {
1382 Slog.e(TAG, "Remote exception", e);
1383 }
1384 }
1385 }
1386
1387 private void handleOnDismissed(int reason) {
1388 if (mCurrentAuthSession == null) {
Kevin Chync53d9812019-07-30 18:10:30 -07001389 Slog.e(TAG, "onDismissed: " + reason + ", auth session null");
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001390 return;
1391 }
1392
1393 logDialogDismissed(reason);
1394
1395 try {
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001396 switch (reason) {
Kevin Chynff168dc2019-09-16 16:04:38 -07001397 case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
1398 case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
1399 case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
1400 if (mCurrentAuthSession.mTokenEscrow != null) {
1401 mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
1402 }
Curtis Belmonte6601bc32020-01-09 17:46:31 -08001403 mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(
1404 Utils.getAuthenticationTypeForResult(reason));
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001405 break;
1406
1407 case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
1408 mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
1409 // Cancel authentication. Skip the token/package check since we are cancelling
1410 // from system server. The interface is permission protected so this is fine.
1411 cancelInternal(null /* token */, null /* package */, false /* fromClient */);
1412 break;
1413
1414 case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
1415 mCurrentAuthSession.mClientReceiver.onError(
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001416 mCurrentAuthSession.mModality,
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001417 BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001418 0 /* vendorCode */
1419 );
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001420 // Cancel authentication. Skip the token/package check since we are cancelling
1421 // from system server. The interface is permission protected so this is fine.
1422 cancelInternal(null /* token */, null /* package */, false /* fromClient */);
1423 break;
1424
Kevin Chyn050315f2019-08-08 14:22:54 -07001425 case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED:
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001426 case BiometricPrompt.DISMISSED_REASON_ERROR:
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001427 mCurrentAuthSession.mClientReceiver.onError(
1428 mCurrentAuthSession.mModality,
1429 mCurrentAuthSession.mErrorEscrow,
1430 mCurrentAuthSession.mVendorCodeEscrow
1431 );
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001432 break;
1433
1434 default:
1435 Slog.w(TAG, "Unhandled reason: " + reason);
1436 break;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001437 }
Kevin Chyn5a90a652019-03-25 18:11:16 -07001438
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001439 // Dialog is gone, auth session is done.
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001440 mCurrentAuthSession = null;
1441
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001442 } catch (RemoteException e) {
1443 Slog.e(TAG, "Remote exception", e);
1444 }
1445 }
1446
1447 private void handleOnTryAgainPressed() {
1448 Slog.d(TAG, "onTryAgainPressed");
1449 // No need to check permission, since it can only be invoked by SystemUI
1450 // (or system server itself).
1451 authenticateInternal(mCurrentAuthSession.mToken,
1452 mCurrentAuthSession.mSessionId,
1453 mCurrentAuthSession.mUserId,
1454 mCurrentAuthSession.mClientReceiver,
1455 mCurrentAuthSession.mOpPackageName,
1456 mCurrentAuthSession.mBundle,
1457 mCurrentAuthSession.mCallingUid,
1458 mCurrentAuthSession.mCallingPid,
1459 mCurrentAuthSession.mCallingUserId,
joshmccloskey568d3292019-09-17 14:02:23 -07001460 mCurrentAuthSession.mModality);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001461 }
1462
Kevin Chynff168dc2019-09-16 16:04:38 -07001463 private void handleOnDeviceCredentialPressed() {
1464 Slog.d(TAG, "onDeviceCredentialPressed");
1465 if (mCurrentAuthSession == null) {
1466 Slog.e(TAG, "Auth session null");
1467 return;
1468 }
1469
1470 // Cancel authentication. Skip the token/package check since we are cancelling
1471 // from system server. The interface is permission protected so this is fine.
1472 cancelInternal(null /* token */, null /* package */, false /* fromClient */);
1473
1474 mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
1475 }
1476
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001477 /**
1478 * Invoked when each service has notified that its client is ready to be started. When
1479 * all biometrics are ready, this invokes the SystemUI dialog through StatusBar.
1480 */
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001481 private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation,
1482 int userId) {
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001483 if (mPendingAuthSession == null) {
1484 // Only should happen if a biometric was locked out when authenticate() was invoked.
1485 // In that case, if device credentials are allowed, the UI is already showing. If not
1486 // allowed, the error has already been returned to the caller.
1487 Slog.w(TAG, "Pending auth session null");
1488 return;
1489 }
1490
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001491 Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
1492 while (it.hasNext()) {
1493 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1494 if (pair.getValue() == cookie) {
1495 mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
1496 mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
1497 Slog.d(TAG, "Matched cookie: " + cookie + ", "
1498 + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
1499 break;
1500 }
1501 }
1502
1503 if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
1504 final boolean continuing = mCurrentAuthSession != null
1505 && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
1506
1507 mCurrentAuthSession = mPendingAuthSession;
Kevin Chyn7fca2362019-06-20 17:20:42 -07001508
1509 // Time starts when lower layers are ready to start the client.
1510 mCurrentAuthSession.mStartTimeMs = System.currentTimeMillis();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001511 mPendingAuthSession = null;
1512
1513 mCurrentAuthSession.mState = STATE_AUTH_STARTED;
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001514 int modality = TYPE_NONE;
1515 it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
1516 while (it.hasNext()) {
1517 Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
1518 boolean foundAuthenticator = false;
1519 for (AuthenticatorWrapper authenticator : mAuthenticators) {
1520 if (authenticator.modality == pair.getKey()) {
1521 foundAuthenticator = true;
1522 try {
1523 authenticator.impl.startPreparedClient(pair.getValue());
1524 } catch (RemoteException e) {
1525 Slog.e(TAG, "Remote exception", e);
1526 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001527 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001528 }
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001529 if (!foundAuthenticator) {
1530 Slog.e(TAG, "Unknown modality: " + pair.getKey());
1531 }
1532 modality |= pair.getKey();
1533 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001534
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001535 if (!continuing) {
1536 try {
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001537 mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle,
Kevin Chyn050315f2019-08-08 14:22:54 -07001538 mInternalReceiver, modality, requireConfirmation, userId,
1539 mCurrentAuthSession.mOpPackageName);
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001540 } catch (RemoteException e) {
1541 Slog.e(TAG, "Remote exception", e);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001542 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001543 }
1544 }
1545 }
1546
1547 private void handleAuthenticate(IBinder token, long sessionId, int userId,
1548 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
joshmccloskey568d3292019-09-17 14:02:23 -07001549 int callingUid, int callingPid, int callingUserId) {
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001550
1551 mHandler.post(() -> {
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001552 int modality = TYPE_NONE;
1553 int result;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001554
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001555 try {
joshmccloskey15c0a442019-10-18 16:09:06 -07001556 final boolean checkDevicePolicyManager = bundle.getBoolean(
1557 BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001558 final Pair<Integer, Integer> pair = checkAndGetAuthenticators(userId, bundle,
joshmccloskey15c0a442019-10-18 16:09:06 -07001559 opPackageName, checkDevicePolicyManager);
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001560 modality = pair.first;
1561 result = pair.second;
1562 } catch (RemoteException e) {
1563 Slog.e(TAG, "Remote exception", e);
1564 result = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001565 }
1566
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001567 try {
1568 if (result == BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL) {
1569 // If the app allowed device credential but the user hasn't set it up yet,
1570 // return this error.
1571 receiver.onError(modality, result, 0 /* vendorCode */);
1572 } else if (result != BiometricConstants.BIOMETRIC_SUCCESS) {
1573 if (Utils.isDeviceCredentialAllowed(bundle)) {
1574 // If there's a problem with biometrics but device credential is allowed,
1575 // only show credential UI.
1576 bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
1577 Authenticators.DEVICE_CREDENTIAL);
1578 authenticateInternal(token, sessionId, userId, receiver, opPackageName,
1579 bundle, callingUid, callingPid, callingUserId, modality);
1580 } else {
1581 receiver.onError(modality, result, 0 /* vendorCode */);
1582 }
1583 } else {
1584 // BIOMETRIC_SUCCESS, proceed to authentication
1585 authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
1586 callingUid, callingPid, callingUserId, modality);
1587 }
1588 } catch (RemoteException e) {
1589 Slog.e(TAG, "Remote exception", e);
1590 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001591 });
1592 }
1593
1594 /**
Ilya Matyukhin8344b1bf2019-09-19 16:46:22 -07001595 * handleAuthenticate() (above) which is called from BiometricPrompt determines which
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001596 * modality/modalities to start authenticating with. authenticateInternal() should only be
1597 * used for:
1598 * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001599 * invoked, shortly after which BiometricPrompt is shown and authentication starts
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001600 * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001601 * and the user has pressed "try again"
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001602 */
1603 private void authenticateInternal(IBinder token, long sessionId, int userId,
1604 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
joshmccloskey568d3292019-09-17 14:02:23 -07001605 int callingUid, int callingPid, int callingUserId, int modality) {
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001606 boolean requireConfirmation = bundle.getBoolean(
1607 BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
1608 if ((modality & TYPE_FACE) != 0) {
1609 // Check if the user has forced confirmation to be required in Settings.
1610 requireConfirmation = requireConfirmation
1611 || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
1612 }
1613 // Generate random cookies to pass to the services that should prepare to start
1614 // authenticating. Store the cookie here and wait for all services to "ack"
1615 // with the cookie. Once all cookies are received, we can show the prompt
1616 // and let the services start authenticating. The cookie should be non-zero.
1617 final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
Curtis Belmonte13eb5812019-10-22 14:17:30 -07001618 final @Authenticators.Types int authenticators = bundle.getInt(
1619 BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001620 Slog.d(TAG, "Creating auth session. Modality: " + modality
1621 + ", cookie: " + cookie
1622 + ", authenticators: " + authenticators);
1623 final HashMap<Integer, Integer> modalities = new HashMap<>();
1624
1625 // If it's only device credential, we don't need to wait - LockSettingsService is
1626 // always ready to check credential (SystemUI invokes that path).
Curtis Belmonte13eb5812019-10-22 14:17:30 -07001627 if ((authenticators & ~Authenticators.DEVICE_CREDENTIAL) != 0) {
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001628 modalities.put(modality, cookie);
1629 }
1630 mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId,
1631 receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
1632 modality, requireConfirmation);
1633
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001634 try {
Curtis Belmonte13eb5812019-10-22 14:17:30 -07001635 if (authenticators == Authenticators.DEVICE_CREDENTIAL) {
Kevin Chync70d6b82019-10-03 15:32:37 -07001636 mPendingAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
1637 mCurrentAuthSession = mPendingAuthSession;
1638 mPendingAuthSession = null;
1639
1640 mStatusBarService.showAuthenticationDialog(
1641 mCurrentAuthSession.mBundle,
1642 mInternalReceiver,
1643 0 /* biometricModality */,
1644 false /* requireConfirmation */,
1645 mCurrentAuthSession.mUserId,
1646 mCurrentAuthSession.mOpPackageName);
1647 } else {
1648 mPendingAuthSession.mState = STATE_AUTH_CALLED;
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001649 for (AuthenticatorWrapper authenticator : mAuthenticators) {
Ilya Matyukhin30f1dd82019-11-18 18:08:56 -08001650 // TODO(b/141025588): use ids instead of modalities to avoid ambiguity.
1651 if (authenticator.modality == modality) {
1652 authenticator.impl.prepareForAuthentication(requireConfirmation, token,
1653 sessionId, userId, mInternalReceiver, opPackageName, cookie,
1654 callingUid, callingPid, callingUserId);
1655 break;
1656 }
Kevin Chync70d6b82019-10-03 15:32:37 -07001657 }
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001658 }
1659 } catch (RemoteException e) {
1660 Slog.e(TAG, "Unable to start authentication", e);
1661 }
1662 }
1663
1664 private void handleCancelAuthentication(IBinder token, String opPackageName) {
1665 if (token == null || opPackageName == null) {
1666 Slog.e(TAG, "Unable to cancel, one or more null arguments");
1667 return;
1668 }
1669
joshmccloskey568d3292019-09-17 14:02:23 -07001670 if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
Kevin Chyn5a90a652019-03-25 18:11:16 -07001671 // We need to check the current authenticators state. If we're pending confirm
1672 // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
1673 // since we won't be getting an onError from the driver.
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001674 try {
1675 // Send error to client
1676 mCurrentAuthSession.mClientReceiver.onError(
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001677 mCurrentAuthSession.mModality,
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001678 BiometricConstants.BIOMETRIC_ERROR_CANCELED,
Ilya Matyukhin0f9da352019-10-03 14:10:01 -07001679 0 /* vendorCode */
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001680 );
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001681 mCurrentAuthSession = null;
Kevin Chyn86f1b8e2019-09-24 19:00:49 -07001682 mStatusBarService.hideAuthenticationDialog();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001683 } catch (RemoteException e) {
1684 Slog.e(TAG, "Remote exception", e);
1685 }
1686 } else {
joshmccloskey568d3292019-09-17 14:02:23 -07001687 cancelInternal(token, opPackageName, true /* fromClient */);
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001688 }
1689 }
1690
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001691 void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
1692 final int callingUid = Binder.getCallingUid();
1693 final int callingPid = Binder.getCallingPid();
1694 final int callingUserId = UserHandle.getCallingUserId();
Kevin Chyn84c2f5e2019-03-16 13:05:16 -07001695
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001696 if (mCurrentAuthSession == null) {
1697 Slog.w(TAG, "Skipping cancelInternal");
1698 return;
1699 } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
1700 Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
1701 return;
1702 }
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001703
Ilya Matyukhinf2da1a12019-09-25 15:31:03 -07001704 // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
1705 // drivers have canceled authentication.
1706 for (AuthenticatorWrapper authenticator : mAuthenticators) {
1707 if ((authenticator.modality & mCurrentAuthSession.mModality) != 0) {
1708 try {
1709 authenticator.impl.cancelAuthenticationFromService(token, opPackageName,
1710 callingUid, callingPid, callingUserId, fromClient);
1711 } catch (RemoteException e) {
1712 Slog.e(TAG, "Unable to cancel authentication");
1713 }
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001714 }
Kevin Chyn9f8b2fc2019-08-02 18:24:32 -07001715 }
1716 }
Kevin Chyna24e9fd2018-08-27 12:39:17 -07001717}