From 64c499113a758cf80cddfd4d0183f944a1a6645a Mon Sep 17 00:00:00 2001 From: Tammo Spalink Date: Tue, 5 May 2009 19:57:57 +0800 Subject: 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 --- telephony/java/android/telephony/SmsMessage.java | 6 +- .../java/android/telephony/gsm/SmsMessage.java | 7 +- .../android/internal/telephony/GsmAlphabet.java | 5 +- .../android/internal/telephony/SMSDispatcher.java | 78 +++-- .../com/android/internal/telephony/SmsHeader.java | 382 +++++++++++---------- .../android/internal/telephony/SmsMessageBase.java | 11 +- .../internal/telephony/cdma/CdmaSMSDispatcher.java | 181 ++++------ .../internal/telephony/cdma/SmsMessage.java | 197 ++++------- .../internal/telephony/cdma/sms/BearerData.java | 92 +++-- .../internal/telephony/cdma/sms/UserData.java | 17 +- .../internal/telephony/gsm/GsmSMSDispatcher.java | 176 +++------- .../android/internal/telephony/gsm/SmsMessage.java | 36 +- 12 files changed, 528 insertions(+), 660 deletions(-) (limited to 'telephony') 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 m_elements = new ArrayList(); - 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 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 miscEltList = new ArrayList(); - 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 parts, ArrayList sentIntents, ArrayList 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 SubmitPdu containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. - */ - public static SubmitPdu getSubmitPdu(String scAddress, - String destinationAddress, String message, - boolean statusReportRequested) { - return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null); + uData.userDataHeader = smsHeader; + return privateGetSubmitPdu(destAddr, statusReportRequested, uData); } /** * Get an SMS-SUBMIT PDU for a data message to a destination address & port * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message - * @param destinationPort the port to deliver the message to at the + * @param scAddr Service Centre address. null == use default + * @param destAddr the address of the destination for the message + * @param destPort the port to deliver the message to at the * destination * @param data the data for the message * @return a SubmitPdu 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 parts, ArrayList sentIntents, ArrayList 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 parts, - ArrayList sentIntents, + ArrayList sentIntents, ArrayList 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 map = new HashMap(); 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; -- cgit v1.1