docusign / mobile-android-sdk

The Official Docusign Android SDK for integrating e-signature & signing documents. Click the link below to view SDK API Documentation https://docusign.github.io/mobile-android-sdk/
Other
17 stars 12 forks source link

DocuSign SDK crashes when discarding Captive Signing #39

Closed tomaszshowoff closed 1 year ago

tomaszshowoff commented 1 year ago

Hi there,

I'm having a problem with DocuSign Android SDK v1.7.4 when using Captive Signing. The signing works perfectly but if I decide to discard the process by pressing the phone's back button, and tap "Discard" then my app crashes.

Device info: Samsung Galaxy A32, Android 12 (but happens on every device I tested it on).

DocuSign SDK is correctly initialised in the Application class that extends MultiDexApplication:

DocuSign.init(this, BuildConfig.DOCUSIGN_KEY, DSMode.DEBUG)

Then in one of the app's fragment i invoke the following:

val signingDelegate = DocuSign.getInstance().getSigningDelegate()
signingDelegate.launchCaptiveSigning(context, signingUrl, envelopeId, recipientId, object : DSCaptiveSigningListener {
                override fun onCancel(envelopeId: String, recipientId: String) {

                }
                override fun onRecipientSigningSuccess(envelopeId: String, recipientId: String) {

                }
                override fun onStart(envelopeId: String) {

                }
                override fun onSuccess(envelopeId: String) {
                    handleSuccess(envelopeId)
                }
                override fun onError(envelopeId: String?, exception: DSSigningException) {
                    showErrorAlert()
                }
                override fun onRecipientSigningError(envelopeId: String, recipientId: String, exception: DSSigningException) {
                    showErrorAlert()
                }
            })

The signing process works 100% and has been tested with various docs, etc. However, when a user decides to skip it by pressing a back button on their Android device the alert dialog is presented with "Discard Envelope?" message. After the discard button is tapped the DocuSign Activity goes through its final lifecycle steps and throws this:

022-11-24 08:54:45.843 23009-23009/com.noname.app.dev D/DocuSign_ClientLog: OnlineSigningFragment java.lang.IllegalArgumentException: Receiver not registered: com.docusign.androidsdk.ui.viewmodels.DownloadFileViewModel$downloadReceiver$1@40a6cfc at android.app.LoadedApk.forgetReceiverDispatcher(LoadedApk.java:1656) at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:1858) at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:786) at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:786) at com.docusign.androidsdk.ui.fragments.OnlineSigningFragment.onDestroy(OnlineSigningFragment.kt:368) at androidx.fragment.app.Fragment.performDestroy(Fragment.java:3219) at androidx.fragment.app.FragmentStateManager.destroy(FragmentStateManager.java:774) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:350) at androidx.fragment.app.SpecialEffectsController$FragmentStateManagerOperation.complete(SpecialEffectsController.java:745) at androidx.fragment.app.SpecialEffectsController$Operation.cancel(SpecialEffectsController.java:597) at androidx.fragment.app.SpecialEffectsController.forceCompleteAllOperations(SpecialEffectsController.java:332) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3132) at androidx.fragment.app.FragmentManager.dispatchDestroy(FragmentManager.java:3107) at androidx.fragment.app.FragmentController.dispatchDestroy(FragmentController.java:334) at androidx.fragment.app.FragmentActivity.onDestroy(FragmentActivity.java:330) at androidx.appcompat.app.AppCompatActivity.onDestroy(AppCompatActivity.java:278) at android.app.Activity.performDestroy(Activity.java:8571) at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1364) at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5937) at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5995) at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:47) at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2438) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

After that the DocuSign SDK Activity dismisses and hits onCancel callback in our app. Around the same time the crash happens:

