Merge change 133 into donut

* changes:
  Initial code for cdma sms encode and decode, in java, with simple tests.
This commit is contained in:
Android (Google) Code Review
2009-04-14 13:20:43 -07:00
13 changed files with 1156 additions and 1929 deletions

View File

@@ -20,20 +20,7 @@ import android.util.Log;
import com.android.internal.telephony.SmsHeader;
import java.util.Arrays;
import static android.telephony.SmsMessage.ENCODING_7BIT;
import static android.telephony.SmsMessage.ENCODING_16BIT;
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
import static android.telephony.SmsMessage.MessageClass;
import static com.android.internal.telephony.SmsAddress.TON_ABBREVIATED;
import static com.android.internal.telephony.SmsAddress.TON_ALPHANUMERIC;
import static com.android.internal.telephony.SmsAddress.TON_INTERNATIONAL;
import static com.android.internal.telephony.SmsAddress.TON_NATIONAL;
import static com.android.internal.telephony.SmsAddress.TON_NETWORK;
import static com.android.internal.telephony.SmsAddress.TON_SUBSCRIBER;
import static com.android.internal.telephony.SmsAddress.TON_UNKNOWN;
/**
* Base class declaring the specific methods and members for SmsMessage.
@@ -385,4 +372,3 @@ public abstract class SmsMessageBase {
}
}

View File

@@ -27,7 +27,6 @@ import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
import com.android.internal.telephony.cdma.sms.SmsDataCoding;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
@@ -50,23 +49,6 @@ import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
import static android.telephony.SmsMessage.MessageClass;
import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_NONE;
import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_TEMPORARY;
import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_PERMANENT;
import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_DELIVER;
import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_SUBMIT;
import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_CANCELLATION;
import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_DELIVERY_ACK;
import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_USER_ACK;
import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_READ_ACK;
import static com.android.internal.telephony.cdma.sms.CdmaSmsAddress.SMS_ADDRESS_MAX;
import static com.android.internal.telephony.cdma.sms.CdmaSmsAddress.SMS_SUBADDRESS_MAX;
import static com.android.internal.telephony.cdma.sms.SmsEnvelope.SMS_BEARER_DATA_MAX;
import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_7BIT_ASCII;
import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_GSM_7BIT_ALPHABET;
import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_IA5;
import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_OCTET;
import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_UNICODE_16;
/**
* A Short Message Service message.
@@ -186,7 +168,7 @@ public class SmsMessage extends SmsMessageBase {
// ignore subaddress
p.readInt(); //p_cur->sSubAddress.subaddressType
p.readByte(); //p_cur->sSubAddress.odd
p.readInt(); //p_cur->sSubAddress.odd
count = p.readByte(); //p_cur->sSubAddress.number_of_digits
//p_cur->sSubAddress.digits[digitCount] :
for (int index=0; index < count; index++) {
@@ -309,15 +291,15 @@ public class SmsMessage extends SmsMessageBase {
int septetCount = GsmAlphabet.countGsmSeptets(message, true);
// User Data (and length)
uData.userData = message.getBytes();
uData.payload = message.getBytes();
if (uData.userData.length > MAX_USER_DATA_SEPTETS) {
if (uData.payload.length > MAX_USER_DATA_SEPTETS) {
// Message too long
return null;
}
// desired TP-Data-Coding-Scheme
uData.userDataEncoding = UserData.UD_ENCODING_GSM_7BIT_ALPHABET;
uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
// paddingBits not needed for UD_ENCODING_GSM_7BIT_ALPHABET
@@ -341,15 +323,15 @@ public class SmsMessage extends SmsMessageBase {
return null;
}
uData.userData = textPart;
uData.payload = textPart;
if (uData.userData.length > MAX_USER_DATA_BYTES) {
if (uData.payload.length > MAX_USER_DATA_BYTES) {
// Message too long
return null;
}
// TP-Data-Coding-Scheme
uData.userDataEncoding = UserData.UD_ENCODING_UNICODE_16;
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
// sms header
if(headerData != null) {
@@ -425,8 +407,8 @@ public class SmsMessage extends SmsMessageBase {
// TP-Data-Coding-Scheme
// No class, 8 bit data
uData.userDataEncoding = UserData.UD_ENCODING_OCTET;
uData.userData = data;
uData.msgEncoding = UserData.ENCODING_OCTET;
uData.payload = data;
byte[] msgData = sms.getEnvelope(destinationAddress, statusReportRequested, uData,
true, true);
@@ -619,21 +601,21 @@ public class SmsMessage extends SmsMessageBase {
* Parses a SMS message from its BearerData stream. (mobile-terminated only)
*/
protected void parseSms() {
mBearerData = SmsDataCoding.decodeCdmaSms(mEnvelope.bearerData);
messageRef = mBearerData.messageID;
mBearerData = BearerData.decode(mEnvelope.bearerData);
messageRef = mBearerData.messageId;
// TP-Message-Type-Indicator
// (See 3GPP2 C.S0015-B, v2, 4.5.1)
int messageType = mBearerData.messageType;
switch (messageType) {
case MESSAGE_TYPE_USER_ACK:
case MESSAGE_TYPE_READ_ACK:
case MESSAGE_TYPE_DELIVER:
case BearerData.MESSAGE_TYPE_USER_ACK:
case BearerData.MESSAGE_TYPE_READ_ACK:
case BearerData.MESSAGE_TYPE_DELIVER:
// Deliver (mobile-terminated only)
parseSmsDeliver();
break;
case MESSAGE_TYPE_DELIVERY_ACK:
case BearerData.MESSAGE_TYPE_DELIVERY_ACK:
parseSmsDeliveryAck();
break;
@@ -699,22 +681,22 @@ public class SmsMessage extends SmsMessageBase {
return;
}
encodingType = uData.userDataEncoding;
encodingType = uData.msgEncoding;
// insert DCS-decoding here when type is supported by ril-library
userData = uData.userData;
userData = uData.payload;
userDataHeader = uData.userDataHeader;
switch (encodingType) {
case UD_ENCODING_GSM_7BIT_ALPHABET:
case UD_ENCODING_UNICODE_16:
case UserData.ENCODING_GSM_7BIT_ALPHABET:
case UserData.ENCODING_UNICODE_16:
// user data was already decoded by wmsts-library
messageBody = new String(userData);
break;
// data and unsupported encodings:
case UD_ENCODING_OCTET:
case UserData.ENCODING_OCTET:
default:
messageBody = null;
break;
@@ -771,7 +753,7 @@ public class SmsMessage extends SmsMessageBase {
if (useNewId) {
setNextMessageId();
}
mBearerData.messageID = nextMessageId;
mBearerData.messageId = nextMessageId;
// Set the reply options (See C.S0015-B, v2.0, 4.5.11)
if(statusReportRequested) {
@@ -795,7 +777,7 @@ public class SmsMessage extends SmsMessageBase {
// ** encode BearerData **
byte[] encodedBearerData = null;
try {
encodedBearerData = SmsDataCoding.encodeCdmaSms(mBearerData);
encodedBearerData = BearerData.encode(mBearerData);
} catch (Exception e) {
Log.e(LOG_TAG, "doGetSubmitPdu: EncodeCdmaSMS function in JNI interface failed: "
+ e.getMessage());

View File

@@ -16,7 +16,52 @@
package com.android.internal.telephony.cdma.sms;
import android.util.Log;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.util.HexDump;
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.BitwiseOutputStream;
/**
* XXX
*
*
*/
public final class BearerData{
private final static String LOG_TAG = "SMS";
/**
* Bearer Data Subparameter Indentifiers
* (See 3GPP2 C.S0015-B, v2.0, table 4.5-1)
*/
private final static byte SUBPARAM_MESSAGE_IDENTIFIER = 0x00;
private final static byte SUBPARAM_USER_DATA = 0x01;
private final static byte SUBPARAM_USER_REPONSE_CODE = 0x02;
private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP = 0x03;
//private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE = 0x04;
//private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE = 0x05;
//private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE = 0x06;
//private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE = 0x07;
//private final static byte SUBPARAM_PRIORITY_INDICATOR = 0x08;
//private final static byte SUBPARAM_PRIVACY_INDICATOR = 0x09;
private final static byte SUBPARAM_REPLY_OPTION = 0x0A;
private final static byte SUBPARAM_NUMBER_OF_MESSAGES = 0x0B;
//private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY = 0x0C;
//private final static byte SUBPARAM_LANGUAGE_INDICATOR = 0x0D;
private final static byte SUBPARAM_CALLBACK_NUMBER = 0x0E;
//private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE = 0x0F;
//private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA = 0x10;
//private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX = 0x11;
//private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12;
//private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
private final static byte SUBPARAM_MESSAGE_STATUS = 0x14;
//private final static byte SUBPARAM_TP_FAILURE_CAUSE = 0x15;
//private final static byte SUBPARAM_ENHANCED_VMN = 0x16;
//private final static byte SUBPARAM_ENHANCED_VMN_ACK = 0x17;
// For completeness the following fields are listed, though not used yet.
/**
@@ -94,9 +139,6 @@ public final class BearerData{
public static final int ERROR_UNDEFINED = 0xFF;
public static final int STATUS_UNDEFINED = 0xFF;
/** Bit-mask indicating used fields for SmsDataCoding */
public int mask;
/**
* 4-bit value indicating the message type in accordance to
* table 4.5.1-1
@@ -109,7 +151,7 @@ public final class BearerData{
* (Special rules apply for WAP-messages.)
* (See 3GPP2 C.S0015-B, v2, 4.5.1)
*/
public int messageID;
public int messageId;
/**
* 1-bit value that indicates whether a User Data Header is present.
@@ -188,5 +230,440 @@ public final class BearerData{
*/
public int messageStatus = STATUS_UNDEFINED;
}
private static class CodingException extends Exception {
public CodingException(String s) {
super(s);
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("BearerData:\n");
builder.append(" messageType: " + messageType + "\n");
builder.append(" messageId: " + (int)messageId + "\n");
builder.append(" hasUserDataHeader: " + hasUserDataHeader + "\n");
builder.append(" timeStamp: " + timeStamp + "\n");
builder.append(" userAckReq: " + userAckReq + "\n");
builder.append(" deliveryAckReq: " + deliveryAckReq + "\n");
builder.append(" readAckReq: " + readAckReq + "\n");
builder.append(" reportReq: " + reportReq + "\n");
builder.append(" numberOfMessages: " + numberOfMessages + "\n");
builder.append(" callbackNumber: " + callbackNumber + "\n");
builder.append(" displayMode: " + displayMode + "\n");
builder.append(" errorClass: " + errorClass + "\n");
builder.append(" messageStatus: " + messageStatus + "\n");
builder.append(" userData: " + userData + "\n");
return builder.toString();
}
private static void encodeMessageId(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException
{
outStream.write(8, 3);
outStream.write(4, bData.messageType);
outStream.write(8, bData.messageId >> 8);
outStream.write(8, bData.messageId);
outStream.write(1, bData.hasUserDataHeader ? 1 : 0);
outStream.skip(3);
}
private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException
{
int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
byte[] headerData = null;
if (bData.hasUserDataHeader) {
headerData = bData.userData.userDataHeader.toByteArray();
dataBits += headerData.length * 8;
}
int paramBits = dataBits + 13;
if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
(bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
paramBits += 8;
}
int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
int paddingBits = (paramBytes * 8) - paramBits;
outStream.write(8, paramBytes);
outStream.write(5, bData.userData.msgEncoding);
if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
(bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
outStream.write(8, bData.userData.msgType);
}
outStream.write(8, bData.userData.numFields);
if (headerData != null) outStream.writeByteArray(headerData.length * 8, headerData);
outStream.writeByteArray(dataBits, bData.userData.payload);
if (paddingBits > 0) outStream.write(paddingBits, 0);
}
private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException
{
outStream.write(8, 1);
outStream.write(1, bData.userAckReq ? 1 : 0);
outStream.write(1, bData.deliveryAckReq ? 1 : 0);
outStream.write(1, bData.readAckReq ? 1 : 0);
outStream.write(1, bData.reportReq ? 1 : 0);
outStream.write(4, 0);
}
private static byte[] encodeDtmfSmsAddress(String address) {
int digits = address.length();
int dataBits = digits * 4;
int dataBytes = (dataBits / 8);
dataBytes += (dataBits % 8) > 0 ? 1 : 0;
byte[] rawData = new byte[dataBytes];
for (int i = 0; i < digits; i++) {
char c = address.charAt(i);
int val = 0;
if ((c >= '1') && (c <= '9')) val = c - '0';
else if (c == '0') val = 10;
else if (c == '*') val = 11;
else if (c == '#') val = 12;
else return null;
rawData[i / 2] |= val << (4 - ((i % 2) * 4));
}
return rawData;
}
private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
try {
addr.origBytes = addr.address.getBytes("US-ASCII");
} catch (java.io.UnsupportedEncodingException ex) {
throw new CodingException("invalid SMS address, cannot convert to ASCII");
}
} else {
addr.origBytes = encodeDtmfSmsAddress(addr.address);
}
}
private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException, CodingException
{
CdmaSmsAddress addr = bData.callbackNumber;
encodeCdmaSmsAddress(addr);
int paramBits = 9;
int dataBits = 0;
if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
paramBits += 7;
dataBits = addr.numberOfDigits * 8;
} else {
dataBits = addr.numberOfDigits * 4;
}
paramBits += dataBits;
int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
int paddingBits = (paramBytes * 8) - paramBits;
outStream.write(8, paramBytes);
outStream.write(1, addr.digitMode);
if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
outStream.write(3, addr.ton);
outStream.write(4, addr.numberPlan);
}
outStream.write(8, addr.numberOfDigits);
outStream.writeByteArray(dataBits, addr.origBytes);
if (paddingBits > 0) outStream.write(paddingBits, 0);
}
private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException
{
outStream.write(8, 1);
outStream.write(2, bData.errorClass);
outStream.write(6, bData.messageStatus);
}
private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException
{
outStream.write(8, 1);
outStream.write(8, bData.numberOfMessages);
}
private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException
{
outStream.write(8, 6);
outStream.writeByteArray(6 * 8, bData.timeStamp);
}
/**
* Create serialized representation for BearerData object.
* (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
*
* @param bearerData an instance of BearerData.
*
* @return data byta array of raw encoded SMS bearer data.
*/
public static byte[] encode(BearerData bData) {
try {
BitwiseOutputStream outStream = new BitwiseOutputStream(200);
outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
encodeMessageId(bData, outStream);
if (bData.userData != null) {
outStream.write(8, SUBPARAM_USER_DATA);
encodeUserData(bData, outStream);
}
if (bData.callbackNumber != null) {
outStream.write(8, SUBPARAM_CALLBACK_NUMBER);
encodeCallbackNumber(bData, outStream);
}
if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) {
outStream.write(8, SUBPARAM_REPLY_OPTION);
encodeReplyOption(bData, outStream);
}
if (bData.numberOfMessages != 0) {
outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
encodeMsgCount(bData, outStream);
}
if (bData.timeStamp != null) {
outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP);
encodeMsgCenterTimeStamp(bData, outStream);
}
return outStream.toByteArray();
} catch (BitwiseOutputStream.AccessException ex) {
Log.e(LOG_TAG, "BearerData encode failed: " + ex);
} catch (CodingException ex) {
Log.e(LOG_TAG, "BearerData encode failed: " + ex);
}
return null;
}
private static void decodeMessageId(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
if (inStream.read(8) != 3) {
throw new CodingException("MESSAGE_IDENTIFIER subparam size incorrect");
}
bData.messageType = inStream.read(4);
bData.messageId = inStream.read(8) << 8;
bData.messageId |= inStream.read(8);
bData.hasUserDataHeader = (inStream.read(1) == 1);
inStream.skip(3);
}
private static void decodeUserData(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException
{
byte paramBytes = inStream.read(8);
bData.userData = new UserData();
bData.userData.msgEncoding = inStream.read(5);
bData.userData.msgType = 0;
int consumedBits = 5;
if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
(bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
bData.userData.msgType = inStream.read(8);
consumedBits += 8;
}
bData.userData.numFields = inStream.read(8);
consumedBits += 8;
int dataBits = (paramBytes * 8) - consumedBits;
bData.userData.payload = inStream.readByteArray(dataBits);
}
private static String decodePayloadStr(byte[] data, int offset, int numFields, String format)
throws CodingException
{
try {
return new String(data, offset, numFields, format);
} catch (java.io.UnsupportedEncodingException ex) {
throw new CodingException("invalid ASCII user data code");
}
}
private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
throws CodingException
{
int offset = 0;
if (hasUserDataHeader) {
int UdhLen = userData.payload[0];
byte[] headerData = new byte[UdhLen];
System.arraycopy(userData.payload, 1, headerData, 0, UdhLen);
userData.userDataHeader = SmsHeader.parse(headerData);
}
switch (userData.msgEncoding) {
case UserData.ENCODING_GSM_7BIT_ALPHABET:
userData.payloadStr = GsmAlphabet.gsm7BitPackedToString(userData.payload,
offset, userData.numFields);
break;
case UserData.ENCODING_7BIT_ASCII:
userData.payloadStr = decodePayloadStr(userData.payload, offset,
userData.numFields, "US-ASCII");
break;
case UserData.ENCODING_UNICODE_16:
userData.payloadStr = decodePayloadStr(userData.payload, offset,
userData.numFields * 2, "UTF-16");
break;
default:
throw new CodingException("unsupported user data encoding ("
+ userData.msgEncoding + ")");
}
}
private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
byte paramBytes = inStream.read(8);
if (paramBytes != 1) {
throw new CodingException("REPLY_OPTION subparam size incorrect");
}
bData.userAckReq = (inStream.read(1) == 1);
bData.deliveryAckReq = (inStream.read(1) == 1);
bData.readAckReq = (inStream.read(1) == 1);
bData.reportReq = (inStream.read(1) == 1);
inStream.skip(4);
}
private static void decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
if (inStream.read(8) != 1) {
throw new CodingException("NUMBER_OF_MESSAGES subparam size incorrect");
}
bData.numberOfMessages = inStream.read(8);
}
private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
throws CodingException
{
/* DTMF 4-bit digit encoding, defined in at
* 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
StringBuffer strBuf = new StringBuffer(numFields);
for (int i = 0; i < numFields; i++) {
int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
else if (val == 10) strBuf.append('0');
else if (val == 11) strBuf.append('*');
else if (val == 12) strBuf.append('#');
else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
}
return strBuf.toString();
}
private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException {
if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
try {
/* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
* just 7-bit ASCII encoding, with the MSB being zero. */
addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
} catch (java.io.UnsupportedEncodingException ex) {
throw new CodingException("invalid SMS address ASCII code");
}
} else {
addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
}
}
private static void decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
byte paramBytes = inStream.read(8);
CdmaSmsAddress addr = new CdmaSmsAddress();
addr.digitMode = inStream.read(1);
byte fieldBits = 4;
byte consumedBits = 1;
if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
addr.ton = inStream.read(3);
addr.numberPlan = inStream.read(4);
fieldBits = 8;
consumedBits += 7;
}
addr.numberOfDigits = inStream.read(8);
consumedBits += 8;
int remainingBits = (paramBytes * 8) - consumedBits;
int dataBits = addr.numberOfDigits * fieldBits;
int paddingBits = remainingBits - dataBits;
if (remainingBits < dataBits) {
throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
"remainingBits " + remainingBits + ", dataBits " +
dataBits + ", paddingBits " + paddingBits + ")");
}
addr.origBytes = inStream.readByteArray(dataBits);
inStream.skip(paddingBits);
decodeSmsAddress(addr);
bData.callbackNumber = addr;
}
private static void decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
if (inStream.read(8) != 1) {
throw new CodingException("MESSAGE_STATUS subparam size incorrect");
}
bData.errorClass = inStream.read(2);
bData.messageStatus = inStream.read(6);
}
private static void decodeMsgCenterTimeStamp(BearerData bData,
BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
if (inStream.read(8) != 6) {
throw new CodingException("MESSAGE_CENTER_TIME_STAMP subparam size incorrect");
}
bData.timeStamp = inStream.readByteArray(6 * 8);
}
/**
* Create BearerData object from serialized representation.
* (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
*
* @param smsData byte array of raw encoded SMS bearer data.
*
* @return an instance of BearerData.
*/
public static BearerData decode(byte[] smsData) {
try {
BitwiseInputStream inStream = new BitwiseInputStream(smsData);
BearerData bData = new BearerData();
int foundSubparamMask = 0;
while (inStream.available() > 0) {
int subparamId = inStream.read(8);
int subparamIdBit = 1 << subparamId;
if ((foundSubparamMask & subparamIdBit) != 0) {
throw new CodingException("illegal duplicate subparameter (" +
subparamId + ")");
}
foundSubparamMask |= subparamIdBit;
switch (subparamId) {
case SUBPARAM_MESSAGE_IDENTIFIER:
decodeMessageId(bData, inStream);
break;
case SUBPARAM_USER_DATA:
decodeUserData(bData, inStream);
break;
case SUBPARAM_REPLY_OPTION:
decodeReplyOption(bData, inStream);
break;
case SUBPARAM_NUMBER_OF_MESSAGES:
decodeMsgCount(bData, inStream);
break;
case SUBPARAM_CALLBACK_NUMBER:
decodeCallbackNumber(bData, inStream);
break;
case SUBPARAM_MESSAGE_STATUS:
decodeMsgStatus(bData, inStream);
break;
case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
decodeMsgCenterTimeStamp(bData, inStream);
break;
default:
throw new CodingException("unsupported bearer data subparameter ("
+ subparamId + ")");
}
}
if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
}
if (bData.userData != null) {
decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
}
return bData;
} catch (BitwiseInputStream.AccessException ex) {
Log.e(LOG_TAG, "BearerData decode failed: " + ex);
} catch (CodingException ex) {
Log.e(LOG_TAG, "BearerData decode failed: " + ex);
}
return null;
}
}

