Goal: (NOTE: The answer selected generates a GSM (3gpp) PDU) for CDMA (3gpp2) please refer here
To create a PDU that can be passed into SmsMessage.createFromPdu(byte[] pdu)
.
I'm "Broadcasting an Intent" to one of my BroadcastReciever
that listens for SMS messages.
One BroadcastReciever
Using android.provider.Telephony.SMS_RECEIVED
for "real" SMS's
Using a custom intent-filter
action for these new "application SMS's".
@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];
// getting SMS information from Pdu.
for (int i = 0; i < pdusObj.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
}
for (SmsMessage currentMessage : messages) {
//the currentMessage.getDisplayOriginatingAddress()
//or .getDisplayMessageBody() is null if I Broadcast a fake sms
Log.i("BB", "address:"+currentMessage.getDisplayOriginatingAddress()+" message:"+currentMessage.getDisplayMessageBody());
...
So I want my BroadcastReciever
to be able to handle both types of messages without adding extra code
(yes I know I can have a different BroadcastReciever
for the different intent-filter
action but I would like to actually pull this off as I know it can be done, I'm stubborn)
Research:
I've been doing research all day/night. I've tried writing my own even though I'm very terrible with the math and conversions and creating a suitable algorithm. I've looked over Stack topics on PDUs, and Create PDU Android but the link is broken in the answer. I even Looked at com.google.android.mms.pdu
source code
so far I've only been able to create a PDU without a "originating address" using some code from http://www.wrankl.de/JavaPC/SMSTools.html
PDU:
destination: 555 message: helloworld
"1100038155f50000aa0ae8329bfdbebfe56c32"
Which obviously isn't valid...
Side Notes:
I don't plan on doing anything with the PDU besides local use, I do not want hard coded PDU's in my code because I'm not reusing the PDU.
If there is anything I can add to the code I'm using to add in a "originating address", that will work. Or does anyone have info on a Library I'm not aware of?
Thanks
Updates:
tried
byte[] by =(byte[])(SmsMessage.getSubmitPdu("12345", "1234", "hello", false).encodedMessage);
which gives me the following (in hex representation)
"0000100200000000000000000000000004010203040000000e000320ec400107102e8cbb366f00"
did't work
Maybe this snippet doesn't have many detail fields like you want but for my simple purpose it can invoke notification like another sms.
private static void createFakeSms(Context context, String sender,
String body) {
byte[] pdu = null;
byte[] scBytes = PhoneNumberUtils
.networkPortionToCalledPartyBCD("0000000000");
byte[] senderBytes = PhoneNumberUtils
.networkPortionToCalledPartyBCD(sender);
int lsmcs = scBytes.length;
byte[] dateBytes = new byte[7];
Calendar calendar = new GregorianCalendar();
dateBytes[0] = reverseByte((byte) (calendar.get(Calendar.YEAR)));
dateBytes[1] = reverseByte((byte) (calendar.get(Calendar.MONTH) + 1));
dateBytes[2] = reverseByte((byte) (calendar.get(Calendar.DAY_OF_MONTH)));
dateBytes[3] = reverseByte((byte) (calendar.get(Calendar.HOUR_OF_DAY)));
dateBytes[4] = reverseByte((byte) (calendar.get(Calendar.MINUTE)));
dateBytes[5] = reverseByte((byte) (calendar.get(Calendar.SECOND)));
dateBytes[6] = reverseByte((byte) ((calendar.get(Calendar.ZONE_OFFSET) + calendar
.get(Calendar.DST_OFFSET)) / (60 * 1000 * 15)));
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
bo.write(lsmcs);
bo.write(scBytes);
bo.write(0x04);
bo.write((byte) sender.length());
bo.write(senderBytes);
bo.write(0x00);
bo.write(0x00); // encoding: 0 for default 7bit
bo.write(dateBytes);
try {
String sReflectedClassName = "com.android.internal.telephony.GsmAlphabet";
Class cReflectedNFCExtras = Class.forName(sReflectedClassName);
Method stringToGsm7BitPacked = cReflectedNFCExtras.getMethod(
"stringToGsm7BitPacked", new Class[] { String.class });
stringToGsm7BitPacked.setAccessible(true);
byte[] bodybytes = (byte[]) stringToGsm7BitPacked.invoke(null,
body);
bo.write(bodybytes);
} catch (Exception e) {
}
pdu = bo.toByteArray();
} catch (IOException e) {
}
Intent intent = new Intent();
intent.setClassName("com.android.mms",
"com.android.mms.transaction.SmsReceiverService");
intent.setAction("android.provider.Telephony.SMS_RECEIVED");
intent.putExtra("pdus", new Object[] { pdu });
intent.putExtra("format", "3gpp");
context.startService(intent);
}
private static byte reverseByte(byte b) {
return (byte) ((b & 0xF0) >> 4 | (b & 0x0F) << 4);
}
Hope you will find something useful
Update :
public static final 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;
}
Its been a LONG time since I've done any direct PDU wrangling, but when I did I quickly gave up and used SMSLib: the PDU utilities it has worked perfectly for sending via Nokia phones (over a serial link). My assumption (which may be wrong) is that they will work for Android as well, assuming the interface is actually compliant with the spec.
Do check this code in console.c. This is where android emulator creates the pdu and RIL.java where the CMT message is converted to an SmsMessage. You can use SmsMessage.getPdu
to get the pdu. But SmsMessage.newFromCmt
looks to be a internal api. So it might not be reliable.
Further this is just for Gsm, cdma has a completely different codeflow and since RIL.java and modem are completely manufacturer specific, this might only work on emulator.
Usually GSM code is more reliable on android, so might as well work on gsm phone. Do give it a try.