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         binderDiedInternal(true /* clearListener */);
237     }
238 
binderDiedInternal(boolean clearListener)239     void binderDiedInternal(boolean clearListener) {
240         // If the current client dies we should cancel the current operation.
241         Slog.e(getLogTag(), "Binder died, cancelling client");
242         stop(false /* initiatedByClient */);
243         mToken = null;
244         if (clearListener) {
245             mListener = null;
246         }
247     }
248 
249     @Override
finalize()250     protected void finalize() throws Throwable {
251         try {
252             if (mToken != null) {
253                 if (DEBUG) Slog.w(getLogTag(), "removing leaked reference: " + mToken);
254                 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
255                         0 /* vendorCode */);
256             }
257         } finally {
258             super.finalize();
259         }
260     }
261 
getContext()262     public final Context getContext() {
263         return mContext;
264     }
265 
getHalDeviceId()266     public final long getHalDeviceId() {
267         return mHalDeviceId;
268     }
269 
getOwnerString()270     public final String getOwnerString() {
271         return mOwner;
272     }
273 
getListener()274     public final BiometricServiceBase.ServiceListener getListener() {
275         return mListener;
276     }
277 
getDaemonWrapper()278     public final BiometricServiceBase.DaemonWrapper getDaemonWrapper() {
279         return mDaemon;
280     }
281 
getIsRestricted()282     public final boolean getIsRestricted() {
283         return mIsRestricted;
284     }
285 
getTargetUserId()286     public final int getTargetUserId() {
287         return mTargetUserId;
288     }
289 
getGroupId()290     public final int getGroupId() {
291         return mGroupId;
292     }
293 
getToken()294     public final IBinder getToken() {
295         return mToken;
296     }
297 
vibrateSuccess()298     public final void vibrateSuccess() {
299         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
300         if (vibrator != null) {
301             vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
302         }
303     }
304 
vibrateError()305     public final void vibrateError() {
306         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
307         if (vibrator != null) {
308             vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
309         }
310     }
311 }
312