• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.hardware.biometrics.BiometricsProtoEnums;
23 import android.os.IBinder;
24 import android.os.RemoteException;
25 import android.security.KeyStore;
26 import android.util.Slog;
27 
28 import java.util.ArrayList;
29 
30 /**
31  * A class to keep track of the authentication state for a given client.
32  */
33 public abstract class AuthenticationClient extends ClientMonitor {
34     private long mOpId;
35 
handleFailedAttempt()36     public abstract int handleFailedAttempt();
resetFailedAttempts()37     public void resetFailedAttempts() {}
38 
39     public static final int LOCKOUT_NONE = 0;
40     public static final int LOCKOUT_TIMED = 1;
41     public static final int LOCKOUT_PERMANENT = 2;
42 
43     private final boolean mRequireConfirmation;
44 
45     // We need to track this state since it's possible for applications to request for
46     // authentication while the device is already locked out. In that case, the client is created
47     // but not started yet. The user shouldn't receive the error haptics in this case.
48     private boolean mStarted;
49 
50     /**
51      * This method is called when authentication starts.
52      */
onStart()53     public abstract void onStart();
54 
55     /**
56      * This method is called when a biometric is authenticated or authentication is stopped
57      * (cancelled by the user, or an error such as lockout has occurred).
58      */
onStop()59     public abstract void onStop();
60 
61     /**
62      * @return true if the framework should handle lockout.
63      */
shouldFrameworkHandleLockout()64     public abstract boolean shouldFrameworkHandleLockout();
65 
wasUserDetected()66     public abstract boolean wasUserDetected();
67 
AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation)68     public AuthenticationClient(Context context, Constants constants,
69             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
70             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
71             boolean restricted, String owner, int cookie, boolean requireConfirmation) {
72         super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId,
73                 restricted, owner, cookie);
74         mOpId = opId;
75         mRequireConfirmation = requireConfirmation;
76     }
77 
78     @Override
binderDied()79     public void binderDied() {
80         super.binderDied();
81         // When the binder dies, we should stop the client. This probably belongs in
82         // ClientMonitor's binderDied(), but testing all the cases would be tricky.
83         // AuthenticationClient is the most user-visible case.
84         stop(false /* initiatedByClient */);
85     }
86 
87     @Override
statsAction()88     protected int statsAction() {
89         return BiometricsProtoEnums.ACTION_AUTHENTICATE;
90     }
91 
isBiometricPrompt()92     public boolean isBiometricPrompt() {
93         return getCookie() != 0;
94     }
95 
getRequireConfirmation()96     public boolean getRequireConfirmation() {
97         return mRequireConfirmation;
98     }
99 
100     @Override
isCryptoOperation()101     protected boolean isCryptoOperation() {
102         return mOpId != 0;
103     }
104 
105     @Override
onError(long deviceId, int error, int vendorCode)106     public boolean onError(long deviceId, int error, int vendorCode) {
107         if (!shouldFrameworkHandleLockout()) {
108             switch (error) {
109                 case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
110                     if (!wasUserDetected() && !isBiometricPrompt()) {
111                         // No vibration if user was not detected on keyguard
112                         break;
113                     }
114                 case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
115                 case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
116                     if (mStarted) {
117                         vibrateError();
118                     }
119                     break;
120                 default:
121                     break;
122             }
123         }
124         return super.onError(deviceId, error, vendorCode);
125     }
126 
127     @Override
onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token)128     public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
129             boolean authenticated, ArrayList<Byte> token) {
130         super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
131                 getTargetUserId(), isBiometricPrompt());
132 
133         final BiometricServiceBase.ServiceListener listener = getListener();
134 
135         mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated);
136         boolean result = false;
137 
138         try {
139             if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + authenticated + ")"
140                     + ", ID:" + identifier.getBiometricId()
141                     + ", Owner: " + getOwnerString()
142                     + ", isBP: " + isBiometricPrompt()
143                     + ", listener: " + listener
144                     + ", requireConfirmation: " + mRequireConfirmation
145                     + ", user: " + getTargetUserId());
146 
147             if (authenticated) {
148                 mAlreadyDone = true;
149 
150                 if (listener != null) {
151                     vibrateSuccess();
152                 }
153                 result = true;
154                 if (shouldFrameworkHandleLockout()) {
155                     resetFailedAttempts();
156                 }
157                 onStop();
158 
159                 final byte[] byteToken = new byte[token.size()];
160                 for (int i = 0; i < token.size(); i++) {
161                     byteToken[i] = token.get(i);
162                 }
163                 if (isBiometricPrompt() && listener != null) {
164                     // BiometricService will add the token to keystore
165                     listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
166                 } else if (!isBiometricPrompt() && listener != null) {
167                     KeyStore.getInstance().addAuthToken(byteToken);
168                     try {
169                         // Explicitly have if/else here to make it super obvious in case the code is
170                         // touched in the future.
171                         if (!getIsRestricted()) {
172                             listener.onAuthenticationSucceeded(
173                                     getHalDeviceId(), identifier, getTargetUserId());
174                         } else {
175                             listener.onAuthenticationSucceeded(
176                                     getHalDeviceId(), null, getTargetUserId());
177                         }
178                     } catch (RemoteException e) {
179                         Slog.e(getLogTag(), "Remote exception", e);
180                     }
181                 } else {
182                     // Client not listening
183                     Slog.w(getLogTag(), "Client not listening");
184                     result = true;
185                 }
186             } else {
187                 if (listener != null) {
188                     vibrateError();
189                 }
190 
191                 // Allow system-defined limit of number of attempts before giving up
192                 final int lockoutMode = handleFailedAttempt();
193                 if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) {
194                     Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
195                             + lockoutMode + ")");
196                     stop(false);
197                     final int errorCode = lockoutMode == LOCKOUT_TIMED
198                             ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
199                             : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
200                     onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
201                 } else {
202                     // Don't send onAuthenticationFailed if we're in lockout, it causes a
203                     // janky UI on Keyguard/BiometricPrompt since "authentication failed"
204                     // will show briefly and be replaced by "device locked out" message.
205                     if (listener != null) {
206                         if (isBiometricPrompt()) {
207                             listener.onAuthenticationFailedInternal(getCookie(),
208                                     getRequireConfirmation());
209                         } else {
210                             listener.onAuthenticationFailed(getHalDeviceId());
211                         }
212                     }
213                 }
214                 result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
215             }
216         } catch (RemoteException e) {
217             Slog.e(getLogTag(), "Remote exception", e);
218             result = true;
219         }
220         return result;
221     }
222 
223     /**
224      * Start authentication
225      */
226     @Override
start()227     public int start() {
228         mStarted = true;
229         onStart();
230         try {
231             final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
232             if (result != 0) {
233                 Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
234                 mMetricsLogger.histogram(mConstants.tagAuthStartError(), result);
235                 onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
236                         0 /* vendorCode */);
237                 return result;
238             }
239             if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
240         } catch (RemoteException e) {
241             Slog.e(getLogTag(), "startAuthentication failed", e);
242             return ERROR_ESRCH;
243         }
244         return 0; // success
245     }
246 
247     @Override
stop(boolean initiatedByClient)248     public int stop(boolean initiatedByClient) {
249         if (mAlreadyCancelled) {
250             Slog.w(getLogTag(), "stopAuthentication: already cancelled!");
251             return 0;
252         }
253 
254         mStarted = false;
255 
256         onStop();
257 
258         try {
259             final int result = getDaemonWrapper().cancel();
260             if (result != 0) {
261                 Slog.w(getLogTag(), "stopAuthentication failed, result=" + result);
262                 return result;
263             }
264             if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() +
265                     " is no longer authenticating");
266         } catch (RemoteException e) {
267             Slog.e(getLogTag(), "stopAuthentication failed", e);
268             return ERROR_ESRCH;
269         }
270 
271         mAlreadyCancelled = true;
272         return 0; // success
273     }
274 
275     @Override
onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)276     public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
277             int remaining) {
278         if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for authenticate!");
279         return true; // Invalid for Authenticate
280     }
281 
282     @Override
onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)283     public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
284         if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for authenticate!");
285         return true; // Invalid for Authenticate
286     }
287 
288     @Override
onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining)289     public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
290             int remaining) {
291         if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for authenticate!");
292         return true; // Invalid for Authenticate
293     }
294 }
295