Question

I am preparing an app which requires me to have user's phone number and then send a hashcode the user programatically as SMS so that it will be used as their token afterwards.

I am making the user enter the number in the MainActivty and have my receiver which listens to SMS_RECEIVED so that it will notiy the user, first of all I want to make sure I am sending and receiving the user's number correctly and receving it. However my receiver does not seems to listen to the trigger. I can see the received sms on notification as well as I can hear the sound but broadcast receiver does not listens to it.

My Code for MainActivity is

public class MainActivity extends Activity
{    
Button submit;
EditText contact;
static String phNo;
ProgressDialog progress;
static Boolean wasMyOwnNumber;
static Boolean workDone;
final static int SMS_ROUNDTRIP_TIMOUT = 30000;

@Override
protected void onCreate(Bundle savedInstanceState)
{
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  contact = (EditText)findViewById(R.id.mobileNumber);
  submit = (Button) findViewById(R.id.button1);
  wasMyOwnNumber = false;
  workDone = false;
  submit.setOnClickListener(new View.OnClickListener()
  {
          public void onClick(View v)
          {
              phNo = contact.getText().toString();
              new CheckOwnMobileNumber().execute();
          }
  });

}

 private class CheckOwnMobileNumber extends AsyncTask<String, Void, String>
 {
  @Override
  protected void onPostExecute(String result)
  {
      // TODO Auto-generated method stub
      if(progress.isShowing())
      {
          progress.dismiss();
          if(wasMyOwnNumber)
          {
              Toast.makeText(getApplicationContext(), "Number matched.", Toast.LENGTH_LONG).show();
              wasMyOwnNumber = false;
              workDone = false;
          }
          else
          {
              Toast.makeText(getApplicationContext(), "Wrong number.", Toast.LENGTH_LONG).show();
              wasMyOwnNumber = false;
              workDone = false;
              return;
          }
      }
      super.onPostExecute(result);
  }

  @Override
  protected String doInBackground(String... params)
  {
      // TODO Auto-generated method stub
      String msg = phNo;
      try
      {
          SmsManager sms = SmsManager.getDefault();
          sms.sendTextMessage(phNo, null, msg, null, null);
          timeout();
      }
      catch(Exception ex)
      {
          Log.v("Exception :", ""+ex);
      }
      return null;
  }

  @Override
  protected void onPreExecute() 
  {
      // TODO Auto-generated method stub
      progress = ProgressDialog.show(MainActivity.this, "","Checking Mobile Number...");
      progress.setIndeterminate(true);
      progress.getWindow().setLayout(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
      super.onPreExecute();
  }

}

 private boolean timeout()
 {
     int waited = 0;
     while (waited < SMS_ROUNDTRIP_TIMOUT)
     {
        try
        {
          Thread.sleep(100);
        }
        catch (InterruptedException e)
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        waited += 100;
        if(phoneNumberConfirmationReceived())
        {
            waited=SMS_ROUNDTRIP_TIMOUT;
            workDone = true;
        }
     }
     /*Log.v("MainActivity:timeout2: Waited: " , ""+waited);
     Log.v("MainActivity:timeout2:Comparision: ", ""+ phoneNumberConfirmationReceived());
     Log.v("MainActivity:timeout2: WorkDone value after wait complete : ", ""+workDone);*/
  return workDone;

}

