Android - Create SMS from PDU deprecated API? - android

I want to create an app that gets notified when an SMS arrives and processes that SMS, but reading the reference for the createFromPdu function, it states that:
This method will soon be deprecated and all applications which handle incoming SMS messages by processing the SMS_RECEIVED_ACTION broadcast intent must now pass the new format String extra from the intent into the new method createFromPdu(byte[], String) which takes an extra format parameter. This is required in order to correctly decode the PDU on devices that require support for both 3GPP and 3GPP2 formats at the same time, such as dual-mode GSM/CDMA and CDMA/LTE phones.
However the createFromPdu(byte[], String) function is missing from both the documentation and the SDK.
What should I do? I need to support these dual-sim dual-mode phones.

In short, use this:
SmsMessage smsMessage;
if (Build.VERSION.SDK_INT >= 19) { //KITKAT
SmsMessage[] msgs = Telephony.Sms.Intents.getMessagesFromIntent(intent);
smsMessage = msgs[0];
} else {
Object pdus[] = (Object[]) bundle.get("pdus");
smsMessage = SmsMessage.createFromPdu((byte[]) pdus[0]);
}

You don't mention which Android version you're targeting but given the date of the question I'm assuming Jelly Bean 4.x.
At the time of writing this, we're at Lollipop MR1 and the deprecation note about using createFromPdu with the format parameter still stands:
https://android.googlesource.com/platform/frameworks/opt/telephony/+/android-5.1.0_r3/src/java/android/telephony/SmsMessage.java
Instead of using that API directly however, you can use the Telephony provider API getMessagesFromIntent:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.2_r1/android/provider/Telephony.java/#686
That method considers the PDU format (and the subscription ID in Lollipop for Multi SIM devices) and gives you an array of SmsMessage instances.
An example of how it's used can be seen in Google's MMS app:
https://android.googlesource.com/platform/packages/apps/Mms/+/master/src/com/android/mms/transaction/SmsReceiverService.java (see handleSmsReceived)

createFromPdu(byte[]) is deprecated from SDK_INT 23 so you have to use
createFromPdu((byte[]) , format);
here is my code to get incoming SMS
i am using SMSReceiver class as an innerclass of an Activity.
this is my working code
public class SamplaActivity extends BaseActivity {
SMSReceiver otp;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
otp=new SMSReceiver();
........
........
}
#Override
protected void onResume() {
super.onResume();
// register broadcast receiver
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(otp, filter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(otp);
}
private class SMSReceiver extends BroadcastReceiver {
private Bundle bundle;
private SmsMessage currentSMS;
private String message;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
bundle = intent.getExtras();
if (bundle != null) {
Object[] pdu_Objects = (Object[]) bundle.get("pdus");
if (pdu_Objects != null) {
for (Object aObject : pdu_Objects) {
currentSMS = getIncomingMessage(aObject, bundle);
String senderNo = currentSMS.getDisplayOriginatingAddress();
message = currentSMS.getDisplayMessageBody();
Toast.makeText(OtpActivity.this, "senderNum: " + senderNo + " :\n message: " + message, Toast.LENGTH_LONG).show();
}
this.abortBroadcast();
// End of loop
}
}
} // bundle null
}
}
private SmsMessage getIncomingMessage(Object aObject, Bundle bundle) {
SmsMessage currentSMS;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String format = bundle.getString("format");
currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format);
} else {
currentSMS = SmsMessage.createFromPdu((byte[]) aObject);
}
return currentSMS;
}
}

sorry for late response but it may help anyway.
apart from checking the android version and if it is above M and extract sing format.
I would suggest to use:
compile 'me.everything:providers-android:1.0.1'
Manifest
<uses-permission android:name="android.permission.READ_SMS" />
And in your codes
public List<Sms> mySmses(Context context)throws Exception{
TelephonyProvider telephonyProvider = new TelephonyProvider(context);
List<Sms> smses = telephonyProvider.getSms(TelephonyProvider.Filter.INBOX).getList();
if(smses.isEmpty()){
throw new Exception("No SMS found");
}
return smses;
}
Remember to use AsyncTask to fetch SMSes(consuming the above method) as this is a long running operation. It may crash your app(in certain occasion) if it is executed on the UI thread.
This library saved my time. Hope it will do the same to someone.

