• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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