 private boolean phoneNumberConfirmationReceived()
 {
  if(wasMyOwnNumber)
  {
      workDone = true;
  }
  return workDone;

} MyCode for SMSReceiver is

public class SMSReceiver extends BroadcastReceiver
{
private static final String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
Context mContext;
private Intent mIntent;
static String address, str = null;
boolean isSame;

// Retrieve SMS
public void onReceive(Context context, Intent intent)
{
    Log.v("ONMESSAGE", "RECEIVED");
    mContext = context;
    mIntent = intent;
    String action = intent.getAction();       
        SmsMessage[] msgs = getMessagesFromIntent(mIntent);
        if (msgs != null)
        {
            for (int i = 0; i < msgs.length; i++)
            {
                address = msgs[i].getOriginatingAddress();
                str = msgs[i].getMessageBody().toString();
            }
        }
        Log.v("Originating Address : Sender :", ""+address);
        Log.v("Message from sender :", ""+str);
        isSame = PhoneNumberUtils.compare(str, MainActivity.phNo);
        Log.v("Comparison :", "Yes this true. "+isSame);
        if(isSame)
        {
             MainActivity.wasMyOwnNumber = isSame;
             MainActivity.workDone=true;
        }

        // ---send a broadcast intent to update the SMS received in the
        // activity---
        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("SMS_RECEIVED_ACTION");
        broadcastIntent.putExtra("sms", str);
        context.sendBroadcast(broadcastIntent);

   }

public static SmsMessage[] getMessagesFromIntent(Intent intent)
{
    Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
    byte[][] pduObjs = new byte[messages.length][];

    for (int i = 0; i < messages.length; i++)
    {
        pduObjs[i] = (byte[]) messages[i];
    }

    byte[][] pdus = new byte[pduObjs.length][];
    int pduCount = pdus.length;
    SmsMessage[] msgs = new SmsMessage[pduCount];
    for (int i = 0; i < pduCount; i++)
    {
        pdus[i] = pduObjs[i];
        msgs[i] = SmsMessage.createFromPdu(pdus[i]);
    }
    return msgs;
}
}  

*My entries in Manifest is *

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.getphonenumber"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/> 
<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="com.example.getphonenumber.MainActivity"
        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.getphonenumber.SMSReceiver">  
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
</application>

</manifest>
Was it helpful?

Solution

Give a priority value for the intent..

change your <intent-filter> as following

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

Have a try..

Also add permission to receive sms

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

OTHER TIPS

After spending most of a day stuck here, figured I'd share my experience in case anyone is stuck in the same boat. I spent several hours troubleshooting a failure to get any sms notifications which I finally resolved with calls to acquire permissions. I believe it is related to the changes in permissions in recent sdk versions.

I set up receiver in androidmanifest.xml as stated above, and I also added it programmatically without any luck.

I had permissions in androidmanifest.xml:

<uses-feature android:name="android.permission.RECEIVE_SMS" android:required="true"></uses-feature>
<uses-feature android:name="android.permission.READ_SMS" android:required="true"></uses-feature>

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

I had the same issue whether I put the receiver in the androidmanifest.xml (in the receiver block under the application block) or if I did it programmatically as follows:

private void InstallFunction () {
        final YourActivity activity = this;

        this.smsReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                activity.onReceiveIntent(context, intent);
            }
        };
        filter = new IntentFilter();
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); // tried 0, 999, Integer.MaxValue, etc.
        filter.addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION); // SMS
        this.registerReceiver(smsReceiver, filter);

It gave no error nor logged any warnings, but the onReceive was never called using sms on emulator or on my phone.