Related

Write received message to SMS provider ( API level 19+ )

I am creating an SMS manager for KitKat and later version. I have implemented all the receivers as directed in the official doc by android.
I have to receive the SMS SMS_DELIVER broadcast receiver and read it and then have to write to the SMS provider.
till now I am able to read the SMS received. I have set my app as the default SMS app in the device. I am also parsing the SMS and can see it in the log.
problem
I am unable to write the SMS to the SMS provider.
here is the broadcast receiver:
public class SmsReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdusObj = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdusObj.length];
for (int i = 0; i < messages.length; i++) {
String format = bundle.getString("format");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdusObj[i], format);
} else {
messages[i] = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
}
}
for (SmsMessage msg : messages) {
Log.i("log", "display msg body : " + msg.getDisplayMessageBody() + "originating address : " + msg.getDisplayOriginatingAddress() + " get message body : " + msg.getMessageBody());
//here I have to write the message to the sms provider.
}
}
}
}
does anyone have any suggestions? please help me.
Update
i have tried android-kitkat-api-19-how-to-write-messages-in-sms-content-provider-without so question but i am unable to get around it. That solution is for writing to the sent SMS without doing anything like sending the SMS. but I want to write the received SMS here.
Thanks to Mike M. for the help. i got help from this answer - sms-doesnt-save-on-kitkat-4-4-already-set-as-default-messaging-app from SO and this post - kitkat-sms-mms-supports .
here is what i have done :
to write the sms into the sms provider of android system i used content provider. and passed value to it. code snippet is :
ContentValues values = new ContentValues();
values.put(Telephony.Sms.ADDRESS, msg.getDisplayOriginatingAddress());
values.put(Telephony.Sms.BODY, msg.getMessageBody());
context.getApplicationContext().getContentResolver().insert(Telephony.Sms.Sent.CONTENT_URI, values);
this code will save the received sms into the system sms provider and even after your ap is uninstalled other sms app can read it. keep in mind that you need to be the default sms app to do this operation. and you have to provide WRITE_SMS permission in manifest. i have targeted kitkat and versions after it. for previous versions you have to some part of code differently.
the whole SmsReceiver class after completion is :
public class SmsReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdusObj = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdusObj.length];
for (int i = 0; i < messages.length; i++) {
String format = bundle.getString("format");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdusObj[i], format);
} else {
messages[i] = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
}
}
for (SmsMessage msg : messages) {
Log.i("log", "display msg body : " + msg.getDisplayMessageBody() + "originating address : " + msg.getDisplayOriginatingAddress() + " get message body : " + msg.getMessageBody());
ContentValues values = new ContentValues();
values.put(Telephony.Sms.ADDRESS, msg.getDisplayOriginatingAddress());
values.put(Telephony.Sms.BODY, msg.getMessageBody());
context.getApplicationContext().getContentResolver().insert(Telephony.Sms.Sent.CONTENT_URI, values);
}
}
}
}
I had the same problem. You have to register a SMSBroadcaseReceiver, MMSBroadcastReceiver, a QuickreplyService in order to be be able to be set as default SMS receiver.
This helped me:
https://www.androidauthority.com/how-to-create-an-sms-app-part-2-724264/

Unit Testing Android SMS Receiver

