Merge "strong fp lockout after 20 failed attempts" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
310818ff82
@@ -118,6 +118,15 @@ public class FingerprintManager {
|
||||
* @hide
|
||||
*/
|
||||
public static final int FINGERPRINT_ERROR_VENDOR = 8;
|
||||
|
||||
/**
|
||||
* The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
|
||||
* Fingerprint authentication is disabled until the user unlocks with strong authentication
|
||||
* (PIN/Pattern/Password)
|
||||
* @hide
|
||||
*/
|
||||
public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -1013,6 +1022,9 @@ public class FingerprintManager {
|
||||
return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled);
|
||||
case FINGERPRINT_ERROR_LOCKOUT:
|
||||
return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout);
|
||||
case FINGERPRINT_ERROR_LOCKOUT_PERMANENT:
|
||||
return mContext.getString(
|
||||
com.android.internal.R.string.fingerprint_error_lockout_permanent);
|
||||
case FINGERPRINT_ERROR_VENDOR: {
|
||||
String[] msgArray = mContext.getResources().getStringArray(
|
||||
com.android.internal.R.array.fingerprint_error_vendor);
|
||||
|
||||
@@ -54,4 +54,7 @@ message FingerprintActionStatsProto {
|
||||
|
||||
// Total number of lockouts.
|
||||
int32 lockout = 4;
|
||||
|
||||
// Total number of permanent lockouts.
|
||||
int32 lockout_permanent = 5;
|
||||
}
|
||||
|
||||
@@ -1335,6 +1335,8 @@
|
||||
<string name="fingerprint_error_canceled">Fingerprint operation canceled.</string>
|
||||
<!-- Generic error message shown when the fingerprint operation fails because too many attempts have been made. -->
|
||||
<string name="fingerprint_error_lockout">Too many attempts. Try again later.</string>
|
||||
<!-- Generic error message shown when the fingerprint operation fails because strong authentication is required -->
|
||||
<string name="fingerprint_error_lockout_permanent">Too many attempts. Fingerprint sensor disabled.</string>
|
||||
<!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint -->
|
||||
<string name="fingerprint_error_unable_to_process">Try again.</string>
|
||||
|
||||
|
||||
@@ -2281,6 +2281,7 @@
|
||||
<java-symbol type="array" name="fingerprint_acquired_vendor" />
|
||||
<java-symbol type="string" name="fingerprint_error_canceled" />
|
||||
<java-symbol type="string" name="fingerprint_error_lockout" />
|
||||
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
|
||||
<java-symbol type="string" name="fingerprint_name_template" />
|
||||
|
||||
<!-- Fingerprint config -->
|
||||
|
||||
@@ -502,6 +502,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
|
||||
}
|
||||
}
|
||||
|
||||
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
|
||||
mLockPatternUtils.requireStrongAuth(
|
||||
LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
|
||||
getCurrentUser());
|
||||
}
|
||||
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
|
||||
if (cb != null) {
|
||||
|
||||
@@ -34,8 +34,13 @@ import android.util.Slog;
|
||||
public abstract class AuthenticationClient extends ClientMonitor {
|
||||
private long mOpId;
|
||||
|
||||
public abstract boolean handleFailedAttempt();
|
||||
public abstract int handleFailedAttempt();
|
||||
public abstract void resetFailedAttempts();
|
||||
|
||||
public static final int LOCKOUT_NONE = 0;
|
||||
public static final int LOCKOUT_TIMED = 1;
|
||||
public static final int LOCKOUT_PERMANENT = 2;
|
||||
|
||||
private boolean mAlreadyCancelled;
|
||||
|
||||
public AuthenticationClient(Context context, long halDeviceId, IBinder token,
|
||||
@@ -79,19 +84,21 @@ public abstract class AuthenticationClient extends ClientMonitor {
|
||||
FingerprintUtils.vibrateFingerprintError(getContext());
|
||||
}
|
||||
// allow system-defined limit of number of attempts before giving up
|
||||
boolean inLockoutMode = handleFailedAttempt();
|
||||
// send lockout event in case driver doesn't enforce it.
|
||||
if (inLockoutMode) {
|
||||
int lockoutMode = handleFailedAttempt();
|
||||
if (lockoutMode != LOCKOUT_NONE) {
|
||||
try {
|
||||
Slog.w(TAG, "Forcing lockout (fp driver code should do this!)");
|
||||
stop(false); // cancel fingerprint authentication
|
||||
receiver.onError(getHalDeviceId(),
|
||||
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT, 0 /* vendorCode */);
|
||||
Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
|
||||
lockoutMode + ")");
|
||||
stop(false);
|
||||
int errorCode = lockoutMode == LOCKOUT_TIMED ?
|
||||
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
|
||||
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
|
||||
receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to notify lockout:", e);
|
||||
}
|
||||
}
|
||||
result |= inLockoutMode;
|
||||
result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
|
||||
} else {
|
||||
if (receiver != null) {
|
||||
FingerprintUtils.vibrateFingerprintSuccess(getContext());
|
||||
|
||||
@@ -108,6 +108,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
|
||||
// acquisition in some cases (too fast, too slow, dirty sensor, etc.)
|
||||
int lockout; // total number of lockouts
|
||||
int permanentLockout; // total number of permanent lockouts
|
||||
}
|
||||
|
||||
private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
|
||||
@@ -118,13 +119,16 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
Collections.synchronizedMap(new HashMap<>());
|
||||
private final AppOpsManager mAppOps;
|
||||
private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
|
||||
private static final int MAX_FAILED_ATTEMPTS = 5;
|
||||
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
|
||||
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
|
||||
|
||||
private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
|
||||
private final String mKeyguardPackage;
|
||||
private int mCurrentUserId = UserHandle.USER_NULL;
|
||||
private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
|
||||
private Context mContext;
|
||||
private long mHalDeviceId;
|
||||
private boolean mTimedLockoutCleared;
|
||||
private int mFailedAttempts;
|
||||
@GuardedBy("this")
|
||||
private IBiometricsFingerprint mDaemon;
|
||||
@@ -173,7 +177,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
|
||||
resetFailedAttempts();
|
||||
resetFailedAttempts(false /* clearAttemptCounter */);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -181,7 +185,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
private final Runnable mResetFailedAttemptsRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
resetFailedAttempts();
|
||||
resetFailedAttempts(true /* clearAttemptCounter */);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -369,6 +373,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
if (client != null && client.onError(error, vendorCode)) {
|
||||
removeClient(client);
|
||||
}
|
||||
|
||||
if (DEBUG) Slog.v(TAG, "handleError(client="
|
||||
+ (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
|
||||
// This is the magic code that starts the next client when the old client finishes.
|
||||
@@ -438,7 +443,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
|
||||
removeClient(client);
|
||||
}
|
||||
if (mPerformanceStats != null && !inLockoutMode()
|
||||
if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
|
||||
&& client instanceof AuthenticationClient) {
|
||||
// ignore enrollment acquisitions or acquisitions when we're locked out
|
||||
mPerformanceStats.acquire++;
|
||||
@@ -482,8 +487,14 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
}
|
||||
}
|
||||
|
||||
private boolean inLockoutMode() {
|
||||
return mFailedAttempts >= MAX_FAILED_ATTEMPTS;
|
||||
private int getLockoutMode() {
|
||||
if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
|
||||
return AuthenticationClient.LOCKOUT_PERMANENT;
|
||||
} else if (mFailedAttempts > 0 && mTimedLockoutCleared == false &&
|
||||
(mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
|
||||
return AuthenticationClient.LOCKOUT_TIMED;
|
||||
}
|
||||
return AuthenticationClient.LOCKOUT_NONE;
|
||||
}
|
||||
|
||||
private void scheduleLockoutReset() {
|
||||
@@ -801,22 +812,27 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
|
||||
receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
|
||||
@Override
|
||||
public boolean handleFailedAttempt() {
|
||||
public int handleFailedAttempt() {
|
||||
mFailedAttempts++;
|
||||
if (mFailedAttempts == MAX_FAILED_ATTEMPTS) {
|
||||
mTimedLockoutCleared = false;
|
||||
final int lockoutMode = getLockoutMode();
|
||||
if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
|
||||
mPerformanceStats.permanentLockout++;
|
||||
} else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
|
||||
mPerformanceStats.lockout++;
|
||||
}
|
||||
if (inLockoutMode()) {
|
||||
// Failing multiple times will continue to push out the lockout time.
|
||||
|
||||
// Failing multiple times will continue to push out the lockout time
|
||||
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
|
||||
scheduleLockoutReset();
|
||||
return true;
|
||||
return lockoutMode;
|
||||
}
|
||||
return false;
|
||||
return AuthenticationClient.LOCKOUT_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetFailedAttempts() {
|
||||
FingerprintService.this.resetFailedAttempts();
|
||||
FingerprintService.this.resetFailedAttempts(true /* clearAttemptCounter */);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -830,11 +846,15 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
}
|
||||
};
|
||||
|
||||
if (inLockoutMode()) {
|
||||
Slog.v(TAG, "In lockout mode; disallowing authentication");
|
||||
// Don't bother starting the client. Just send the error message.
|
||||
if (!client.onError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT, 0 /* vendorCode */)) {
|
||||
Slog.w(TAG, "Cannot send timeout message to client");
|
||||
int lockoutMode = getLockoutMode();
|
||||
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
|
||||
Slog.v(TAG, "In lockout mode(" + lockoutMode +
|
||||
") ; disallowing authentication");
|
||||
int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
|
||||
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
|
||||
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
|
||||
if (!client.onError(errorCode, 0 /* vendorCode */)) {
|
||||
Slog.w(TAG, "Cannot send permanent lockout message to client");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -864,11 +884,16 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
startClient(client, true /* initiatedByClient */);
|
||||
}
|
||||
|
||||
protected void resetFailedAttempts() {
|
||||
if (DEBUG && inLockoutMode()) {
|
||||
Slog.v(TAG, "Reset fingerprint lockout");
|
||||
// attempt counter should only be cleared when Keyguard goes away or when
|
||||
// a fingerprint is successfully authenticated
|
||||
protected void resetFailedAttempts(boolean clearAttemptCounter) {
|
||||
if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
|
||||
Slog.v(TAG, "Reset fingerprint lockout, clearAttemptCounter=" + clearAttemptCounter);
|
||||
}
|
||||
mFailedAttempts = 0;
|
||||
if (clearAttemptCounter) {
|
||||
mFailedAttempts = 0;
|
||||
}
|
||||
mTimedLockoutCleared = true;
|
||||
// If we're asked to reset failed attempts externally (i.e. from Keyguard),
|
||||
// the alarm might still be pending; remove it.
|
||||
cancelLockoutReset();
|
||||
@@ -1301,6 +1326,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
set.put("reject", (stats != null) ? stats.reject : 0);
|
||||
set.put("acquire", (stats != null) ? stats.acquire : 0);
|
||||
set.put("lockout", (stats != null) ? stats.lockout : 0);
|
||||
set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
|
||||
// cryptoStats measures statistics about secure fingerprint transactions
|
||||
// (e.g. to unlock password storage, make secure purchases, etc.)
|
||||
set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
|
||||
@@ -1336,6 +1362,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
proto.write(FingerprintActionStatsProto.REJECT, normal.reject);
|
||||
proto.write(FingerprintActionStatsProto.ACQUIRE, normal.acquire);
|
||||
proto.write(FingerprintActionStatsProto.LOCKOUT, normal.lockout);
|
||||
proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
|
||||
proto.end(countsToken);
|
||||
}
|
||||
|
||||
@@ -1348,6 +1375,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
proto.write(FingerprintActionStatsProto.REJECT, crypto.reject);
|
||||
proto.write(FingerprintActionStatsProto.ACQUIRE, crypto.acquire);
|
||||
proto.write(FingerprintActionStatsProto.LOCKOUT, crypto.lockout);
|
||||
proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
|
||||
proto.end(countsToken);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user