Receiving SMS on Android App

i followed a tutorial to receive SMS on my application and read it to pass the SMSbody to Toast. that is the Receiver class.

public class SmsReciever extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
        Bundle bundle= intent.getExtras();
        SmsMessage[] msgs= null;
        String str="";
        if(bundle != null ){
            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]);
                str+= msgs[i].getMessageBody();

            }
             Toast.makeText(context, str, Toast.LENGTH_LONG).show();
        }
        }

    }

the manifest file

    <receiver android:name="com.msoft.masrooq.SmsReciever">
    <intent-filter>
    <action android:name="android.provider.telephony.SMS_RECIEVED"></action>
    </intent-filter>
    </receiver>
        <uses-permission android:name="android.permission.RECEIVE_SMS"/>
            <uses-permission android:name="android.permission.READ_SMS" />

the app starts fine but it doesn't response to receiving sms it doesn't do anything.


Solution 1:

Here is my implementation of receiving sms messages. Sms message may be broken into many, notice how it is treated. Also check the android:priority attribute.

public class SmsReceiver extends BroadcastReceiver {

    private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(SMS_RECEIVED)) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                // get sms objects
                Object[] pdus = (Object[]) bundle.get("pdus");
                if (pdus.length == 0) {
                    return;
                }
                // large message might be broken into many
                SmsMessage[] messages = new SmsMessage[pdus.length];
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < pdus.length; i++) {
                    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                    sb.append(messages[i].getMessageBody());
                }
                String sender = messages[0].getOriginatingAddress();
                String message = sb.toString();
                Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
                // prevent any other broadcast receivers from receiving broadcast
                // abortBroadcast();
            }
        }
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.smsreceiver"
    android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk android:minSdkVersion="4" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity
            android:name=".SmsLoggerActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="com.example.smsreceiver.SmsReceiver" android:enabled="true">
            <intent-filter android:priority="2147483647">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

Few notes: If you declare your receiver in xml than system can use your receiver regardless of your application was ever launched. Since Android 1.6 notifications about received sms messages are delivered as ordered broadcasts, you can use android:priority attribute of <intent-filter> to tell the system send the sms first to your application (you can also call abortBroadcast() so other applications won't receive the sms, e.g. the native sms app). Don't forget broadcast receiver has about 10 seconds for executing its operation, otherwise it can be prematurely terminated before finishing its job.

Solution 2:

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

 <application
 <uses-permission android:name="android.permission.RECEIVE_SMS" />
 <uses-permission android:name="android.permission.READ_SMS" />
 ...
  <receiver
        android:name=".SMSReceiver">
        <intent-filter android:priority="100">
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
  </receiver>
 ...
</application>

And here Java code :

public class SMSReceiver extends BroadcastReceiver {
public static final String ACTION ="android.provider.Telephony.SMS_RECEIVED";
private static final String SMS_SENDER="123456789";

@Override
public void onReceive(Context context, Intent intent) {
        if (intent != null && intent.getAction() != null &&
                ACTION.compareToIgnoreCase(intent.getAction()) == 0) {
            Object[] pduArray = (Object[]) intent.getExtras().get("pdus");
            SmsMessage[] messages = new SmsMessage[pduArray.length];
            for (int i = 0; i < pduArray.length; i++) {
                messages[i] = SmsMessage.createFromPdu((byte[]) pduArray[i]);
            }
            // SMS Sender, example: 123456789
            String sms_from = messages[0].getDisplayOriginatingAddress(); 

            //Lets check if SMS sender is 123456789
            if (sms_from.equalsIgnoreCase(SMS_SENDER)) {
                StringBuilder bodyText = new StringBuilder();

                // If SMS has several parts, lets combine it :)
                for (int i = 0; i < messages.length; i++) {
                    bodyText.append(messages[i].getMessageBody());
                }
                //SMS Body
                String body = bodyText.toString();
                // Lets get SMS Code
                String code  = body.replaceAll("[^0-9]", ""); 
            }
      }
 }

Solution 3:

Broadcasts are case-sensitive. Use android.provider.Telephony.SMS_RECEIVED not android.provider.telephony.SMS_RECEIVED.

Also, I also have a category set, but I'm not sure it's mandatory:

<intent-filter>
   <action android:name="android.provider.Telephony.SMS_RECEIVED" />
   <category android:name="android.intent.category.DEFAULT" />
</intent-filter>