27

I've looked at a few SMS message examples and Activities are typically used to receive an SMS. However, what I'd like to do is have my background service receive the SMS (the service will process the message and decide whether it is applicable to the app - then inform the user)

In my Manifest, the service is defined as follows:

    <service android:name=".service.myService"
        android:enabled="true">
        <intent-filter>
            <action android:name="package.com.service.myService"/>
        </intent-filter>
</service>

to have the service receive the SMS, will this work ?

<receiver android:name=".service.myService" android:exported="true" > 
  <intent-filter android:priority="999"> 
    <action android:name="android.provider.Telephony.SMS_RECEIVED" />
  </intent-filter> 
</receiver>

The sample code I studied came from: http://www.apriorit.com/our-company/dev-blog/227-handle-sms-on-android

I can't test it yet because my development module doesn't have a phone number to send an SMS to.

Someone Somewhere
  • 23,475
  • 11
  • 118
  • 166
  • > 2) DO NOT update the manifest with the receiver intent filter ! (which > every sample code online seems to do) Can you post your AndroidManifest.xml? –  May 28 '13 at 08:02
  • Your code seems to be correct. You could test your code with the emulator. The emulator supports also to send sms to the virtual device. See also the screenshot: ![Screenshot](http://i.stack.imgur.com/Dy88x.png) Screen from [this answer](https://stackoverflow.com/a/6417467/995926). – rekire Dec 30 '11 at 22:45

4 Answers4

60

I found the solution. To have a Service receive SMS messages:

  1. Update the manifest to give your app the permissions to receive SMS (WRITE_SMS, READ_SMS, RECEIVE_SMS)
  2. DO NOT update the manifest with the receiver intent filter ! (which every sample code online seems to do)
  3. In your Service, create a nested BroadcastReceiver class within your Service class

    private class SMSreceiver extends BroadcastReceiver
    {
        private final String TAG = this.getClass().getSimpleName();
    
        @Override
        public void onReceive(Context context, Intent intent)
        {
            Bundle extras = intent.getExtras();
    
            String strMessage = "";
    
            if ( extras != null )
            {
                Object[] smsextras = (Object[]) extras.get( "pdus" );
    
                for ( int i = 0; i < smsextras.length; i++ )
                {
                    SmsMessage smsmsg = SmsMessage.createFromPdu((byte[])smsextras[i]);
    
                    String strMsgBody = smsmsg.getMessageBody().toString();
                    String strMsgSrc = smsmsg.getOriginatingAddress();
    
                    strMessage += "SMS from " + strMsgSrc + " : " + strMsgBody;                    
    
                    Log.i(TAG, strMessage);
                }
    
            }
    
        }
    
    }
    
  4. In your Service class, register to receive the android.provider.Telephony.SMS_RECEIVED intent filter :

    public class ServiceCommunicator extends Service
    {
        private SMSreceiver mSMSreceiver;
        private IntentFilter mIntentFilter;
    
        @Override
        public void onCreate()
        {
            super.onCreate();
    
            //SMS event receiver
            mSMSreceiver = new SMSreceiver();
            mIntentFilter = new IntentFilter();
            mIntentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
            registerReceiver(mSMSreceiver, mIntentFilter);
        }
    
        @Override
        public void onDestroy()
        {
            super.onDestroy();
    
            // Unregister the SMS receiver
            unregisterReceiver(mSMSreceiver);
        }
    }
    

That's it !

note: encase you're wondering why I didn't bind to my service from within a separate BroadcastReceiver class - it doesn't work because bindService() isn't available.

Someone Somewhere
  • 23,475
  • 11
  • 118
  • 166
  • how can i read the incoming sms in my service class through the broadcast receiver in the SMSreceiver class? – progNewbie Aug 15 '14 at 12:52
  • @someone can you please explain more how the manifest file should be or if you can provide a copy ? – Akram Lazkanee Apr 07 '15 at 16:36
  • @AboJihadBazoka he stated the permissions in his post step 1. Should be enough though. – Torsten Ojaperv Apr 13 '15 at 15:33
  • @Someone Somewhere Hi there! I've found this topic, cause I have a problem with running service with nested broadcast receiver. I've tried the approach suggested by you, but apparently I'm doing something wrong, cause I still get `permission denial error`. Could you please look into http://stackoverflow.com/questions/33540766/sms-notification-not-working ? Thanks! – Paweł Poręba Nov 06 '15 at 12:46
  • @Someone Somewhere would you share your project? Thank you – Sonic Master Dec 10 '15 at 04:29
  • Very good sum up ! But please note that `createFromPdu(...)` method is now deprecated. For API >= 19, you'd rather use `Telephony.Sms.Intents.getMessagesFromIntent(intent);` – Alex Aug 01 '16 at 14:33
  • 1
    Besides this, I can't manage to make it work... can you put your manifest file please ? (If you are still alive since 2012 lol) Thanks a lot – Alex Aug 01 '16 at 15:39
  • Why not just instantiate the extended broadcastreceiver in the activity/fragment? why use a service – Evgeny Danilenko Jan 22 '17 at 17:37
  • This doesn't work. Got the error cannot resolve symbol `LocalBinder` – Trevor Oct 09 '17 at 01:09
1

I have this solution worked for me perfectly by adding BROADCAST_SMS permission:

<receiver android:name="com.mohamedtest.sendandreceivesms_m.SMSReceiver"
        android:enabled="true"
        android:exported="true"
        android:permission="android.permission.BROADCAST_SMS">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
        </intent-filter>
    </receiver>
halfer
  • 19,824
  • 17
  • 99
  • 186
m0haMed
  • 420
  • 5
  • 7
0

You can make the SmsReceiver separately from the Sevice using context from the onReceive method to star service. That lets you not to run service all the time. Or even not starting activity to register receiver. Though I can mistake.

Something like this:

    @Override
public void onReceive(Context context, Intent intent) {

        if (intent.getAction().equals(SMS_RECEIVED)) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                Object[] pdus = (Object[]) bundle.get("pdus");
                if (pdus.length == 0) {
                    return;
                }
                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 number = messages[0].getOriginatingAddress();
                String messageText = sb.toString();


                Intent intent1 = new Intent(context, SMSreceiver.class);
                intent1.putExtra(PHONE_NUMBER, number);
                intent1.putExtra(SMS_TEXT, messageText);

                context.startService(intent1);
            }
        }
}
0
manifest file


<application
    android:allowBackup="true"
    android:dataExtractionRules="@xml/data_extraction_rules"
    android:fullBackupContent="@xml/backup_rules"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.Send"
    tools:targetApi="31">
    <service
        android:name=".ForegroundService"
        android:enabled="true"
        android:exported="true">

    </service>
    <receiver
        android:name="com.customer.send.SmsReceiver"
        android:enabled="true"
        android:exported="true"
        android:permission="android.permission.BROADCAST_SMS">
        <intent-filter android:priority="999">
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>

    </receiver>