summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTammo Spalink <tammo@google.com>2009-05-05 19:57:57 +0800
committerTammo Spalink <tammo@google.com>2009-05-20 09:27:10 +0800
commit64c499113a758cf80cddfd4d0183f944a1a6645a (patch)
tree0283297f3f208a79dd57d42096cd41cbd794818c
parent2f11599eb317887a8cca14d8e66bfc485e5169e7 (diff)
downloadframeworks_base-64c499113a758cf80cddfd4d0183f944a1a6645a.zip
frameworks_base-64c499113a758cf80cddfd4d0183f944a1a6645a.tar.gz
frameworks_base-64c499113a758cf80cddfd4d0183f944a1a6645a.tar.bz2
SmsHeader rewrite, in preparation for migration to public API.
See http://b/issue?id=1751571 Changes the semantics of SmsHeader from containing only opaque data to exposing occurs-once frequently-used fields together with a list of opaque fields. Also changes the coding to and from byte array to be symmetrical, whereas previous encoding had an extra length field. Cdma SmsMessage -- cleanup of code paths along with adjustments to match the new header semantics, which should address at least some of the issues with concatenated messages. See http://b/issue?id=1809759
-rw-r--r--telephony/java/android/telephony/SmsMessage.java6
-rw-r--r--telephony/java/android/telephony/gsm/SmsMessage.java7
-rw-r--r--telephony/java/com/android/internal/telephony/GsmAlphabet.java5
-rw-r--r--telephony/java/com/android/internal/telephony/SMSDispatcher.java78
-rw-r--r--telephony/java/com/android/internal/telephony/SmsHeader.java382
-rw-r--r--telephony/java/com/android/internal/telephony/SmsMessageBase.java11
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java181
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/SmsMessage.java197
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java92
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/UserData.java17
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java176
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java36
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java75
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/SMSTest.java25
-rw-r--r--tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java19
-rw-r--r--tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java36
16 files changed, 653 insertions, 690 deletions
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 3b7f4b5..e73de3c 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -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);
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 0928ddf..84dfca0 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -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 {
}
}
}
-
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 8f4c69c..8e2941b 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -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;
}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index f2bd361..d055c31 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -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;
}
}
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index 64b884e..d220648 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -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;
- public static final int PORT_WAP_PUSH = 2948;
- public static final int PORT_WAP_WSP = 9200;
-
- private byte[] m_data;
- private ArrayList<Element> m_elements = new ArrayList<Element>();
- public int nbrOfHeaders;
+ // 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...
- /**
- * Creates an SmsHeader object from raw user data header bytes.
- *
- * @param data is user data header bytes
- * @return an SmsHeader object
+ /** SMS user data header information element identifiers.
+ * (see TS 23.040 9.2.3.24)
*/
- public static SmsHeader parse(byte[] data) {
- SmsHeader header = new SmsHeader();
- header.m_data = 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 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;
- return header;
- }
+ public static final int PORT_WAP_PUSH = 2948;
+ public static final int PORT_WAP_WSP = 9200;
- public SmsHeader() { }
+ public static class PortAddrs {
+ public int destPort;
+ public int origPort;
+ public boolean areEightBits;
+ }
- /**
- * Returns the list of SmsHeader Elements that make up the header.
- *
- * @return the list of SmsHeader Elements.
- */
- public ArrayList<Element> getElements() {
- return m_elements;
+ public static class ConcatRef {
+ public int refNumber;
+ public int seqNumber;
+ public int msgCount;
+ public boolean isEightBits;
}
/**
- * Add an element to the SmsHeader.
- *
- * @param element to add.
+ * A header element that is not explicitly parsed, meaning not
+ * PortAddrs or ConcatRef.
*/
- public void add(Element element) {
- m_elements.add(element);
- }
-
- @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;
- }
- }
- }
-
- return builder.toString();
+ public static class MiscElt {
+ public int id;
+ public byte[] data;
}
- 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
- }
+ public PortAddrs portAddrs;
+ public ConcatRef concatRef;
+ public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>();
- return size;
- }
+ public SmsHeader() {}
/**
- * Converts SmsHeader object to a byte array as specified in TS 23.040 9.2.3.24.
- * @return Byte array representing the 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 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;
+ 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 m_data;
+ return smsHeader;
}
/**
- * A single Element in the SMS User Data Header.
- *
- * See TS 23.040 9.2.3.24.
- *
+ * 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 class Element {
- private byte[] m_data;
- private int m_id;
-
- public Element(int id, byte[] data) {
- m_id = id;
- m_data = data;
+ public static byte[] toByteArray(SmsHeader smsHeader) {
+ if ((smsHeader.portAddrs == null) &&
+ (smsHeader.concatRef == null) &&
+ (smsHeader.miscEltList.size() == 0)) {
+ return null;
}
- /**
- * Returns the Information Element Identifier for this element.
- *
- * @return the IE identifier.
- */
- public int getID() {
- return m_id;
+ 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();
+ }
- /**
- * Returns the data portion of this element.
- *
- * @return element data.
- */
- public byte[] getData() {
- return m_data;
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ 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();
}
+
}
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 1aad38d..31bb652 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -245,8 +245,6 @@ public abstract class SmsMessageBase {
/**
* Returns an object representing the user data header
*
- * @return an object representing the user data header
- *
* {@hide}
*/
public SmsHeader getUserDataHeader() {
@@ -254,9 +252,14 @@ public abstract class SmsMessageBase {
}
/**
+ * 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;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 42c0583..2bb17e4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -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.
-
- 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): 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();
+
+ /**
+ * 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;
- PendingIntent sentIntent = null;
- PendingIntent deliveryIntent = null;
+ 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;
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} */
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 343a22e..b2083ed 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -277,6 +277,15 @@ public class SmsMessage extends SmsMessageBase {
}
/**
+ * 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
*
* @param scAddr Service Centre address. Null means use default.
@@ -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) {
+ public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
+ boolean statusReportRequested, SmsHeader smsHeader) {
+
/**
- * 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?
+ * 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,58 +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);
- }
- }
-
- /**
- * 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: "
- + originatingAddress.address);
- }
-
- if (mBearerData.timeStamp != null) {
- scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp);
+ throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
}
- 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: "
@@ -632,26 +563,13 @@ public class SmsMessage extends SmsMessageBase {
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);
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index e64d022..05c8c9d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -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:
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index 02e94ad..7c37bc2 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -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();
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 3e73caf..2fce188 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -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));
- PendingIntent sentIntent = null;
- PendingIntent deliveryIntent = null;
+ 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;
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;
-
- 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;
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 {
}
}
-
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 867b719..ed61c3f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -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);
-
- // User data header size
- bo.write(0x06); // header is 6 octets
+ // Total size
+ bo.write(data.length + smsHeaderData.length + 1);
- // 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;
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index b3e88e1..f8d5d4d 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -136,6 +136,81 @@ public class CdmaSmsTest extends AndroidTestCase {
}
@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";
BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java b/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
index 360352b..9d44fd9 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
@@ -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
diff --git a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java
index 5df8991..e2336f8 100644
--- a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java
@@ -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));
}
}
-
diff --git a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java b/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java
index 5d5d1f9..8a66614 100644
--- a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java
@@ -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