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