| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.biometrics; |
| |
| import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; |
| import static android.hardware.biometrics.BiometricManager.Authenticators; |
| |
| import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.ActivityManager; |
| import android.app.IActivityManager; |
| import android.app.UserSwitchObserver; |
| import android.app.admin.DevicePolicyManager; |
| import android.app.trust.ITrustManager; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.database.ContentObserver; |
| import android.hardware.biometrics.BiometricAuthenticator; |
| import android.hardware.biometrics.BiometricConstants; |
| import android.hardware.biometrics.BiometricPrompt; |
| import android.hardware.biometrics.IBiometricAuthenticator; |
| import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; |
| import android.hardware.biometrics.IBiometricSensorReceiver; |
| import android.hardware.biometrics.IBiometricService; |
| import android.hardware.biometrics.IBiometricServiceReceiver; |
| import android.hardware.biometrics.IBiometricSysuiReceiver; |
| import android.hardware.biometrics.IInvalidationCallback; |
| import android.hardware.biometrics.ITestSession; |
| import android.hardware.biometrics.ITestSessionCallback; |
| import android.hardware.biometrics.PromptInfo; |
| import android.hardware.biometrics.SensorPropertiesInternal; |
| import android.hardware.fingerprint.FingerprintManager; |
| import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; |
| import android.net.Uri; |
| import android.os.Binder; |
| import android.os.Build; |
| import android.os.DeadObjectException; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.security.KeyStore; |
| import android.text.TextUtils; |
| import android.util.ArraySet; |
| import android.util.Pair; |
| import android.util.Slog; |
| import android.util.proto.ProtoOutputStream; |
| |
| import com.android.internal.R; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.os.SomeArgs; |
| import com.android.internal.statusbar.IStatusBarService; |
| import com.android.internal.util.DumpUtils; |
| import com.android.server.SystemService; |
| import com.android.server.biometrics.sensors.CoexCoordinator; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.Set; |
| |
| /** |
| * System service that arbitrates the modality for BiometricPrompt to use. |
| */ |
| public class BiometricService extends SystemService { |
| |
| static final String TAG = "BiometricService"; |
| |
| private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2; |
| private static final int MSG_ON_AUTHENTICATION_REJECTED = 3; |
| private static final int MSG_ON_ERROR = 4; |
| private static final int MSG_ON_ACQUIRED = 5; |
| private static final int MSG_ON_DISMISSED = 6; |
| private static final int MSG_ON_TRY_AGAIN_PRESSED = 7; |
| private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8; |
| private static final int MSG_AUTHENTICATE = 9; |
| private static final int MSG_CANCEL_AUTHENTICATION = 10; |
| private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11; |
| private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12; |
| private static final int MSG_ON_SYSTEM_EVENT = 13; |
| private static final int MSG_CLIENT_DIED = 14; |
| private static final int MSG_ON_DIALOG_ANIMATED_IN = 15; |
| private static final int MSG_ON_START_FINGERPRINT_NOW = 16; |
| |
| private final Injector mInjector; |
| private final DevicePolicyManager mDevicePolicyManager; |
| @VisibleForTesting |
| final IBiometricService.Stub mImpl; |
| @VisibleForTesting |
| final SettingObserver mSettingObserver; |
| private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks; |
| private final Random mRandom = new Random(); |
| |
| @VisibleForTesting |
| IStatusBarService mStatusBarService; |
| @VisibleForTesting |
| KeyStore mKeyStore; |
| @VisibleForTesting |
| ITrustManager mTrustManager; |
| |
| // Get and cache the available biometric authenticators and their associated info. |
| final ArrayList<BiometricSensor> mSensors = new ArrayList<>(); |
| |
| BiometricStrengthController mBiometricStrengthController; |
| |
| // The current authentication session, null if idle/done. |
| @VisibleForTesting |
| AuthSession mCurrentAuthSession; |
| |
| @VisibleForTesting |
| final Handler mHandler = new Handler(Looper.getMainLooper()) { |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_ON_AUTHENTICATION_SUCCEEDED: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| handleAuthenticationSucceeded( |
| args.argi1 /* sensorId */, |
| (byte[]) args.arg1 /* token */); |
| args.recycle(); |
| break; |
| } |
| |
| case MSG_ON_AUTHENTICATION_REJECTED: { |
| handleAuthenticationRejected(); |
| break; |
| } |
| |
| case MSG_ON_ERROR: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| handleOnError( |
| args.argi1 /* sensorId */, |
| args.argi2 /* cookie */, |
| args.argi3 /* error */, |
| args.argi4 /* vendorCode */); |
| args.recycle(); |
| break; |
| } |
| |
| case MSG_ON_ACQUIRED: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| handleOnAcquired( |
| args.argi1 /* sensorId */, |
| args.argi2 /* acquiredInfo */, |
| args.argi3 /* vendorCode */); |
| args.recycle(); |
| break; |
| } |
| |
| case MSG_ON_DISMISSED: { |
| handleOnDismissed(msg.arg1, (byte[]) msg.obj); |
| break; |
| } |
| |
| case MSG_ON_TRY_AGAIN_PRESSED: { |
| handleOnTryAgainPressed(); |
| break; |
| } |
| |
| case MSG_ON_READY_FOR_AUTHENTICATION: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| handleOnReadyForAuthentication( |
| args.argi1 /* cookie */); |
| args.recycle(); |
| break; |
| } |
| |
| case MSG_AUTHENTICATE: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| handleAuthenticate( |
| (IBinder) args.arg1 /* token */, |
| (long) args.arg2 /* operationId */, |
| args.argi1 /* userid */, |
| (IBiometricServiceReceiver) args.arg3 /* receiver */, |
| (String) args.arg4 /* opPackageName */, |
| (PromptInfo) args.arg5 /* promptInfo */); |
| args.recycle(); |
| break; |
| } |
| |
| case MSG_CANCEL_AUTHENTICATION: { |
| handleCancelAuthentication(); |
| break; |
| } |
| |
| case MSG_ON_AUTHENTICATION_TIMED_OUT: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| handleAuthenticationTimedOut( |
| args.argi1 /* sensorId */, |
| args.argi2 /* cookie */, |
| args.argi3 /* error */, |
| args.argi4 /* vendorCode */); |
| args.recycle(); |
| break; |
| } |
| |
| case MSG_ON_DEVICE_CREDENTIAL_PRESSED: { |
| handleOnDeviceCredentialPressed(); |
| break; |
| } |
| |
| case MSG_ON_SYSTEM_EVENT: { |
| handleOnSystemEvent((int) msg.obj); |
| break; |
| } |
| |
| case MSG_CLIENT_DIED: { |
| handleClientDied(); |
| break; |
| } |
| |
| case MSG_ON_DIALOG_ANIMATED_IN: { |
| handleOnDialogAnimatedIn(); |
| break; |
| } |
| |
| case MSG_ON_START_FINGERPRINT_NOW: { |
| handleOnStartFingerprintNow(); |
| break; |
| } |
| |
| default: |
| Slog.e(TAG, "Unknown message: " + msg); |
| break; |
| } |
| } |
| }; |
| |
| /** |
| * Tracks authenticatorId invalidation. For more details, see |
| * {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}. |
| */ |
| @VisibleForTesting |
| static class InvalidationTracker { |
| @NonNull private final IInvalidationCallback mClientCallback; |
| @NonNull private final Set<Integer> mSensorsPendingInvalidation; |
| |
| public static InvalidationTracker start(@NonNull Context context, |
| @NonNull ArrayList<BiometricSensor> sensors, |
| int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) { |
| return new InvalidationTracker(context, sensors, userId, fromSensorId, clientCallback); |
| } |
| |
| private InvalidationTracker(@NonNull Context context, |
| @NonNull ArrayList<BiometricSensor> sensors, int userId, |
| int fromSensorId, @NonNull IInvalidationCallback clientCallback) { |
| mClientCallback = clientCallback; |
| mSensorsPendingInvalidation = new ArraySet<>(); |
| |
| for (BiometricSensor sensor : sensors) { |
| if (sensor.id == fromSensorId) { |
| continue; |
| } |
| |
| if (!Utils.isAtLeastStrength(sensor.oemStrength, Authenticators.BIOMETRIC_STRONG)) { |
| continue; |
| } |
| |
| try { |
| if (!sensor.impl.hasEnrolledTemplates(userId, context.getOpPackageName())) { |
| continue; |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote Exception", e); |
| } |
| |
| Slog.d(TAG, "Requesting authenticatorId invalidation for sensor: " + sensor.id); |
| |
| synchronized (this) { |
| mSensorsPendingInvalidation.add(sensor.id); |
| } |
| |
| try { |
| sensor.impl.invalidateAuthenticatorId(userId, new IInvalidationCallback.Stub() { |
| @Override |
| public void onCompleted() { |
| onInvalidated(sensor.id); |
| } |
| }); |
| } catch (RemoteException e) { |
| Slog.d(TAG, "RemoteException", e); |
| } |
| } |
| |
| synchronized (this) { |
| if (mSensorsPendingInvalidation.isEmpty()) { |
| try { |
| Slog.d(TAG, "No sensors require invalidation"); |
| mClientCallback.onCompleted(); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote Exception", e); |
| } |
| } |
| } |
| } |
| |
| @VisibleForTesting |
| void onInvalidated(int sensorId) { |
| synchronized (this) { |
| mSensorsPendingInvalidation.remove(sensorId); |
| |
| Slog.d(TAG, "Sensor " + sensorId + " invalidated, remaining size: " |
| + mSensorsPendingInvalidation.size()); |
| |
| if (mSensorsPendingInvalidation.isEmpty()) { |
| try { |
| mClientCallback.onCompleted(); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote Exception", e); |
| } |
| } |
| } |
| } |
| } |
| |
| @VisibleForTesting |
| public static class SettingObserver extends ContentObserver { |
| |
| private static final boolean DEFAULT_KEYGUARD_ENABLED = true; |
| private static final boolean DEFAULT_APP_ENABLED = true; |
| private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false; |
| |
| // Some devices that shipped before S already have face-specific settings. Instead of |
| // migrating, which is complicated, let's just keep using the existing settings. |
| private final boolean mUseLegacyFaceOnlySettings; |
| |
| // Only used for legacy face-only devices |
| private final Uri FACE_UNLOCK_KEYGUARD_ENABLED = |
| Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED); |
| private final Uri FACE_UNLOCK_APP_ENABLED = |
| Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED); |
| |
| // Continues to be used, even though it's face-specific. |
| private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION = |
| Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION); |
| |
| // Used for all devices other than legacy face-only devices |
| private final Uri BIOMETRIC_KEYGUARD_ENABLED = |
| Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED); |
| private final Uri BIOMETRIC_APP_ENABLED = |
| Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED); |
| |
| private final ContentResolver mContentResolver; |
| private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks; |
| |
| private final Map<Integer, Boolean> mBiometricEnabledOnKeyguard = new HashMap<>(); |
| private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>(); |
| private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>(); |
| |
| /** |
| * Creates a content observer. |
| * |
| * @param handler The handler to run {@link #onChange} on, or null if none. |
| */ |
| public SettingObserver(Context context, Handler handler, |
| List<BiometricService.EnabledOnKeyguardCallback> callbacks) { |
| super(handler); |
| mContentResolver = context.getContentResolver(); |
| mCallbacks = callbacks; |
| |
| final boolean hasFingerprint = context.getPackageManager() |
| .hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); |
| final boolean hasFace = context.getPackageManager() |
| .hasSystemFeature(PackageManager.FEATURE_FACE); |
| |
| // Use the legacy setting on face-only devices that shipped on or before Q |
| mUseLegacyFaceOnlySettings = |
| Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q |
| && hasFace && !hasFingerprint; |
| |
| updateContentObserver(); |
| } |
| |
| public void updateContentObserver() { |
| mContentResolver.unregisterContentObserver(this); |
| |
| if (mUseLegacyFaceOnlySettings) { |
| mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED, |
| false /* notifyForDescendants */, |
| this /* observer */, |
| UserHandle.USER_ALL); |
| mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED, |
| false /* notifyForDescendants */, |
| this /* observer */, |
| UserHandle.USER_ALL); |
| } else { |
| mContentResolver.registerContentObserver(BIOMETRIC_KEYGUARD_ENABLED, |
| false /* notifyForDescendants */, |
| this /* observer */, |
| UserHandle.USER_ALL); |
| mContentResolver.registerContentObserver(BIOMETRIC_APP_ENABLED, |
| false /* notifyForDescendants */, |
| this /* observer */, |
| UserHandle.USER_ALL); |
| } |
| mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, |
| false /* notifyForDescendants */, |
| this /* observer */, |
| UserHandle.USER_ALL); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange, Uri uri, int userId) { |
| if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) { |
| mBiometricEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser( |
| mContentResolver, |
| Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, |
| DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */, |
| userId) != 0); |
| |
| if (userId == ActivityManager.getCurrentUser() && !selfChange) { |
| notifyEnabledOnKeyguardCallbacks(userId); |
| } |
| } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) { |
| mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser( |
| mContentResolver, |
| Settings.Secure.FACE_UNLOCK_APP_ENABLED, |
| DEFAULT_APP_ENABLED ? 1 : 0 /* default */, |
| userId) != 0); |
| } else if (FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION.equals(uri)) { |
| mFaceAlwaysRequireConfirmation.put(userId, Settings.Secure.getIntForUser( |
| mContentResolver, |
| Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, |
| DEFAULT_ALWAYS_REQUIRE_CONFIRMATION ? 1 : 0 /* default */, |
| userId) != 0); |
| } else if (BIOMETRIC_KEYGUARD_ENABLED.equals(uri)) { |
| mBiometricEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser( |
| mContentResolver, |
| Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED, |
| DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */, |
| userId) != 0); |
| |
| if (userId == ActivityManager.getCurrentUser() && !selfChange) { |
| notifyEnabledOnKeyguardCallbacks(userId); |
| } |
| } else if (BIOMETRIC_APP_ENABLED.equals(uri)) { |
| mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser( |
| mContentResolver, |
| Settings.Secure.BIOMETRIC_APP_ENABLED, |
| DEFAULT_APP_ENABLED ? 1 : 0 /* default */, |
| userId) != 0); |
| } |
| } |
| |
| public boolean getEnabledOnKeyguard(int userId) { |
| if (!mBiometricEnabledOnKeyguard.containsKey(userId)) { |
| if (mUseLegacyFaceOnlySettings) { |
| onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, userId); |
| } else { |
| onChange(true /* selfChange */, BIOMETRIC_KEYGUARD_ENABLED, userId); |
| } |
| } |
| return mBiometricEnabledOnKeyguard.get(userId); |
| } |
| |
| public boolean getEnabledForApps(int userId) { |
| if (!mBiometricEnabledForApps.containsKey(userId)) { |
| if (mUseLegacyFaceOnlySettings) { |
| onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId); |
| } else { |
| onChange(true /* selfChange */, BIOMETRIC_APP_ENABLED, userId); |
| } |
| } |
| return mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED); |
| } |
| |
| public boolean getConfirmationAlwaysRequired(@BiometricAuthenticator.Modality int modality, |
| int userId) { |
| switch (modality) { |
| case BiometricAuthenticator.TYPE_FACE: |
| if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) { |
| onChange(true /* selfChange */, |
| FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, |
| userId); |
| } |
| return mFaceAlwaysRequireConfirmation.get(userId); |
| |
| default: |
| return false; |
| } |
| } |
| |
| void notifyEnabledOnKeyguardCallbacks(int userId) { |
| List<EnabledOnKeyguardCallback> callbacks = mCallbacks; |
| for (int i = 0; i < callbacks.size(); i++) { |
| callbacks.get(i).notify( |
| mBiometricEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED), |
| userId); |
| } |
| } |
| } |
| |
| final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient { |
| |
| private final IBiometricEnabledOnKeyguardCallback mCallback; |
| |
| EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) { |
| mCallback = callback; |
| try { |
| mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Unable to linkToDeath", e); |
| } |
| } |
| |
| void notify(boolean enabled, int userId) { |
| try { |
| mCallback.onChanged(enabled, userId); |
| } catch (DeadObjectException e) { |
| Slog.w(TAG, "Death while invoking notify", e); |
| mEnabledOnKeyguardCallbacks.remove(this); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Failed to invoke onChanged", e); |
| } |
| } |
| |
| @Override |
| public void binderDied() { |
| Slog.e(TAG, "Enabled callback binder died"); |
| mEnabledOnKeyguardCallbacks.remove(this); |
| } |
| } |
| |
| // Receives events from individual biometric sensors. |
| @VisibleForTesting |
| final IBiometricSensorReceiver mBiometricSensorReceiver = new IBiometricSensorReceiver.Stub() { |
| @Override |
| public void onAuthenticationSucceeded(int sensorId, byte[] token) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.argi1 = sensorId; |
| args.arg1 = token; |
| mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget(); |
| } |
| |
| @Override |
| public void onAuthenticationFailed(int sensorId) { |
| Slog.v(TAG, "onAuthenticationFailed"); |
| mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED).sendToTarget(); |
| } |
| |
| @Override |
| public void onError(int sensorId, int cookie, @BiometricConstants.Errors int error, |
| int vendorCode) { |
| // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are |
| // soft errors and we should allow the user to try authenticating again instead of |
| // dismissing BiometricPrompt. |
| if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.argi1 = sensorId; |
| args.argi2 = cookie; |
| args.argi3 = error; |
| args.argi4 = vendorCode; |
| mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, args).sendToTarget(); |
| } else { |
| SomeArgs args = SomeArgs.obtain(); |
| args.argi1 = sensorId; |
| args.argi2 = cookie; |
| args.argi3 = error; |
| args.argi4 = vendorCode; |
| mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget(); |
| } |
| } |
| |
| @Override |
| public void onAcquired(int sensorId, int acquiredInfo, int vendorCode) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.argi1 = sensorId; |
| args.argi2 = acquiredInfo; |
| args.argi3 = vendorCode; |
| mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget(); |
| } |
| }; |
| |
| final IBiometricSysuiReceiver mSysuiReceiver = new IBiometricSysuiReceiver.Stub() { |
| @Override |
| public void onDialogDismissed(@BiometricPrompt.DismissedReason int reason, |
| @Nullable byte[] credentialAttestation) { |
| mHandler.obtainMessage(MSG_ON_DISMISSED, |
| reason, |
| 0 /* arg2 */, |
| credentialAttestation /* obj */).sendToTarget(); |
| } |
| |
| @Override |
| public void onTryAgainPressed() { |
| mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED); |
| } |
| |
| @Override |
| public void onDeviceCredentialPressed() { |
| mHandler.sendEmptyMessage(MSG_ON_DEVICE_CREDENTIAL_PRESSED); |
| } |
| |
| @Override |
| public void onSystemEvent(int event) { |
| mHandler.obtainMessage(MSG_ON_SYSTEM_EVENT, event).sendToTarget(); |
| } |
| |
| @Override |
| public void onDialogAnimatedIn() { |
| mHandler.obtainMessage(MSG_ON_DIALOG_ANIMATED_IN).sendToTarget(); |
| } |
| |
| @Override |
| public void onStartFingerprintNow() { |
| mHandler.obtainMessage(MSG_ON_START_FINGERPRINT_NOW).sendToTarget(); |
| } |
| }; |
| |
| private final AuthSession.ClientDeathReceiver mClientDeathReceiver = () -> { |
| mHandler.sendEmptyMessage(MSG_CLIENT_DIED); |
| }; |
| |
| /** |
| * Implementation of the BiometricPrompt/BiometricManager APIs. Handles client requests, |
| * sensor arbitration, threading, etc. |
| */ |
| private final class BiometricServiceWrapper extends IBiometricService.Stub { |
| @Override // Binder call |
| public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, |
| @NonNull String opPackageName) throws RemoteException { |
| checkInternalPermission(); |
| |
| for (BiometricSensor sensor : mSensors) { |
| if (sensor.id == sensorId) { |
| return sensor.impl.createTestSession(callback, opPackageName); |
| } |
| } |
| |
| Slog.e(TAG, "Unknown sensor for createTestSession: " + sensorId); |
| return null; |
| } |
| |
| @Override // Binder call |
| public List<SensorPropertiesInternal> getSensorProperties(String opPackageName) |
| throws RemoteException { |
| checkInternalPermission(); |
| |
| final List<SensorPropertiesInternal> sensors = new ArrayList<>(); |
| for (BiometricSensor sensor : mSensors) { |
| // Explicitly re-create as the super class, since AIDL doesn't play nicely with |
| // "List<? extends SensorPropertiesInternal> ... |
| final SensorPropertiesInternal prop = SensorPropertiesInternal |
| .from(sensor.impl.getSensorProperties(opPackageName)); |
| sensors.add(prop); |
| } |
| |
| return sensors; |
| } |
| |
| @Override // Binder call |
| public void onReadyForAuthentication(int cookie) { |
| checkInternalPermission(); |
| |
| SomeArgs args = SomeArgs.obtain(); |
| args.argi1 = cookie; |
| mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget(); |
| } |
| |
| @Override // Binder call |
| public void authenticate(IBinder token, long operationId, int userId, |
| IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) { |
| checkInternalPermission(); |
| |
| if (token == null || receiver == null || opPackageName == null || promptInfo == null) { |
| Slog.e(TAG, "Unable to authenticate, one or more null arguments"); |
| return; |
| } |
| |
| if (!Utils.isValidAuthenticatorConfig(promptInfo)) { |
| throw new SecurityException("Invalid authenticator configuration"); |
| } |
| |
| Utils.combineAuthenticatorBundles(promptInfo); |
| |
| // Set the default title if necessary. |
| if (promptInfo.isUseDefaultTitle()) { |
| if (TextUtils.isEmpty(promptInfo.getTitle())) { |
| promptInfo.setTitle(getContext() |
| .getString(R.string.biometric_dialog_default_title)); |
| } |
| } |
| |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = token; |
| args.arg2 = operationId; |
| args.argi1 = userId; |
| args.arg3 = receiver; |
| args.arg4 = opPackageName; |
| args.arg5 = promptInfo; |
| |
| mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget(); |
| } |
| |
| @Override // Binder call |
| public void cancelAuthentication(IBinder token, String opPackageName) { |
| checkInternalPermission(); |
| |
| mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION).sendToTarget(); |
| } |
| |
| @Override // Binder call |
| public int canAuthenticate(String opPackageName, int userId, int callingUserId, |
| @Authenticators.Types int authenticators) { |
| checkInternalPermission(); |
| |
| Slog.d(TAG, "canAuthenticate: User=" + userId |
| + ", Caller=" + callingUserId |
| + ", Authenticators=" + authenticators); |
| |
| if (!Utils.isValidAuthenticatorConfig(authenticators)) { |
| throw new SecurityException("Invalid authenticator configuration"); |
| } |
| |
| try { |
| final PreAuthInfo preAuthInfo = |
| createPreAuthInfo(opPackageName, userId, authenticators); |
| return preAuthInfo.getCanAuthenticateResult(); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote exception", e); |
| return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE; |
| } |
| } |
| |
| @Override |
| public boolean hasEnrolledBiometrics(int userId, String opPackageName) { |
| checkInternalPermission(); |
| |
| try { |
| for (BiometricSensor sensor : mSensors) { |
| if (sensor.impl.hasEnrolledTemplates(userId, opPackageName)) { |
| return true; |
| } |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote exception", e); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public synchronized void registerAuthenticator(int id, int modality, |
| @Authenticators.Types int strength, |
| @NonNull IBiometricAuthenticator authenticator) { |
| checkInternalPermission(); |
| |
| Slog.d(TAG, "Registering ID: " + id |
| + " Modality: " + modality |
| + " Strength: " + strength); |
| |
| if (authenticator == null) { |
| throw new IllegalArgumentException("Authenticator must not be null." |
| + " Did you forget to modify the core/res/res/values/xml overlay for" |
| + " config_biometric_sensors?"); |
| } |
| |
| // Note that we allow BIOMETRIC_CONVENIENCE to register because BiometricService |
| // also does / will do other things such as keep track of lock screen timeout, etc. |
| // Just because a biometric is registered does not mean it can participate in |
| // the android.hardware.biometrics APIs. |
| if (strength != Authenticators.BIOMETRIC_STRONG |
| && strength != Authenticators.BIOMETRIC_WEAK |
| && strength != Authenticators.BIOMETRIC_CONVENIENCE) { |
| throw new IllegalStateException("Unsupported strength"); |
| } |
| |
| for (BiometricSensor sensor : mSensors) { |
| if (sensor.id == id) { |
| throw new IllegalStateException("Cannot register duplicate authenticator"); |
| } |
| } |
| |
| mSensors.add(new BiometricSensor(getContext(), id, modality, strength, authenticator) { |
| @Override |
| boolean confirmationAlwaysRequired(int userId) { |
| return mSettingObserver.getConfirmationAlwaysRequired(modality, userId); |
| } |
| |
| @Override |
| boolean confirmationSupported() { |
| return Utils.isConfirmationSupported(modality); |
| } |
| }); |
| |
| mBiometricStrengthController.updateStrengths(); |
| } |
| |
| @Override // Binder call |
| public void registerEnabledOnKeyguardCallback( |
| IBiometricEnabledOnKeyguardCallback callback, int callingUserId) { |
| checkInternalPermission(); |
| |
| mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback)); |
| try { |
| callback.onChanged(mSettingObserver.getEnabledOnKeyguard(callingUserId), |
| callingUserId); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Remote exception", e); |
| } |
| } |
| |
| @Override // Binder call |
| public void invalidateAuthenticatorIds(int userId, int fromSensorId, |
| IInvalidationCallback callback) { |
| checkInternalPermission(); |
| |
| InvalidationTracker.start(getContext(), mSensors, userId, fromSensorId, callback); |
| } |
| |
| @Override // Binder call |
| public long[] getAuthenticatorIds(int callingUserId) { |
| checkInternalPermission(); |
| |
| final List<Long> authenticatorIds = new ArrayList<>(); |
| for (BiometricSensor sensor : mSensors) { |
| try { |
| final boolean hasEnrollments = sensor.impl.hasEnrolledTemplates(callingUserId, |
| getContext().getOpPackageName()); |
| final long authenticatorId = sensor.impl.getAuthenticatorId(callingUserId); |
| if (hasEnrollments && Utils.isAtLeastStrength(sensor.getCurrentStrength(), |
| Authenticators.BIOMETRIC_STRONG)) { |
| authenticatorIds.add(authenticatorId); |
| } else { |
| Slog.d(TAG, "Sensor " + sensor + ", sensorId " + sensor.id |
| + ", hasEnrollments: " + hasEnrollments |
| + " cannot participate in Keystore operations"); |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "RemoteException", e); |
| } |
| } |
| |
| long[] result = new long[authenticatorIds.size()]; |
| for (int i = 0; i < authenticatorIds.size(); i++) { |
| result[i] = authenticatorIds.get(i); |
| } |
| return result; |
| } |
| |
| @Override // Binder call |
| public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, |
| int userId, byte[] hardwareAuthToken) { |
| checkInternalPermission(); |
| |
| // Check originating strength |
| if (!Utils.isAtLeastStrength(getSensorForId(fromSensorId).getCurrentStrength(), |
| Authenticators.BIOMETRIC_STRONG)) { |
| Slog.w(TAG, "Sensor: " + fromSensorId + " is does not meet the required strength to" |
| + " request resetLockout"); |
| return; |
| } |
| |
| // Request resetLockout for applicable sensors |
| for (BiometricSensor sensor : mSensors) { |
| if (sensor.id == fromSensorId) { |
| continue; |
| } |
| try { |
| final SensorPropertiesInternal props = sensor.impl |
| .getSensorProperties(getContext().getOpPackageName()); |
| final boolean supportsChallengelessHat = |
| props.resetLockoutRequiresHardwareAuthToken |
| && !props.resetLockoutRequiresChallenge; |
| final boolean doesNotRequireHat = !props.resetLockoutRequiresHardwareAuthToken; |
| |
| if (supportsChallengelessHat || doesNotRequireHat) { |
| Slog.d(TAG, "resetLockout from: " + fromSensorId |
| + ", for: " + sensor.id |
| + ", userId: " + userId); |
| sensor.impl.resetLockout(token, opPackageName, userId, |
| hardwareAuthToken); |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote exception", e); |
| } |
| } |
| } |
| |
| @Override // Binder call |
| public int getCurrentStrength(int sensorId) { |
| checkInternalPermission(); |
| |
| for (BiometricSensor sensor : mSensors) { |
| if (sensor.id == sensorId) { |
| return sensor.getCurrentStrength(); |
| } |
| } |
| Slog.e(TAG, "Unknown sensorId: " + sensorId); |
| return Authenticators.EMPTY_SET; |
| } |
| |
| @Override // Binder call |
| public int getCurrentModality( |
| String opPackageName, |
| int userId, |
| int callingUserId, |
| @Authenticators.Types int authenticators) { |
| |
| checkInternalPermission(); |
| |
| Slog.d(TAG, "getCurrentModality: User=" + userId |
| + ", Caller=" + callingUserId |
| + ", Authenticators=" + authenticators); |
| |
| if (!Utils.isValidAuthenticatorConfig(authenticators)) { |
| throw new SecurityException("Invalid authenticator configuration"); |
| } |
| |
| try { |
| final PreAuthInfo preAuthInfo = |
| createPreAuthInfo(opPackageName, userId, authenticators); |
| return preAuthInfo.getPreAuthenticateStatus().first; |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote exception", e); |
| return BiometricAuthenticator.TYPE_NONE; |
| } |
| } |
| |
| @Override // Binder call |
| public int getSupportedModalities(@Authenticators.Types int authenticators) { |
| checkInternalPermission(); |
| |
| Slog.d(TAG, "getSupportedModalities: Authenticators=" + authenticators); |
| |
| if (!Utils.isValidAuthenticatorConfig(authenticators)) { |
| throw new SecurityException("Invalid authenticator configuration"); |
| } |
| |
| @BiometricAuthenticator.Modality int modality = |
| Utils.isCredentialRequested(authenticators) |
| ? BiometricAuthenticator.TYPE_CREDENTIAL |
| : BiometricAuthenticator.TYPE_NONE; |
| |
| if (Utils.isBiometricRequested(authenticators)) { |
| @Authenticators.Types final int requestedStrength = |
| Utils.getPublicBiometricStrength(authenticators); |
| |
| // Add modalities of all biometric sensors that meet the authenticator requirements. |
| for (final BiometricSensor sensor : mSensors) { |
| @Authenticators.Types final int sensorStrength = sensor.getCurrentStrength(); |
| if (Utils.isAtLeastStrength(sensorStrength, requestedStrength)) { |
| modality |= sensor.modality; |
| } |
| } |
| } |
| |
| return modality; |
| } |
| |
| @Override |
| protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) { |
| if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { |
| return; |
| } |
| |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| if (args.length > 0 && "--proto".equals(args[0])) { |
| final boolean clearSchedulerBuffer = args.length > 1 |
| && "--clear-scheduler-buffer".equals(args[1]); |
| Slog.d(TAG, "ClearSchedulerBuffer: " + clearSchedulerBuffer); |
| final ProtoOutputStream proto = new ProtoOutputStream(fd); |
| proto.write(BiometricServiceStateProto.AUTH_SESSION_STATE, |
| mCurrentAuthSession != null ? mCurrentAuthSession.getState() |
| : STATE_AUTH_IDLE); |
| for (BiometricSensor sensor : mSensors) { |
| byte[] serviceState = sensor.impl |
| .dumpSensorServiceStateProto(clearSchedulerBuffer); |
| proto.write(BiometricServiceStateProto.SENSOR_SERVICE_STATES, serviceState); |
| } |
| proto.flush(); |
| } else { |
| dumpInternal(pw); |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote exception", e); |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| } |
| |
| private void checkInternalPermission() { |
| getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL, |
| "Must have USE_BIOMETRIC_INTERNAL permission"); |
| } |
| |
| @NonNull |
| private PreAuthInfo createPreAuthInfo( |
| @NonNull String opPackageName, |
| int userId, |
| @Authenticators.Types int authenticators) throws RemoteException { |
| |
| final PromptInfo promptInfo = new PromptInfo(); |
| promptInfo.setAuthenticators(authenticators); |
| |
| return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors, |
| userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */); |
| } |
| |
| /** |
| * Class for injecting dependencies into BiometricService. |
| * TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger). |
| */ |
| @VisibleForTesting |
| public static class Injector { |
| |
| public IActivityManager getActivityManagerService() { |
| return ActivityManager.getService(); |
| } |
| |
| public ITrustManager getTrustManager() { |
| return ITrustManager.Stub.asInterface(ServiceManager.getService(Context.TRUST_SERVICE)); |
| } |
| |
| public IStatusBarService getStatusBarService() { |
| return IStatusBarService.Stub.asInterface( |
| ServiceManager.getService(Context.STATUS_BAR_SERVICE)); |
| } |
| |
| /** |
| * Allows to mock SettingObserver for testing. |
| */ |
| public SettingObserver getSettingObserver(Context context, Handler handler, |
| List<EnabledOnKeyguardCallback> callbacks) { |
| return new SettingObserver(context, handler, callbacks); |
| } |
| |
| public KeyStore getKeyStore() { |
| return KeyStore.getInstance(); |
| } |
| |
| /** |
| * Allows to enable/disable debug logs. |
| */ |
| public boolean isDebugEnabled(Context context, int userId) { |
| return Utils.isDebugEnabled(context, userId); |
| } |
| |
| /** |
| * Allows to stub publishBinderService(...) for testing. |
| */ |
| public void publishBinderService(BiometricService service, IBiometricService.Stub impl) { |
| service.publishBinderService(Context.BIOMETRIC_SERVICE, impl); |
| } |
| |
| /** |
| * Allows to mock BiometricStrengthController for testing. |
| */ |
| public BiometricStrengthController getBiometricStrengthController( |
| BiometricService service) { |
| return new BiometricStrengthController(service); |
| } |
| |
| /** |
| * Allows to test with various device sensor configurations. |
| * @param context System Server context |
| * @return the sensor configuration from core/res/res/values/config.xml |
| */ |
| public String[] getConfiguration(Context context) { |
| return context.getResources().getStringArray(R.array.config_biometric_sensors); |
| } |
| |
| public DevicePolicyManager getDevicePolicyManager(Context context) { |
| return context.getSystemService(DevicePolicyManager.class); |
| } |
| |
| public List<FingerprintSensorPropertiesInternal> getFingerprintSensorProperties( |
| Context context) { |
| if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { |
| final FingerprintManager fpm = context.getSystemService(FingerprintManager.class); |
| if (fpm != null) { |
| return fpm.getSensorPropertiesInternal(); |
| } |
| } |
| return new ArrayList<>(); |
| } |
| |
| public boolean isAdvancedCoexLogicEnabled(Context context) { |
| return Settings.Secure.getInt(context.getContentResolver(), |
| CoexCoordinator.SETTING_ENABLE_NAME, 1) != 0; |
| } |
| |
| public boolean isCoexFaceNonBypassHapticsDisabled(Context context) { |
| return Settings.Secure.getInt(context.getContentResolver(), |
| CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0; |
| } |
| } |
| |
| /** |
| * Initializes the system service. |
| * <p> |
| * Subclasses must define a single argument constructor that accepts the context |
| * and passes it to super. |
| * </p> |
| * |
| * @param context The system server context. |
| */ |
| public BiometricService(Context context) { |
| this(context, new Injector()); |
| } |
| |
| @VisibleForTesting |
| BiometricService(Context context, Injector injector) { |
| super(context); |
| |
| mInjector = injector; |
| mDevicePolicyManager = mInjector.getDevicePolicyManager(context); |
| mImpl = new BiometricServiceWrapper(); |
| mEnabledOnKeyguardCallbacks = new ArrayList<>(); |
| mSettingObserver = mInjector.getSettingObserver(context, mHandler, |
| mEnabledOnKeyguardCallbacks); |
| |
| // TODO(b/193089985) This logic lives here (outside of CoexCoordinator) so that it doesn't |
| // need to depend on context. We can remove this code once the advanced logic is enabled |
| // by default. |
| CoexCoordinator coexCoordinator = CoexCoordinator.getInstance(); |
| coexCoordinator.setAdvancedLogicEnabled(injector.isAdvancedCoexLogicEnabled(context)); |
| coexCoordinator.setFaceHapticDisabledWhenNonBypass( |
| injector.isCoexFaceNonBypassHapticsDisabled(context)); |
| |
| try { |
| injector.getActivityManagerService().registerUserSwitchObserver( |
| new UserSwitchObserver() { |
| @Override |
| public void onUserSwitchComplete(int newUserId) { |
| mSettingObserver.updateContentObserver(); |
| mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId); |
| } |
| }, BiometricService.class.getName() |
| ); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Failed to register user switch observer", e); |
| } |
| } |
| |
| @Override |
| public void onStart() { |
| mKeyStore = mInjector.getKeyStore(); |
| mStatusBarService = mInjector.getStatusBarService(); |
| mTrustManager = mInjector.getTrustManager(); |
| mInjector.publishBinderService(this, mImpl); |
| mBiometricStrengthController = mInjector.getBiometricStrengthController(this); |
| mBiometricStrengthController.startListening(); |
| } |
| |
| private boolean isStrongBiometric(int id) { |
| for (BiometricSensor sensor : mSensors) { |
| if (sensor.id == id) { |
| return Utils.isAtLeastStrength(sensor.getCurrentStrength(), |
| Authenticators.BIOMETRIC_STRONG); |
| } |
| } |
| Slog.e(TAG, "Unknown sensorId: " + id); |
| return false; |
| } |
| |
| private void handleAuthenticationSucceeded(int sensorId, byte[] token) { |
| Slog.v(TAG, "handleAuthenticationSucceeded(), sensorId: " + sensorId); |
| // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded |
| // after user dismissed/canceled dialog). |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleAuthenticationSucceeded: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onAuthenticationSucceeded(sensorId, isStrongBiometric(sensorId), token); |
| } |
| |
| private void handleAuthenticationRejected() { |
| Slog.v(TAG, "handleAuthenticationRejected()"); |
| |
| // Should never happen, log this to catch bad HAL behavior (e.g. auth rejected |
| // after user dismissed/canceled dialog). |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleAuthenticationRejected: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onAuthenticationRejected(); |
| } |
| |
| private void handleAuthenticationTimedOut(int sensorId, int cookie, int error, int vendorCode) { |
| Slog.v(TAG, "handleAuthenticationTimedOut(), sensorId: " + sensorId |
| + ", cookie: " + cookie |
| + ", error: " + error |
| + ", vendorCode: " + vendorCode); |
| // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded |
| // after user dismissed/canceled dialog). |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleAuthenticationTimedOut: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onAuthenticationTimedOut(sensorId, cookie, error, vendorCode); |
| } |
| |
| private void handleOnError(int sensorId, int cookie, @BiometricConstants.Errors int error, |
| int vendorCode) { |
| Slog.d(TAG, "handleOnError() sensorId: " + sensorId |
| + ", cookie: " + cookie |
| + ", error: " + error |
| + ", vendorCode: " + vendorCode); |
| |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleOnError: AuthSession is null"); |
| return; |
| } |
| |
| try { |
| final boolean finished = mCurrentAuthSession |
| .onErrorReceived(sensorId, cookie, error, vendorCode); |
| if (finished) { |
| Slog.d(TAG, "handleOnError: AuthSession finished"); |
| mCurrentAuthSession = null; |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "RemoteException", e); |
| } |
| } |
| |
| private void handleOnAcquired(int sensorId, int acquiredInfo, int vendorCode) { |
| // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded |
| // after user dismissed/canceled dialog). |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "onAcquired: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onAcquired(sensorId, acquiredInfo, vendorCode); |
| } |
| |
| private void handleOnDismissed(@BiometricPrompt.DismissedReason int reason, |
| @Nullable byte[] credentialAttestation) { |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "onDismissed: " + reason + ", AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onDialogDismissed(reason, credentialAttestation); |
| mCurrentAuthSession = null; |
| } |
| |
| private void handleOnTryAgainPressed() { |
| Slog.d(TAG, "onTryAgainPressed"); |
| // No need to check permission, since it can only be invoked by SystemUI |
| // (or system server itself). |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleOnTryAgainPressed: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onTryAgainPressed(); |
| } |
| |
| private void handleOnDeviceCredentialPressed() { |
| Slog.d(TAG, "onDeviceCredentialPressed"); |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleOnDeviceCredentialPressed: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onDeviceCredentialPressed(); |
| } |
| |
| private void handleOnSystemEvent(int event) { |
| Slog.d(TAG, "onSystemEvent: " + event); |
| |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleOnSystemEvent: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onSystemEvent(event); |
| } |
| |
| private void handleClientDied() { |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleClientDied: AuthSession is null"); |
| return; |
| } |
| |
| Slog.e(TAG, "Session: " + mCurrentAuthSession); |
| final boolean finished = mCurrentAuthSession.onClientDied(); |
| if (finished) { |
| mCurrentAuthSession = null; |
| } |
| } |
| |
| private void handleOnDialogAnimatedIn() { |
| Slog.d(TAG, "handleOnDialogAnimatedIn"); |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleOnDialogAnimatedIn: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onDialogAnimatedIn(); |
| } |
| |
| private void handleOnStartFingerprintNow() { |
| Slog.d(TAG, "handleOnStartFingerprintNow"); |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleOnStartFingerprintNow: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onStartFingerprint(); |
| } |
| |
| /** |
| * Invoked when each service has notified that its client is ready to be started. When |
| * all biometrics are ready, this invokes the SystemUI dialog through StatusBar. |
| */ |
| private void handleOnReadyForAuthentication(int cookie) { |
| if (mCurrentAuthSession == null) { |
| // Only should happen if a biometric was locked out when authenticate() was invoked. |
| // In that case, if device credentials are allowed, the UI is already showing. If not |
| // allowed, the error has already been returned to the caller. |
| Slog.w(TAG, "handleOnReadyForAuthentication: AuthSession is null"); |
| return; |
| } |
| |
| mCurrentAuthSession.onCookieReceived(cookie); |
| } |
| |
| private void handleAuthenticate(IBinder token, long operationId, int userId, |
| IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) { |
| mHandler.post(() -> { |
| try { |
| final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, |
| mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo, |
| opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists()); |
| |
| final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus(); |
| |
| Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first |
| + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo); |
| |
| if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) { |
| // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but |
| // CREDENTIAL is requested and available, set the bundle to only request |
| // CREDENTIAL. |
| // TODO: We should clean this up, as well as the interface with SystemUI |
| if (preAuthInfo.credentialRequested && preAuthInfo.credentialAvailable |
| && preAuthInfo.eligibleSensors.isEmpty()) { |
| promptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL); |
| } |
| |
| authenticateInternal(token, operationId, userId, receiver, opPackageName, |
| promptInfo, preAuthInfo); |
| } else { |
| receiver.onError(preAuthStatus.first /* modality */, |
| preAuthStatus.second /* errorCode */, |
| 0 /* vendorCode */); |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Remote exception", e); |
| } |
| }); |
| } |
| |
| /** |
| * handleAuthenticate() (above) which is called from BiometricPrompt determines which |
| * modality/modalities to start authenticating with. authenticateInternal() should only be |
| * used for preparing <Biometric>Services for authentication when BiometricPrompt#authenticate |
| * is invoked, shortly after which BiometricPrompt is shown and authentication starts. |
| * |
| * Note that this path is NOT invoked when the BiometricPrompt "Try again" button is pressed. |
| * In that case, see {@link #handleOnTryAgainPressed()}. |
| */ |
| private void authenticateInternal(IBinder token, long operationId, int userId, |
| IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo, |
| PreAuthInfo preAuthInfo) { |
| Slog.d(TAG, "Creating authSession with authRequest: " + preAuthInfo); |
| |
| // No need to dismiss dialog / send error yet if we're continuing authentication, e.g. |
| // "Try again" is showing due to something like ERROR_TIMEOUT. |
| if (mCurrentAuthSession != null) { |
| // Forcefully cancel authentication. Dismiss the UI, and immediately send |
| // ERROR_CANCELED to the client. Note that we should/will ignore HAL ERROR_CANCELED. |
| // Expect to see some harmless "unknown cookie" errors. |
| Slog.w(TAG, "Existing AuthSession: " + mCurrentAuthSession); |
| mCurrentAuthSession.onCancelAuthSession(true /* force */); |
| mCurrentAuthSession = null; |
| } |
| |
| final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId); |
| mCurrentAuthSession = new AuthSession(getContext(), mStatusBarService, mSysuiReceiver, |
| mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, operationId, userId, |
| mBiometricSensorReceiver, receiver, opPackageName, promptInfo, debugEnabled, |
| mInjector.getFingerprintSensorProperties(getContext())); |
| try { |
| mCurrentAuthSession.goToInitialState(); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "RemoteException", e); |
| } |
| } |
| |
| private void handleCancelAuthentication() { |
| if (mCurrentAuthSession == null) { |
| Slog.e(TAG, "handleCancelAuthentication: AuthSession is null"); |
| return; |
| } |
| |
| final boolean finished = mCurrentAuthSession.onCancelAuthSession(false /* force */); |
| if (finished) { |
| Slog.d(TAG, "handleCancelAuthentication: AuthSession finished"); |
| mCurrentAuthSession = null; |
| } |
| } |
| |
| @Nullable |
| private BiometricSensor getSensorForId(int sensorId) { |
| for (BiometricSensor sensor : mSensors) { |
| if (sensor.id == sensorId) { |
| return sensor; |
| } |
| } |
| return null; |
| } |
| |
| private void dumpInternal(PrintWriter pw) { |
| pw.println("Legacy Settings: " + mSettingObserver.mUseLegacyFaceOnlySettings); |
| pw.println(); |
| |
| pw.println("Sensors:"); |
| for (BiometricSensor sensor : mSensors) { |
| pw.println(" " + sensor); |
| } |
| pw.println(); |
| pw.println("CurrentSession: " + mCurrentAuthSession); |
| pw.println(); |
| pw.println("CoexCoordinator: " + CoexCoordinator.getInstance().toString()); |
| pw.println(); |
| } |
| } |