Skip to content

Commit 62ace3d

Browse files
[local_auth] Fix default deviceSupportsBiometrics
Fixes a regression from the federation of local_auth in the default method channel implementation of `deviceSupportsBiometrics`. It needs to use the unfiltered method channel list in order to work correctly, not the results of the Dart method that filterns out the sentinel for unenrolled devices. See flutter#5309 (comment) for context.
1 parent f29c36c commit 62ace3d

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

packages/local_auth/local_auth_platform_interface/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 1.0.2
2+
3+
* Fixes regression in the default method channel implementation of
4+
`deviceSupportsBiometrics` from federation that would cause it to return true
5+
only if something is enrolled.
6+
17
## 1.0.1
28

39
* Export externally used types from local_auth_platform_interface.dart directly.

packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class DefaultLocalAuthPlatform extends LocalAuthPlatform {
5757
biometrics.add(BiometricType.iris);
5858
break;
5959
case 'undefined':
60+
// Sentinel value for the case when nothing in enrolled, but hardware
61+
// support for biometrics is available.
6062
break;
6163
}
6264
}
@@ -65,7 +67,14 @@ class DefaultLocalAuthPlatform extends LocalAuthPlatform {
6567

6668
@override
6769
Future<bool> deviceSupportsBiometrics() async {
68-
return (await getEnrolledBiometrics()).isNotEmpty;
70+
final List<String> availableBiometrics =
71+
(await _channel.invokeListMethod<String>(
72+
'getAvailableBiometrics',
73+
)) ??
74+
<String>[];
75+
// If anything, including the 'undefined' sentinel, is returned, then there
76+
// is device support for biometrics.
77+
return availableBiometrics.isNotEmpty;
6978
}
7079

7180
@override

packages/local_auth/local_auth_platform_interface/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/l
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22
55
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
66
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
7-
version: 1.0.1
7+
version: 1.0.2
88

99
environment:
1010
sdk: ">=2.14.0 <3.0.0"
@@ -19,4 +19,4 @@ dependencies:
1919
dev_dependencies:
2020
flutter_test:
2121
sdk: flutter
22-
mockito: ^5.0.0
22+
mockito: ^5.0.0

packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ void main() {
1919
'plugins.flutter.io/local_auth',
2020
);
2121

22-
final List<MethodCall> log = <MethodCall>[];
22+
late List<MethodCall> log;
2323
late LocalAuthPlatform localAuthentication;
2424

25+
setUp(() async {
26+
log = <MethodCall>[];
27+
});
28+
2529
test(
2630
'DefaultLocalAuthPlatform is registered as the default platform implementation',
2731
() async {
@@ -32,10 +36,9 @@ void main() {
3236
test('getAvailableBiometrics', () async {
3337
channel.setMockMethodCallHandler((MethodCall methodCall) {
3438
log.add(methodCall);
35-
return Future<dynamic>.value(<BiometricType>[]);
39+
return Future<dynamic>.value(<String>[]);
3640
});
3741
localAuthentication = DefaultLocalAuthPlatform();
38-
log.clear();
3942
await localAuthentication.getEnrolledBiometrics();
4043
expect(
4144
log,
@@ -45,14 +48,36 @@ void main() {
4548
);
4649
});
4750

51+
test('deviceSupportsBiometrics handles special sentinal value', () async {
52+
// The pre-federation implementation of the platform channels, which the
53+
// default implementation retains compatibility with for the benefit of any
54+
// existing unendorsed implementations, used 'undefined' as a special
55+
// return value from `getAvailableBiometrics` to indicate that nothing was
56+
// enrolled, but that the hardware does support biometrics.
57+
channel.setMockMethodCallHandler((MethodCall methodCall) {
58+
log.add(methodCall);
59+
return Future<dynamic>.value(<String>['undefined']);
60+
});
61+
62+
localAuthentication = DefaultLocalAuthPlatform();
63+
final bool supportsBiometrics =
64+
await localAuthentication.deviceSupportsBiometrics();
65+
expect(supportsBiometrics, true);
66+
expect(
67+
log,
68+
<Matcher>[
69+
isMethodCall('getAvailableBiometrics', arguments: null),
70+
],
71+
);
72+
});
73+
4874
group('Boolean returning methods', () {
4975
setUp(() {
5076
channel.setMockMethodCallHandler((MethodCall methodCall) {
5177
log.add(methodCall);
5278
return Future<dynamic>.value(true);
5379
});
5480
localAuthentication = DefaultLocalAuthPlatform();
55-
log.clear();
5681
});
5782

5883
test('isDeviceSupported', () async {

0 commit comments

Comments
 (0)