• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.ActivityTaskManager;
23 import android.app.TaskStackListener;
24 import android.content.Context;
25 import android.content.pm.ApplicationInfo;
26 import android.hardware.biometrics.BiometricAuthenticator;
27 import android.hardware.biometrics.BiometricConstants;
28 import android.hardware.biometrics.BiometricManager;
29 import android.hardware.biometrics.BiometricOverlayConstants;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.os.SystemClock;
33 import android.security.KeyStore;
34 import android.util.EventLog;
35 import android.util.Slog;
36 
37 import com.android.server.biometrics.BiometricsProto;
38 import com.android.server.biometrics.Utils;
39 import com.android.server.biometrics.log.BiometricContext;
40 import com.android.server.biometrics.log.BiometricLogger;
41 
42 import java.util.ArrayList;
43 import java.util.function.Supplier;
44 
45 /**
46  * A class to keep track of the authentication state for a given client.
47  */
48 public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
49         implements AuthenticationConsumer  {
50 
51     private static final String TAG = "Biometrics/AuthenticationClient";
52 
53     // New, has not started yet
54     public static final int STATE_NEW = 0;
55     // Framework/HAL have started this operation
56     public static final int STATE_STARTED = 1;
57     // Operation is started, but requires some user action to start (such as finger lift & re-touch)
58     public static final int STATE_STARTED_PAUSED = 2;
59     // Same as above, except auth was attempted (rejected, timed out, etc).
60     public static final int STATE_STARTED_PAUSED_ATTEMPTED = 3;
61     // Done, errored, canceled, etc. HAL/framework are not running this sensor anymore.
62     public static final int STATE_STOPPED = 4;
63 
64     @IntDef({STATE_NEW,
65             STATE_STARTED,
66             STATE_STARTED_PAUSED,
67             STATE_STARTED_PAUSED_ATTEMPTED,
68             STATE_STOPPED})
69     @interface State {}
70 
71     private final boolean mIsStrongBiometric;
72     private final boolean mRequireConfirmation;
73     private final ActivityTaskManager mActivityTaskManager;
74     private final BiometricManager mBiometricManager;
75     @Nullable private final TaskStackListener mTaskStackListener;
76     private final LockoutTracker mLockoutTracker;
77     private final boolean mIsRestricted;
78     private final boolean mAllowBackgroundAuthentication;
79     private final boolean mIsKeyguardBypassEnabled;
80 
81     protected final long mOperationId;
82 
83     private long mStartTimeMs;
84 
85     private boolean mAuthAttempted;
86     private boolean mAuthSuccess = false;
87 
88     // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update
89     //  the state. We should think of a way to improve this in the future.
90     protected @State int mState = STATE_NEW;
91 
AuthenticationClient(@onNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, int sensorId, @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication, boolean shouldVibrate, boolean isKeyguardBypassEnabled)92     public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
93             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
94             int targetUserId, long operationId, boolean restricted, @NonNull String owner,
95             int cookie, boolean requireConfirmation, int sensorId,
96             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
97             boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener,
98             @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
99             boolean shouldVibrate, boolean isKeyguardBypassEnabled) {
100         super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
101                 shouldVibrate, biometricLogger, biometricContext);
102         mIsStrongBiometric = isStrongBiometric;
103         mOperationId = operationId;
104         mRequireConfirmation = requireConfirmation;
105         mActivityTaskManager = getActivityTaskManager();
106         mBiometricManager = context.getSystemService(BiometricManager.class);
107         mTaskStackListener = taskStackListener;
108         mLockoutTracker = lockoutTracker;
109         mIsRestricted = restricted;
110         mAllowBackgroundAuthentication = allowBackgroundAuthentication;
111         mIsKeyguardBypassEnabled = isKeyguardBypassEnabled;
112     }
113 
handleFailedAttempt(int userId)114     public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
115         final @LockoutTracker.LockoutMode int lockoutMode =
116                 mLockoutTracker.getLockoutModeForUser(userId);
117         final PerformanceTracker performanceTracker =
118                 PerformanceTracker.getInstanceForSensorId(getSensorId());
119 
120         if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
121             performanceTracker.incrementPermanentLockoutForUser(userId);
122         } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
123             performanceTracker.incrementTimedLockoutForUser(userId);
124         }
125 
126         return lockoutMode;
127     }
128 
getStartTimeMs()129     protected long getStartTimeMs() {
130         return mStartTimeMs;
131     }
132 
getActivityTaskManager()133     protected ActivityTaskManager getActivityTaskManager() {
134         return ActivityTaskManager.getInstance();
135     }
136 
137     @Override
binderDied()138     public void binderDied() {
139         final boolean clearListener = !isBiometricPrompt();
140         binderDiedInternal(clearListener);
141     }
142 
isBiometricPrompt()143     public boolean isBiometricPrompt() {
144         return getCookie() != 0;
145     }
146 
getOperationId()147     public long getOperationId() {
148         return mOperationId;
149     }
150 
isRestricted()151     public boolean isRestricted() {
152         return mIsRestricted;
153     }
154 
isKeyguard()155     public boolean isKeyguard() {
156         return Utils.isKeyguard(getContext(), getOwnerString());
157     }
158 
isSettings()159     private boolean isSettings() {
160         return Utils.isSettings(getContext(), getOwnerString());
161     }
162 
163     @Override
isCryptoOperation()164     protected boolean isCryptoOperation() {
165         return mOperationId != 0;
166     }
167 
168     @Override
onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> hardwareAuthToken)169     public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
170             boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
171         getLogger().logOnAuthenticated(getContext(), getOperationContext(),
172                 authenticated, mRequireConfirmation, getTargetUserId(), isBiometricPrompt());
173 
174         final ClientMonitorCallbackConverter listener = getListener();
175 
176         if (DEBUG) Slog.v(TAG, "onAuthenticated(" + authenticated + ")"
177                 + ", ID:" + identifier.getBiometricId()
178                 + ", Owner: " + getOwnerString()
179                 + ", isBP: " + isBiometricPrompt()
180                 + ", listener: " + listener
181                 + ", requireConfirmation: " + mRequireConfirmation
182                 + ", user: " + getTargetUserId()
183                 + ", clientMonitor: " + toString());
184 
185         final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
186         if (isCryptoOperation()) {
187             pm.incrementCryptoAuthForUser(getTargetUserId(), authenticated);
188         } else {
189             pm.incrementAuthForUser(getTargetUserId(), authenticated);
190         }
191 
192         if (mAllowBackgroundAuthentication) {
193             Slog.w(TAG, "Allowing background authentication,"
194                     + " this is allowed only for platform or test invocations");
195         }
196 
197         // Ensure authentication only succeeds if the client activity is on top.
198         boolean isBackgroundAuth = false;
199         if (!mAllowBackgroundAuthentication && authenticated
200                 && !Utils.isKeyguard(getContext(), getOwnerString())
201                 && !Utils.isSystem(getContext(), getOwnerString())) {
202             isBackgroundAuth = Utils.isBackground(getOwnerString());
203         }
204 
205         // Fail authentication if we can't confirm the client activity is on top.
206         if (isBackgroundAuth) {
207             Slog.e(TAG, "Failing possible background authentication");
208             authenticated = false;
209 
210             // SafetyNet logging for exploitation attempts of b/159249069.
211             final ApplicationInfo appInfo = getContext().getApplicationInfo();
212             EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1,
213                     "Attempted background authentication");
214         }
215 
216         if (authenticated) {
217             // SafetyNet logging for b/159249069 if constraint is violated.
218             if (isBackgroundAuth) {
219                 final ApplicationInfo appInfo = getContext().getApplicationInfo();
220                 EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1,
221                         "Successful background authentication!");
222             }
223 
224             mAuthSuccess = true;
225             markAlreadyDone();
226 
227             if (mTaskStackListener != null) {
228                 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
229             }
230 
231             final byte[] byteToken = new byte[hardwareAuthToken.size()];
232             for (int i = 0; i < hardwareAuthToken.size(); i++) {
233                 byteToken[i] = hardwareAuthToken.get(i);
234             }
235 
236             if (mIsStrongBiometric) {
237                 mBiometricManager.resetLockoutTimeBound(getToken(),
238                         getContext().getOpPackageName(),
239                         getSensorId(), getTargetUserId(), byteToken);
240             }
241 
242             final CoexCoordinator coordinator = CoexCoordinator.getInstance();
243             coordinator.onAuthenticationSucceeded(SystemClock.uptimeMillis(), this,
244                     new CoexCoordinator.Callback() {
245                 @Override
246                 public void sendAuthenticationResult(boolean addAuthTokenIfStrong) {
247                     if (addAuthTokenIfStrong && mIsStrongBiometric) {
248                         final int result = KeyStore.getInstance().addAuthToken(byteToken);
249                         Slog.d(TAG, "addAuthToken: " + result);
250                     } else {
251                         Slog.d(TAG, "Skipping addAuthToken");
252                     }
253 
254                     if (listener != null) {
255                         try {
256                             // Explicitly have if/else here to make it super obvious in case the
257                             // code is touched in the future.
258                             if (!mIsRestricted) {
259                                 listener.onAuthenticationSucceeded(getSensorId(),
260                                         identifier,
261                                         byteToken,
262                                         getTargetUserId(),
263                                         mIsStrongBiometric);
264                             } else {
265                                 listener.onAuthenticationSucceeded(getSensorId(),
266                                         null /* identifier */,
267                                         byteToken,
268                                         getTargetUserId(),
269                                         mIsStrongBiometric);
270                             }
271                         } catch (RemoteException e) {
272                             Slog.e(TAG, "Unable to notify listener", e);
273                         }
274                     } else {
275                         Slog.w(TAG, "Client not listening");
276                     }
277                 }
278 
279                 @Override
280                 public void sendHapticFeedback() {
281                     if (listener != null && mShouldVibrate) {
282                         vibrateSuccess();
283                     }
284                 }
285 
286                 @Override
287                 public void handleLifecycleAfterAuth() {
288                     AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */);
289                 }
290 
291                 @Override
292                 public void sendAuthenticationCanceled() {
293                     sendCancelOnly(listener);
294                 }
295             });
296         } else { // not authenticated
297             if (isBackgroundAuth) {
298                 Slog.e(TAG, "cancelling due to background auth");
299                 cancel();
300             } else {
301                 // Allow system-defined limit of number of attempts before giving up
302                 final @LockoutTracker.LockoutMode int lockoutMode =
303                         handleFailedAttempt(getTargetUserId());
304                 if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
305                     markAlreadyDone();
306                 }
307 
308                 final CoexCoordinator coordinator = CoexCoordinator.getInstance();
309                 coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode,
310                         new CoexCoordinator.Callback() {
311                             @Override
312                             public void sendAuthenticationResult(boolean addAuthTokenIfStrong) {
313                                 if (listener != null) {
314                                     try {
315                                         listener.onAuthenticationFailed(getSensorId());
316                                     } catch (RemoteException e) {
317                                         Slog.e(TAG, "Unable to notify listener", e);
318                                     }
319                                 }
320                             }
321 
322                             @Override
323                             public void sendHapticFeedback() {
324                                 if (listener != null && mShouldVibrate) {
325                                     vibrateError();
326                                 }
327                             }
328 
329                             @Override
330                             public void handleLifecycleAfterAuth() {
331                                 AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
332                             }
333 
334                             @Override
335                             public void sendAuthenticationCanceled() {
336                                 sendCancelOnly(listener);
337                             }
338                         });
339             }
340         }
341     }
342 
343     /**
344      * Only call this method on interfaces where lockout does not come from onError, I.E. the
345      * old HIDL implementation.
346      */
onLockoutTimed(long durationMillis)347     protected void onLockoutTimed(long durationMillis) {
348         final ClientMonitorCallbackConverter listener = getListener();
349         final CoexCoordinator coordinator = CoexCoordinator.getInstance();
350         coordinator.onAuthenticationError(this, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
351                 new CoexCoordinator.ErrorCallback() {
352             @Override
353             public void sendHapticFeedback() {
354                 if (listener != null && mShouldVibrate) {
355                     vibrateError();
356                 }
357             }
358         });
359     }
360 
361     /**
362      * Only call this method on interfaces where lockout does not come from onError, I.E. the
363      * old HIDL implementation.
364      */
onLockoutPermanent()365     protected void onLockoutPermanent() {
366         final ClientMonitorCallbackConverter listener = getListener();
367         final CoexCoordinator coordinator = CoexCoordinator.getInstance();
368         coordinator.onAuthenticationError(this,
369                 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
370                 new CoexCoordinator.ErrorCallback() {
371             @Override
372             public void sendHapticFeedback() {
373                 if (listener != null && mShouldVibrate) {
374                     vibrateError();
375                 }
376             }
377         });
378     }
379 
sendCancelOnly(@ullable ClientMonitorCallbackConverter listener)380     private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) {
381         if (listener == null) {
382             Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null");
383             return;
384         }
385         try {
386             listener.onError(getSensorId(),
387                     getCookie(),
388                     BiometricConstants.BIOMETRIC_ERROR_CANCELED,
389                     0 /* vendorCode */);
390         } catch (RemoteException e) {
391             Slog.e(TAG, "Remote exception", e);
392         }
393     }
394 
395     @Override
onAcquired(int acquiredInfo, int vendorCode)396     public void onAcquired(int acquiredInfo, int vendorCode) {
397         super.onAcquired(acquiredInfo, vendorCode);
398 
399         final @LockoutTracker.LockoutMode int lockoutMode =
400                 mLockoutTracker.getLockoutModeForUser(getTargetUserId());
401         if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
402             PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
403             pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
404         }
405     }
406 
407     @Override
onError(@iometricConstants.Errors int errorCode, int vendorCode)408     public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) {
409         super.onError(errorCode, vendorCode);
410         mState = STATE_STOPPED;
411 
412         CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError);
413     }
414 
415     /**
416      * Start authentication
417      */
418     @Override
start(@onNull ClientMonitorCallback callback)419     public void start(@NonNull ClientMonitorCallback callback) {
420         super.start(callback);
421 
422         final @LockoutTracker.LockoutMode int lockoutMode =
423                 mLockoutTracker.getLockoutModeForUser(getTargetUserId());
424         if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
425             Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
426             int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
427                     ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
428                     : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
429             onError(errorCode, 0 /* vendorCode */);
430             return;
431         }
432 
433         if (mTaskStackListener != null) {
434             mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
435         }
436 
437         Slog.d(TAG, "Requesting auth for " + getOwnerString());
438 
439         mStartTimeMs = System.currentTimeMillis();
440         mAuthAttempted = true;
441         startHalOperation();
442     }
443 
444     @Override
cancel()445     public void cancel() {
446         super.cancel();
447         if (mTaskStackListener != null) {
448             mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
449         }
450     }
451 
452     /**
453      * Handles lifecycle, e.g. {@link BiometricScheduler},
454      * {@link com.android.server.biometrics.sensors.BaseClientMonitor.Callback} after authentication
455      * results are known. Note that this happens asynchronously from (but shortly after)
456      * {@link #onAuthenticated(BiometricAuthenticator.Identifier, boolean, ArrayList)} and allows
457      * {@link CoexCoordinator} a chance to invoke/delay this event.
458      * @param authenticated
459      */
handleLifecycleAfterAuth(boolean authenticated)460     protected abstract void handleLifecycleAfterAuth(boolean authenticated);
461 
462     /**
463      * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched.
464      *         etc)
465      */
wasUserDetected()466     public abstract boolean wasUserDetected();
467 
getState()468     public @State int getState() {
469         return mState;
470     }
471 
472     /**
473      * @return true if the client supports bypass (e.g. passive auth such as face), and if it's
474      * enabled by the user.
475      */
isKeyguardBypassEnabled()476     public boolean isKeyguardBypassEnabled() {
477         return mIsKeyguardBypassEnabled;
478     }
479 
480     @Override
getProtoEnum()481     public int getProtoEnum() {
482         return BiometricsProto.CM_AUTHENTICATE;
483     }
484 
485     @Override
interruptsPrecedingClients()486     public boolean interruptsPrecedingClients() {
487         return true;
488     }
489 
wasAuthAttempted()490     public boolean wasAuthAttempted() {
491         return mAuthAttempted;
492     }
493 
494     /** If an auth attempt completed successfully. */
wasAuthSuccessful()495     public boolean wasAuthSuccessful() {
496         return mAuthSuccess;
497     }
498 
getShowOverlayReason()499     protected int getShowOverlayReason() {
500         if (isKeyguard()) {
501             return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
502         } else if (isBiometricPrompt()) {
503             // BP reason always takes precedent over settings, since callers from within
504             // settings can always invoke BP.
505             return BiometricOverlayConstants.REASON_AUTH_BP;
506         } else if (isSettings()) {
507             // This is pretty much only for FingerprintManager#authenticate usage from
508             // FingerprintSettings.
509             return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
510         } else {
511             return BiometricOverlayConstants.REASON_AUTH_OTHER;
512         }
513     }
514 }
515