Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reply already submitted crash #513

Closed
1 of 2 tasks
humazed opened this issue Sep 4, 2020 · 16 comments
Closed
1 of 2 tasks

Reply already submitted crash #513

humazed opened this issue Sep 4, 2020 · 16 comments
Labels
platform: android Issue is related to the Android platform. status: closed (missing info) Indicates the issue was automatically closed due to a lack of information. status: in progress Indicates that this issue is currently being worked on. type: bug Something isn't working

Comments

@humazed
Copy link

humazed commented Sep 4, 2020

🐛 Bug Report

In the sample app LastKnownLocationExampleWidget deny the permission close and reopen the app I get the following exception and the app crashes.

E/AndroidRuntime(12916): Process: com.baseflow.geolocator_example, PID: 12916
E/AndroidRuntime(12916): java.lang.IllegalStateException: Reply already submitted
E/AndroidRuntime(12916): 	at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:139)
E/AndroidRuntime(12916): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:235)
E/AndroidRuntime(12916): 	at com.baseflow.geolocator.MethodCallHandlerImpl.lambda$onGetLastKnownPosition$2(MethodCallHandlerImpl.java:149)
E/AndroidRuntime(12916): 	at com.baseflow.geolocator.-$$Lambda$MethodCallHandlerImpl$tyxDngVVF7PcImb4PihgQ0FJ8po.onPositionChanged(Unknown Source:2)
E/AndroidRuntime(12916): 	at com.baseflow.geolocator.location.-$$Lambda$FyNSKWRJ6K8pfIKflyK9zilTOk0.onSuccess(Unknown Source:4)
E/AndroidRuntime(12916): 	at com.google.android.gms.tasks.zzn.run(Unknown Source:4)
E/AndroidRuntime(12916): 	at android.os.Handler.handleCallback(Handler.java:883)
E/AndroidRuntime(12916): 	at android.os.Handler.dispatchMessage(Handler.java:100)
E/AndroidRuntime(12916): 	at android.os.Looper.loop(Looper.java:214)
E/AndroidRuntime(12916): 	at android.app.ActivityThread.main(ActivityThread.java:7356)
E/AndroidRuntime(12916): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(12916): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
E/AndroidRuntime(12916): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Version: 6.0.0+3

Platform:

  • 📱 iOS
  • 🤖 Android
@humazed humazed changed the title Reply already submitted Reply already submitted crash Sep 5, 2020
@mvanbeusekom
Copy link
Member

mvanbeusekom commented Sep 6, 2020

@humazed thanks for the bug report. I kinda know where the error is coming from (and I can make it go away), but I cannot really reproduce it and according to the stack traces you send me this error should not happen in the first place.

So I am wondering if you can help me figure out what is causing this issue so I can solve it in the correct manner. So to give you some details, here is what I think is happening (based on the stack trace information and the steps you describe):

Step 1:

  1. You start the example App using the debugger (for the first time) and it will ask you for permissions;
  2. You deny permissions and the App shows you the message "User denied permission to access the device's location.";
  3. At this point you stop debugging and the App will close.

So far all is good.

Step 2:

  1. You start debugging the App again and the App will start;
  2. At this point the App should detect that permissions were denied and should show a popup again asking for permission (this is what's happening to me), however for you the App crashes with the above stack trace.

The crash happens because the App somehow enters the Java onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) without having showed the permission popup and the permissions and grantResults arrays are empty. In my previous update (6.0.0+3) I detect that these parameters are empty and try to send back that permissions are denied (see code snippet below):

    @Override
    public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode != PERMISSION_REQUEST_CODE || permissions.length == 0 || grantResults.length == 0) {
            if (this.resultCallback != null) {
                this.resultCallback.onResult(LocationPermission.denied);
            }
            return false;
        }

        ....
  }

As you would guess sending back the permission status in this situation will trigger the exception you listed above, because information has already been send on the channel. This is strange because from the way I understand the steps you take this would be a fresh channel and really should be the first time information is send back on the channel.

