Baseflow / Baseflow/flutter-permission-handler
[Bug]: PhonePermissionStrategy.checkServiceStatus calls completionHandler twice, causing crashes
- Dominant language
- Dart
- Stars
- 2.2k
- Forks
- 970
- Avg merge
- 14h 22m
- Merged PRs (30d)
- 2
Description
### Please check the following before submitting a new issue.
- [x] I have searched the [existing issues](https://github.com/baseflow/flutter-permission-handler/issues).
- [x] I have carefully [read the documentation](https://github.com/Baseflow/flutter-permission-handler/blob/main/permission_handler/README.md) and verified I have added the required platform specific configuration.
### Please select affected platform(s)
- [ ] Android
- [x] iOS
- [ ] Windows
### Steps to reproduce
1. Call `Permission.phone.serviceStatus` (or anything that triggers `checkServiceStatus` for the phone permission) on a device/simulator where `[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]]` returns `NO` (e.g. iPad, or a device without telephony capability).
2. In `PhonePermissionStrategy.m`, the `if (![app canOpenURL:telURL])` branch calls `completionHandler(ServiceStatusNotApplicable)` but does not `return`, so execution falls through and calls `completionHandler(...)` a second time with the enabled/disabled status.
3. The Flutter method channel result callback backing `completionHandler` is only expected to be invoked once; the second invocation raises an exception/crash.
We noticed this because `checkServiceStatus`/`PhonePermissionStrategy` shows up as one of the **top crashes in our production consumer app in Firebase Crashlytics**, affecting users on devices where `canOpenURL:` returns `NO` for `tel://`.
### Expected results
`completionHandler` should be invoked exactly once per call to `checkServiceStatus`.
### Actual results
`completionHandler` is invoked twice when the device cannot open `tel://` URLs, which can crash the app.
### Code sample
Code sample
```objc
// permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/strategies/PhonePermissionStrategy.m
- (void)checkServiceStatus:(PermissionGroup)permission completionHandler:(ServiceStatusHandler)completionHandler {
UIApplication *app = [UIApplication sharedApplication];
NSURL *telURL = [NSURL URLWithString:@"tel://"];
if (![app canOpenURL:telURL]) {
completionHandler(ServiceStatusNotApplicable);
// missing `return;` here — falls through and calls completionHandler again below
}
completionHandler([self canDevicePlaceAPhoneCall] ? ServiceStatusEnabled : ServiceStatusDisabled);
}
```
### Screenshots or video
Screenshots or video demonstration
Crashlytics shows this as a top crash in our production app; no visual repro available since it's a native-side double-callback crash.
### Version
9.6.1 (permission_handler_apple)
### Flutter Doctor output
Doctor output
```console
N/A — issue is isolated to the native iOS implementation and reproducible by code inspection, independent of Flutter/toolchain version.
```
Contributor guide
Research direction
Open permission_handler_apple/ios/permission_handler_apple/Sources/permission_handler_apple/strategies/PhonePermissionStrategy.m and start at checkServiceStatus:completionHandler:. Inspect the canOpenURL:tel:// branch and the following status check. Done means the completionHandler is invoked exactly once when tel:// cannot be opened, without the iOS method-channel crash.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- objective-c
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 1/5
- Estimated time
- Under an hour
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100