Android – Listen For Incoming SMS Messages

AndroidSmsAndroid Service

Android Problem Overview


I am trying to create an application for monitoring incoming SMS messages, and launch a program via incoming SMS, also it should read the content from the SMS.

Workflow:

  • SMS sent to Android device
  • self executable Application
  • Read the SMS information

Android Solutions


Solution 1 - Android

public class SmsListener extends BroadcastReceiver{

    private SharedPreferences preferences;
   
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub

        if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
            Bundle bundle = intent.getExtras();           //---get the SMS message passed in---
            SmsMessage[] msgs = null;
            String msg_from;
            if (bundle != null){
                //---retrieve the SMS message received---
                try{
                    Object[] pdus = (Object[]) bundle.get("pdus");
                    msgs = new SmsMessage[pdus.length];
                    for(int i=0; i<msgs.length; i++){
                        msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                        msg_from = msgs[i].getOriginatingAddress();
                        String msgBody = msgs[i].getMessageBody();
                    }
                }catch(Exception e){
//                            Log.d("Exception caught",e.getMessage());
                }
            }
        }
    }
}

Note: In your manifest file add the BroadcastReceiver-

<receiver android:name=".listener.SmsListener">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Add this permission:

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

Solution 2 - Android

Note that on some devices your code wont work without android:priority="1000" in intent filter:

<receiver android:name=".listener.SmsListener">
    <intent-filter android:priority="1000">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

And here is some optimizations:

public class SmsListener extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
                String messageBody = smsMessage.getMessageBody();
            }
        }
    }
}

Note:
The value must be an integer, such as "100". Higher numbers have a higher priority. The default value is 0. The value must be greater than -1000 and less than 1000.

Here's a link.

Solution 3 - Android

@Mike M. and I found an issue with the accepted answer (see our comments):

Basically, there is no point in going through the for loop if we are not concatenating the multipart message each time:

for (int i = 0; i < msgs.length; i++) {
    msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
    msg_from = msgs[i].getOriginatingAddress();
    String msgBody = msgs[i].getMessageBody();
}

Notice that we just set msgBody to the string value of the respective part of the message no matter what index we are on, which makes the entire point of looping through the different parts of the SMS message useless, since it will just be set to the very last index value. Instead we should use +=, or as Mike noted, StringBuilder:

All in all, here is what my SMS receiving code looks like:

if (myBundle != null) {
    Object[] pdus = (Object[]) myBundle.get("pdus"); // pdus is key for SMS in bundle

    //Object [] pdus now contains array of bytes
    messages = new SmsMessage[pdus.length];
    for (int i = 0; i < messages.length; i++) {
         messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); //Returns one message, in array because multipart message due to sms max char
         Message += messages[i].getMessageBody(); // Using +=, because need to add multipart from before also
    }
    
    contactNumber = messages[0].getOriginatingAddress(); //This could also be inside the loop, but there is no need
}

Just putting this answer out there in case anyone else has the same confusion.

Solution 4 - Android

This is what i used!

public class SMSListener extends BroadcastReceiver {

    // Get the object of SmsManager
    final SmsManager sms = SmsManager.getDefault();
String mobile,body;

    public void onReceive(Context context, Intent intent) {

        // Retrieves a map of extended data from the intent.
        final Bundle bundle = intent.getExtras();

        try {

            if (bundle != null) {

                final Object[] pdusObj = (Object[]) bundle.get("pdus");

                for (int i = 0; i < pdusObj.length; i++) {

                    SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                    String phoneNumber = currentMessage.getDisplayOriginatingAddress();

                    String senderNum = phoneNumber;
                    String message = currentMessage.getDisplayMessageBody();
                     mobile=senderNum.replaceAll("\\s","");
                     body=message.replaceAll("\\s","+");

                   
                    Log.i("SmsReceiver", "senderNum: "+ senderNum + "; message: " + body);


                    // Show Alert
                    int duration = Toast.LENGTH_LONG;
                    Toast toast = Toast.makeText(context,
                            "senderNum: "+ mobile+ ", message: " + message, duration);
                    toast.show();

                } // end for loop
            } // bundle is null

        } catch (Exception e) {
            Log.e("SmsReceiver", "Exception smsReceiver" +e);

        }
    }
}

Solution 5 - Android