View File

@@ -17,6 +17,7 @@
package com.android.internal.telephony.cdma.sms;
import com.android.internal.telephony.SmsAddress;
import com.android.internal.util.HexDump;
public class CdmaSmsAddress extends SmsAddress {
/**
@@ -95,4 +96,18 @@ public class CdmaSmsAddress extends SmsAddress {
public CdmaSmsAddress(){
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("CdmaSmsAddress:\n");
builder.append(" digitMode: " + digitMode + "\n");
builder.append(" numberMode: " + numberMode + "\n");
builder.append(" numberPlan: " + numberPlan + "\n");
builder.append(" numberOfDigits: " + numberOfDigits + "\n");
builder.append(" ton: " + ton + "\n");
builder.append(" address: " + address + "\n");
builder.append(" origBytes: " + HexDump.toHexString(origBytes) + "\n");
return builder.toString();
}
}

View File

@@ -1,371 +0,0 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony.cdma.sms;
import com.android.internal.telephony.SmsHeader;
/*
* The SMSDataCoding class encodes and decodes CDMA SMS messages.
*/
public class SmsDataCoding {
private final static String TAG = "CDMA_SMS_JNI";
private final static int CDMA_SMS_WMS_MASK_BD_NULL = 0x00000000;
private final static int CDMA_SMS_WMS_MASK_BD_MSG_ID = 0x00000001;
private final static int CDMA_SMS_WMS_MASK_BD_USER_DATA = 0x00000002;
// private final static int CDMA_SMS_WMS_MASK_BD_USER_RESP = 0x00000004;
private final static int CDMA_SMS_WMS_MASK_BD_MC_TIME = 0x00000008;
// private final static int CDMA_SMS_WMS_MASK_BD_VALID_ABS = 0x00000010;
// private final static int CDMA_SMS_WMS_MASK_BD_VALID_REL = 0x00000020;
// private final static int CDMA_SMS_WMS_MASK_BD_DEFER_ABS = 0x00000040;
// private final static int CDMA_SMS_WMS_MASK_BD_DEFER_REL = 0x00000080;
// private final static int CDMA_SMS_WMS_MASK_BD_PRIORITY = 0x00000100;
// private final static int CDMA_SMS_WMS_MASK_BD_PRIVACY = 0x00000200;
// private final static int CDMA_SMS_WMS_MASK_BD_REPLY_OPTION = 0x00000400;
private final static int CDMA_SMS_WMS_MASK_BD_NUM_OF_MSGS = 0x00000800;
// private final static int CDMA_SMS_WMS_MASK_BD_ALERT = 0x00001000;
// private final static int CDMA_SMS_WMS_MASK_BD_LANGUAGE = 0x00002000;
private final static int CDMA_SMS_WMS_MASK_BD_CALLBACK = 0x00004000;
private final static int CDMA_SMS_WMS_MASK_BD_DISPLAY_MODE = 0x00008000;
// private final static int CDMA_SMS_WMS_MASK_BD_SCPT_DATA = 0x00010000;
// private final static int CDMA_SMS_WMS_MASK_BD_SCPT_RESULT = 0x00020000;
// private final static int CDMA_SMS_WMS_MASK_BD_DEPOSIT_INDEX = 0x00040000;
// private final static int CDMA_SMS_WMS_MASK_BD_DELIVERY_STATUS = 0x00080000;
// private final static int CDMA_SMS_WMS_MASK_BD_IP_ADDRESS = 0x10000000;
// private final static int CDMA_SMS_WMS_MASK_BD_RSN_NO_NOTIFY = 0x20000000;
// private final static int CDMA_SMS_WMS_MASK_BD_OTHER = 0x40000000;
/**
* Successful operation.
*/
private static final int JNI_CDMA_SMS_SUCCESS = 0;
/**
* General failure.
*/
private static final int JNI_CDMA_SMS_FAILURE = 1;
/**
* Data length is out of length.
*/
private static final int JNI_CDMA_SMS_DATA_LEN_OUT_OF_RANGE = 2;
/**
* Class name unknown.
*/
private static final int JNI_CDMA_SMS_CLASS_UNKNOWN = 3;
/**
* Field ID unknown.
*/
private static final int JNI_CDMA_SMS_FIELD_ID_UNKNOWN = 4;
/**
* Memory allocation failed.
*/
private static final int JNI_CDMA_SMS_OUT_OF_MEMORY = 5;
/**
* Encode SMS.
*
* @param bearerData an instance of BearerData.
*
* @return the encoded SMS as byte[].
*/
public static byte[] encodeCdmaSms(BearerData bearerData) {
byte[] encodedSms;
if( nativeCdmaSmsConstructClientBD() == JNI_CDMA_SMS_FAILURE){
return null;
}
// check bearer data and generate bit mask
generateBearerDataBitMask(bearerData);
encodedSms = startEncoding(bearerData);
if( nativeCdmaSmsDestructClientBD() == JNI_CDMA_SMS_FAILURE){
return null;
}
return encodedSms;
}
/**
* Decode SMS.
*
* @param SmsData the encoded SMS.
*
* @return an instance of BearerData.
*/
public static BearerData decodeCdmaSms(byte[] SmsData) {
BearerData bearerData;
if( nativeCdmaSmsConstructClientBD() == JNI_CDMA_SMS_FAILURE){
return null;
}
bearerData = startDecoding(SmsData);
if( nativeCdmaSmsDestructClientBD() == JNI_CDMA_SMS_FAILURE){
return null;
}
return bearerData;
}
private static void generateBearerDataBitMask(BearerData bearerData) {
// initial
bearerData.mask = CDMA_SMS_WMS_MASK_BD_NULL;
// check message type
if (bearerData.messageType != 0){
bearerData.mask |= CDMA_SMS_WMS_MASK_BD_MSG_ID;
}
// check mUserData
if (bearerData.userData != null){
bearerData.mask |= CDMA_SMS_WMS_MASK_BD_USER_DATA;
}
// check mTimeStamp
if (bearerData.timeStamp != null){
bearerData.mask |= CDMA_SMS_WMS_MASK_BD_MC_TIME;
}
// check mNumberOfMessages
if (bearerData.numberOfMessages > 0){
bearerData.mask |= CDMA_SMS_WMS_MASK_BD_NUM_OF_MSGS;
}
// check mCallbackNumber
if(bearerData.callbackNumber != null){
bearerData.mask |= CDMA_SMS_WMS_MASK_BD_CALLBACK;
}
// check DisplayMode
if(bearerData.displayMode == BearerData.DISPLAY_DEFAULT ||
bearerData.displayMode == BearerData.DISPLAY_IMMEDIATE ||
bearerData.displayMode == BearerData.DISPLAY_USER){
bearerData.mask |= CDMA_SMS_WMS_MASK_BD_DISPLAY_MODE;
}
}
private static byte[] startEncoding(BearerData bearerData) {
int m_id;
byte[] m_data;
int dataLength;
byte[] encodedSms;
int nbrOfHeaders = 0;
if( nativeCdmaSmsSetBearerDataPrimitives(bearerData) == JNI_CDMA_SMS_FAILURE){
return null;
}
if ((bearerData.mask & CDMA_SMS_WMS_MASK_BD_USER_DATA) == CDMA_SMS_WMS_MASK_BD_USER_DATA){
if( nativeCdmaSmsSetUserData(bearerData.userData) == JNI_CDMA_SMS_FAILURE){
return null;
}
if (bearerData.userData.userDataHeader != null){
nbrOfHeaders = bearerData.userData.userDataHeader.nbrOfHeaders;
}
for (int i = 0; i < nbrOfHeaders; i++) {
m_id = bearerData.userData.userDataHeader.getElements().get(i).getID();
m_data = bearerData.userData.userDataHeader.getElements().get(i).getData();
dataLength = m_data.length;
if( nativeCdmaSmsSetUserDataHeader(m_id, m_data, dataLength, i)
== JNI_CDMA_SMS_FAILURE){
return null;
}
}
}
if ((bearerData.mask & CDMA_SMS_WMS_MASK_BD_CALLBACK) == CDMA_SMS_WMS_MASK_BD_CALLBACK) {
if( nativeCdmaSmsSetSmsAddress(bearerData.callbackNumber) == JNI_CDMA_SMS_FAILURE){
return null;
}
}
/* call native method to encode SMS */
encodedSms = nativeCdmaSmsEncodeSms();
return encodedSms;
}
private static BearerData startDecoding(byte[] SmsData) {
BearerData bData = new BearerData();
byte[] udhData;
/* call native method to decode SMS */
if( nativeCdmaSmsDecodeSms(SmsData) == JNI_CDMA_SMS_FAILURE){
return null;
}
if( nativeCdmaSmsGetBearerDataPrimitives(bData) == JNI_CDMA_SMS_FAILURE){
return null;
}
if ((bData.mask & CDMA_SMS_WMS_MASK_BD_USER_DATA) == CDMA_SMS_WMS_MASK_BD_USER_DATA) {
bData.userData = new UserData();
if( nativeCdmaSmsGetUserData(bData.userData) == JNI_CDMA_SMS_FAILURE){
return null;
}
udhData = nativeCdmaSmsGetUserDataHeader();
if (udhData != null) {
bData.userData.userDataHeader = SmsHeader.parse(udhData);
}
}
if ((bData.mask & CDMA_SMS_WMS_MASK_BD_CALLBACK) == CDMA_SMS_WMS_MASK_BD_CALLBACK) {
bData.callbackNumber = new CdmaSmsAddress();
if( nativeCdmaSmsGetSmsAddress(bData.callbackNumber) == JNI_CDMA_SMS_FAILURE){
return null;
}
}
return bData;
}
// native methods
/**
* native method: Allocate memory for clientBD structure
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsConstructClientBD();
/**
* native method: Free memory used for clientBD structure
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsDestructClientBD();
/**
* native method: fill clientBD structure with bearerData primitives
*
* @param bearerData an instance of BearerData.
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsSetBearerDataPrimitives(BearerData bearerData);
/**
* native method: fill bearerData primitives with clientBD variables
*
* @param bearerData an instance of BearerData.
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsGetBearerDataPrimitives(BearerData bearerData);
/**
* native method: fill clientBD.user_data with UserData primitives
*
* @param userData an instance of UserData.
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsSetUserData(UserData userData);
/**
* native method: fill UserData primitives with clientBD.user_data
*
* @param userData an instance of UserData.
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsGetUserData(UserData userData);
/**
* native method: fill clientBD.user_data.headers with UserDataHeader primitives
*
* @param ID ID of element.
* @param data element data.
* @param dataLength data length
* @param index index of element
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsSetUserDataHeader(
int ID, byte[] data, int dataLength, int index);
/**
* native method: fill UserDataHeader primitives with clientBD.user_data.headers
*
* @return user data headers
*/
private static native byte[] nativeCdmaSmsGetUserDataHeader();
/**
* native method: fill clientBD.callback with SmsAddress primitives
*
* @param smsAddr an instance of SmsAddress.
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsSetSmsAddress(CdmaSmsAddress smsAddr);
/**
* native method: fill SmsAddress primitives with clientBD.callback
*
* @param smsAddr an instance of SmsAddress.
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsGetSmsAddress(CdmaSmsAddress smsAddr);
/**
* native method: call encoding functions and get encoded SMS
*
* @return the encoded SMS
*/
private static native byte[] nativeCdmaSmsEncodeSms();
/**
* native method: call decode functions
*
* @param encodedSMS encoded SMS.
*
* @return #JNI_CDMA_SMS_SUCCESS if succeed.
* #JNI_CDMA_SMS_FAILURE if fail.
*/
private static native int nativeCdmaSmsDecodeSms(byte[] encodedSMS);
/**
* Load the shared library to link the native methods.
*/
static {
try {
System.loadLibrary("cdma_sms_jni");
}
catch (UnsatisfiedLinkError ule) {
System.err.println("WARNING: Could not load cdma_sms_jni.so");
}
}
}

View File

@@ -17,24 +17,25 @@
package com.android.internal.telephony.cdma.sms;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.util.HexDump;
public class UserData{
/**
* Supported user data encoding types
* User data encoding types
* (See 3GPP2 C.R1001-F, v1.0, table 9.1-1)
*/
public static final int UD_ENCODING_OCTET = 0x00;
//public static final int UD_ENCODING_EXTENDED_PROTOCOL = 0x01;
public static final int UD_ENCODING_7BIT_ASCII = 0x02;
public static final int UD_ENCODING_IA5 = 0x03;
public static final int UD_ENCODING_UNICODE_16 = 0x04;
//public static final int UD_ENCODING_SHIFT_JIS = 0x05;
//public static final int UD_ENCODING_KOREAN = 0x06;
//public static final int UD_ENCODING_LATIN_HEBREW = 0x07;
//public static final int UD_ENCODING_LATIN = 0x08;
public static final int UD_ENCODING_GSM_7BIT_ALPHABET = 0x09;
//public static final int UD_ENCODING_GSM_DCS = 0x0A;
public static final int ENCODING_OCTET = 0x00;
public static final int ENCODING_IS91_EXTENDED_PROTOCOL = 0x01;
public static final int ENCODING_7BIT_ASCII = 0x02;
//public static final int ENCODING_IA5 = 0x03;
public static final int ENCODING_UNICODE_16 = 0x04;
//public static final int ENCODING_SHIFT_JIS = 0x05;
//public static final int ENCODING_KOREAN = 0x06;
//public static final int ENCODING_LATIN_HEBREW = 0x07;
//public static final int ENCODING_LATIN = 0x08;
public static final int ENCODING_GSM_7BIT_ALPHABET = 0x09;
public static final int ENCODING_GSM_DCS = 0x0A;
/**
* Contains the data header of the user data
@@ -44,20 +45,37 @@ public class UserData{
/**
* Contains the data encoding type for the SMS message
*/
public int userDataEncoding;
public int msgEncoding;
// needed when encoding is IS91 or DCS (not supported yet):
//public int messageType;
// XXX needed when encoding is IS91 or DCS (not supported yet):
public int msgType;
/**
* Number of invalid bits in the last byte of data.
*/
public int paddingBits;
public int numFields;
/**
* Contains the user data of a SMS message
* (See 3GPP2 C.S0015-B, v2, 4.5.2)
*/
public byte[] userData;
public byte[] payload;
public String payloadStr;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("UserData:\n");
builder.append(" msgEncoding: " + msgEncoding + "\n");
builder.append(" msgType: " + msgType + "\n");
builder.append(" paddingBits: " + paddingBits + "\n");
builder.append(" numFields: " + (int)numFields + "\n");
builder.append(" userDataHeader: " + userDataHeader + "\n");
builder.append(" payload: " + HexDump.toHexString(payload));
builder.append(" payloadStr: " + payloadStr);
return builder.toString();
}
}