A call to check if I had the permissions as per the one below said I had both.

    canReadSMS = ActivityCompat.checkSelfPermission(this,
            Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED;
canReceiveSMS = ActivityCompat.checkSelfPermission(this,
    Manifest.permission.Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_GRANTED);

canReadSMS and canReceiveSMS were both true.

Despite that, no events ever arrived. After rewriting it several times, and trying everything a million ways, I finally decided to ignore the permissions responses and ask for permission anyway.

        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.RECEIVE_SMS) ||
            ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.READ_SMS)) {
            new AlertDialog.Builder(this)
                    .setMessage("Would you like TheApp to handle confirmation codes text messages for you to make registration easier?")
                    .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            ActivityCompat.requestPermissions(activity,
                                    new String[]{
                                            Manifest.permission.RECEIVE_SMS,
                                            Manifest.permission.READ_SMS
                                    },
                                    SMS_PERMISSIONS_REQUEST_ID);
                        }
                    })
                    .setNegativeButton("No", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .create()
                    .show();

After running this, the notifications started arriving and I was very excited. Originally, I did not have the calls to 'shouldShowRequestPermissionRationale' which seemed to lead to problems with crashing or ending up at an activity where the input wouldn't let me enter data. After some research, I came across the ActivityCompat.shouldShowRequestPermissionRationale(... as per the code above. After the user has given the app permissions for sms, that function seems to no longer return true. The end result is that they are asked a single time for the permission for the app. The other complication I think brought about by the new permissions scheme was that if I asked for permission on the confirmation page, they could be responding to the permissions query when the text arrived -- in which case the text would not arrive to the message handler. I resorted to asking for permissions on the registration page prior to opening up the confirmation page. This worked nicely.

Note: SMS_PERMISSIONS_REQUEST_ID is a just variable with a particular integer value assigned. Assign it any reasonable name and number you like.

In general, you implement the override "onRequestPermissionsResult" function to determine if you got permissions or not, but in my case the permission was optional -- if the app cannot read the sms, the user can still manually type or past the code. That being the case, I was able to omit that override. Depending on your implementation, you might need it -- many samples are available for that handler. The ID defined above is used in that handler to determine that you are getting the result for the permissions request you just made.

There are also numerous examples of processing the sms messages, but in case anyone lands here, I'll include my code that processed incoming SMS. It's intentionally written a bit wordy and easy to digest as I was hunting problems.

private void onReceiveIntent (Context context, Intent intent) {
    if (context == this &&
            intent != null) {
        if (intent.getAction ().toString ().equals (Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
            Bundle bundle;

            bundle = intent.getExtras ();
            if (bundle != null) {
                Object[] pdus;
                String format;

                format = bundle.getString ("format");
                pdus = (Object[]) bundle.get("pdus");
                if (pdus != null &&
                        pdus.length > 0) {
                    StringBuilder body;
                    SmsMessage header;
                    SmsMessage sms;

                    body = new StringBuilder();
                    header = SmsMessage.createFromPdu((byte[]) pdus[0],
                            format);
                    for (Object pdu : pdus) {
                        //sms = SmsMessage.createFromPdu ((byte[]) pdu); <-- has been deprecated
                        sms = SmsMessage.createFromPdu ((byte[]) pdu,
                                format); // <-- the new way -- get the format as per above.
                        if (sms != null) {
                            body.append (sms.getDisplayMessageBody());
                        }
                    }
                    ProcessSmsMessage (header,
                            body.toString ());
                }
            }
        }
    }
}

private void ProcessSmsMessage(SmsMessage header,
                               String message) {
    if (header != null &&
            message != null) {
        String originator;

        originator = header.getOriginatingAddress();
        ProcessSmsMessage(originator,
                message);
    }
}

private void ProcessSmsMessage(String originator,
                               String message) {
    if (originator != null &&
        originator.equals ("##########")) { // does it come from my twilio sms number?
        if (message.startsWith("$TheApp$ Device Verification Code:")) {
            final DeviceConfirmationActivity confirmationActivity = this;
            final String code;

            code = message.substring (##); // past the name and text message above
            this.mConfirmationCodeView.setText (code); // <-- the manual entry box now filled for them

    // now you can do some work.  I use a custom timer to do this work on main thread
    // per much research, you should avoid doing lengthy operation in this thread
    // You can also set up a job.
            confirmationActivity.DoDeviceConfirmation(code);
        }
    }
}

As mentioned in the code, the

sms = SmsMessage.createFromPdu ((byte[]) pdu);

used in most sample code is now deprecated. Fortunately, found some samples that showed the new version:

sms = SmsMessage.createFromPdu ((byte[]) pdu,
                format);

and that the format comes from: bundle.getString ("format"); as per code above.

Thanks a lot to Shirish Herwade especially: SmsMessage.createFromPdu is deprecated in android API level 23

Hope that helps someone stuck the way I was. I am fairly new to Android programming having resorted to cordova in the past, so someone else may be able to provide better insight on why that permissions call had to be made before getting notifications even though permissions checks return values that would make one think I already had said permission.

Add permission :

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

Write in manifest:

       <receiver
            android:name="yourpackagename.SmsReceive"
            android:enabled="true" >
            <intent-filter android:priority="1000" >
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>




SmsReceive class:

public void onReceive(Context context, Intent intent) {

        Bundle bundle = intent.getExtras();
        SmsMessage[] msgs = null;
        if (bundle != null) {

            str = "";
            substr = 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 += "SMS from " + msgs[i].getOriginatingAddress();
                sender=msgs[i].getOriginatingAddress();
                str += msgs[i].getMessageBody().toString();


                if(str.length()!=0)
                {

                //write the logic 
                }
            }
        }
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top