1 /* 2 * Copyright (C) 2023 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 android.annotation.NonNull; 20 import android.hardware.biometrics.AuthenticationStateListener; 21 import android.hardware.biometrics.events.AuthenticationAcquiredInfo; 22 import android.hardware.biometrics.events.AuthenticationErrorInfo; 23 import android.hardware.biometrics.events.AuthenticationFailedInfo; 24 import android.hardware.biometrics.events.AuthenticationHelpInfo; 25 import android.hardware.biometrics.events.AuthenticationStartedInfo; 26 import android.hardware.biometrics.events.AuthenticationStoppedInfo; 27 import android.hardware.biometrics.events.AuthenticationSucceededInfo; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.Slog; 31 32 import java.util.concurrent.CopyOnWriteArrayList; 33 34 /** 35 * Low-level callback interface between BiometricManager and AuthService. Allows core system 36 * services (e.g. SystemUI) to register and unregister listeners for updates about the current 37 * state of biometric authentication. 38 * @hide */ 39 public class AuthenticationStateListeners implements IBinder.DeathRecipient { 40 41 private static final String TAG = "AuthenticationStateListeners"; 42 43 @NonNull 44 private final CopyOnWriteArrayList<AuthenticationStateListener> mAuthenticationStateListeners = 45 new CopyOnWriteArrayList<>(); 46 47 /** 48 * Enables clients to register an AuthenticationStateListener for updates about the current 49 * state of biometric authentication. 50 * @param listener listener to register 51 */ registerAuthenticationStateListener( @onNull AuthenticationStateListener listener)52 public void registerAuthenticationStateListener( 53 @NonNull AuthenticationStateListener listener) { 54 mAuthenticationStateListeners.add(listener); 55 try { 56 listener.asBinder().linkToDeath(this, 0 /* flags */); 57 } catch (RemoteException e) { 58 Slog.e(TAG, "Failed to link to death", e); 59 } 60 } 61 62 /** 63 * Enables clients to unregister an AuthenticationStateListener. 64 * @param listener listener to register 65 */ unregisterAuthenticationStateListener( @onNull AuthenticationStateListener listener)66 public void unregisterAuthenticationStateListener( 67 @NonNull AuthenticationStateListener listener) { 68 mAuthenticationStateListeners.remove(listener); 69 } 70 71 /** 72 * Defines behavior in response to biometric authentication being acquired. 73 * @param authInfo information related to the biometric authentication acquired. 74 */ onAuthenticationAcquired(AuthenticationAcquiredInfo authInfo)75 public void onAuthenticationAcquired(AuthenticationAcquiredInfo authInfo) { 76 for (AuthenticationStateListener listener: mAuthenticationStateListeners) { 77 try { 78 listener.onAuthenticationAcquired(authInfo); 79 } catch (RemoteException e) { 80 Slog.e(TAG, "Remote exception in notifying listener that authentication " 81 + "acquired", e); 82 } 83 } 84 } 85 86 /** 87 * Defines behavior in response to an unrecoverable error encountered during authentication. 88 * @param authInfo information related to the unrecoverable auth error encountered 89 */ onAuthenticationError(AuthenticationErrorInfo authInfo)90 public void onAuthenticationError(AuthenticationErrorInfo authInfo) { 91 for (AuthenticationStateListener listener : mAuthenticationStateListeners) { 92 try { 93 listener.onAuthenticationError(authInfo); 94 } catch (RemoteException e) { 95 Slog.e(TAG, "Remote exception in notifying listener of unrecoverable" 96 + " authentication error", e); 97 } 98 } 99 } 100 101 /** 102 * Defines behavior in response to a failed authentication 103 * @param authInfo information related to the failed authentication 104 */ onAuthenticationFailed(AuthenticationFailedInfo authInfo)105 public void onAuthenticationFailed(AuthenticationFailedInfo authInfo) { 106 for (AuthenticationStateListener listener : mAuthenticationStateListeners) { 107 try { 108 listener.onAuthenticationFailed(authInfo); 109 } catch (RemoteException e) { 110 Slog.e(TAG, "Remote exception in notifying listener that authentication " 111 + "failed", e); 112 } 113 } 114 } 115 116 /** 117 * Defines behavior in response to a recoverable error encountered during authentication. 118 * @param authInfo information related to the recoverable auth error encountered 119 */ onAuthenticationHelp(AuthenticationHelpInfo authInfo)120 public void onAuthenticationHelp(AuthenticationHelpInfo authInfo) { 121 for (AuthenticationStateListener listener : mAuthenticationStateListeners) { 122 try { 123 listener.onAuthenticationHelp(authInfo); 124 } catch (RemoteException e) { 125 Slog.e(TAG, "Remote exception in notifying listener of recoverable" 126 + " authentication error", e); 127 } 128 } 129 } 130 131 /** 132 * Defines behavior in response to authentication starting 133 * @param authInfo information related to the authentication starting 134 */ onAuthenticationStarted(AuthenticationStartedInfo authInfo)135 public void onAuthenticationStarted(AuthenticationStartedInfo authInfo) { 136 for (AuthenticationStateListener listener: mAuthenticationStateListeners) { 137 try { 138 listener.onAuthenticationStarted(authInfo); 139 } catch (RemoteException e) { 140 Slog.e(TAG, "Remote exception in notifying listener that authentication " 141 + "started", e); 142 } 143 } 144 } 145 146 /** 147 * Defines behavior in response to authentication stopping 148 * @param authInfo information related to the authentication stopping 149 */ onAuthenticationStopped(AuthenticationStoppedInfo authInfo)150 public void onAuthenticationStopped(AuthenticationStoppedInfo authInfo) { 151 for (AuthenticationStateListener listener: mAuthenticationStateListeners) { 152 try { 153 listener.onAuthenticationStopped(authInfo); 154 } catch (RemoteException e) { 155 Slog.e(TAG, "Remote exception in notifying listener that authentication " 156 + "stopped", e); 157 } 158 } 159 } 160 161 /** 162 * Defines behavior in response to a successful authentication 163 * @param authInfo information related to the successful authentication 164 */ onAuthenticationSucceeded(AuthenticationSucceededInfo authInfo)165 public void onAuthenticationSucceeded(AuthenticationSucceededInfo authInfo) { 166 for (AuthenticationStateListener listener: mAuthenticationStateListeners) { 167 try { 168 listener.onAuthenticationSucceeded(authInfo); 169 } catch (RemoteException e) { 170 Slog.e(TAG, "Remote exception in notifying listener that authentication " 171 + "succeeded", e); 172 } 173 } 174 } 175 176 @Override binderDied()177 public void binderDied() { 178 // Do nothing, handled below 179 } 180 181 @Override binderDied(IBinder who)182 public void binderDied(IBinder who) { 183 Slog.w(TAG, "Callback binder died: " + who); 184 if (mAuthenticationStateListeners.removeIf(listener -> listener.asBinder().equals(who))) { 185 Slog.w(TAG, "Removed dead listener for " + who); 186 } else { 187 Slog.w(TAG, "No dead listeners found"); 188 } 189 } 190 } 191