Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package at.tuwien.mnsa.smssender; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang3.ArrayUtils; /** * * @author Thomas */ public class SMS { private String recipient; private String text; /** * Create a new SMS * @param recipient e.g. +436604678660 (international format) * @param text */ public SMS(String recipient, String text) { //normalize receipient if (recipient.startsWith("+")) { recipient = recipient.substring(1); } else if (recipient.startsWith("00")) { recipient = recipient.substring(2); } else if (recipient.startsWith("0")) { recipient = recipient.substring(1); } this.recipient = recipient; this.text = text; } /** * @return the recipient */ public String getRecipient() { return recipient; } /** * @return the text */ public String getText() { return text; } /** * Get the PDU representation of a single SMS message (<= 160 chars) * @return */ public SMSRepresentation getMessage() { //https://en.wikipedia.org/wiki/GSM_03.40 //https://stackoverflow.com/questions/12298558/how-to-build-concatenated-sms-pdu-getting-junk-chars //http://mobiletidings.com/2009/02/18/combining-sms-messages/ List<Byte> bytes = new ArrayList<>(); bytes.add((byte) 0x00); //SMSC Information bytes.add((byte) 0x11); //First octed of the SMS-Submit message bytes.add((byte) 0x00); //TP-Message-Reference (0: phone default) int lenRec = this.recipient.length(); bytes.add((byte) lenRec); bytes.add((byte) 0x91); bytes.addAll(Arrays.asList(ArrayUtils.toObject(getSemiOctet(this.recipient)))); bytes.add((byte) 0x00); //TP-PID Protocol Identifier bytes.add((byte) 0x00); //7 bit encoding bytes.add((byte) 0xAA); //validity (4 days) - @TODO: optional? SMSPDUConverter.SMSPDUConversionResult result = SMSPDUConverter.getInstance().getContent(this.text); int lenContent = result.len; //count of septets bytes.add((byte) lenContent); byte[] message = result.message; bytes.addAll(Arrays.asList(ArrayUtils.toObject(message))); String sMessage = DatatypeConverter .printHexBinary(ArrayUtils.toPrimitive(bytes.toArray(new Byte[bytes.size()]))); int smsLen = (sMessage.length() / 2) - 1; SMSRepresentation r = new SMSRepresentation(sMessage, smsLen); return r; } /** * Get the list of single SMS for a possible concatenated SMS * @return */ public List<SMSRepresentation> getMultiMessage() { List<SMSRepresentation> messages = new LinkedList<>(); //try to get all in a single message SMSRepresentation single = this.getMessage(); if (single.len <= 160) { messages.add(single); return messages; } //else, one message is not sufficient :-( //we have to split the messages String remainingText = this.text; int len = 153; int remaining = this.text.length(); List<SMSPDUConverter.SMSPDUConversionResult> messageResults = new ArrayList<>(); while (remaining > 0) { String text = remainingText.substring(0, Math.min(remainingText.length(), len)); SMSPDUConverter.SMSPDUConversionResult conversionResult = SMSPDUConverter.getInstance().getContent(text, 1); if (conversionResult.len > 153) { //we got too much :-( //try shorten it len--; continue; } //add the message messageResults.add(conversionResult); remaining = remaining - len; //continue in the text if (remaining > 0) { remainingText = remainingText.substring(len); } } for (int i = 0; i < messageResults.size(); i++) { List<Byte> bytes = new ArrayList<>(); bytes.add((byte) 0x00); //SMSC Information bytes.add((byte) 0x41); //First octed of the SMS-Submit message bytes.add((byte) i); //TP-Message-Reference (0: phone default) //The message reference should be different with each SMS-SUBMIT PDU sent. (mobiletidings.com) int lenRec = this.recipient.length(); bytes.add((byte) lenRec); bytes.add((byte) 0x91); bytes.addAll(Arrays.asList(ArrayUtils.toObject(getSemiOctet(this.recipient)))); bytes.add((byte) 0x00); //TP-PID Protocol Identifier bytes.add((byte) 0x00); //7 bit encoding //bytes.add((byte) 0xAA); //validity (4 days) - @TODO: optional? int lenContent = Math.min(160, messageResults.get(i).len + 7); //count of septets bytes.add((byte) lenContent); bytes.add((byte) 0x05); //User data header length (UDHL) bytes.addAll(getMultiMessagePayloadHeader(i + 1, messageResults.size())); byte[] message = messageResults.get(i).message; bytes.addAll(Arrays.asList(ArrayUtils.toObject(message))); String sMessage = DatatypeConverter .printHexBinary(ArrayUtils.toPrimitive(bytes.toArray(new Byte[bytes.size()]))); int smsLen = (sMessage.length() / 2) - 1; messages.add(new SMSRepresentation(sMessage, smsLen)); //System.out.println("length: " + (((messages.get(i).length()/2)-1))); } //return DatatypeConverter.printHexBinary(ArrayUtils.toPrimitive(bytes.toArray(new Byte[bytes.size()]))); return messages; } public class SMSRepresentation { public final String hex; public final int len; private SMSRepresentation(String hex, int len) { this.hex = hex; this.len = len; } } private List<Byte> getMultiMessagePayloadHeader(int part, int total) { List<Byte> header = new LinkedList<>(); header.add((byte) 0x0); //IEI -> Multimessage header.add((byte) 0x03); //Information element header length (3 more octets are coming!) header.add((byte) 0x0); //Ref nr (has to be the same - @TODO: Random?) header.add((byte) total); //# messages in total header.add((byte) part); //number of this message, starting at 1 return header; } /** * Convert to Semi Octet * e.g. Dez: 15 -> Hex 51 * Dez 50 -> Hex 05 * @param number Phone number String * @return */ private byte[] getSemiOctet(String number) { if (number.length() % 2 == 1) { number += "F"; } byte[] ret = new byte[number.length() / 2]; for (int i = 0; i < number.length() / 2; i++) { int lower = (byte) Integer.parseInt(number.substring((i * 2 + 1), i * 2 + 2), 16); int higher = (byte) Integer.parseInt(number.substring((i * 2), i * 2 + 1), 16); byte hex = (byte) ((lower << 4) | higher); ret[i] = hex; } return ret; } @Override public String toString() { return "+" + this.recipient + ": " + this.text; } }