The accepted answer is correct and works on older versions of Android where Android OS asks for permissions at the app install, However on newer versions Android it doesn't work straight away because newer Android OS asks for permissions during runtime when the app requires that feature. Therefore in order to receive SMS on newer versions of Android using technique mentioned in accepted answer programmer must also implement code that will check and ask for permissions from user during runtime. In this case permissions checking functionality/code can be implemented in onCreate() of app's first activity. Just copy and paste following two methods in your first activity and call checkForSmsReceivePermissions() method at the end of onCreate().

    void checkForSmsReceivePermissions(){
    // Check if App already has permissions for receiving SMS
    if(ContextCompat.checkSelfPermission(getBaseContext(), "android.permission.RECEIVE_SMS") == PackageManager.PERMISSION_GRANTED) {
        // App has permissions to listen incoming SMS messages
        Log.d("adnan", "checkForSmsReceivePermissions: Allowed");
    } else {
        // App don't have permissions to listen incoming SMS messages
        Log.d("adnan", "checkForSmsReceivePermissions: Denied");
        
        // Request permissions from user 
        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECEIVE_SMS}, 43391);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == 43391){
        if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            Log.d("adnan", "Sms Receive Permissions granted");
        } else {
            Log.d("adnan", "Sms Receive Permissions denied");
        }
    }
}

Solution 6 - Android

If someone referring how to do the same feature (reading OTP using received SMS) on Xamarin Android like me :

  1. Add this code to your AndroidManifest.xml file :

     <receiver android:name=".listener.BroadcastReveiverOTP">
     <intent-filter>
         <action android:name="android.provider.Telephony.SMS_RECEIVED" />
     </intent-filter>
     </receiver>
     <uses-permission android:name="android.permission.RECEIVE_SMS" />
     <uses-permission android:name="android.permission.BROADCAST_SMS" />
     <uses-permission android:name="android.permission.READ_SMS" />
    
  2. Then create your BroadcastReveiver class in your Android Project.

     [BroadcastReceiver(Enabled = true)] [IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)] 
     public class BroadcastReveiverOTP : BroadcastReceiver {
             public static readonly string INTENT_ACTION = "android.provider.Telephony.SMS_RECEIVED";
    
             protected string message, address = string.Empty;
    
             public override void OnReceive(Context context, Intent intent)
             {
                 if (intent.HasExtra("pdus"))
                 {
                     var smsArray = (Java.Lang.Object[])intent.Extras.Get("pdus");
                     foreach (var item in smsArray)
                     {
                         var sms = SmsMessage.CreateFromPdu((byte[])item);
                         address = sms.OriginatingAddress;
                         if (address.Equals("NotifyDEMO"))
                         {
                             message = sms.MessageBody;
                             string[] pin = message.Split(' ');
                             if (!string.IsNullOrWhiteSpace(pin[0]))
                             { 
                                     // NOTE : Here I'm passing received OTP to Portable Project using MessagingCenter. So I can display the OTP in the relevant entry field.
                                     MessagingCenter.Send<object, string>(this,MessengerKeys.OnBroadcastReceived, pin[0]);
                             }
                             }
                     }
                 }
             }
     }
    
  3. Register this BroadcastReceiver class in your MainActivity class on Android Project:

     public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity {
     
             // Initialize your class
             private BroadcastReveiverOTP _receiver = new BroadcastReveiverOTP ();
         
             protected override void OnCreate(Bundle bundle) { 
                     base.OnCreate(bundle);
    
                     global::Xamarin.Forms.Forms.Init(this, bundle);
                     LoadApplication(new App());
    
                     // Register your receiver :  RegisterReceiver(_receiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
         
             }
     }
    

Solution 7 - Android

In case you want to handle intent on opened activity, you can use PendintIntent (Complete steps below):