2022-11-24 08:50:50.553 21505-21505/com.noname.app.dev D/DocuSign_OnlineSigningFragment: url: https://noname.com/?event=cancel 2022-11-24 08:50:51.216 21505-21939/com.noname.app.dev E/chromium: [ERROR:ssl_client_socket_impl.cc(982)] handshake failed; returned -1, SSL error code 1, net_error -200 2022-11-24 08:50:51.248 21505-21505/com.noname.app.dev E/DocuSign_OnlineSigningFragment: onReceivedSslError 2022-11-24 08:50:51.249 21505-21505/com.noname.app.dev D/AndroidRuntime: Shutting down VM

--------- beginning of crash 2022-11-24 08:50:51.257 21505-21505/com.noname.app.dev E/AndroidRuntime: FATAL EXCEPTION: main Process: com.noname.app.dev, PID: 21505 java.lang.IllegalStateException: Fragment OnlineSigningFragment{1496f01} (581fa327-5efc-4ad2-aa15-7800604de940) not attached to a context. at androidx.fragment.app.Fragment.requireContext(Fragment.java:900) at androidx.fragment.app.Fragment.getResources(Fragment.java:964) at androidx.fragment.app.Fragment.getString(Fragment.java:986) at com.docusign.androidsdk.ui.fragments.OnlineSigningFragment.onReceivedSslError(OnlineSigningFragment.kt:597) at com.docusign.androidsdk.ui.activities.DSWebViewClient.onReceivedSslError(DSWebActivity.kt:211) at ea.run(chromium-TrichromeWebViewGoogle.aab-stable-530410533:41) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135) 2022-11-24 08:50:51.814 21505-21505/com.noname.app.dev E/DocuSign_DSMTelemetryDelegate: Fragment OnlineSigningFragment{1496f01} (581fa327-5efc-4ad2-aa15-7800604de940) not attached to a context.

It looks like the DocuSign SDKs webview gets SslError and tries to interact but the context isn't there due to the whole SDK being in the process of closing.

Thanks, Tom

Yara-Khalil-DS commented 1 year ago

How do you handle the back button press in your app? Do you have a code for that? Are you using our SDK along with a code that you built?

Please use this link on how to handle back button press https://developer.android.com/reference/androidx/activity/OnBackPressedDispatcher

tomaszshowoff commented 1 year ago

Hey @Yara-Khalil-DS

Thanks for your response.

Let me answer your questions:

  1. How do you handle the back button press in your app?

I don't handle this onBackPressed at all. It happens inside the DocuSign SDK and it works the way it should. As I mentioned it triggers the alert dialog if a user wants to discard envelope.

  1. Are you using our SDK along with a code that you built?

I'm not sure if I understand your question, but I don't use any additional code related to DocuSign. In our app I just initialise the SDK and trigger captive signing as per your documentation.

The happy path works perfectly. Everything seems to work, really. Only if I decide to skip the signing flow (exit your SDK) by tapping on device's back button and tapping on "discard envelope" it crashes after 2-3s. From the logs I see it's due to the late callback inside your SDK's fragment that tries to interact with the UI when fragment is in its last stages of the lifecycle. It's in the OnlineSigningFragment.onReceivedSslError. The context is no longer there as the SDK is being dismissed and I guess the SDK tries to display some sort of Error Alert. These callbacks should have context null check added or have try-catch there imo.

Let me add that it doesn't happen in older version of your SDK 1.5.5, but this version seems to have problems with redirect urls during signing.

Thanks, Tom

naveentds commented 1 year ago

Hi Tom We are trying to reproduce this issue on our end and let you know once we have the fix for the issue.

Thanks Naveen

tomaszshowoff commented 1 year ago

Thanks @naveentds Really appreciate it.

naveentds commented 1 year ago

We are able to reproduce the issue. We will fix this issue in SDK v1.7.6 release.

tomaszshowoff commented 1 year ago

Great stuff! Thanks lads!

naveentds commented 1 year ago

Hi @tomaszshowoff , SDK v1.7.6 has been released today which fixed this issue.

