Merge change 1127 into donut
* changes: SmsHeader rewrite, in preparation for migration to public API.
This commit is contained in:
@ -20,6 +20,7 @@ import android.os.Parcel;
|
||||
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.EncodeException;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.SmsMessageBase;
|
||||
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
|
||||
|
||||
@ -307,7 +308,8 @@ public class SmsMessage {
|
||||
|
||||
if (PHONE_TYPE_CDMA == activePhone) {
|
||||
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested, header);
|
||||
destinationAddress, message, statusReportRequested,
|
||||
SmsHeader.fromByteArray(header));
|
||||
} else {
|
||||
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested, header);
|
||||
@ -331,7 +333,7 @@ public class SmsMessage {
|
||||
|
||||
if (PHONE_TYPE_CDMA == activePhone) {
|
||||
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested);
|
||||
destinationAddress, message, statusReportRequested, null);
|
||||
} else {
|
||||
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested);
|
||||
|
@ -21,6 +21,7 @@ import android.telephony.TelephonyManager;
|
||||
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.EncodeException;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.SmsMessageBase;
|
||||
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
|
||||
|
||||
@ -369,7 +370,8 @@ public class SmsMessage {
|
||||
|
||||
if (PHONE_TYPE_CDMA == activePhone) {
|
||||
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested, header);
|
||||
destinationAddress, message, statusReportRequested,
|
||||
SmsHeader.fromByteArray(header));
|
||||
} else {
|
||||
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested, header);
|
||||
@ -395,7 +397,7 @@ public class SmsMessage {
|
||||
|
||||
if (PHONE_TYPE_CDMA == activePhone) {
|
||||
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested);
|
||||
destinationAddress, message, statusReportRequested, null);
|
||||
} else {
|
||||
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
|
||||
destinationAddress, message, statusReportRequested);
|
||||
@ -744,4 +746,3 @@ public class SmsMessage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ public class GsmAlphabet {
|
||||
return stringToGsm7BitPacked(data);
|
||||
}
|
||||
|
||||
int headerBits = header.length * 8;
|
||||
int headerBits = (header.length + 1) * 8;
|
||||
int headerSeptets = headerBits / 7;
|
||||
headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
|
||||
|
||||
@ -194,7 +194,8 @@ public class GsmAlphabet {
|
||||
(headerSeptets*7), true);
|
||||
|
||||
// Paste in the header
|
||||
System.arraycopy(header, 0, ret, 1, header.length);
|
||||
ret[1] = (byte)header.length;
|
||||
System.arraycopy(header, 0, ret, 2, header.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ public abstract class SMSDispatcher extends Handler {
|
||||
* CONCATENATED_16_BIT_REFERENCE message set. Should be
|
||||
* incremented for each set of concatenated messages.
|
||||
*/
|
||||
protected static int sConcatenatedRef;
|
||||
private static int sConcatenatedRef;
|
||||
|
||||
private SmsCounter mCounter;
|
||||
|
||||
@ -132,6 +132,11 @@ public abstract class SMSDispatcher extends Handler {
|
||||
private static SmsMessageBase mSmsMessageBase;
|
||||
private SmsMessageBase.SubmitPduBase mSubmitPduBase;
|
||||
|
||||
protected static int getNextConcatenatedRef() {
|
||||
sConcatenatedRef += 1;
|
||||
return sConcatenatedRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the per-application based SMS control, which only allows
|
||||
* a limit on the number of SMS/MMS messages an app can send in checking
|
||||
@ -419,12 +424,15 @@ public abstract class SMSDispatcher extends Handler {
|
||||
/**
|
||||
* If this is the last part send the parts out to the application, otherwise
|
||||
* the part is stored for later processing.
|
||||
*
|
||||
* NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
|
||||
*/
|
||||
protected void processMessagePart(SmsMessageBase sms, int referenceNumber,
|
||||
int sequence, int count, int destinationPort) {
|
||||
protected void processMessagePart(SmsMessageBase sms,
|
||||
SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
|
||||
|
||||
// Lookup all other related parts
|
||||
StringBuilder where = new StringBuilder("reference_number =");
|
||||
where.append(referenceNumber);
|
||||
where.append(concatRef.refNumber);
|
||||
where.append(" AND address = ?");
|
||||
String[] whereArgs = new String[] {sms.getOriginatingAddress()};
|
||||
|
||||
@ -433,20 +441,19 @@ public abstract class SMSDispatcher extends Handler {
|
||||
try {
|
||||
cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null);
|
||||
int cursorCount = cursor.getCount();
|
||||
if (cursorCount != count - 1) {
|
||||
if (cursorCount != concatRef.msgCount - 1) {
|
||||
// We don't have all the parts yet, store this one away
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("date", new Long(sms.getTimestampMillis()));
|
||||
values.put("pdu", HexDump.toHexString(sms.getPdu()));
|
||||
values.put("address", sms.getOriginatingAddress());
|
||||
values.put("reference_number", referenceNumber);
|
||||
values.put("count", count);
|
||||
values.put("sequence", sequence);
|
||||
if (destinationPort != -1) {
|
||||
values.put("destination_port", destinationPort);
|
||||
values.put("reference_number", concatRef.refNumber);
|
||||
values.put("count", concatRef.msgCount);
|
||||
values.put("sequence", concatRef.seqNumber);
|
||||
if (portAddrs != null) {
|
||||
values.put("destination_port", portAddrs.destPort);
|
||||
}
|
||||
mResolver.insert(mRawUri, values);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -454,7 +461,7 @@ public abstract class SMSDispatcher extends Handler {
|
||||
int pduColumn = cursor.getColumnIndex("pdu");
|
||||
int sequenceColumn = cursor.getColumnIndex("sequence");
|
||||
|
||||
pdus = new byte[count][];
|
||||
pdus = new byte[concatRef.msgCount][];
|
||||
for (int i = 0; i < cursorCount; i++) {
|
||||
cursor.moveToNext();
|
||||
int cursorSequence = (int)cursor.getLong(sequenceColumn);
|
||||
@ -462,7 +469,7 @@ public abstract class SMSDispatcher extends Handler {
|
||||
cursor.getString(pduColumn));
|
||||
}
|
||||
// This one isn't in the DB, so add it
|
||||
pdus[sequence - 1] = sms.getPdu();
|
||||
pdus[concatRef.seqNumber - 1] = sms.getPdu();
|
||||
|
||||
// Remove the parts from the database
|
||||
mResolver.delete(mRawUri, where.toString(), whereArgs);
|
||||
@ -473,31 +480,34 @@ public abstract class SMSDispatcher extends Handler {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(cleanup): The following code has duplicated logic with
|
||||
* the radio-specific dispatchMessage code, which is fragile,
|
||||
* in addition to being redundant. Instead, if this method
|
||||
* maybe returned the reassembled message (or just contents),
|
||||
* the following code (which is not really related to
|
||||
* reconstruction) could be better consolidated.
|
||||
*/
|
||||
|
||||
// Dispatch the PDUs to applications
|
||||
switch (destinationPort) {
|
||||
case SmsHeader.PORT_WAP_PUSH: {
|
||||
// Build up the data stream
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
for (int i = 0; i < count; i++) {
|
||||
SmsMessage msg = SmsMessage.createFromPdu(pdus[i]);
|
||||
byte[] data = msg.getUserData();
|
||||
output.write(data, 0, data.length);
|
||||
if (portAddrs != null) {
|
||||
if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
|
||||
// Build up the data stream
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
for (int i = 0; i < concatRef.msgCount; i++) {
|
||||
SmsMessage msg = SmsMessage.createFromPdu(pdus[i]);
|
||||
byte[] data = msg.getUserData();
|
||||
output.write(data, 0, data.length);
|
||||
}
|
||||
// Handle the PUSH
|
||||
mWapPush.dispatchWapPdu(output.toByteArray());
|
||||
} else {
|
||||
// The messages were sent to a port, so concoct a URI for it
|
||||
dispatchPortAddressedPdus(pdus, portAddrs.destPort);
|
||||
}
|
||||
|
||||
// Handle the PUSH
|
||||
mWapPush.dispatchWapPdu(output.toByteArray());
|
||||
break;
|
||||
}
|
||||
|
||||
case -1:
|
||||
} else {
|
||||
// The messages were not sent to a port
|
||||
dispatchPdus(pdus);
|
||||
break;
|
||||
|
||||
default:
|
||||
// The messages were sent to a port, so concoct a URI for it
|
||||
dispatchPortAddressedPdus(pdus, destinationPort);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,227 +16,233 @@
|
||||
|
||||
package com.android.internal.telephony;
|
||||
|
||||
import android.telephony.SmsMessage;
|
||||
|
||||
import com.android.internal.util.HexDump;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class represents a SMS user data header.
|
||||
*
|
||||
* SMS user data header, as specified in TS 23.040 9.2.3.24.
|
||||
*/
|
||||
public class SmsHeader {
|
||||
/** See TS 23.040 9.2.3.24 for description of this element ID. */
|
||||
public static final int CONCATENATED_8_BIT_REFERENCE = 0x00;
|
||||
/** See TS 23.040 9.2.3.24 for description of this element ID. */
|
||||
public static final int SPECIAL_SMS_MESSAGE_INDICATION = 0x01;
|
||||
/** See TS 23.040 9.2.3.24 for description of this element ID. */
|
||||
public static final int APPLICATION_PORT_ADDRESSING_8_BIT = 0x04;
|
||||
/** See TS 23.040 9.2.3.24 for description of this element ID. */
|
||||
public static final int APPLICATION_PORT_ADDRESSING_16_BIT= 0x05;
|
||||
/** See TS 23.040 9.2.3.24 for description of this element ID. */
|
||||
public static final int CONCATENATED_16_BIT_REFERENCE = 0x08;
|
||||
|
||||
// TODO(cleanup): this datastructure is generally referred to as
|
||||
// the 'user data header' or UDH, and so the class name should
|
||||
// change to reflect this...
|
||||
|
||||
/** SMS user data header information element identifiers.
|
||||
* (see TS 23.040 9.2.3.24)
|
||||
*/
|
||||
public static final int ELT_ID_CONCATENATED_8_BIT_REFERENCE = 0x00;
|
||||
public static final int ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION = 0x01;
|
||||
public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT = 0x04;
|
||||
public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT = 0x05;
|
||||
public static final int ELT_ID_SMSC_CONTROL_PARAMS = 0x06;
|
||||
public static final int ELT_ID_UDH_SOURCE_INDICATION = 0x07;
|
||||
public static final int ELT_ID_CONCATENATED_16_BIT_REFERENCE = 0x08;
|
||||
public static final int ELT_ID_WIRELESS_CTRL_MSG_PROTOCOL = 0x09;
|
||||
public static final int ELT_ID_TEXT_FORMATTING = 0x0A;
|
||||
public static final int ELT_ID_PREDEFINED_SOUND = 0x0B;
|
||||
public static final int ELT_ID_USER_DEFINED_SOUND = 0x0C;
|
||||
public static final int ELT_ID_PREDEFINED_ANIMATION = 0x0D;
|
||||
public static final int ELT_ID_LARGE_ANIMATION = 0x0E;
|
||||
public static final int ELT_ID_SMALL_ANIMATION = 0x0F;
|
||||
public static final int ELT_ID_LARGE_PICTURE = 0x10;
|
||||
public static final int ELT_ID_SMALL_PICTURE = 0x11;
|
||||
public static final int ELT_ID_VARIABLE_PICTURE = 0x12;
|
||||
public static final int ELT_ID_USER_PROMPT_INDICATOR = 0x13;
|
||||
public static final int ELT_ID_EXTENDED_OBJECT = 0x14;
|
||||
public static final int ELT_ID_REUSED_EXTENDED_OBJECT = 0x15;
|
||||
public static final int ELT_ID_COMPRESSION_CONTROL = 0x16;
|
||||
public static final int ELT_ID_OBJECT_DISTR_INDICATOR = 0x17;
|
||||
public static final int ELT_ID_STANDARD_WVG_OBJECT = 0x18;
|
||||
public static final int ELT_ID_CHARACTER_SIZE_WVG_OBJECT = 0x19;
|
||||
public static final int ELT_ID_EXTENDED_OBJECT_DATA_REQUEST_CMD = 0x1A;
|
||||
public static final int ELT_ID_RFC_822_EMAIL_HEADER = 0x20;
|
||||
public static final int ELT_ID_HYPERLINK_FORMAT_ELEMENT = 0x21;
|
||||
public static final int ELT_ID_REPLY_ADDRESS_ELEMENT = 0x22;
|
||||
public static final int ELT_ID_ENHANCED_VOICE_MAIL_INFORMATION = 0x23;
|
||||
|
||||
public static final int PORT_WAP_PUSH = 2948;
|
||||
public static final int PORT_WAP_WSP = 9200;
|
||||
public static final int PORT_WAP_WSP = 9200;
|
||||
|
||||
private byte[] m_data;
|
||||
private ArrayList<Element> m_elements = new ArrayList<Element>();
|
||||
public int nbrOfHeaders;
|
||||
public static class PortAddrs {
|
||||
public int destPort;
|
||||
public int origPort;
|
||||
public boolean areEightBits;
|
||||
}
|
||||
|
||||
public static class ConcatRef {
|
||||
public int refNumber;
|
||||
public int seqNumber;
|
||||
public int msgCount;
|
||||
public boolean isEightBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SmsHeader object from raw user data header bytes.
|
||||
*
|
||||
* @param data is user data header bytes
|
||||
* @return an SmsHeader object
|
||||
* A header element that is not explicitly parsed, meaning not
|
||||
* PortAddrs or ConcatRef.
|
||||
*/
|
||||
public static SmsHeader parse(byte[] data) {
|
||||
SmsHeader header = new SmsHeader();
|
||||
header.m_data = data;
|
||||
public static class MiscElt {
|
||||
public int id;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
header.nbrOfHeaders = 0;
|
||||
while (index < data.length) {
|
||||
int id = data[index++] & 0xff;
|
||||
int length = data[index++] & 0xff;
|
||||
byte[] elementData = new byte[length];
|
||||
System.arraycopy(data, index, elementData, 0, length);
|
||||
header.add(new Element(id, elementData));
|
||||
index += length;
|
||||
header.nbrOfHeaders++;
|
||||
public PortAddrs portAddrs;
|
||||
public ConcatRef concatRef;
|
||||
public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>();
|
||||
|
||||
public SmsHeader() {}
|
||||
|
||||
/**
|
||||
* Create structured SmsHeader object from serialized byte array representation.
|
||||
* (see TS 23.040 9.2.3.24)
|
||||
* @param data is user data header bytes
|
||||
* @return SmsHeader object
|
||||
*/
|
||||
public static SmsHeader fromByteArray(byte[] data) {
|
||||
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
while (inStream.available() > 0) {
|
||||
/**
|
||||
* NOTE: as defined in the spec, ConcatRef and PortAddr
|
||||
* fields should not reoccur, but if they do the last
|
||||
* occurrence is to be used.
|
||||
*/
|
||||
int id = inStream.read();
|
||||
int length = inStream.read();
|
||||
ConcatRef concatRef;
|
||||
PortAddrs portAddrs;
|
||||
switch (id) {
|
||||
case ELT_ID_CONCATENATED_8_BIT_REFERENCE:
|
||||
concatRef = new ConcatRef();
|
||||
concatRef.refNumber = inStream.read();
|
||||
concatRef.msgCount = inStream.read();
|
||||
concatRef.seqNumber = inStream.read();
|
||||
concatRef.isEightBits = true;
|
||||
smsHeader.concatRef = concatRef;
|
||||
break;
|
||||
case ELT_ID_CONCATENATED_16_BIT_REFERENCE:
|
||||
concatRef = new ConcatRef();
|
||||
concatRef.refNumber = (inStream.read() << 8) | inStream.read();
|
||||
concatRef.msgCount = inStream.read();
|
||||
concatRef.seqNumber = inStream.read();
|
||||
concatRef.isEightBits = false;
|
||||
smsHeader.concatRef = concatRef;
|
||||
break;
|
||||
case ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT:
|
||||
portAddrs = new PortAddrs();
|
||||
portAddrs.destPort = inStream.read();
|
||||
portAddrs.origPort = inStream.read();
|
||||
portAddrs.areEightBits = true;
|
||||
smsHeader.portAddrs = portAddrs;
|
||||
break;
|
||||
case ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT:
|
||||
portAddrs = new PortAddrs();
|
||||
portAddrs.destPort = (inStream.read() << 8) | inStream.read();
|
||||
portAddrs.origPort = (inStream.read() << 8) | inStream.read();
|
||||
portAddrs.areEightBits = false;
|
||||
smsHeader.portAddrs = portAddrs;
|
||||
break;
|
||||
default:
|
||||
MiscElt miscElt = new MiscElt();
|
||||
miscElt.id = id;
|
||||
miscElt.data = new byte[length];
|
||||
inStream.read(miscElt.data, 0, length);
|
||||
smsHeader.miscEltList.add(miscElt);
|
||||
}
|
||||
}
|
||||
return smsHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create serialized byte array representation from structured SmsHeader object.
|
||||
* (see TS 23.040 9.2.3.24)
|
||||
* @return Byte array representing the SmsHeader
|
||||
*/
|
||||
public static byte[] toByteArray(SmsHeader smsHeader) {
|
||||
if ((smsHeader.portAddrs == null) &&
|
||||
(smsHeader.concatRef == null) &&
|
||||
(smsHeader.miscEltList.size() == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
public SmsHeader() { }
|
||||
|
||||
/**
|
||||
* Returns the list of SmsHeader Elements that make up the header.
|
||||
*
|
||||
* @return the list of SmsHeader Elements.
|
||||
*/
|
||||
public ArrayList<Element> getElements() {
|
||||
return m_elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element to the SmsHeader.
|
||||
*
|
||||
* @param element to add.
|
||||
*/
|
||||
public void add(Element element) {
|
||||
m_elements.add(element);
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream(SmsMessage.MAX_USER_DATA_BYTES);
|
||||
ConcatRef concatRef = smsHeader.concatRef;
|
||||
if (concatRef != null) {
|
||||
if (concatRef.isEightBits) {
|
||||
outStream.write(ELT_ID_CONCATENATED_8_BIT_REFERENCE);
|
||||
outStream.write(3);
|
||||
outStream.write(concatRef.refNumber);
|
||||
} else {
|
||||
outStream.write(ELT_ID_CONCATENATED_16_BIT_REFERENCE);
|
||||
outStream.write(4);
|
||||
outStream.write(concatRef.refNumber >>> 8);
|
||||
outStream.write(concatRef.refNumber & 0x00FF);
|
||||
}
|
||||
outStream.write(concatRef.msgCount);
|
||||
outStream.write(concatRef.seqNumber);
|
||||
}
|
||||
PortAddrs portAddrs = smsHeader.portAddrs;
|
||||
if (portAddrs != null) {
|
||||
if (portAddrs.areEightBits) {
|
||||
outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT);
|
||||
outStream.write(2);
|
||||
outStream.write(portAddrs.destPort);
|
||||
outStream.write(portAddrs.origPort);
|
||||
} else {
|
||||
outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT);
|
||||
outStream.write(4);
|
||||
outStream.write(portAddrs.destPort >>> 8);
|
||||
outStream.write(portAddrs.destPort & 0x00FF);
|
||||
outStream.write(portAddrs.origPort >>> 8);
|
||||
outStream.write(portAddrs.origPort & 0x00FF);
|
||||
}
|
||||
}
|
||||
for (MiscElt miscElt : smsHeader.miscEltList) {
|
||||
outStream.write(miscElt.id);
|
||||
outStream.write(miscElt.data.length);
|
||||
outStream.write(miscElt.data, 0, miscElt.data.length);
|
||||
}
|
||||
return outStream.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("UDH LENGTH: " + m_data.length + " octets");
|
||||
builder.append("UDH: ");
|
||||
builder.append(HexDump.toHexString(m_data));
|
||||
builder.append("\n");
|
||||
|
||||
for (Element e : getElements()) {
|
||||
builder.append(" 0x" + HexDump.toHexString((byte)e.getID()) + " - ");
|
||||
switch (e.getID()) {
|
||||
case CONCATENATED_8_BIT_REFERENCE: {
|
||||
builder.append("Concatenated Short Message 8bit ref\n");
|
||||
byte[] data = e.getData();
|
||||
builder.append(" " + data.length + " (0x");
|
||||
builder.append(HexDump.toHexString((byte)data.length)
|
||||
+ ") Bytes - Information Element\n");
|
||||
builder.append(" " + data[0] + " : SM reference number\n");
|
||||
builder.append(" " + data[1] + " : number of messages\n");
|
||||
builder.append(" " + data[2] + " : this SM sequence number\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case CONCATENATED_16_BIT_REFERENCE: {
|
||||
builder.append("Concatenated Short Message 16bit ref\n");
|
||||
byte[] data = e.getData();
|
||||
builder.append(" " + data.length + " (0x");
|
||||
builder.append(HexDump.toHexString((byte)data.length)
|
||||
+ ") Bytes - Information Element\n");
|
||||
builder.append(" " + (data[0] & 0xff) * 256 + (data[1] & 0xff)
|
||||
+ " : SM reference number\n");
|
||||
builder.append(" " + data[2] + " : number of messages\n");
|
||||
builder.append(" " + data[3] + " : this SM sequence number\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case APPLICATION_PORT_ADDRESSING_8_BIT:
|
||||
{
|
||||
builder.append("Application port addressing 8bit\n");
|
||||
byte[] data = e.getData();
|
||||
|
||||
builder.append(" " + data.length + " (0x");
|
||||
builder.append(HexDump.toHexString(
|
||||
(byte)data.length) + ") Bytes - Information Element\n");
|
||||
|
||||
int source = (data[0] & 0xff);
|
||||
builder.append(" " + source + " : DESTINATION port\n");
|
||||
|
||||
int dest = (data[1] & 0xff);
|
||||
builder.append(" " + dest + " : SOURCE port\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case APPLICATION_PORT_ADDRESSING_16_BIT: {
|
||||
builder.append("Application port addressing 16bit\n");
|
||||
byte[] data = e.getData();
|
||||
|
||||
builder.append(" " + data.length + " (0x");
|
||||
builder.append(HexDump.toHexString((byte)data.length)
|
||||
+ ") Bytes - Information Element\n");
|
||||
|
||||
int source = (data[0] & 0xff) << 8;
|
||||
source |= (data[1] & 0xff);
|
||||
builder.append(" " + source + " : DESTINATION port\n");
|
||||
|
||||
int dest = (data[2] & 0xff) << 8;
|
||||
dest |= (data[3] & 0xff);
|
||||
builder.append(" " + dest + " : SOURCE port\n");
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
builder.append("Unknown element\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
builder.append("UserDataHeader ");
|
||||
builder.append("{ ConcatRef ");
|
||||
if (concatRef == null) {
|
||||
builder.append("unset");
|
||||
} else {
|
||||
builder.append("{ refNumber=" + concatRef.refNumber);
|
||||
builder.append(", msgCount=" + concatRef.msgCount);
|
||||
builder.append(", seqNumber=" + concatRef.seqNumber);
|
||||
builder.append(", isEightBits=" + concatRef.isEightBits);
|
||||
builder.append(" }");
|
||||
}
|
||||
|
||||
builder.append(", PortAddrs ");
|
||||
if (portAddrs == null) {
|
||||
builder.append("unset");
|
||||
} else {
|
||||
builder.append("{ destPort=" + portAddrs.destPort);
|
||||
builder.append(", origPort=" + portAddrs.origPort);
|
||||
builder.append(", areEightBits=" + portAddrs.areEightBits);
|
||||
builder.append(" }");
|
||||
}
|
||||
for (MiscElt miscElt : miscEltList) {
|
||||
builder.append(", MiscElt ");
|
||||
builder.append("{ id=" + miscElt.id);
|
||||
builder.append(", length=" + miscElt.data.length);
|
||||
builder.append(", data=" + HexDump.toHexString(miscElt.data));
|
||||
builder.append(" }");
|
||||
}
|
||||
builder.append(" }");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private int calcSize() {
|
||||
int size = 1; // +1 for the UDHL field
|
||||
for (Element e : m_elements) {
|
||||
size += e.getData().length;
|
||||
size += 2; // 1 byte ID, 1 byte length
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts SmsHeader object to a byte array as specified in TS 23.040 9.2.3.24.
|
||||
* @return Byte array representing the SmsHeader
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
if (m_elements.size() == 0) return null;
|
||||
|
||||
if (m_data == null) {
|
||||
int size = calcSize();
|
||||
int cur = 1;
|
||||
m_data = new byte[size];
|
||||
|
||||
m_data[0] = (byte) (size-1); // UDHL does not include itself
|
||||
|
||||
for (Element e : m_elements) {
|
||||
int length = e.getData().length;
|
||||
m_data[cur++] = (byte) e.getID();
|
||||
m_data[cur++] = (byte) length;
|
||||
System.arraycopy(e.getData(), 0, m_data, cur, length);
|
||||
cur += length;
|
||||
}
|
||||
}
|
||||
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single Element in the SMS User Data Header.
|
||||
*
|
||||
* See TS 23.040 9.2.3.24.
|
||||
*
|
||||
*/
|
||||
public static class Element {
|
||||
private byte[] m_data;
|
||||
private int m_id;
|
||||
|
||||
public Element(int id, byte[] data) {
|
||||
m_id = id;
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Information Element Identifier for this element.
|
||||
*
|
||||
* @return the IE identifier.
|
||||
*/
|
||||
public int getID() {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data portion of this element.
|
||||
*
|
||||
* @return element data.
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return m_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,18 +245,21 @@ public abstract class SmsMessageBase {
|
||||
/**
|
||||
* Returns an object representing the user data header
|
||||
*
|
||||
* @return an object representing the user data header
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public SmsHeader getUserDataHeader() {
|
||||
return userDataHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(cleanup): The term PDU is used in a seemingly non-unique
|
||||
* manner -- for example, what is the difference between this byte
|
||||
* array and the contents of SubmitPdu objects. Maybe a more
|
||||
* illustrative term would be appropriate.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the raw PDU for the message.
|
||||
*
|
||||
* @return the raw PDU for the message.
|
||||
*/
|
||||
public byte[] getPdu() {
|
||||
return mPdu;
|
||||
|
@ -29,7 +29,6 @@ import android.util.Log;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.SmsMessageBase;
|
||||
import com.android.internal.telephony.SMSDispatcher;
|
||||
//import com.android.internal.telephony.SMSDispatcher.SmsTracker;
|
||||
import com.android.internal.telephony.cdma.SmsMessage;
|
||||
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
|
||||
import com.android.internal.util.HexDump;
|
||||
@ -42,8 +41,11 @@ import java.util.HashMap;
|
||||
final class CdmaSMSDispatcher extends SMSDispatcher {
|
||||
private static final String TAG = "CDMA";
|
||||
|
||||
private CDMAPhone mCdmaPhone;
|
||||
|
||||
CdmaSMSDispatcher(CDMAPhone phone) {
|
||||
super(phone);
|
||||
mCdmaPhone = phone;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,50 +72,39 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
|
||||
if (smsb == null) {
|
||||
return;
|
||||
}
|
||||
SmsMessage sms = (SmsMessage) smsb;
|
||||
int teleService;
|
||||
boolean handled = false;
|
||||
|
||||
// Decode BD stream and set sms variables.
|
||||
SmsMessage sms = (SmsMessage) smsb;
|
||||
sms.parseSms();
|
||||
teleService = sms.getTeleService();
|
||||
int teleService = sms.getTeleService();
|
||||
boolean handled = false;
|
||||
|
||||
// Teleservices W(E)MT and VMN are handled together:
|
||||
if ((SmsEnvelope.TELESERVICE_WMT == teleService)
|
||||
||(SmsEnvelope.TELESERVICE_WEMT == teleService)
|
||||
||(SmsEnvelope.TELESERVICE_VMN == teleService)){
|
||||
if ((teleService == SmsEnvelope.TELESERVICE_WMT)
|
||||
|| (teleService == SmsEnvelope.TELESERVICE_WEMT)
|
||||
|| (teleService == SmsEnvelope.TELESERVICE_VMN)) {
|
||||
// From here on we need decoded BD.
|
||||
// Special case the message waiting indicator messages
|
||||
if (sms.isMWISetMessage()) {
|
||||
((CDMAPhone) mPhone).updateMessageWaitingIndicator(true);
|
||||
|
||||
if (sms.isMwiDontStore()) {
|
||||
handled = true;
|
||||
}
|
||||
|
||||
mCdmaPhone.updateMessageWaitingIndicator(true);
|
||||
handled |= sms.isMwiDontStore();
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG,
|
||||
"Received voice mail indicator set SMS shouldStore=" + !handled);
|
||||
Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
|
||||
}
|
||||
} else if (sms.isMWIClearMessage()) {
|
||||
((CDMAPhone) mPhone).updateMessageWaitingIndicator(false);
|
||||
|
||||
if (sms.isMwiDontStore()) {
|
||||
handled = true;
|
||||
}
|
||||
|
||||
mCdmaPhone.updateMessageWaitingIndicator(false);
|
||||
handled |= sms.isMwiDontStore();
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG,
|
||||
"Received voice mail indicator clear SMS shouldStore=" + !handled);
|
||||
Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null == sms.getUserData()){
|
||||
handled = true;
|
||||
if (sms.getUserData() == null) {
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG, "Received SMS without user data");
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (handled) return;
|
||||
@ -123,82 +114,44 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the headers to see if this is partial, or port addressed
|
||||
int referenceNumber = -1;
|
||||
int count = 0;
|
||||
int sequence = 0;
|
||||
int destPort = -1;
|
||||
// From here on we need BD distributed to SMS member variables.
|
||||
/**
|
||||
* TODO(cleanup): Why are we using a getter method for this
|
||||
* (and for so many other sms fields)? Trivial getters and
|
||||
* setters like this are direct violations of the style guide.
|
||||
* If the purpose is to protect agaist writes (by not
|
||||
* providing a setter) then any protection is illusory (and
|
||||
* hence bad) for cases where the values are not primitives,
|
||||
* such as this call for the header. Since this is an issue
|
||||
* with the public API it cannot be changed easily, but maybe
|
||||
* something can be done eventually.
|
||||
*/
|
||||
SmsHeader smsHeader = sms.getUserDataHeader();
|
||||
|
||||
SmsHeader header = sms.getUserDataHeader();
|
||||
if (header != null) {
|
||||
for (SmsHeader.Element element : header.getElements()) {
|
||||
try {
|
||||
switch (element.getID()) {
|
||||
case SmsHeader.CONCATENATED_8_BIT_REFERENCE: {
|
||||
byte[] data = element.getData();
|
||||
|
||||
referenceNumber = data[0] & 0xff;
|
||||
count = data[1] & 0xff;
|
||||
sequence = data[2] & 0xff;
|
||||
|
||||
// Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence
|
||||
// is zero, or sequence > count, ignore the entire element
|
||||
if (count == 0 || sequence == 0 || sequence > count) {
|
||||
referenceNumber = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SmsHeader.CONCATENATED_16_BIT_REFERENCE: {
|
||||
byte[] data = element.getData();
|
||||
|
||||
referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff);
|
||||
count = data[2] & 0xff;
|
||||
sequence = data[3] & 0xff;
|
||||
|
||||
// Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence
|
||||
// is zero, or sequence > count, ignore the entire element
|
||||
if (count == 0 || sequence == 0 || sequence > count) {
|
||||
referenceNumber = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: {
|
||||
byte[] data = element.getData();
|
||||
|
||||
destPort = (data[0] & 0xff) << 8;
|
||||
destPort |= (data[1] & 0xff);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
Log.e(TAG, "Bad element in header", e);
|
||||
return; // TODO: NACK the message or something, don't just discard.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (referenceNumber == -1) {
|
||||
// notify everyone of the message if it isn't partial
|
||||
/**
|
||||
* TODO(cleanup): Since both CDMA and GSM use the same header
|
||||
* format, this dispatch processing is naturally identical,
|
||||
* and code should probably not be replicated explicitly.
|
||||
*/
|
||||
// See if message is partial or port addressed.
|
||||
if ((smsHeader == null) || (smsHeader.concatRef == null)) {
|
||||
// Message is not partial (not part of concatenated sequence).
|
||||
byte[][] pdus = new byte[1][];
|
||||
pdus[0] = sms.getPdu();
|
||||
|
||||
if (destPort != -1) {// GSM-style WAP indication
|
||||
if (destPort == SmsHeader.PORT_WAP_PUSH) {
|
||||
if (smsHeader.portAddrs != null) {
|
||||
if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
|
||||
// GSM-style WAP indication
|
||||
mWapPush.dispatchWapPdu(sms.getUserData());
|
||||
}
|
||||
// The message was sent to a port, so concoct a URI for it
|
||||
dispatchPortAddressedPdus(pdus, destPort);
|
||||
// The message was sent to a port, so concoct a URI for it.
|
||||
dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
|
||||
} else {
|
||||
// It's a normal message, dispatch it
|
||||
// Normal short and non-port-addressed message, dispatch it.
|
||||
dispatchPdus(pdus);
|
||||
}
|
||||
} else {
|
||||
// Process the message part
|
||||
processMessagePart(sms, referenceNumber, sequence, count, destPort);
|
||||
// Process the message part.
|
||||
processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,41 +267,49 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
protected void sendMultipartText(String destinationAddress, String scAddress,
|
||||
protected void sendMultipartText(String destAddr, String scAddr,
|
||||
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
|
||||
ArrayList<PendingIntent> deliveryIntents) {
|
||||
|
||||
int ref = ++sConcatenatedRef & 0xff;
|
||||
/**
|
||||
* TODO(cleanup): There is no real code difference between
|
||||
* this and the GSM version, and hence it should be moved to
|
||||
* the base class or consolidated somehow, provided calling
|
||||
* the proper submitpdu stuff can be arranged.
|
||||
*/
|
||||
|
||||
for (int i = 0, count = parts.size(); i < count; i++) {
|
||||
// build SmsHeader data
|
||||
byte[] data = new byte[5];
|
||||
data[0] = (byte) SmsHeader.CONCATENATED_8_BIT_REFERENCE;
|
||||
data[1] = (byte) 3; // 3 bytes follow
|
||||
data[2] = (byte) ref; // reference #, unique per message
|
||||
data[3] = (byte) count; // total part count
|
||||
data[4] = (byte) (i + 1); // 1-based sequence
|
||||
int refNumber = getNextConcatenatedRef() & 0x00FF;
|
||||
|
||||
for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
|
||||
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
|
||||
concatRef.refNumber = refNumber;
|
||||
concatRef.seqNumber = i + 1; // 1-based sequence
|
||||
concatRef.msgCount = msgCount;
|
||||
concatRef.isEightBits = true;
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.concatRef = concatRef;
|
||||
|
||||
PendingIntent sentIntent = null;
|
||||
PendingIntent deliveryIntent = null;
|
||||
|
||||
if (sentIntents != null && sentIntents.size() > i) {
|
||||
sentIntent = sentIntents.get(i);
|
||||
}
|
||||
|
||||
PendingIntent deliveryIntent = null;
|
||||
if (deliveryIntents != null && deliveryIntents.size() > i) {
|
||||
deliveryIntent = deliveryIntents.get(i);
|
||||
}
|
||||
|
||||
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
|
||||
parts.get(i), deliveryIntent != null, data);
|
||||
SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(scAddr, destAddr,
|
||||
parts.get(i), deliveryIntent != null, smsHeader);
|
||||
|
||||
sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
|
||||
sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
|
||||
protected void sendSubmitPdu(SmsMessage.SubmitPdu submitPdu, PendingIntent sentIntent,
|
||||
PendingIntent deliveryIntent) {
|
||||
super.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent);
|
||||
sendRawPdu(submitPdu.encodedScAddress, submitPdu.encodedMessage,
|
||||
sentIntent, deliveryIntent);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
@ -276,6 +276,15 @@ public class SmsMessage extends SmsMessageBase {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(cleanup): why do getSubmitPdu methods take an scAddr input
|
||||
* and do nothing with it? GSM allows us to specify a SC (eg,
|
||||
* when responding to an SMS that explicitly requests the response
|
||||
* is sent to a specific SC), or pass null to use the default
|
||||
* value. Is there no similar notion in CDMA? Or do we just not
|
||||
* have it hooked up?
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get an SMS-SUBMIT PDU for a destination address and a message
|
||||
*
|
||||
@ -290,88 +299,53 @@ public class SmsMessage extends SmsMessageBase {
|
||||
* Returns null on encode error.
|
||||
* @hide
|
||||
*/
|
||||
public static SubmitPdu getSubmitPdu(String scAddr,
|
||||
String destAddr, String message,
|
||||
boolean statusReportRequested, byte[] headerData) {
|
||||
/**
|
||||
* TODO(cleanup): why does this method take an scAddr input
|
||||
* and do nothing with it? GSM allows us to specify a SC (eg,
|
||||
* when responding to an SMS that explicitly requests the
|
||||
* response is sent to a specific SC), or pass null to use the
|
||||
* default value. Is there no similar notion in CDMA? Or do
|
||||
* we just not have it hooked up?
|
||||
*/
|
||||
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
|
||||
boolean statusReportRequested, SmsHeader smsHeader) {
|
||||
|
||||
/**
|
||||
* TODO(cleanup): Do we really want silent failure like this?
|
||||
* Would it not be much more reasonable to make sure we don't
|
||||
* call this function if we really want nothing done?
|
||||
*/
|
||||
if (message == null || destAddr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserData uData = new UserData();
|
||||
uData.payloadStr = message;
|
||||
if(headerData != null) {
|
||||
/**
|
||||
* TODO(cleanup): we force the outside to deal with _all_
|
||||
* of the raw details of properly constructing serialized
|
||||
* headers, unserialze here, and then promptly reserialze
|
||||
* during encoding -- rather undesirable.
|
||||
*/
|
||||
uData.userDataHeader = SmsHeader.parse(headerData);
|
||||
}
|
||||
|
||||
return privateGetSubmitPdu(destAddr, statusReportRequested, uData, (headerData == null));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an SMS-SUBMIT PDU for a destination address and a message
|
||||
*
|
||||
* @param scAddress Service Centre address. Null means use default.
|
||||
* @return a <code>SubmitPdu</code> containing the encoded SC
|
||||
* address, if applicable, and the encoded message.
|
||||
* Returns null on encode error.
|
||||
*/
|
||||
public static SubmitPdu getSubmitPdu(String scAddress,
|
||||
String destinationAddress, String message,
|
||||
boolean statusReportRequested) {
|
||||
return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null);
|
||||
uData.userDataHeader = smsHeader;
|
||||
return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SMS-SUBMIT PDU for a data message to a destination address & port
|
||||
*
|
||||
* @param scAddress Service Centre address. null == use default
|
||||
* @param destinationAddress the address of the destination for the message
|
||||
* @param destinationPort the port to deliver the message to at the
|
||||
* @param scAddr Service Centre address. null == use default
|
||||
* @param destAddr the address of the destination for the message
|
||||
* @param destPort the port to deliver the message to at the
|
||||
* destination
|
||||
* @param data the data for the message
|
||||
* @return a <code>SubmitPdu</code> containing the encoded SC
|
||||
* address, if applicable, and the encoded message.
|
||||
* Returns null on encode error.
|
||||
*/
|
||||
public static SubmitPdu getSubmitPdu(String scAddress,
|
||||
String destAddr, short destinationPort, byte[] data,
|
||||
boolean statusReportRequested) {
|
||||
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, short destPort,
|
||||
byte[] data, boolean statusReportRequested) {
|
||||
|
||||
/**
|
||||
* TODO(cleanup): if we had properly exposed SmsHeader
|
||||
* information, this mess of many getSubmitPdu public
|
||||
* interface methods that currently pollute the api could have
|
||||
* been much more cleanly collapsed into one.
|
||||
* TODO(cleanup): this is not a general-purpose SMS creation
|
||||
* method, but rather something specialized to messages
|
||||
* containing OCTET encoded (meaning non-human-readable) user
|
||||
* data. The name should reflect that, and not just overload.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO(cleanup): header serialization should be put somewhere
|
||||
* canonical to allow proper debugging and reuse.
|
||||
*/
|
||||
byte[] destPort = new byte[4];
|
||||
destPort[0] = (byte) ((destinationPort >> 8) & 0xFF); // MSB of destination port
|
||||
destPort[1] = (byte) (destinationPort & 0xFF); // LSB of destination port
|
||||
destPort[2] = 0x00; // MSB of originating port
|
||||
destPort[3] = 0x00; // LSB of originating port
|
||||
SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
|
||||
portAddrs.destPort = destPort;
|
||||
portAddrs.origPort = 0;
|
||||
portAddrs.areEightBits = false;
|
||||
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.add(
|
||||
new SmsHeader.Element(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT, destPort));
|
||||
smsHeader.nbrOfHeaders = smsHeader.getElements().size();
|
||||
smsHeader.portAddrs = portAddrs;
|
||||
|
||||
UserData uData = new UserData();
|
||||
uData.userDataHeader = smsHeader;
|
||||
@ -379,7 +353,7 @@ public class SmsMessage extends SmsMessageBase {
|
||||
uData.msgEncodingSet = true;
|
||||
uData.payload = data;
|
||||
|
||||
return privateGetSubmitPdu(destAddr, statusReportRequested, uData, true);
|
||||
return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
|
||||
}
|
||||
|
||||
static class PduParser {
|
||||
@ -445,31 +419,23 @@ public class SmsMessage extends SmsMessageBase {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isMWIClearMessage() {
|
||||
if ((mBearerData != null) && (0 == mBearerData.numberOfMessages)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ((mBearerData != null) && (mBearerData.numberOfMessages == 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isMWISetMessage() {
|
||||
if ((mBearerData != null) && (mBearerData.numberOfMessages >0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ((mBearerData != null) && (mBearerData.numberOfMessages > 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isMwiDontStore() {
|
||||
if ((mBearerData != null) && (mBearerData.numberOfMessages >0)
|
||||
&& (null == mBearerData.userData)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ((mBearerData != null) &&
|
||||
(mBearerData.numberOfMessages > 0) &&
|
||||
(mBearerData.userData == null));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -478,7 +444,7 @@ public class SmsMessage extends SmsMessageBase {
|
||||
* shifted to the bits 31-16.
|
||||
*/
|
||||
public int getStatus() {
|
||||
return(status<<16);
|
||||
return (status << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -518,7 +484,7 @@ public class SmsMessage extends SmsMessageBase {
|
||||
*/
|
||||
private void parsePdu(byte[] pdu) {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
|
||||
DataInputStream dis = new DataInputStream(new BufferedInputStream(bais));
|
||||
DataInputStream dis = new DataInputStream(bais);
|
||||
byte length;
|
||||
int bearerDataLength;
|
||||
SmsEnvelope env = new SmsEnvelope();
|
||||
@ -568,38 +534,23 @@ public class SmsMessage extends SmsMessageBase {
|
||||
protected void parseSms() {
|
||||
mBearerData = BearerData.decode(mEnvelope.bearerData);
|
||||
messageRef = mBearerData.messageId;
|
||||
if (mBearerData.userData != null) {
|
||||
userData = mBearerData.userData.payload;
|
||||
userDataHeader = mBearerData.userData.userDataHeader;
|
||||
messageBody = mBearerData.userData.payloadStr;
|
||||
}
|
||||
|
||||
// TP-Message-Type-Indicator
|
||||
// (See 3GPP2 C.S0015-B, v2, 4.5.1)
|
||||
int messageType = mBearerData.messageType;
|
||||
|
||||
switch (messageType) {
|
||||
// TP-Message-Type-Indicator (See 3GPP2 C.S0015-B, v2, 4.5.1)
|
||||
switch (mBearerData.messageType) {
|
||||
case BearerData.MESSAGE_TYPE_USER_ACK:
|
||||
case BearerData.MESSAGE_TYPE_READ_ACK:
|
||||
case BearerData.MESSAGE_TYPE_DELIVER:
|
||||
// Deliver (mobile-terminated only)
|
||||
parseSmsDeliver();
|
||||
break;
|
||||
case BearerData.MESSAGE_TYPE_DELIVERY_ACK:
|
||||
parseSmsDeliveryAck();
|
||||
break;
|
||||
|
||||
default:
|
||||
// the rest of these
|
||||
throw new RuntimeException("Unsupported message type: " + messageType);
|
||||
throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(cleanup): why are there two nearly identical functions
|
||||
* below? More rubbish...
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses a SMS-DELIVER message. (mobile-terminated only)
|
||||
* See 3GPP2 C.S0015-B, v2, 4.4.1
|
||||
*/
|
||||
private void parseSmsDeliver() {
|
||||
if (originatingAddress != null) {
|
||||
originatingAddress.address = new String(originatingAddress.origBytes);
|
||||
if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
|
||||
@ -612,46 +563,13 @@ public class SmsMessage extends SmsMessageBase {
|
||||
|
||||
if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
|
||||
|
||||
parseUserData(mBearerData.userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a SMS-DELIVER message. (mobile-terminated only)
|
||||
* See 3GPP2 C.S0015-B, v2, 4.4.1
|
||||
*/
|
||||
private void parseSmsDeliveryAck() {
|
||||
if (originatingAddress != null) {
|
||||
originatingAddress.address = new String(originatingAddress.origBytes);
|
||||
if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
|
||||
+ originatingAddress.address);
|
||||
}
|
||||
|
||||
if (mBearerData.timeStamp != null) {
|
||||
scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp);
|
||||
}
|
||||
|
||||
if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
|
||||
|
||||
if (mBearerData.errorClass != BearerData.ERROR_UNDEFINED) {
|
||||
// TODO(Teleca): do we really want this test to occur only for DELIVERY_ACKs?
|
||||
if ((mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK) &&
|
||||
(mBearerData.errorClass != BearerData.ERROR_UNDEFINED)) {
|
||||
status = mBearerData.errorClass << 8;
|
||||
status |= mBearerData.messageStatus;
|
||||
}
|
||||
|
||||
parseUserData(mBearerData.userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy parsed user data out from internal datastructures.
|
||||
*/
|
||||
private void parseUserData(UserData uData) {
|
||||
if (uData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
userData = uData.payload;
|
||||
userDataHeader = uData.userDataHeader;
|
||||
messageBody = uData.payloadStr;
|
||||
|
||||
if (messageBody != null) {
|
||||
if (Config.LOGV) Log.v(LOG_TAG, "SMS message body: '" + messageBody + "'");
|
||||
parseMessageBody();
|
||||
@ -708,7 +626,7 @@ public class SmsMessage extends SmsMessageBase {
|
||||
* @return byte stream for SubmitPdu.
|
||||
*/
|
||||
private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
|
||||
UserData userData, boolean useNewId) {
|
||||
UserData userData) {
|
||||
|
||||
/**
|
||||
* TODO(cleanup): give this function a more meaningful name.
|
||||
@ -720,7 +638,7 @@ public class SmsMessage extends SmsMessageBase {
|
||||
BearerData bearerData = new BearerData();
|
||||
bearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
|
||||
|
||||
if (useNewId) setNextMessageId();
|
||||
if (userData != null) setNextMessageId();
|
||||
bearerData.messageId = nextMessageId;
|
||||
|
||||
bearerData.deliveryAckReq = statusReportRequested;
|
||||
@ -812,6 +730,15 @@ public class SmsMessage extends SmsMessageBase {
|
||||
dos.write(env.bearerData, 0, env.bearerData.length);
|
||||
dos.close();
|
||||
|
||||
/**
|
||||
* TODO(cleanup) -- This is the only place where mPdu is
|
||||
* defined, and this is not obviously the only place where
|
||||
* it needs to be defined. It would be much nicer if
|
||||
* accessing the serialized representation used a less
|
||||
* fragile mechanism. Maybe the getPdu method could
|
||||
* generate a representation if there was not yet one?
|
||||
*/
|
||||
|
||||
mPdu = baos.toByteArray();
|
||||
} catch (IOException ex) {
|
||||
Log.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex);
|
||||
|
@ -189,8 +189,12 @@ public final class BearerData{
|
||||
public int messageStatus = STATUS_UNDEFINED;
|
||||
|
||||
/**
|
||||
* 1-bit value that indicates whether a User Data Header is present.
|
||||
* 1-bit value that indicates whether a User Data Header (UDH) is present.
|
||||
* (See 3GPP2 C.S0015-B, v2, 4.5.1)
|
||||
*
|
||||
* NOTE: during encoding, this value will be set based on the
|
||||
* presence of a UDH in the structured data, any existing setting
|
||||
* will be overwritten.
|
||||
*/
|
||||
public boolean hasUserDataHeader;
|
||||
|
||||
@ -248,25 +252,27 @@ public final class BearerData{
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("BearerData:\n");
|
||||
builder.append(" messageType: " + messageType + "\n");
|
||||
builder.append(" messageId: " + (int)messageId + "\n");
|
||||
builder.append(" priority: " + (priorityIndicatorSet ? priority : "not set") + "\n");
|
||||
builder.append(" privacy: " + (privacyIndicatorSet ? privacy : "not set") + "\n");
|
||||
builder.append(" alert: " + (alertIndicatorSet ? alert : "not set") + "\n");
|
||||
builder.append(" displayMode: " + (displayModeSet ? displayMode : "not set") + "\n");
|
||||
builder.append(" language: " + (languageIndicatorSet ? language : "not set") + "\n");
|
||||
builder.append(" errorClass: " + (messageStatusSet ? errorClass : "not set") + "\n");
|
||||
builder.append(" msgStatus: " + (messageStatusSet ? messageStatus : "not set") + "\n");
|
||||
builder.append(" hasUserDataHeader: " + hasUserDataHeader + "\n");
|
||||
builder.append(" timeStamp: " + timeStamp + "\n");
|
||||
builder.append(" userAckReq: " + userAckReq + "\n");
|
||||
builder.append(" deliveryAckReq: " + deliveryAckReq + "\n");
|
||||
builder.append(" readAckReq: " + readAckReq + "\n");
|
||||
builder.append(" reportReq: " + reportReq + "\n");
|
||||
builder.append(" numberOfMessages: " + numberOfMessages + "\n");
|
||||
builder.append(" callbackNumber: " + callbackNumber + "\n");
|
||||
builder.append(" userData: " + userData + "\n");
|
||||
builder.append("BearerData ");
|
||||
builder.append("{ messageType=" + messageType);
|
||||
builder.append(", messageId=" + (int)messageId);
|
||||
builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset"));
|
||||
builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset"));
|
||||
builder.append(", alert=" + (alertIndicatorSet ? alert : "unset"));
|
||||
builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset"));
|
||||
builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
|
||||
builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
|
||||
builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
|
||||
builder.append(", timeStamp=" +
|
||||
((timeStamp != null) ? HexDump.toHexString(timeStamp) : "unset"));
|
||||
builder.append(", userAckReq=" + userAckReq);
|
||||
builder.append(", deliveryAckReq=" + deliveryAckReq);
|
||||
builder.append(", readAckReq=" + readAckReq);
|
||||
builder.append(", reportReq=" + reportReq);
|
||||
builder.append(", numberOfMessages=" + numberOfMessages);
|
||||
builder.append(", callbackNumber=" + callbackNumber);
|
||||
builder.append(", hasUserDataHeader=" + hasUserDataHeader);
|
||||
builder.append(", userData=" + userData);
|
||||
builder.append(" }");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@ -335,12 +341,19 @@ public final class BearerData{
|
||||
private static void encodeUserDataPayload(UserData uData)
|
||||
throws CodingException
|
||||
{
|
||||
byte[] headerData = null;
|
||||
if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader);
|
||||
int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet
|
||||
|
||||
byte[] payloadData;
|
||||
if (uData.msgEncodingSet) {
|
||||
if (uData.msgEncoding == UserData.ENCODING_OCTET) {
|
||||
if (uData.payload == null) {
|
||||
Log.e(LOG_TAG, "user data with octet encoding but null payload");
|
||||
// TODO(code_review): reasonable for fail case? or maybe bail on encoding?
|
||||
uData.payload = new byte[0];
|
||||
payloadData = new byte[0];
|
||||
} else {
|
||||
payloadData = uData.payload;
|
||||
}
|
||||
} else {
|
||||
if (uData.payloadStr == null) {
|
||||
@ -349,11 +362,11 @@ public final class BearerData{
|
||||
uData.payloadStr = "";
|
||||
}
|
||||
if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
|
||||
uData.payload = encode7bitGsm(uData.payloadStr);
|
||||
payloadData = encode7bitGsm(uData.payloadStr);
|
||||
} else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
|
||||
uData.payload = encode7bitAscii(uData.payloadStr);
|
||||
payloadData = encode7bitAscii(uData.payloadStr);
|
||||
} else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
|
||||
uData.payload = encodeUtf16(uData.payloadStr);
|
||||
payloadData = encodeUtf16(uData.payloadStr);
|
||||
} else {
|
||||
throw new CodingException("unsupported user data encoding (" +
|
||||
uData.msgEncoding + ")");
|
||||
@ -367,19 +380,28 @@ public final class BearerData{
|
||||
uData.payloadStr = "";
|
||||
}
|
||||
try {
|
||||
uData.payload = encode7bitAscii(uData.payloadStr);
|
||||
payloadData = encode7bitAscii(uData.payloadStr);
|
||||
uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
|
||||
} catch (CodingException ex) {
|
||||
uData.payload = encodeUtf16(uData.payloadStr);
|
||||
payloadData = encodeUtf16(uData.payloadStr);
|
||||
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
|
||||
}
|
||||
uData.msgEncodingSet = true;
|
||||
uData.numFields = uData.payloadStr.length();
|
||||
}
|
||||
if (uData.payload.length > SmsMessage.MAX_USER_DATA_BYTES) {
|
||||
throw new CodingException("encoded user data too large (" + uData.payload.length +
|
||||
|
||||
int totalLength = payloadData.length + headerDataLen;
|
||||
if (totalLength > SmsMessage.MAX_USER_DATA_BYTES) {
|
||||
throw new CodingException("encoded user data too large (" + totalLength +
|
||||
" > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
|
||||
}
|
||||
|
||||
uData.payload = new byte[totalLength];
|
||||
if (headerData != null) {
|
||||
uData.payload[0] = (byte)headerData.length;
|
||||
System.arraycopy(headerData, 0, uData.payload, 1, headerData.length);
|
||||
}
|
||||
System.arraycopy(payloadData, 0, uData.payload, headerDataLen, payloadData.length);
|
||||
}
|
||||
|
||||
private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
|
||||
@ -394,11 +416,6 @@ public final class BearerData{
|
||||
*
|
||||
*/
|
||||
int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
|
||||
byte[] headerData = null;
|
||||
if (bData.hasUserDataHeader) {
|
||||
headerData = bData.userData.userDataHeader.toByteArray();
|
||||
dataBits += headerData.length * 8;
|
||||
}
|
||||
int paramBits = dataBits + 13;
|
||||
if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
|
||||
(bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
|
||||
@ -413,7 +430,6 @@ public final class BearerData{
|
||||
outStream.write(8, bData.userData.msgType);
|
||||
}
|
||||
outStream.write(8, bData.userData.numFields);
|
||||
if (headerData != null) outStream.writeByteArray(headerData.length * 8, headerData);
|
||||
outStream.writeByteArray(dataBits, bData.userData.payload);
|
||||
if (paddingBits > 0) outStream.write(paddingBits, 0);
|
||||
}
|
||||
@ -557,6 +573,8 @@ public final class BearerData{
|
||||
* @return data byta array of raw encoded SMS bearer data.
|
||||
*/
|
||||
public static byte[] encode(BearerData bData) {
|
||||
bData.hasUserDataHeader = ((bData.userData != null) &&
|
||||
(bData.userData.userDataHeader != null));
|
||||
try {
|
||||
BitwiseOutputStream outStream = new BitwiseOutputStream(200);
|
||||
outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
|
||||
@ -723,11 +741,11 @@ public final class BearerData{
|
||||
{
|
||||
int offset = 0;
|
||||
if (hasUserDataHeader) {
|
||||
int udhLen = userData.payload[0];
|
||||
offset += udhLen;
|
||||
int udhLen = userData.payload[0] & 0x00FF;
|
||||
offset += udhLen + 1;
|
||||
byte[] headerData = new byte[udhLen];
|
||||
System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
|
||||
userData.userDataHeader = SmsHeader.parse(headerData);
|
||||
userData.userDataHeader = SmsHeader.fromByteArray(headerData);
|
||||
}
|
||||
switch (userData.msgEncoding) {
|
||||
case UserData.ENCODING_OCTET:
|
||||
|
@ -93,14 +93,15 @@ public class UserData {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("UserData:\n");
|
||||
builder.append(" msgEncoding: " + (msgEncodingSet ? msgEncoding : "not set") + "\n");
|
||||
builder.append(" msgType: " + msgType + "\n");
|
||||
builder.append(" paddingBits: " + paddingBits + "\n");
|
||||
builder.append(" numFields: " + (int)numFields + "\n");
|
||||
builder.append(" userDataHeader: " + userDataHeader + "\n");
|
||||
builder.append(" payload: '" + HexDump.toHexString(payload) + "'");
|
||||
builder.append(", payloadStr: '" + payloadStr + "'");
|
||||
builder.append("UserData ");
|
||||
builder.append("{ msgEncoding=" + (msgEncodingSet ? msgEncoding : "unset"));
|
||||
builder.append(", msgType=" + msgType);
|
||||
builder.append(", paddingBits=" + paddingBits);
|
||||
builder.append(", numFields=" + (int)numFields);
|
||||
builder.append(", userDataHeader=" + userDataHeader);
|
||||
builder.append(", payload='" + HexDump.toHexString(payload) + "'");
|
||||
builder.append(", payloadStr='" + payloadStr + "'");
|
||||
builder.append(" }");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
@ -39,8 +39,11 @@ import java.util.HashMap;
|
||||
final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
private static final String TAG = "GSM";
|
||||
|
||||
private GSMPhone mGsmPhone;
|
||||
|
||||
GsmSMSDispatcher(GSMPhone phone) {
|
||||
super(phone);
|
||||
mGsmPhone = phone;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,110 +100,41 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|
||||
// Special case the message waiting indicator messages
|
||||
if (sms.isMWISetMessage()) {
|
||||
((GSMPhone) mPhone).updateMessageWaitingIndicator(true);
|
||||
|
||||
if (sms.isMwiDontStore()) {
|
||||
handled = true;
|
||||
}
|
||||
|
||||
mGsmPhone.updateMessageWaitingIndicator(true);
|
||||
handled |= sms.isMwiDontStore();
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG,
|
||||
"Received voice mail indicator set SMS shouldStore="
|
||||
+ !handled);
|
||||
Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
|
||||
}
|
||||
} else if (sms.isMWIClearMessage()) {
|
||||
((GSMPhone) mPhone).updateMessageWaitingIndicator(false);
|
||||
|
||||
if (sms.isMwiDontStore()) {
|
||||
handled = true;
|
||||
}
|
||||
|
||||
mGsmPhone.updateMessageWaitingIndicator(false);
|
||||
handled |= sms.isMwiDontStore();
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG,
|
||||
"Received voice mail indicator clear SMS shouldStore="
|
||||
+ !handled);
|
||||
Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
return;
|
||||
}
|
||||
if (handled) return;
|
||||
|
||||
// Parse the headers to see if this is partial, or port addressed
|
||||
int referenceNumber = -1;
|
||||
int count = 0;
|
||||
int sequence = 0;
|
||||
int destPort = -1;
|
||||
|
||||
SmsHeader header = sms.getUserDataHeader();
|
||||
if (header != null) {
|
||||
for (SmsHeader.Element element : header.getElements()) {
|
||||
try {
|
||||
switch (element.getID()) {
|
||||
case SmsHeader.CONCATENATED_8_BIT_REFERENCE: {
|
||||
byte[] data = element.getData();
|
||||
|
||||
referenceNumber = data[0] & 0xff;
|
||||
count = data[1] & 0xff;
|
||||
sequence = data[2] & 0xff;
|
||||
|
||||
// Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence
|
||||
// is zero, or sequence > count, ignore the entire element
|
||||
if (count == 0 || sequence == 0 || sequence > count) {
|
||||
referenceNumber = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SmsHeader.CONCATENATED_16_BIT_REFERENCE: {
|
||||
byte[] data = element.getData();
|
||||
|
||||
referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff);
|
||||
count = data[2] & 0xff;
|
||||
sequence = data[3] & 0xff;
|
||||
|
||||
// Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence
|
||||
// is zero, or sequence > count, ignore the entire element
|
||||
if (count == 0 || sequence == 0 || sequence > count) {
|
||||
referenceNumber = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: {
|
||||
byte[] data = element.getData();
|
||||
|
||||
destPort = (data[0] & 0xff) << 8;
|
||||
destPort |= (data[1] & 0xff);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
Log.e(TAG, "Bad element in header", e);
|
||||
return; // TODO: NACK the message or something, don't just discard.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (referenceNumber == -1) {
|
||||
// notify everyone of the message if it isn't partial
|
||||
SmsHeader smsHeader = sms.getUserDataHeader();
|
||||
// See if message is partial or port addressed.
|
||||
if ((smsHeader == null) || (smsHeader.concatRef == null)) {
|
||||
// Message is not partial (not part of concatenated sequence).
|
||||
byte[][] pdus = new byte[1][];
|
||||
pdus[0] = sms.getPdu();
|
||||
|
||||
if (destPort != -1) {
|
||||
if (destPort == SmsHeader.PORT_WAP_PUSH) {
|
||||
if (smsHeader.portAddrs != null) {
|
||||
if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
|
||||
mWapPush.dispatchWapPdu(sms.getUserData());
|
||||
}
|
||||
// The message was sent to a port, so concoct a URI for it
|
||||
dispatchPortAddressedPdus(pdus, destPort);
|
||||
// The message was sent to a port, so concoct a URI for it.
|
||||
dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
|
||||
} else {
|
||||
// It's a normal message, dispatch it
|
||||
// Normal short and non-port-addressed message, dispatch it.
|
||||
dispatchPdus(pdus);
|
||||
}
|
||||
} else {
|
||||
// Process the message part
|
||||
processMessagePart(sms, referenceNumber, sequence, count, destPort);
|
||||
// Process the message part.
|
||||
processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,28 +142,30 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
protected void sendMultipartText(String destinationAddress, String scAddress,
|
||||
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
|
||||
ArrayList<PendingIntent> deliveryIntents) {
|
||||
int ref = ++sConcatenatedRef & 0xff;
|
||||
|
||||
for (int i = 0, count = parts.size(); i < count; i++) {
|
||||
// build SmsHeader
|
||||
byte[] data = new byte[3];
|
||||
data[0] = (byte) ref; // reference #, unique per message
|
||||
data[1] = (byte) count; // total part count
|
||||
data[2] = (byte) (i + 1); // 1-based sequence
|
||||
SmsHeader header = new SmsHeader();
|
||||
header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
|
||||
int refNumber = getNextConcatenatedRef() & 0x00FF;
|
||||
|
||||
for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
|
||||
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
|
||||
concatRef.refNumber = refNumber;
|
||||
concatRef.seqNumber = i + 1; // 1-based sequence
|
||||
concatRef.msgCount = msgCount;
|
||||
concatRef.isEightBits = false;
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.concatRef = concatRef;
|
||||
|
||||
PendingIntent sentIntent = null;
|
||||
PendingIntent deliveryIntent = null;
|
||||
|
||||
if (sentIntents != null && sentIntents.size() > i) {
|
||||
sentIntent = sentIntents.get(i);
|
||||
}
|
||||
|
||||
PendingIntent deliveryIntent = null;
|
||||
if (deliveryIntents != null && deliveryIntents.size() > i) {
|
||||
deliveryIntent = deliveryIntents.get(i);
|
||||
}
|
||||
|
||||
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
|
||||
parts.get(i), deliveryIntent != null, header.toByteArray());
|
||||
parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader));
|
||||
|
||||
sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
|
||||
}
|
||||
@ -259,18 +195,16 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
* to the recipient. The raw pdu of the status report is in the
|
||||
* extended data ("pdu").
|
||||
*/
|
||||
private void sendMultipartTextWithPermit(String destinationAddress,
|
||||
private void sendMultipartTextWithPermit(String destinationAddress,
|
||||
String scAddress, ArrayList<String> parts,
|
||||
ArrayList<PendingIntent> sentIntents,
|
||||
ArrayList<PendingIntent> sentIntents,
|
||||
ArrayList<PendingIntent> deliveryIntents) {
|
||||
|
||||
PendingIntent sentIntent = null;
|
||||
PendingIntent deliveryIntent = null;
|
||||
|
||||
|
||||
// check if in service
|
||||
int ss = mPhone.getServiceState().getState();
|
||||
if (ss != ServiceState.STATE_IN_SERVICE) {
|
||||
for (int i = 0, count = parts.size(); i < count; i++) {
|
||||
PendingIntent sentIntent = null;
|
||||
if (sentIntents != null && sentIntents.size() > i) {
|
||||
sentIntent = sentIntents.get(i);
|
||||
}
|
||||
@ -280,26 +214,29 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
return;
|
||||
}
|
||||
|
||||
int ref = ++sConcatenatedRef & 0xff;
|
||||
int refNumber = getNextConcatenatedRef() & 0x00FF;
|
||||
|
||||
for (int i = 0, count = parts.size(); i < count; i++) {
|
||||
// build SmsHeader
|
||||
byte[] data = new byte[3];
|
||||
data[0] = (byte) ref; // reference #, unique per message
|
||||
data[1] = (byte) count; // total part count
|
||||
data[2] = (byte) (i + 1); // 1-based sequence
|
||||
SmsHeader header = new SmsHeader();
|
||||
header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
|
||||
|
||||
for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
|
||||
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
|
||||
concatRef.refNumber = refNumber;
|
||||
concatRef.seqNumber = i + 1; // 1-based sequence
|
||||
concatRef.msgCount = msgCount;
|
||||
concatRef.isEightBits = false;
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.concatRef = concatRef;
|
||||
|
||||
PendingIntent sentIntent = null;
|
||||
if (sentIntents != null && sentIntents.size() > i) {
|
||||
sentIntent = sentIntents.get(i);
|
||||
}
|
||||
|
||||
PendingIntent deliveryIntent = null;
|
||||
if (deliveryIntents != null && deliveryIntents.size() > i) {
|
||||
deliveryIntent = deliveryIntents.get(i);
|
||||
}
|
||||
|
||||
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
|
||||
parts.get(i), deliveryIntent != null, header.toByteArray());
|
||||
parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader));
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("smsc", pdus.encodedScAddress);
|
||||
@ -307,7 +244,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|
||||
SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent);
|
||||
sendSms(tracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@ -376,4 +313,3 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -330,9 +330,20 @@ public class SmsMessage extends SmsMessageBase{
|
||||
public static SubmitPdu getSubmitPdu(String scAddress,
|
||||
String destinationAddress, short destinationPort, byte[] data,
|
||||
boolean statusReportRequested) {
|
||||
if (data.length > (MAX_USER_DATA_BYTES - 7 /* UDH size */)) {
|
||||
|
||||
SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
|
||||
portAddrs.destPort = destinationPort;
|
||||
portAddrs.origPort = 0;
|
||||
portAddrs.areEightBits = false;
|
||||
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.portAddrs = portAddrs;
|
||||
|
||||
byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader);
|
||||
|
||||
if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) {
|
||||
Log.e(LOG_TAG, "SMS data message may only contain "
|
||||
+ (MAX_USER_DATA_BYTES - 7) + " bytes");
|
||||
+ (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -348,21 +359,12 @@ public class SmsMessage extends SmsMessageBase{
|
||||
|
||||
// (no TP-Validity-Period)
|
||||
|
||||
// User data size
|
||||
bo.write(data.length + 7);
|
||||
// Total size
|
||||
bo.write(data.length + smsHeaderData.length + 1);
|
||||
|
||||
// User data header size
|
||||
bo.write(0x06); // header is 6 octets
|
||||
|
||||
// User data header, indicating the destination port
|
||||
bo.write(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT); // port
|
||||
// addressing
|
||||
// header
|
||||
bo.write(0x04); // each port is 2 octets
|
||||
bo.write((destinationPort >> 8) & 0xFF); // MSB of destination port
|
||||
bo.write(destinationPort & 0xFF); // LSB of destination port
|
||||
bo.write(0x00); // MSB of originating port
|
||||
bo.write(0x00); // LSB of originating port
|
||||
// User data header
|
||||
bo.write(smsHeaderData.length);
|
||||
bo.write(smsHeaderData, 0, smsHeaderData.length);
|
||||
|
||||
// User data
|
||||
bo.write(data, 0, data.length);
|
||||
@ -562,7 +564,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
|
||||
byte[] udh = new byte[userDataHeaderLength];
|
||||
System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);
|
||||
userDataHeader = SmsHeader.parse(udh);
|
||||
userDataHeader = SmsHeader.fromByteArray(udh);
|
||||
offset += userDataHeaderLength;
|
||||
|
||||
int headerBits = (userDataHeaderLength + 1) * 8;
|
||||
|
@ -135,6 +135,81 @@ public class CdmaSmsTest extends AndroidTestCase {
|
||||
assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testUserDataHeaderConcatRefFeedback() throws Exception {
|
||||
BearerData bearerData = new BearerData();
|
||||
bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
|
||||
bearerData.messageId = 55;
|
||||
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
|
||||
concatRef.refNumber = 0xEE;
|
||||
concatRef.msgCount = 2;
|
||||
concatRef.seqNumber = 2;
|
||||
concatRef.isEightBits = true;
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.concatRef = concatRef;
|
||||
byte[] encodedHeader = SmsHeader.toByteArray(smsHeader);
|
||||
SmsHeader decodedHeader = SmsHeader.fromByteArray(encodedHeader);
|
||||
assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
|
||||
assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
|
||||
assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
|
||||
assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
|
||||
assertEquals(decodedHeader.portAddrs, null);
|
||||
UserData userData = new UserData();
|
||||
userData.payloadStr = "User Data Header (UDH) feedback test";
|
||||
userData.userDataHeader = smsHeader;
|
||||
bearerData.userData = userData;
|
||||
byte[] encodedSms = BearerData.encode(bearerData);
|
||||
BearerData revBearerData = BearerData.decode(encodedSms);
|
||||
decodedHeader = revBearerData.userData.userDataHeader;
|
||||
assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
|
||||
assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
|
||||
assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
|
||||
assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
|
||||
assertEquals(decodedHeader.portAddrs, null);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testUserDataHeaderMixedFeedback() throws Exception {
|
||||
BearerData bearerData = new BearerData();
|
||||
bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
|
||||
bearerData.messageId = 42;
|
||||
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
|
||||
concatRef.refNumber = 0x34;
|
||||
concatRef.msgCount = 5;
|
||||
concatRef.seqNumber = 2;
|
||||
concatRef.isEightBits = false;
|
||||
SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
|
||||
portAddrs.destPort = 88;
|
||||
portAddrs.origPort = 66;
|
||||
portAddrs.areEightBits = false;
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.concatRef = concatRef;
|
||||
smsHeader.portAddrs = portAddrs;
|
||||
byte[] encodedHeader = SmsHeader.toByteArray(smsHeader);
|
||||
SmsHeader decodedHeader = SmsHeader.fromByteArray(encodedHeader);
|
||||
assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
|
||||
assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
|
||||
assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
|
||||
assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
|
||||
assertEquals(decodedHeader.portAddrs.destPort, portAddrs.destPort);
|
||||
assertEquals(decodedHeader.portAddrs.origPort, portAddrs.origPort);
|
||||
assertEquals(decodedHeader.portAddrs.areEightBits, portAddrs.areEightBits);
|
||||
UserData userData = new UserData();
|
||||
userData.payloadStr = "User Data Header (UDH) feedback test";
|
||||
userData.userDataHeader = smsHeader;
|
||||
bearerData.userData = userData;
|
||||
byte[] encodedSms = BearerData.encode(bearerData);
|
||||
BearerData revBearerData = BearerData.decode(encodedSms);
|
||||
decodedHeader = revBearerData.userData.userDataHeader;
|
||||
assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
|
||||
assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
|
||||
assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
|
||||
assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
|
||||
assertEquals(decodedHeader.portAddrs.destPort, portAddrs.destPort);
|
||||
assertEquals(decodedHeader.portAddrs.origPort, portAddrs.origPort);
|
||||
assertEquals(decodedHeader.portAddrs.areEightBits, portAddrs.areEightBits);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testReplyOption() throws Exception {
|
||||
String pdu1 = "0003104090011648b6a794e0705476bf77bceae934fe5f6d94d87450080a0180";
|
||||
|
@ -69,10 +69,15 @@ public class SMSTest extends AndroidTestCase {
|
||||
|
||||
SmsHeader header = sms.getUserDataHeader();
|
||||
assertNotNull(header);
|
||||
|
||||
Iterator<SmsHeader.Element> elements = header.getElements().iterator();
|
||||
assertNotNull(elements);
|
||||
|
||||
assertNotNull(header.concatRef);
|
||||
assertEquals(header.concatRef.refNumber, 42);
|
||||
assertEquals(header.concatRef.msgCount, 2);
|
||||
assertEquals(header.concatRef.seqNumber, 1);
|
||||
assertEquals(header.concatRef.isEightBits, true);
|
||||
assertNotNull(header.portAddrs);
|
||||
assertEquals(header.portAddrs.destPort, 2948);
|
||||
assertEquals(header.portAddrs.origPort, 9200);
|
||||
assertEquals(header.portAddrs.areEightBits, false);
|
||||
|
||||
pdu = "07914140279510F6440A8111110301003BF56080207130238A3B0B05040B8423F"
|
||||
+ "000032A0202362E3130322E3137312E3135302F524E453955304A6D7135514141"
|
||||
@ -81,9 +86,15 @@ public class SMSTest extends AndroidTestCase {
|
||||
|
||||
header = sms.getUserDataHeader();
|
||||
assertNotNull(header);
|
||||
|
||||
elements = header.getElements().iterator();
|
||||
assertNotNull(elements);
|
||||
assertNotNull(header.concatRef);
|
||||
assertEquals(header.concatRef.refNumber, 42);
|
||||
assertEquals(header.concatRef.msgCount, 2);
|
||||
assertEquals(header.concatRef.seqNumber, 2);
|
||||
assertEquals(header.concatRef.isEightBits, true);
|
||||
assertNotNull(header.portAddrs);
|
||||
assertEquals(header.portAddrs.destPort, 2948);
|
||||
assertEquals(header.portAddrs.origPort, 9200);
|
||||
assertEquals(header.portAddrs.areEightBits, false);
|
||||
|
||||
/*
|
||||
* UCS-2 encoded SMS
|
||||
|
@ -28,18 +28,20 @@ public class GsmAlphabetTest extends TestCase {
|
||||
|
||||
@SmallTest
|
||||
public void test7bitWithHeader() throws Exception {
|
||||
byte[] data = new byte[3];
|
||||
data[0] = (byte) 1;
|
||||
data[1] = (byte) 2;
|
||||
data[2] = (byte) 2;
|
||||
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
|
||||
concatRef.refNumber = 1;
|
||||
concatRef.seqNumber = 2;
|
||||
concatRef.msgCount = 2;
|
||||
concatRef.isEightBits = true;
|
||||
SmsHeader header = new SmsHeader();
|
||||
header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
|
||||
header.concatRef = concatRef;
|
||||
|
||||
String message = "aaaaaaaaaabbbbbbbbbbcccccccccc";
|
||||
byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header.toByteArray());
|
||||
String message = "aaaaaaaaaabbbbbbbbbbcccccccccc";
|
||||
byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message,
|
||||
SmsHeader.toByteArray(header));
|
||||
int septetCount = GsmAlphabet.countGsmSeptets(message, false);
|
||||
String parsedMessage = GsmAlphabet.gsm7BitPackedToString(
|
||||
userData, header.toByteArray().length+1, septetCount, 1);
|
||||
userData, SmsHeader.toByteArray(header).length+1, septetCount, 1);
|
||||
assertEquals(message, parsedMessage);
|
||||
}
|
||||
|
||||
@ -306,4 +308,3 @@ public class GsmAlphabetTest extends TestCase {
|
||||
GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,35 +34,38 @@ public class SMSDispatcherTest extends AndroidTestCase {
|
||||
public void testCMT1() throws Exception {
|
||||
SmsMessage sms;
|
||||
SmsHeader header;
|
||||
Iterator<SmsHeader.Element> elements;
|
||||
|
||||
String[] lines = new String[2];
|
||||
|
||||
lines[0] = "+CMT: ,158";
|
||||
|
||||
lines[0] = "+CMT: ,158";
|
||||
lines[1] = "07914140279510F6440A8111110301003BF56080426101748A8C0B05040B"
|
||||
+ "8423F000035502010106276170706C69636174696F6E2F766E642E776170"
|
||||
+ "2E6D6D732D6D65737361676500AF848D0185B4848C8298524F347839776F"
|
||||
+ "7547514D4141424C3641414141536741415A4B554141414141008D908918"
|
||||
+ "802B31363530323438363137392F545950453D504C4D4E008A808E028000"
|
||||
+ "88058103093A8083687474703A2F2F36";
|
||||
|
||||
|
||||
sms = SmsMessage.newFromCMT(lines);
|
||||
header = sms.getUserDataHeader();
|
||||
assertNotNull(header);
|
||||
assertNotNull(sms.getUserData());
|
||||
|
||||
elements = header.getElements().iterator();
|
||||
assertNotNull(elements);
|
||||
assertNotNull(header.concatRef);
|
||||
assertEquals(header.concatRef.refNumber, 85);
|
||||
assertEquals(header.concatRef.msgCount, 2);
|
||||
assertEquals(header.concatRef.seqNumber, 1);
|
||||
assertEquals(header.concatRef.isEightBits, true);
|
||||
assertNotNull(header.portAddrs);
|
||||
assertEquals(header.portAddrs.destPort, 2948);
|
||||
assertEquals(header.portAddrs.origPort, 9200);
|
||||
assertEquals(header.portAddrs.areEightBits, false);
|
||||
}
|
||||
|
||||
|
||||
@MediumTest
|
||||
public void testCMT2() throws Exception {
|
||||
SmsMessage sms;
|
||||
SmsHeader header;
|
||||
Iterator<SmsHeader.Element> elements;
|
||||
|
||||
String[] lines = new String[2];
|
||||
|
||||
|
||||
lines[0] = "+CMT: ,77";
|
||||
lines[1] = "07914140279510F6440A8111110301003BF56080426101848A3B0B05040B8423F"
|
||||
@ -71,12 +74,17 @@ public class SMSDispatcherTest extends AndroidTestCase {
|
||||
|
||||
sms = SmsMessage.newFromCMT(lines);
|
||||
header = sms.getUserDataHeader();
|
||||
System.out.println("header = " + header);
|
||||
assertNotNull(header);
|
||||
assertNotNull(sms.getUserData());
|
||||
|
||||
elements = header.getElements().iterator();
|
||||
assertNotNull(elements);
|
||||
assertNotNull(header.concatRef);
|
||||
assertEquals(header.concatRef.refNumber, 85);
|
||||
assertEquals(header.concatRef.msgCount, 2);
|
||||
assertEquals(header.concatRef.seqNumber, 2);
|
||||
assertEquals(header.concatRef.isEightBits, true);
|
||||
assertNotNull(header.portAddrs);
|
||||
assertEquals(header.portAddrs.destPort, 2948);
|
||||
assertEquals(header.portAddrs.origPort, 9200);
|
||||
assertEquals(header.portAddrs.areEightBits, false);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
|
Reference in New Issue
Block a user