i try to write an unit test on an BroadcastReceiver that gets informed when an SMS was received. Its not meant to be the default Application. Instead i just need this for an two factor authentication. For this case i created an PDU with [1].
But when i pass it down to the BroadcastReceiver the phone nr of the Sender never gets read by android it's just null. The body text is returned.
#TargetApi(Build.VERSION_CODES.KITKAT)
#Test
public void testOnReceive() throws Exception {
final byte[] decodedPDU = BaseEncoding.base16().decode(PDU);
final ReceiveSmsBroadcastReceiver receiveSmsBroadcastReceiver = spy(new ReceiveSmsBroadcastReceiver(true));
final Intent intent = new Intent();
intent.putExtra("format",SmsConstants.FORMAT_3GPP);
intent.putExtra("pdus", new Object[]{decodedPDU});
intent.setAction("android.provider.Telephony.SMS_RECEIVED");
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 1);
receiveSmsBroadcastReceiver.onReceive(InstrumentationRegistry.getTargetContext(), intent);
In the receiver i do this to get the SMSMessage Objects:
#TargetApi(Build.VERSION_CODES.KITKAT)
private void getSMSKitKat(final Context context, final Intent intent) {
final SmsMessage[] messagesFromIntent = Telephony.Sms.Intents.getMessagesFromIntent(intent);
I receive an SmsMessage array here, the body message is correct. But i need to test my check sender phone number before i can notify the UI that the SMS is received: But nr is always null here:
private boolean isCorrectSender(#Nullable final SmsMessage message) {
if (message == null) {
return false;
}
final String nr = message.getOriginatingAddress();
Can someone point me whats wrong here ?
PS: SMSConstants and PhoneConstants are all framework classes i took from AOSP to get it running because those APIs are non public
[1] http://twit88.com/home/utility/sms-pdu-encode-decode

Android - LocalBroadcastManager for SMS interception not firing

I've got an JavascriptInterface method that registers a LocalBroadcastManager listening to received SMS's. When an SMS arrives the onReceive method doesn't get called.
public void UIregisterSMSSource(String number) {
LocalBroadcastManager.getInstance(this)
.registerReceiver(mMessageReceiver, new IntentFilter(ACTION_SMS_RECEIVE));
}
/**
* SMS Receiver
*/
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_SMS_RECEIVE)) {
StringBuilder buf = new StringBuilder();
Bundle bundle = intent.getExtras();
if (bundle != null) {
Bundle extras = intent.getExtras();
Object[] pdus = (Object[]) extras.get("pdus");
for (int i = 0; i < pdus.length; i++) {
SmsMessage SMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
String sender = SMessage.getOriginatingAddress();
String body = SMessage.getMessageBody().toString();
Log.d(TAG, "[SMS] onReceive by " + sender + ". Content - " + body);
// Save preferences of the activation code
}
}
}
}
};
This is the ACTION_SMS_RECEIVE variable:
private static final String ACTION_SMS_RECEIVE = "android.provider.Telephony.SMS_RECEIVED";
I've tested this before as BroadcastaReceiver and it worked. I removed the receiver from the manifest too which I don't know if it's right.
Do I need to configure something more? On the examples I've came across there's no further configuration needed.
Thanks in advance.
Receiver mMessageReceiver can only listen the intent which sent via LocalBroadcastManager.sendBroadcast(intent).
Here, intent with android.provider.Telephony.SMS_RECEIVED action, is being raised by system (not using LocalBroadcastManager.sendBroadcast(intent)), so your app does not listen to this intent.
Your previous approach was correct, and you can continue on that logic.
You can read a detailed example of LocalBroadcastManager to make clear your doubts about its working flow.

Android broadcastreceiver creates new instances