public class SMSReciver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final Bundle bundle = intent.getExtras();
        try {
            if (bundle != null) {
                final Object[] pdusObj = (Object[]) bundle.get("pdus");
                for (int i = 0; i < pdusObj.length; i++) {
                    SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                    String phoneNumber = currentMessage.getDisplayOriginatingAddress();
                    String senderNum = phoneNumber;
                    String message = currentMessage.getDisplayMessageBody();
                    try {
                        if (senderNum.contains("MOB_NUMBER")) {
                            Toast.makeText(context,"",Toast.LENGTH_SHORT).show();

                            Intent intentCall = new Intent(context, MainActivity.class);
                            intentCall.putExtra("message", currentMessage.getMessageBody());

                            PendingIntent pendingIntent= PendingIntent.getActivity(context, 0, intentCall, PendingIntent.FLAG_UPDATE_CURRENT);
                            pendingIntent.send();
                        }
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
        }
    }
} 

manifest:

<activity android:name=".MainActivity"
            android:launchMode="singleTask"/>
<receiver android:name=".SMSReciver">
            <intent-filter android:priority="1000">
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
            </intent-filter>
        </receiver>

onNewIntent:

 @Override
         protected void onNewIntent(Intent intent) {
                super.onNewIntent(intent);
                Toast.makeText(this, "onNewIntent", Toast.LENGTH_SHORT).show();

                onSMSReceived(intent.getStringExtra("message"));

            }

permissions:

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

Solution 8 - Android

Thank to @Vineet Shukla (the accepted answer) and @Ruchir Baronia (found the issue in the accepted answer), below is the Kotlin version:

Add permission:

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

Register BroadcastReceiver in AndroidManifest:

<receiver
    android:name=".receiver.SmsReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter android:priority="2332412">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Add implementation for BroadcastReceiver:

class SmsReceiver : BroadcastReceiver() {
    private var mLastTimeReceived = System.currentTimeMillis()

    override fun onReceive(p0: Context?, intent: Intent?) {
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - mLastTimeReceived > 200) {
            mLastTimeReceived = currentTimeMillis

            val pdus: Array<*>
            val msgs: Array<SmsMessage?>
            var msgFrom: String?
            var msgText: String?
            val strBuilder = StringBuilder()
            intent?.extras?.let {
                try {
                    pdus = it.get("pdus") as Array<*>
                    msgs = arrayOfNulls(pdus.size)
                    for (i in msgs.indices) {
                        msgs[i] = SmsMessage.createFromPdu(pdus[i] as ByteArray)
                        strBuilder.append(msgs[i]?.messageBody)
                    }

                    msgText = strBuilder.toString()
                    msgFrom = msgs[0]?.originatingAddress

                    if (!msgFrom.isNullOrBlank() && !msgText.isNullOrBlank()) {
                        //
                        // Do some thing here
                        //
                    }
                } catch (e: Exception) {
                }
            }
        }
    }
}

Sometime event fires twice so I add mLastTimeReceived = System.currentTimeMillis()

Solution 9 - Android

broadcast implementation on Kotlin:

 private class SmsListener : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d(TAG, "SMS Received!")

        val txt = getTextFromSms(intent?.extras)
        Log.d(TAG, "message=" + txt)
    }

    private fun getTextFromSms(extras: Bundle?): String {
        val pdus = extras?.get("pdus") as Array<*>
        val format = extras.getString("format")
        var txt = ""
        for (pdu in pdus) {
            val smsmsg = getSmsMsg(pdu as ByteArray?, format)
            val submsg = smsmsg?.displayMessageBody
            submsg?.let { txt = "$txt$it" }
        }
        return txt
    }

    private fun getSmsMsg(pdu: ByteArray?, format: String?): SmsMessage? {
        return when {
            SDK_INT >= Build.VERSION_CODES.M -> SmsMessage.createFromPdu(pdu, format)
            else -> SmsMessage.createFromPdu(pdu)
        }
    }

    companion object {
        private val TAG = SmsListener::class.java.simpleName
    }
}

Note: In your manifest file add the BroadcastReceiver-

<receiver android:name=".listener.SmsListener">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Add this permission:

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

Solution 10 - Android

Since some time now, it becomes nearly impossible to publish an app with the android.permission.RECEIVE_SMS permission, if you are not a default sms app. Google provide a new tool for SMS catching ==> Automatic SMS Verification with the SMS Retriever API

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioniShaderView Question on Stackoverflow
Solution 1 - AndroidVineet ShuklaView Answer on Stackoverflow
Solution 2 - Androidstefan.nskView Answer on Stackoverflow
Solution 3 - AndroidRuchir BaroniaView Answer on Stackoverflow
Solution 4 - AndroidDebasish GhoshView Answer on Stackoverflow
Solution 5 - AndroidAdnan AhmedView Answer on Stackoverflow
Solution 6 - AndroidPabodha WimalasuriyaView Answer on Stackoverflow
Solution 7 - AndroidAskQView Answer on Stackoverflow
Solution 8 - AndroidLiarView Answer on Stackoverflow
Solution 9 - AndroidSerg BurlakaView Answer on Stackoverflow
Solution 10 - AndroidArno AbomoView Answer on Stackoverflow