What is also strange is that the onRequestPermissionResults is being called with empty permissions and grantResults parameters. I can prevent the exception from occurring by updating the code to:

    @Override
    public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode != PERMISSION_REQUEST_CODE) {
            if (this.resultCallback != null) {
                this.resultCallback.onResult(LocationPermission.denied);
            }
            return false;
        }

        if (permissions.length == 0 || grantResults.length == 0) {
            return false;
        }

        ....
  }

But I am afraid this will mask the real problem that is occurring. Can you help me finding the real problem by checking the following:

  1. Are these indeed the steps you took?
  2. Did you modify anything on the example code?
  3. Could you provide some information on the Android version and device you are using to run the example application?

Your help would be greatly appreciated.

@mvanbeusekom mvanbeusekom added platform: android Issue is related to the Android platform. status: in progress Indicates that this issue is currently being worked on. status: needs more info We need more information before we can continue work on this issue. type: bug Something isn't working labels Sep 6, 2020
@humazed
Copy link
Author

humazed commented Sep 6, 2020

Step 1:
run the app and deny permission.
Step 2:
Click the back button.
Step 3:
open the app and see the crash.

will send you a screen recording.

@humazed
Copy link
Author

humazed commented Sep 6, 2020

recording of the bug
https://drive.google.com/file/d/1dh2dIdiUWcOEysYX1Fh37kuCAc3-gTCl/view?usp=sharing

device:
pixel_2_XL_API_28 emulator

@NindoK
Copy link

NindoK commented Sep 6, 2020

Hi,
I'm experiencing the same problem.

The steps to reproduce mine is simply: Allow location, then allow file and my apps crashed

Device:
Pixel_3a

D/AndroidRuntime(31159): Shutting down VM
E/AndroidRuntime(31159): FATAL EXCEPTION: main
E/AndroidRuntime(31159): Process: rest.clinimetric, PID: 31159
E/AndroidRuntime(31159): java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=35569, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {rest.clinimetric/rest.clinimetric.MainActivity}: java.lang.IllegalStateException: Reply already submitted
E/AndroidRuntime(31159): 	at android.app.ActivityThread.deliverResults(ActivityThread.java:4845)
E/AndroidRuntime(31159): 	at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886)
E/AndroidRuntime(31159): 	at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
E/AndroidRuntime(31159): 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
E/AndroidRuntime(31159): 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
E/AndroidRuntime(31159): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
E/AndroidRuntime(31159): 	at android.os.Handler.dispatchMessage(Handler.java:107)
E/AndroidRuntime(31159): 	at android.os.Looper.loop(Looper.java:214)
E/AndroidRuntime(31159): 	at android.app.ActivityThread.main(ActivityThread.java:7356)
E/AndroidRuntime(31159): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(31159): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
E/AndroidRuntime(31159): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
E/AndroidRuntime(31159): Caused by: java.lang.IllegalStateException: Reply already submitted
E/AndroidRuntime(31159): 	at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:139)
E/AndroidRuntime(31159): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java:235)
E/AndroidRuntime(31159): 	at com.baseflow.geolocator.MethodCallHandlerImpl.lambda$onRequestPermission$0(MethodCallHandlerImpl.java:133)
E/AndroidRuntime(31159): 	at com.baseflow.geolocator.-$$Lambda$MethodCallHandlerImpl$HgwoSKdIeyav7j8ZZJufnb77Lqo.onResult(Unknown Source:2)
E/AndroidRuntime(31159): 	at com.baseflow.geolocator.permission.PermissionManager.onRequestPermissionsResult(PermissionManager.java:103)
E/AndroidRuntime(31159): 	at io.flutter.embedding.engine.FlutterEnginePluginRegistry$FlutterEngineActivityPluginBinding.onRequestPermissionsResult(FlutterEnginePluginRegistry.java:664)
E/AndroidRuntime(31159): 	at io.flutter.embedding.engine.FlutterEnginePluginRegistry.onRequestPermissionsResult(FlutterEnginePluginRegistry.java:364)
E/AndroidRuntime(31159): 	at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onRequestPermissionsResult(FlutterActivityAndFragmentDelegate.java:573)
E/AndroidRuntime(31159): 	at io.flutter.embedding.android.FlutterActivity.onRequestPermissionsResult(FlutterActivity.java:602)
E/AndroidRuntime(31159): 	at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:8289)
E/AndroidRuntime(31159): 	at android.app.Activity.dispatchActivityResult(Activity.java:8139)
E/AndroidRuntime(31159): 	at android.app.ActivityThread.deliverResults(ActivityThread.java:4838)
E/AndroidRuntime(31159): 	... 11 more
I/Process (31159): Sending signal. PID: 31159 SIG: 9
Lost connection to device.

