[local_auth] Fixed crash on API<28 while invoking authenticateWithBiometrics (#1659)
Add better error handling for when too many tries locks out the fingerprint feature.
diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md
index 3921db7..9832440 100644
--- a/packages/local_auth/CHANGELOG.md
+++ b/packages/local_auth/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.5.1
+* Fix crash on Android versions earlier than 28.
+* [`authenticateWithBiometrics`](https://pub.dev/documentation/local_auth/latest/local_auth/LocalAuthentication/authenticateWithBiometrics.html) will not return result unless Biometric Dialog is closed.
+* Added two more error codes `LockedOut` and `PermanentlyLockedOut`.
+
## 0.5.0
* **Breaking change**. Update the Android API to use androidx Biometric package. This gives
the prompt the updated Material look. However, it also requires the activity to be a
diff --git a/packages/local_auth/README.md b/packages/local_auth/README.md
index 89eeb9d..34e43f6 100644
--- a/packages/local_auth/README.md
+++ b/packages/local_auth/README.md
@@ -93,8 +93,8 @@
### Exceptions
-There are 4 types of exceptions: PasscodeNotSet, NotEnrolled, NotAvailable and
-OtherOperatingSystem. They are wrapped in LocalAuthenticationError class. You can
+There are 6 types of exceptions: PasscodeNotSet, NotEnrolled, NotAvailable, OtherOperatingSystem, LockedOut and PermanentlyLockedOut.
+They are wrapped in LocalAuthenticationError class. You can
catch the exception and handle them by different types. For example:
```dart
diff --git a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
index de75741..6e58527 100644
--- a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
+++ b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
@@ -1,7 +1,6 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
package io.flutter.plugins.localauth;
import android.annotation.SuppressLint;
@@ -25,6 +24,7 @@
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.fragment.app.FragmentActivity;
import io.flutter.plugin.common.MethodCall;
+import java.util.concurrent.Executors;
/**
* Authenticates the user with fingerprint and sends corresponding response back to Flutter.
@@ -32,6 +32,7 @@
* <p>One instance per call is generated to ensure readable separation of executable paths across
* method calls.
*/
+@SuppressWarnings("deprecation")
class AuthenticationHelper extends BiometricPrompt.AuthenticationCallback
implements Application.ActivityLifecycleCallbacks {
@@ -106,31 +107,37 @@
/** Start the fingerprint listener. */
private void start() {
activity.getApplication().registerActivityLifecycleCallbacks(this);
- new BiometricPrompt(activity, activity.getMainExecutor(), this).authenticate(promptInfo);
+ new BiometricPrompt(activity, Executors.newSingleThreadExecutor(), this)
+ .authenticate(promptInfo);
}
- /**
- * Stops the fingerprint listener.
- *
- * @param success If the authentication was successful.
- */
- private void stop(boolean success) {
+ /** Stops the fingerprint listener. */
+ private void stop(Boolean success) {
activity.getApplication().unregisterActivityLifecycleCallbacks(this);
- if (success) {
- completionHandler.onSuccess();
- } else {
- completionHandler.onFailure();
- }
+ if (success) completionHandler.onSuccess();
+ else completionHandler.onFailure();
}
+ @SuppressLint("SwitchIntDef")
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
- if (activityPaused && isAuthSticky) {
- return;
+ switch (errorCode) {
+ case BiometricPrompt.ERROR_LOCKOUT:
+ completionHandler.onError(
+ "LockedOut",
+ "The operation was canceled because the API is locked out due to too many attempts. This occurs after 5 failed attempts, and lasts for 30 seconds.");
+ break;
+ case BiometricPrompt.ERROR_LOCKOUT_PERMANENT:
+ completionHandler.onError(
+ "PermanentlyLockedOut",
+ "The operation was canceled because ERROR_LOCKOUT occurred too many times. Biometric authentication is disabled until the user unlocks with strong authentication (PIN/Pattern/Password)");
+ break;
+ case BiometricPrompt.ERROR_CANCELED:
+ if (activityPaused && isAuthSticky) {
+ return;
+ }
+ default:
}
-
- // Either the authentication got cancelled by user or we are not interested
- // in sticky auth, so return failure.
stop(false);
}
@@ -140,9 +147,7 @@
}
@Override
- public void onAuthenticationFailed() {
- stop(false);
- }
+ public void onAuthenticationFailed() {}
/**
* If the activity is paused, we keep track because fingerprint dialog simply returns "User
@@ -160,8 +165,9 @@
if (isAuthSticky) {
activityPaused = false;
final BiometricPrompt prompt =
- new BiometricPrompt(activity, activity.getMainExecutor(), this);
- // When activity is resuming, we cannot show the prompt right away. We need to post it to the UI queue.
+ new BiometricPrompt(activity, Executors.newSingleThreadExecutor(), this);
+ // When activity is resuming, we cannot show the prompt right away. We need to post it to the
+ // UI queue.
new Handler(Looper.myLooper())
.postDelayed(
new Runnable() {
diff --git a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
index 06dc7a8..9c5f8bd 100644
--- a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
+++ b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
@@ -17,6 +17,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
/** LocalAuthPlugin */
+@SuppressWarnings("deprecation")
public class LocalAuthPlugin implements MethodCallHandler {
private final Registrar registrar;
private final AtomicBoolean authInProgress = new AtomicBoolean(false);
diff --git a/packages/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties
index 019065d..5623933 100644
--- a/packages/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,3 +1,4 @@
+#Thu May 30 07:21:52 NPT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/local_auth/example/lib/main.dart b/packages/local_auth/example/lib/main.dart
index 6d0d9f7..feeb578 100644
--- a/packages/local_auth/example/lib/main.dart
+++ b/packages/local_auth/example/lib/main.dart
@@ -56,7 +56,7 @@
authenticated = await auth.authenticateWithBiometrics(
localizedReason: 'Scan your fingerprint to authenticate',
useErrorDialogs: true,
- stickyAuth: false);
+ stickyAuth: true);
} on PlatformException catch (e) {
print(e);
}
diff --git a/packages/local_auth/lib/error_codes.dart b/packages/local_auth/lib/error_codes.dart
index 2d226ed..3f6f298 100644
--- a/packages/local_auth/lib/error_codes.dart
+++ b/packages/local_auth/lib/error_codes.dart
@@ -17,3 +17,10 @@
/// Indicates the device operating system is not iOS or Android.
const String otherOperatingSystem = 'OtherOperatingSystem';
+
+/// Indicates the API lock out due to too many attempts.
+const String lockedOut = 'LockedOut';
+
+/// Indicates the API being disabled due to too many lock outs.
+/// Strong authentication like PIN/Pattern/Password is required to unlock.
+const String permanentlyLockedOut = 'PermanentlyLockedOut';
diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml
index 4e420a9..d1bd578 100644
--- a/packages/local_auth/pubspec.yaml
+++ b/packages/local_auth/pubspec.yaml
@@ -3,7 +3,7 @@
such as Fingerprint Reader and Touch ID.
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth
-version: 0.5.0
+version: 0.5.1
flutter:
plugin: