1 /* 2 * Copyright (C) 2021 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 17 package com.android.server.biometrics.sensors; 18 19 import static android.hardware.biometrics.BiometricStateListener.STATE_AUTH_OTHER; 20 import static android.hardware.biometrics.BiometricStateListener.STATE_BP_AUTH; 21 import static android.hardware.biometrics.BiometricStateListener.STATE_ENROLLING; 22 import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE; 23 import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH; 24 25 import android.annotation.NonNull; 26 import android.hardware.biometrics.BiometricStateListener; 27 import android.hardware.biometrics.IBiometricStateListener; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.Slog; 31 32 import com.android.server.biometrics.Utils; 33 34 import java.util.concurrent.CopyOnWriteArrayList; 35 36 /** 37 * A callback for receiving notifications about biometric sensor state changes. 38 */ 39 public class BiometricStateCallback implements ClientMonitorCallback, IBinder.DeathRecipient { 40 41 private static final String TAG = "BiometricStateCallback"; 42 43 @NonNull 44 private final CopyOnWriteArrayList<IBiometricStateListener> 45 mBiometricStateListeners = new CopyOnWriteArrayList<>(); 46 47 private @BiometricStateListener.State int mBiometricState; 48 BiometricStateCallback()49 public BiometricStateCallback() { 50 mBiometricState = STATE_IDLE; 51 } 52 getBiometricState()53 public int getBiometricState() { 54 return mBiometricState; 55 } 56 57 @Override onClientStarted(@onNull BaseClientMonitor client)58 public void onClientStarted(@NonNull BaseClientMonitor client) { 59 final int previousBiometricState = mBiometricState; 60 61 if (client instanceof AuthenticationClient) { 62 final AuthenticationClient<?> authClient = (AuthenticationClient<?>) client; 63 if (authClient.isKeyguard()) { 64 mBiometricState = STATE_KEYGUARD_AUTH; 65 } else if (authClient.isBiometricPrompt()) { 66 mBiometricState = STATE_BP_AUTH; 67 } else { 68 mBiometricState = STATE_AUTH_OTHER; 69 } 70 } else if (client instanceof EnrollClient) { 71 mBiometricState = STATE_ENROLLING; 72 } else { 73 Slog.w(TAG, "Other authentication client: " + Utils.getClientName(client)); 74 mBiometricState = STATE_IDLE; 75 } 76 77 Slog.d(TAG, "State updated from " + previousBiometricState + " to " + mBiometricState 78 + ", client " + client); 79 notifyBiometricStateListeners(mBiometricState); 80 } 81 82 @Override onClientFinished(@onNull BaseClientMonitor client, boolean success)83 public void onClientFinished(@NonNull BaseClientMonitor client, boolean success) { 84 mBiometricState = STATE_IDLE; 85 Slog.d(TAG, "Client finished, state updated to " + mBiometricState + ", client " 86 + client); 87 88 if (client instanceof EnrollmentModifier) { 89 EnrollmentModifier enrollmentModifier = (EnrollmentModifier) client; 90 final boolean enrollmentStateChanged = enrollmentModifier.hasEnrollmentStateChanged(); 91 Slog.d(TAG, "Enrollment state changed: " + enrollmentStateChanged); 92 if (enrollmentStateChanged) { 93 notifyAllEnrollmentStateChanged(client.getTargetUserId(), 94 client.getSensorId(), 95 enrollmentModifier.hasEnrollments()); 96 } 97 } 98 99 notifyBiometricStateListeners(mBiometricState); 100 } 101 notifyBiometricStateListeners(@iometricStateListener.State int newState)102 private void notifyBiometricStateListeners(@BiometricStateListener.State int newState) { 103 for (IBiometricStateListener listener : mBiometricStateListeners) { 104 try { 105 listener.onStateChanged(newState); 106 } catch (RemoteException e) { 107 Slog.e(TAG, "Remote exception in biometric state change", e); 108 } 109 } 110 } 111 112 @Override onBiometricAction(@iometricStateListener.Action int action)113 public void onBiometricAction(@BiometricStateListener.Action int action) { 114 for (IBiometricStateListener listener : mBiometricStateListeners) { 115 try { 116 listener.onBiometricAction(action); 117 } catch (RemoteException e) { 118 Slog.e(TAG, "Remote exception in onBiometricAction", e); 119 } 120 } 121 } 122 123 /** 124 * This should be invoked when: 125 * 1) Enrolled --> None-enrolled 126 * 2) None-enrolled --> enrolled 127 * 3) HAL becomes ready 128 * 4) Listener is registered 129 */ notifyAllEnrollmentStateChanged(int userId, int sensorId, boolean hasEnrollments)130 public void notifyAllEnrollmentStateChanged(int userId, int sensorId, 131 boolean hasEnrollments) { 132 for (IBiometricStateListener listener : mBiometricStateListeners) { 133 notifyEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments); 134 } 135 } 136 137 /** 138 * Notifies the listener of enrollment state changes. 139 */ notifyEnrollmentStateChanged(@onNull IBiometricStateListener listener, int userId, int sensorId, boolean hasEnrollments)140 public void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener, 141 int userId, int sensorId, boolean hasEnrollments) { 142 try { 143 listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments); 144 } catch (RemoteException e) { 145 Slog.e(TAG, "Remote exception", e); 146 } 147 } 148 149 /** 150 * Enables clients to register a BiometricStateListener. For example, this is used to forward 151 * fingerprint sensor state changes to SideFpsEventHandler. 152 * 153 * @param listener 154 */ registerBiometricStateListener(@onNull IBiometricStateListener listener)155 public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { 156 mBiometricStateListeners.add(listener); 157 try { 158 listener.asBinder().linkToDeath(this, 0 /* flags */); 159 } catch (RemoteException e) { 160 Slog.e(TAG, "Failed to link to death", e); 161 } 162 } 163 164 @Override binderDied()165 public void binderDied() { 166 // Do nothing, handled below 167 } 168 169 @Override binderDied(IBinder who)170 public void binderDied(IBinder who) { 171 Slog.w(TAG, "Callback binder died: " + who); 172 if (mBiometricStateListeners.removeIf(listener -> listener.asBinder().equals(who))) { 173 Slog.w(TAG, "Removed dead listener for " + who); 174 } else { 175 Slog.w(TAG, "No dead listeners found"); 176 } 177 } 178 } 179