@mvanbeusekom
Copy link
Member

Hi @humazed, thank you for providing the reproduction steps, I think I have figured out the real problem that is occurring. I believe the error is happening because the permissions are requested multiple times while the first call is still being handled, which triggers a warning on the Android activity "W/Activity(24060): Can request only one set of permissions at a time", and results in the onRequestPermissionsResult being called with the empty permissions and grantResults parameters. This can for example happen when we directly call the getLastKnownPosition from the Flutter build method which can be triggered multiple times. Or as an example, I can reproduce the problem consistently using the following example code:

import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Geolocator #396'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState 
  extends State<MyHomePage>
  with WidgetsBindingObserver {

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeDependencies() {
    getCurrentPosition(forceAndroidLocationManager: true).then((position) => print('Start: $position'));
    super.didChangeDependencies();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print(state);
    getCurrentPosition(forceAndroidLocationManager: true).then((position) => print('Resume: $position'));
  }

  @override
  Widget build(BuildContext context) {
   return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),// This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
}

The problem is that this only happens when you need to request for permissions. Remember that the getLastKnownPosition, getCurrentPosition and getPositionStream methods will automatically request permissions when applicable. The dilemma is that once you have already requested permission it is just fine to call the getLastKnownPosition (or one of the other position methods) multiple times. So I can handle this in three ways:

  1. Add a check to the onRequestPermissionResults which ignores the call when the permissions and grantResults parameters are empty;
  2. Make sure you can only call the getLastKnownPosition, getCurrentPosition and getPositionStream methods once if their is already an existing request running;
  3. Remove the automatic permission resolution from the getLastKnownPosition, getCurrentPosition and getPositionStream methods and leave it up to the developers to handle permissions.

At the moment I am feeling the most for the first solution, since the others will have other disadvantages or are really introducing huge breaking changes. I will implement the first solution now to make sure we have a workable solution. But I am interested in how you would feel about the different options?

@mvanbeusekom
Copy link
Member

Hi @humazed, @NindoK, I have just released version 6.0.0+4 of the geolocator plugin which should solve this issue using the 1st option I described in the comment above.

Thanks to the information from @humazed I was able to reproduce the problem and confirm that this change should fix the issue. You might however still see the warning "W/Activity(24060): Can request only one set of permissions at a time" which you can ignore.

Let me know if this will solve the issue for you, in the meantime I will keep this issue open.

@mvanbeusekom mvanbeusekom added this to the geolocator_v6.0.0+4 milestone Sep 7, 2020
@NindoK
Copy link

NindoK commented Sep 8, 2020

Thanks it fixed it!

@mvanbeusekom
Copy link
Member

Hi @NindoK, thank you for testing and confirming the problem is fixed. Will close the issue now.

@humazed
Copy link
Author

humazed commented Sep 9, 2020 via email

@xJon
Copy link

xJon commented Sep 17, 2020

I am still experiencing this crash in 6.0.0+4, as the package clashes with a different permission handler's request. This makes sense according to the explanation here. Do you think it'd be possible to add a manual option to disable the automatic permission resolution of the methods?
Thank you!

@mvanbeusekom
Copy link
Member

Hi @xJon, could you maybe provide a stack trace or some logging that shows the problem that is occurring? Can you maybe provide some information on the other plugin you are using that you mentioned is clashing with the geolocator?

We have seen similar issues with other plugins that don't handle the Android onRequestPermissionResult call back correctly, I am thinking this might be the problem here but I would like to investigate and see if I can help out.

@xJon
Copy link

xJon commented Sep 17, 2020

