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