1 /*
2  * Copyright (C) 2018 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;
18 
19 import android.content.Context;
20 import android.hardware.biometrics.BiometricAuthenticator;
21 import android.hardware.biometrics.BiometricConstants;
22 import android.media.AudioAttributes;
23 import android.os.IBinder;
24 import android.os.RemoteException;
25 import android.os.VibrationEffect;
26 import android.os.Vibrator;
27 import android.util.Slog;
28 
29 import com.android.internal.logging.MetricsLogger;
30 
31 import java.util.ArrayList;
32 import java.util.NoSuchElementException;
33 
34 /**
35  * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
36  * the current client.  Subclasses are responsible for coordinating the interaction with
37  * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
38  */
39 public abstract class ClientMonitor extends LoggableMonitor implements IBinder.DeathRecipient {
40     protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
41     protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
42     private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
43             new AudioAttributes.Builder()
44                     .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
45                     .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
46                     .build();
47 
48     private final Context mContext;
49     private final long mHalDeviceId;
50     private final int mTargetUserId;
51     private final int mGroupId;
52     // True if client does not have MANAGE_FINGERPRINT permission
53     private final boolean mIsRestricted;
54     private final String mOwner;
55     private final VibrationEffect mSuccessVibrationEffect;
56     private final VibrationEffect mErrorVibrationEffect;
57     private final BiometricServiceBase.DaemonWrapper mDaemon;
58 
59     private IBinder mToken;
60     private BiometricServiceBase.ServiceListener mListener;
61     // Currently only used for authentication client. The cookie generated by BiometricService
62     // is never 0.
63     private final int mCookie;
64 
65     protected final MetricsLogger mMetricsLogger;
66     protected final Constants mConstants;
67 
68     protected boolean mAlreadyCancelled;
69     protected boolean mAlreadyDone;
70 
71     /**
72      * @param context context of BiometricService
73      * @param daemon interface to call back to a specific biometric's daemon
74      * @param halDeviceId the HAL device ID of the associated biometric hardware
75      * @param token a unique token for the client
76      * @param listener recipient of related events (e.g. authentication)
77      * @param userId target user id for operation
78      * @param groupId groupId for the fingerprint set
79      * @param restricted whether or not client has the MANAGE_* permission
80      * permission
81      * @param owner name of the client that owns this
82      */
ClientMonitor(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, boolean restricted, String owner, int cookie)83     public ClientMonitor(Context context, Constants constants,
84             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
85             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
86             boolean restricted, String owner, int cookie) {
87         mContext = context;
88         mConstants = constants;
89         mDaemon = daemon;
90         mHalDeviceId = halDeviceId;
91         mToken = token;
92         mListener = listener;
93         mTargetUserId = userId;
94         mGroupId = groupId;
95         mIsRestricted = restricted;
96         mOwner = owner;
97         mCookie = cookie;
98         mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
99         mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
100         mMetricsLogger = new MetricsLogger();
101         try {
102             if (token != null) {
103                 token.linkToDeath(this, 0);
104             }
105         } catch (RemoteException e) {
106             Slog.w(getLogTag(), "caught remote exception in linkToDeath: ", e);
107         }
108     }
109 
getLogTag()110     protected String getLogTag() {
111         return mConstants.logTag();
112     }
113 
getCookie()114     public int getCookie() {
115         return mCookie;
116     }
117 
118     /**
119      * Contacts the biometric's HAL to start the client.
120      * @return 0 on success, errno from driver on failure
121      */
start()122     public abstract int start();
123 
124     /**
125      * Contacts the biometric's HAL to stop the client.
126      * @param initiatedByClient whether the operation is at the request of a client
127      */
stop(boolean initiatedByClient)128     public abstract int stop(boolean initiatedByClient);
129 
130     /**
131      * Method to explicitly poke powermanager on events
132      */
notifyUserActivity()133     public abstract void notifyUserActivity();
134 
135     // Event callbacks from driver. Inappropriate calls is flagged/logged by the
136     // respective client (e.g. enrolling shouldn't get authenticate events).
137     // All of these return 'true' if the operation is completed and it's ok to move
138     // to the next client (e.g. authentication accepts or rejects a biometric).
onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)139     public abstract boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
140             int remaining);
onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token)141     public abstract boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
142             boolean authenticated, ArrayList<Byte> token);
onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)143     public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier,
144             int remaining);
onEnumerationResult( BiometricAuthenticator.Identifier identifier, int remaining)145     public abstract boolean onEnumerationResult(
146             BiometricAuthenticator.Identifier identifier, int remaining);
147 
getAcquireIgnorelist()148     public int[] getAcquireIgnorelist() {
149         return new int[0];
150     }
getAcquireVendorIgnorelist()151     public int[] getAcquireVendorIgnorelist() {
152         return new int[0];
153     }
154 
blacklistContains(int acquiredInfo, int vendorCode)155     private boolean blacklistContains(int acquiredInfo, int vendorCode) {
156         if (acquiredInfo == mConstants.acquireVendorCode()) {
157             for (int i = 0; i < getAcquireVendorIgnorelist().length; i++) {
158                 if (getAcquireVendorIgnorelist()[i] == vendorCode) {
159                     if (DEBUG) Slog.v(getLogTag(), "Ignoring vendor message: " + vendorCode);
160                     return true;
161                 }
162             }
163         } else {
164             for (int i = 0; i < getAcquireIgnorelist().length; i++) {
165                 if (getAcquireIgnorelist()[i] == acquiredInfo) {
166                     if (DEBUG) Slog.v(getLogTag(), "Ignoring message: " + acquiredInfo);
167                     return true;
168                 }
169             }
170         }
171         return false;
172     }
173 
isAlreadyDone()174     public boolean isAlreadyDone() {
175         return mAlreadyDone;
176     }
177 
178     /**
179      * Called when we get notification from the biometric's HAL that an image has been acquired.
180      * Common to authenticate and enroll.
181      * @param acquiredInfo info about the current image acquisition
182      * @return true if client should be removed
183      */
onAcquired(int acquiredInfo, int vendorCode)184     public boolean onAcquired(int acquiredInfo, int vendorCode) {
185         super.logOnAcquired(mContext, acquiredInfo, vendorCode, getTargetUserId());
186         if (DEBUG) Slog.v(getLogTag(), "Acquired: " + acquiredInfo + " " + vendorCode);
187         try {
188             if (mListener != null && !blacklistContains(acquiredInfo, vendorCode)) {
189                 mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
190             }
191             return false; // acquisition continues...
192         } catch (RemoteException e) {
193             Slog.w(getLogTag(), "Failed to invoke sendAcquired", e);
194             return true;
195         } finally {
196             // Good scans will keep the device awake
197             if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
198                 notifyUserActivity();
199             }
200         }
201     }
202 
203     /**
204      * Called when we get notification from the biometric's HAL that an error has occurred with the
205      * current operation. Common to authenticate, enroll, enumerate and remove.
206      * @param error
207      * @return true if client should be removed
208      */
onError(long deviceId, int error, int vendorCode)209     public boolean onError(long deviceId, int error, int vendorCode) {
210         super.logOnError(mContext, error, vendorCode, getTargetUserId());
211         try {
212             if (mListener != null) {
213                 mListener.onError(deviceId, error, vendorCode, getCookie());
214             }
215         } catch (RemoteException e) {
216             Slog.w(getLogTag(), "Failed to invoke sendError", e);
217         }
218         return true; // errors always remove current client
219     }
220 
destroy()221     public void destroy() {
222         if (mToken != null) {
223             try {
224                 mToken.unlinkToDeath(this, 0);
225             } catch (NoSuchElementException e) {
226                 // TODO: remove when duplicate call bug is found
227                 Slog.e(getLogTag(), "destroy(): " + this + ":", new Exception("here"));
228             }
229             mToken = null;
230         }
231         mListener = null;
232     }
233 
234     @Override
binderDied()235     public void binderDied() {
236         // If the current client dies we should cancel the current operation.
237         Slog.e(getLogTag(), "Binder died, cancelling client");
238         stop(false /* initiatedByClient */);
239         mToken = null;
240         mListener = null;
241     }
242 
243     @Override
finalize()244     protected void finalize() throws Throwable {
245         try {
246             if (mToken != null) {
247                 if (DEBUG) Slog.w(getLogTag(), "removing leaked reference: " + mToken);
248                 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
249                         0 /* vendorCode */);
250             }
251         } finally {
252             super.finalize();
253         }
254     }
255 
getContext()256     public final Context getContext() {
257         return mContext;
258     }
259 
getHalDeviceId()260     public final long getHalDeviceId() {
261         return mHalDeviceId;
262     }
263 
getOwnerString()264     public final String getOwnerString() {
265         return mOwner;
266     }
267 
getListener()268     public final BiometricServiceBase.ServiceListener getListener() {
269         return mListener;
270     }
271 
getDaemonWrapper()272     public final BiometricServiceBase.DaemonWrapper getDaemonWrapper() {
273         return mDaemon;
274     }
275 
getIsRestricted()276     public final boolean getIsRestricted() {
277         return mIsRestricted;
278     }
279 
getTargetUserId()280     public final int getTargetUserId() {
281         return mTargetUserId;
282     }
283 
getGroupId()284     public final int getGroupId() {
285         return mGroupId;
286     }
287 
getToken()288     public final IBinder getToken() {
289         return mToken;
290     }
291 
vibrateSuccess()292     public final void vibrateSuccess() {
293         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
294         if (vibrator != null) {
295             vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
296         }
297     }
298 
vibrateError()299     public final void vibrateError() {
300         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
301         if (vibrator != null) {
302             vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
303         }
304     }
305 }
306