'Flutter Android receiver not working after restart

In flutter app the receivers for BOOT_COMPLETED and SMS_RECEIVED not work when the real device is restarted but work fine in 'android emulator' and the real device before the restart

permission for RECEIVED_SMS

<uses-permission android:name="android.permission.RECEIVE_SMS"/>

permission for BOOT_COMPLETED

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Class for RECEIVE_SMS is SmsReceiver

Class for BOOT_COMPLETED is BootReceiver

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="ai.torquevision.encredeble"
    android:installLocation="internalOnly">
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.BROADCAST_SMS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

   <application
        android:usesCleartextTraffic="true"
        android:label="encredeble"
        android:icon="@drawable/logo">
       <uses-library android:name="org.apache.http.legacy" android:required="false"/>
        <activity
            android:name="ai.torquevision.encredeble.MainActivity"
            android:supportsPictureInPicture="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <!-- Displays an Android View that continues showing the launch screen
                 Drawable until Flutter paints its first frame, then this splash
                 screen fades out. A splash screen is useful to avoid any visual
                 gap between the end of Android's launch screen and the painting of
                 Flutter's first frame. -->
            <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />


        <receiver android:name=".Utils.BootReceiver" >
           <intent-filter >
               <action android:name="android.intent.action.BOOT_COMPLETED" />
               <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
               <action android:name="android.intent.action.QUICKBOOT_POWERON" />
               <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
           </intent-filter>
       </receiver>
       <receiver android:name=".Utils.SmsReceiver" android:permission="android.permission.RECEIVE_SMS" android:enabled="true" android:exported="true" >
           <intent-filter>
               <action android:name="android.provider.Telephony.SMS_RECEIVED" />

           </intent-filter>

       </receiver>

    </application>
</manifest>

BootReceiver.kt

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import kotlinx.coroutines.delay
import java.lang.Exception

class BootReceiver: BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        
        Log.d("encredeble", "encredeble context: " + context?.toString() + "intent: " + intent?.action);
        Log.i("encredeble", "encredeble context: " + context?.toString() + "intent: " + intent?.action);
        if(context == null || intent == null){
            return;
        }
        try{
            NotificationHandler.notify(context, "Restart action", "action: " + intent?.action);

            Log.w("boot_broadcast_poc", "=======================> starting service...")
            if(intent.action.equals("android.intent.action.BOOT_COMPLETED") || intent.action.equals("android.intent.action.QUICKBOOT_POWERON") || intent.action.equals("android.intent.action.LOCKED_BOOT_COMPLETED")){
                NotificationHandler.notify(context, "Device restarted", "BOOT COMPLETED");
            }
        }
        catch (err: Exception){
            NotificationHandler.notify(context, "Restart error", "Error: " + err.message)
        }
    }
}

SmsReceiver.kt

import ai.torquevision.encredeble.MainActivity
import ai.torquevision.encredeble.R
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Telephony
import androidx.core.app.NotificationCompat
import android.telephony.SmsMessage
import android.util.Log
import androidx.core.app.NotificationManagerCompat
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.plugin.common.MethodChannel
import org.json.JSONArray
import org.json.JSONObject

class SmsReceiver: BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {

        Log.d("Sms receive", "===================================> SMS Received")

        if(intent ==null) {
            return
        }

        if(Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.action)){
            var msg = ""
            var msgObj: JSONArray = JSONArray()
            for (smsMessage in Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
                val messageBody = smsMessage.messageBody

                Log.d("msg body", "message body: " + messageBody)
                msg = msg + messageBody
                var map: JSONObject = JSONObject()
                if(msgObj.length() == 0){
                    map.put("body", messageBody)
                    map.put("originatingAddress", smsMessage.originatingAddress)
                    map.put("displayOriginatingAddress", smsMessage.displayOriginatingAddress)
                    map.put("displayMessageBody", smsMessage.displayMessageBody)
                    msgObj.put(0, map)
                } else {
                    val expMap = msgObj.get(msgObj.length() - 1) as JSONObject
                    if(expMap["originatingAddress"] == smsMessage.originatingAddress || expMap["displayOriginatingAddress"] == smsMessage.displayOriginatingAddress){
                        map = expMap
                        map.put("body", map["body"].toString() + messageBody)
                        map.put("displayMessageBody", map["displayMessageBody"].toString() + smsMessage.displayMessageBody)
                        msgObj.put(msgObj.length() - 1, map)
                    }else{
                        map.put("body", messageBody)
                        map.put("originatingAddress", smsMessage.originatingAddress)
                        map.put("displayOriginatingAddress", smsMessage.displayOriginatingAddress)
                        map.put("displayMessageBody", smsMessage.displayMessageBody)
                        msgObj.put(map)
                    }
                }
            }
            if(context != null){
                SmsMethodChannel.invoke(context, msgObj.toString());
            }
        }
    }
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source