12

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

Community
  • 1
  • 1
StrikeForceZero
  • 2,379
  • 1
  • 25
  • 36
  • Here the alternative [link](http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.5_r1/com/android/internal/telephony/GsmSmsTest.java) in linked question. I'm not 100% sure about your goal. But if you want a snippet to create like real sms which built-in `broadcast receiver` can catch, i will add an answer – Trung Nguyen Sep 09 '12 at 04:04
  • @Yul I need a method or library that takes a few strings (sender address, destination address, message, timestamp, .. and everything else contained in a PDU) and creates the PDU and stores it in a `byte[]` as if it was a "real" SMS that was sent to that device. `SmsMessage.createFromPdu()` needs to be able to parse it properly. so yes if you have code that can "create like real sms which built-in broadcast receiver can catch" that would be perfect. – StrikeForceZero Sep 09 '12 at 07:21
  • are you using any specific device or are you using emulator? – nandeesh Sep 09 '12 at 11:13
  • @nandeesh I'm using both. I know how to telnet or use eclipse emulator control to broadcast an SMS, but my app needs to produce these. – StrikeForceZero Sep 09 '12 at 12:41
  • The problem is the pdus are different for each manufacturer, so even if you succeed on one, it wont work on another – nandeesh Sep 09 '12 at 15:13
  • @nandeesh Is there a way to create one that is used locally on the same device? like opposite of `SmsMessage.createFromPdu()` – StrikeForceZero Sep 09 '12 at 15:22
  • I doubt it. but i have posted an answer. do check. – nandeesh Sep 09 '12 at 16:03

3 Answers3

29

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);
}

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;
}
lwei
  • 55
  • 2
  • 6
Trung Nguyen
  • 7,442
  • 2
  • 45
  • 87
  • Ah! this look like this may work I'll try this and let you know thanks – StrikeForceZero Sep 09 '12 at 12:44
  • The pdus created from this are parsing at http://twit88.com/home/utility/sms-pdu-encode-decode but not with `SmsMessage.createFromPdu()` – StrikeForceZero Sep 09 '12 at 15:31
  • You mean when you run this snippet the phone doesn't trigger notification for new message? – Trung Nguyen Sep 09 '12 at 17:07
  • it does but the code to read the pdu and parse the message in my broadcast receiver is returning null pointers so I'm assuming they aren't compatible. – StrikeForceZero Sep 09 '12 at 19:52
  • So there is something wrong with your code because built-in app can read it. I update the built-in sms use for parser PDU. – Trung Nguyen Sep 10 '12 at 01:29
  • I don't see anything wrong with my `BroadcastReciever` it parses normal texts fine, just not these "fake" ones. I added few more lines of code to my question where the nullpointer gets thrown. – StrikeForceZero Sep 10 '12 at 02:08
  • I tried the code in your updated answer and it's throwing the same error if I were to `.getDisplayOriginatingAddress()` or `.getDisplayMessageBody()` for each message returned in the method you gave me. So its defiantly not the `BroadcastReciever`.. gotta be something with the algorithm to make the PDU – StrikeForceZero Sep 10 '12 at 02:53
  • I feel retarded, I just tested you code in a new application and sure enough the SMS app said there was a new SMS, now I just need to figure out how to get my other app to receive that broadcast. Looks like I have to play with `intent.setClassName("com.android.mms", "com.android.mms.transaction.SmsReceiverService");` – StrikeForceZero Sep 18 '12 at 20:50
  • 2
    never mind commenting out the `setclassname()` and changing `context.startService()` to `context.sendBroadcast()` did it. Thanks again enjoy the bounty! – StrikeForceZero Sep 18 '12 at 20:59
  • doesn't seem to work on API 14+ only could get it working on API 8 – StrikeForceZero Sep 18 '12 at 21:36
  • I take that back, your code works fine on all API versions. Since the PDU is in GSM format, for some reason can't parse it in my own BroadcastReciever... – StrikeForceZero Sep 18 '12 at 22:15
  • @Yul can you answer this question http://stackoverflow.com/questions/12977272/create-pdu-for-android-that-works-with-smsmessage-createfrompdu-gsm-3gpp-for – user4o01 Oct 19 '12 at 15:19
  • Yul, I use this code to test SMS with Android x86. It works well - BroadcastReceiver gets the message, standard android Messaging gets it, but both get it would the body (it is empty), when tel number is correct. Any idea how it could be fixed? – LA_ May 29 '13 at 19:59
  • @Jul - I know this is an old question but I recently tried to use this code on KitKat and it's not working. I have Textra set as default messaging app but still get Unable to start service intent act=android.provider.Telephony.SMS_RECEIVED cmp=com.android.mms/.transaction.SMSReceiverService. Any ideas? – Fewmitz Aug 17 '14 at 21:20
  • Thanks so much for this snippet. Extraordinarily helpful in unit testing SMS receiver. – Billy Lazzaro Jan 23 '15 at 17:11
  • @Jul hello.. I have one question. How to create the pdu for long messages? like one textmessage body contain character greater then standard message limit.If you said about divide contain in standard message length and create the separate pdu and put in object array but how to set the message header information? PLz provide me some help I am stuck with this problem . – Zala Janaksinh Jan 24 '15 at 09:31
  • 1
    @ZalaJanaksinh: I havent work with SMS for a long time. But you can search keyword "Concatenated SMS". [Reference1](https://shekharbiradar.wordpress.com/send-and-receive-concatenated-sms-in-pdu-mode/) and [Reference2](http://en.wikipedia.org/wiki/Concatenated_SMS) – Trung Nguyen Jan 25 '15 at 01:55
  • @Jul thanks.. But any example for that. I will search all thing but can't get the proper result. I am steel try to find out solution. so plz if have you any idea about that please provide me at here... – Zala Janaksinh Jan 27 '15 at 06:00
  • Hi! I am trying to use your code, but i am stuck. Can't create smsMessage fromPdu. pdu was created and it's not null, but smsMessage that creates from it - null. Any ideas why it can happen? – Andriy Antonov Feb 05 '17 at 09:38
  • Error: Caused by: java.lang.SecurityException: Not allowed to start service Intent { act=android.provider.Telephony.SMS_RECEIVED cmp=com.android.mms/.transaction.SmsReceiverService (has extras) } without permission not exported from uid 10045 – Darush Oct 02 '17 at 16:42
1

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.

Femi
  • 64,273
  • 8
  • 118
  • 148
  • Looks promising thanks. I'll have to comb it over and see what I can use. I remember coming across it before, just not sure what made me not browse the source.. – StrikeForceZero Sep 09 '12 at 01:32
0

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.

nandeesh
  • 24,740
  • 6
  • 69
  • 79