Merge change 1127 into donut

* changes:
  SmsHeader rewrite, in preparation for migration to public API.
This commit is contained in:
Android (Google) Code Review
2009-05-19 19:52:07 -07:00
16 changed files with 661 additions and 698 deletions

View File

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

View File

@ -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 {
}
}
}

View File

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

View File

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

View File

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

View File

@ -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;

View File

@ -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} */

View File

@ -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 &amp; 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);

View File

@ -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:

View File

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

View File

@ -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 {
}
}

View File

@ -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;

View File

@ -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";

View File

@ -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

View File

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

View File

@ -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