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.