Well, I have a class extending broadcastreceiver which is listening for messages. Now whenever it receives a message I creates a new instance of my app. So when I am closing it I have to tap back button 2 times.
public class SMSReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Bundle myBundle = intent.getExtras();
SmsMessage [] messages = null;
String strMessage = "";
String phoneNumber = "";
if (myBundle != null) {
Object [] pdus = (Object[]) myBundle.get("pdus");
messages = new SmsMessage[pdus.length];
for (int i = 0; i < messages.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
strMessage += "SMS From: " + messages[i].getOriginatingAddress();
strMessage += " : ";
strMessage += messages[i].getMessageBody();
strMessage += "\n";
phoneNumber = messages[i].getDisplayOriginatingAddress();
}
if (phoneNumber.equals("T-Mobile")) {
Toast.makeText(context, strMessage, Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
}
}
First of all, I don't really think it is creating a new instance of your app. What is more likely happening is that an activity is being brought to the front. I think you should look at the manifest or post it so we can see how the broadcasts are being directed. It could be that the target of the broadcast also has the effect of starting the activity. So check the manifest, there is nothing in the broadcast receiver that will cause this. Having said that its really up to the Android OS what gets shown when. But as far as two instances. Thats just not happening.
PS. I don't know the exact reason but I would say probably don't issue Toast from a broadcast reeiver, instead communicate to an activity using startActivity().

Base64 incompatible with 7-bit GSM encoding?

Since I am not able to compress a 256-byte long md5-like string within 160 characters, I decided to use Multi-part message in Android to divide the SMS into several parts and the receiver will listen the SMS then combine those messages.
I use the combination algorithm from here: Android - receiving long SMS (multipart)
And after a few testing I found that the SMS I sent was totally messed, though I encoded them with Base64 before sending.
Thinking the SMS is a 7-bit Encoding method I thought it might be the encoding problem.
But I have successfully sent Base 64 encoded message, but it was short and within 1 SMS.
My question is:
If it is a encoding problem, then why I can send a totally readable Base64 encoded messages within 160 characters, but cannot get a readable result when sending messages exceeding 160 characters?
I've attached my code here:
The string I intend to send:
static final String bigNum = "C196BA05AC29E1F9C3C72D56DFFC6154A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A067CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE428782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE619ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD92D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BFFAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E5320121496DC65B3930E38047294FF877831A16D5228418DE8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A0402A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83";
The Code
private void sendMsg (String phoneNum, String msg) {
SmsManager sms = SmsManager.getDefault();
ArrayList<String> parts = sms.divideMessage(msg);
sms.sendMultipartTextMessage(phoneNum, null, parts, null, null);
}
public void onStart() {
super.onStart();
final String bigNum64 = Base64.encodeToString(bigNum.getBytes(), 0);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendMsg("5554", bigNum64);
textView.setText(bigNum64);
}
});
}
public void onResume() {
super.onResume();
Bundle receiver = getIntent().getExtras();
if (receiver != null) {
String msg = receiver.getString("SMS");
textView.setText("Received" + msg);
}
}
}
The SMS Receiver:
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdus.length];
String body = "";
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
SmsMessage sms = messages[0];
try {
if (messages.length == 1 || sms.isReplace()) {
body = sms.getDisplayMessageBody();
}
else {
StringBuilder bodyText = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
bodyText.append(messages[i].getMessageBody());
}
body = bodyText.toString();
}
}
catch (Exception e) {
}
Intent start = new Intent(context, SendLongSMSActivity.class);
start.putExtra("SMS", body);
start.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(start);
}
Thanks for your help!
Related Post: Any way to compress a 256 bytes "MD5-like" string into 160 bytes or less?
Even if I give a rather simple long string, like
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
and no matter with or without Base64 encoding, the receiver cannot get the correct results.
UPDATE #2
When using real phone for testing, the receiver can receive correct message without base 64, it was due to the emulator garbled the text.
Question now closed.
Thanks for everyone who helps! And thanx #Dan
UPDATE:
The string "0123456789ABCDEF..." without base64, divided into 2 parts:
part [0] "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345678"
part [1] "9ABCDEF0123456789ABCDEF"
Here's the divided base64 string of the "0123456789ABCDEF...:
part[0] "MDEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4\nOUFCQ0RFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCQ0RFRjAx"
part[1] "\nMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlB\nQkNERUY=\n"
It appears that the sms divide is adding \n characters after every 77 characters and at the end, just strip those from your string and it will decode properly.

Categories

Resources