tomaszshowoff commented 1 year ago

Hey @naveentds just tested the new version and unfortunately it looks like the issue is still there. Slightly different output, the first issue IllegalArgumentException: Receiver not registered: doesn't happen but the IllegalStateException: Fragment OnlineSigningFragment not attached to a context is still there and this crashes the app:

--------- beginning of crash 2022-12-06 19:26:05.337 12861-12861/com.noname.app.dev E/AndroidRuntime: FATAL EXCEPTION: main Process: com.noname.app.dev, PID: 12861 java.lang.IllegalStateException: Fragment OnlineSigningFragment{4cb73d9} (4096987b-31c8-4a8c-8466-8ea1dd76a04f) not attached to a context. at androidx.fragment.app.Fragment.requireContext(Fragment.java:900) at androidx.fragment.app.Fragment.getResources(Fragment.java:964) at androidx.fragment.app.Fragment.getString(Fragment.java:986) at com.docusign.androidsdk.ui.fragments.OnlineSigningFragment.onReceivedSslError(OnlineSigningFragment.kt:607) at com.docusign.androidsdk.ui.activities.DSWebViewClient.onReceivedSslError(DSWebActivity.kt:211) at aw.run(chromium-Monochrome.aab-stable-535907923:41) at android.os.Handler.handleCallback(Handler.java:790) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 2022-12-06 19:26:08.089 12861-12861/com.noname.app.dev E/DocuSign_DSMTelemetryDelegate: Fragment OnlineSigningFragment{4cb73d9} (4096987b-31c8-4a8c-8466-8ea1dd76a04f) not attached to a context. 2022-12-06 19:26:08.135 12861-12861/com.noname.app.dev E/DocuSign_SecureStoreMarshmallowAndAbove: Encrypted data is null

Can't see the codebase of the SDK but I'd suggest to add proper checks in OnlineSigningFragment.onReceivedSslError(). Maybe you're calling fragment's method getString() and this requires resources which requires a context that might not be there anymore. Add a context null check or a lifecycle condition before this or even a try-catch.

Thanks, Tom

naveentds commented 1 year ago

Hey @tomaszshowoff , we found other places where the issue could occur and will get that fixed in next release. It will be great if you can provide the following details:

This will help expediting the request to resolve the issue.

tomaszshowoff commented 1 year ago

Hi @naveentds, thanks for such a prompt response.

Indeed, considering Android's "unique" (ha!) Activity's & Fragment's lifecycle it's crucial to handle this everywhere.

I work for a software agency based in Ireland. However, this project belongs to one of our rather big clients based in the US. Go live is planned for Q1-Q2 of the next year. The user base is large... I mean thousands of accounts, all 3 platforms - web, iOS and Android (no problems with web and iOS SDKs though).

If you need any help with the issue don't hesitate to contact me.

Thanks, Tom

naveentds commented 1 year ago

Thanks for the info. It's not the problem with fragment's lifecycle. It's the issue where SSL error is triggered while activity is destroyed. We are looking into the issue and will provide the fix in the next release.

tomaszshowoff commented 1 year ago

It's the issue where SSL error is triggered while activity is destroyed.

^ Which means it's related to Android's Activity/Fragment lifecycle. The error is triggered after a user dismisses the SDK, when the lifecycle comes to the end (in this case: webview(?) -> fragment -> activity reaching onDestroy) it then tries to getString(). The context isn't there so... boom!

Thanks again for being so responsive and quick to fix the issue. I really appreciate it.

Thanks, Tom

naveentds commented 1 year ago

Hi Tom This issue has been fixed in latest SDK release v1.7.7. Can you try it out and let us know how it goes. Thanks.

tomaszshowoff commented 1 year ago

Hi Naveen,

That's great news! Yes, the SDK doesn't crash anymore when discarding envelope, so I can confirm the issue has been fixed.

Thanks, Tom