Hi @mvanbeusekom, yes this is my log:

    java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=109, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.gather/com.gather.MainActivity}: java.lang.IllegalStateException: Reply already submitted
        at android.app.ActivityThread.deliverResults(ActivityThread.java:3699)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
        at android.app.ActivityThread.-wrap16(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5417)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
     Caused by: java.lang.IllegalStateException: Reply already submitted
        at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:139)
        at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.error(MethodChannel.java:240)
        at com.baseflow.geolocator.MethodCallHandlerImpl.lambda$onGetLastKnownPosition$3(MethodCallHandlerImpl.java:150)
        at com.baseflow.geolocator.-$$Lambda$MethodCallHandlerImpl$OCefduhIkqAV59oMDlIQqmk7z8g.onError(lambda)
        at com.baseflow.geolocator.location.GeolocationManager.lambda$handlePermissions$2(GeolocationManager.java:128)
        at com.baseflow.geolocator.location.-$$Lambda$GeolocationManager$boCQNh-1HzLRO8yyNdeq6cg70Yc.onResult(lambda)
        at com.baseflow.geolocator.permission.PermissionManager.onRequestPermissionsResult(PermissionManager.java:161)
        at io.flutter.embedding.engine.FlutterEnginePluginRegistry$FlutterEngineActivityPluginBinding.onRequestPermissionsResult(FlutterEnginePluginRegistry.java:664)
        at io.flutter.embedding.engine.FlutterEnginePluginRegistry.onRequestPermissionsResult(FlutterEnginePluginRegistry.java:364)
        at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onRequestPermissionsResult(FlutterActivityAndFragmentDelegate.java:573)
        at io.flutter.embedding.android.FlutterActivity.onRequestPermissionsResult(FlutterActivity.java:602)
        at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6553)
        at android.app.Activity.dispatchActivityResult(Activity.java:6432)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
        at android.app.ActivityThread.-wrap16(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:148) 
        at android.app.ActivityThread.main(ActivityThread.java:5417) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

I made sure I am using the latest version of the package.
The other package I am using is permission_handler.

It is odd though, as I am unable to reproduce this issue continuously; I re-added now the code which seemed to cause the issue, and I don't experience the crash.
I would also like to point out that having a feature to disable the automatic permission resolution would be useful for apps which prefer to request certain permissions when first loading / on the splash screen, or when calling getCurrentPosition() and then getLastKnownPosition() (which would otherwise send 2 separate permission requests).

@mvanbeusekom
Copy link
Member

There should be no need to disable the automatic permission resolution since the geolocator will first check if permissions have been granted / disabled before taking any action and request permission. Meaning if you already took care of handling permissions the geolocator would simply skip the permission resolution.

Could it be that you were running an older version of the geolocator plugin and re-adding the code made sure you updated to the latest version of the geolocator? Maybe you ran the flutter pub upgrade in between?

@xJon
Copy link

xJon commented Sep 17, 2020

I could've swore I ran flutter pub upgrade beforehand too, but maybe I didn't. I will update you if the issue arises again.

I don't fully understand what you mean with the automatic permission resolution, as currently if the user denies my first request when the app launches (using the permission_handler package), they will receive another request when getCurrentPosition() runs and another one when getLastKnownPosition() runs (assuming they deny all of them).

@ianendboss
Copy link

ianendboss commented Sep 23, 2020

@xJon What he means is that if a user has "always allowed" permissions or "denied (dont ask me again)" permission, the plugin will not make a request to Android to display the permissions dialog.

The reason you user gets a request is probably because they allowed or denied their permission temporarily (or "just this once").

@xJon
Copy link

xJon commented Sep 23, 2020

@ianendboss I see. Thank you for the explanation!

@mvanbeusekom mvanbeusekom added status: closed (missing info) Indicates the issue was automatically closed due to a lack of information. and removed status: needs more info We need more information before we can continue work on this issue. labels Jul 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform: android Issue is related to the Android platform. status: closed (missing info) Indicates the issue was automatically closed due to a lack of information. status: in progress Indicates that this issue is currently being worked on. type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants