Error receiving broadcast Intent when using SmsRetriever

4.2k views Asked by At

When authenticating using Firebase Auth, I want to auto input the code that is received via SMS. I am able to receive SMS and go through auth process manually, but when I use SmsRetriever, the app crashes and then the bottom sheet dialog shows up. This is everything that that appears in Logcat:

java.lang.RuntimeException: Error receiving broadcast Intent { act=com.google.android.gms.auth.api.phone.SMS_RETRIEVED flg=0x200010 pkg=com.finca.bank (has extras) } in com.google.android.gms.internal.firebase-auth-api.zzvb@45fb8c5
        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1566)
        at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7562)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int java.lang.CharSequence.length()' on a null object reference
        at java.util.regex.Matcher.reset(Matcher.java:280)
        at java.util.regex.Matcher.<init>(Matcher.java:186)
        at java.util.regex.Pattern.matcher(Pattern.java:1034)
        at com.google.android.gms.internal.firebase-auth-api.zzvd.zzf(com.google.firebase:firebase-auth@@20.0.1:1)
        at com.google.android.gms.internal.firebase-auth-api.zzvb.onReceive(com.google.firebase:firebase-auth@@20.0.1:8)
        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1556)
        at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2) 

This is code inside my Fragment that receives SMS:

private val SMS_CONSENT_REQUEST = 2  // Set to an unused request code

    private val smsVerificationReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            try {
                if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
                    val extras = intent.extras
                    val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as Status

                    when (smsRetrieverStatus.statusCode) {
                        CommonStatusCodes.SUCCESS -> {
                            // Get consent intent
                            val consentIntent = extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT)
                            try {
                                // Start activity to show consent dialog to user, activity must be started in
                                // 5 minutes, otherwise you'll receive another TIMEOUT intent
                                startActivityForResult(consentIntent, SMS_CONSENT_REQUEST)
                            } catch (e: ActivityNotFoundException) {
                                // Handle the exception ...
                            }
                        }
                        CommonStatusCodes.TIMEOUT -> {
                            // Time out occurred, handle the error.
                        }
                    }
                }
            } catch (e: Exception) {
                Timber.e(e, "onReceive: ")
            }
        }
    }

    override fun onResume() {
        super.onResume()
        val task = SmsRetriever.getClient(requireActivity()).startSmsUserConsent(null)
        val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
        requireActivity().registerReceiver(smsVerificationReceiver, intentFilter)
    }

    override fun onPause() {
        super.onPause()
        requireActivity().unregisterReceiver(smsVerificationReceiver)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            // ...
            SMS_CONSENT_REQUEST ->
                // Obtain the phone number from the result
                if (resultCode == Activity.RESULT_OK && data != null) {
                    // Get SMS message content
                    val message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
                    // Extract one-time code from the message and complete verification
                    // `message` contains the entire text of the SMS message, so you will need
                    // to parse the string.
                    message?.let { presenter.parseSms(it) }
                    // send one time code to the server
                } else {
                    // Consent denied. User can type OTC manually.
                }
        }
    }

Interesting thing is, the progress goes successfully in rare cases and I don't know what it depends on. Also, everything goes well in debug mode if breakpoints are set in onReceive

3

There are 3 answers

1
Uchechukwu Nnabugwu On

To automatically verify phone numbers, you must implement both the client and server portions of the verification flow. You just implemented the client portion. To listen to SMS and auto input the OTP code you have to also implement the server side. A complete guide is found here Perform SMS Verification on a Server

2
Gabriel Diaz On

After several investigations, the root cause of this crash seems to be related to a conflict between Firebase Auth Instant Verification feature and SMS consent API.

In order to fix it you have two options:

  1. Remove SMS consent API and only rely on Instant Verification
  2. Use SMS Consent API and disable Instant Verification by setting timeout as 0. https://firebase.google.com/docs/reference/android/com/google/firebase/auth/PhoneAuthOptions.Builder#setTimeout(java.lang.Long,%20java.util.concurrent.TimeUnit))

Hope this provides some clarity around this weird issue.

0
Om kumar Yadav On

Got the same error while Integrating the SMS consent API. After Searching and modification got a solution. In the below-attached image, you can see the setTimeout method consists of a parameter called timeout, change that parameter value from 60L to 0L. For more info about the method click here!

Just change that value from 60L to 0L.