Merge change 3874 into donut
* changes: for cdma concatenated (long) messages, replace ascii7bit with gsm7bit encoding
This commit is contained in:
@@ -22,7 +22,6 @@ import android.os.ServiceManager;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.android.internal.telephony.EncodeException;
|
import com.android.internal.telephony.EncodeException;
|
||||||
import com.android.internal.telephony.GsmAlphabet;
|
|
||||||
import com.android.internal.telephony.ISms;
|
import com.android.internal.telephony.ISms;
|
||||||
import com.android.internal.telephony.IccConstants;
|
import com.android.internal.telephony.IccConstants;
|
||||||
import com.android.internal.telephony.SmsRawData;
|
import com.android.internal.telephony.SmsRawData;
|
||||||
@@ -31,14 +30,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static android.telephony.SmsMessage.ENCODING_7BIT;
|
/*
|
||||||
import static android.telephony.SmsMessage.ENCODING_8BIT;
|
* TODO(code review): Curious question... Why are a lot of these
|
||||||
import static android.telephony.SmsMessage.ENCODING_16BIT;
|
* methods not declared as static, since they do not seem to require
|
||||||
import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
|
* any local object state? Assumedly this cannot be changed without
|
||||||
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
|
* interfering with the API...
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages SMS operations such as sending data, text, and pdu SMS messages.
|
* Manages SMS operations such as sending data, text, and pdu SMS messages.
|
||||||
@@ -88,7 +85,7 @@ public final class SmsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Divide a text message into several messages, none bigger than
|
* Divide a message text into several fragments, none bigger than
|
||||||
* the maximum SMS message size.
|
* the maximum SMS message size.
|
||||||
*
|
*
|
||||||
* @param text the original message. Must not be null.
|
* @param text the original message. Must not be null.
|
||||||
@@ -96,40 +93,7 @@ public final class SmsManager {
|
|||||||
* comprise the original message
|
* comprise the original message
|
||||||
*/
|
*/
|
||||||
public ArrayList<String> divideMessage(String text) {
|
public ArrayList<String> divideMessage(String text) {
|
||||||
int size = text.length();
|
return SmsMessage.fragmentText(text);
|
||||||
int[] params = SmsMessage.calculateLength(text, false);
|
|
||||||
/* SmsMessage.calculateLength returns an int[4] with:
|
|
||||||
* int[0] being the number of SMS's required,
|
|
||||||
* int[1] the number of code units used,
|
|
||||||
* int[2] is the number of code units remaining until the next message.
|
|
||||||
* int[3] is the encoding type that should be used for the message.
|
|
||||||
*/
|
|
||||||
int messageCount = params[0];
|
|
||||||
int encodingType = params[3];
|
|
||||||
ArrayList<String> result = new ArrayList<String>(messageCount);
|
|
||||||
|
|
||||||
int start = 0;
|
|
||||||
int limit;
|
|
||||||
|
|
||||||
if (messageCount > 1) {
|
|
||||||
limit = (encodingType == ENCODING_7BIT)?
|
|
||||||
MAX_USER_DATA_SEPTETS_WITH_HEADER: MAX_USER_DATA_BYTES_WITH_HEADER;
|
|
||||||
} else {
|
|
||||||
limit = (encodingType == ENCODING_7BIT)?
|
|
||||||
MAX_USER_DATA_SEPTETS: MAX_USER_DATA_BYTES;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (start < size) {
|
|
||||||
int end = GsmAlphabet.findLimitIndex(text, start, limit, encodingType);
|
|
||||||
result.add(text.substring(start, end));
|
|
||||||
start = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (EncodeException e) {
|
|
||||||
// ignore it.
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,12 +17,17 @@
|
|||||||
package android.telephony;
|
package android.telephony;
|
||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
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.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;
|
||||||
|
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
|
||||||
|
|
||||||
|
import java.lang.Math;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
|
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
|
||||||
|
|
||||||
@@ -44,18 +49,40 @@ public class SmsMessage {
|
|||||||
UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
|
UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Unknown encoding scheme (see TS 23.038) */
|
/**
|
||||||
|
* TODO(cleanup): given that we now have more than one possible
|
||||||
|
* 7bit encoding, this result starts to look rather vague and
|
||||||
|
* maybe confusing... If this is just an indication of code unit
|
||||||
|
* size, maybe that is no problem. Otherwise, should we try to
|
||||||
|
* create an aggregate collection of GSM and CDMA encodings? CDMA
|
||||||
|
* contains a superset of the encodings we use (it does not
|
||||||
|
* support 8-bit GSM, but we also do not use that encoding
|
||||||
|
* currently)... We could get rid of these and directly reference
|
||||||
|
* the CDMA encoding definitions...
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** User data text encoding code unit size */
|
||||||
public static final int ENCODING_UNKNOWN = 0;
|
public static final int ENCODING_UNKNOWN = 0;
|
||||||
/** 7-bit encoding scheme (see TS 23.038) */
|
|
||||||
public static final int ENCODING_7BIT = 1;
|
public static final int ENCODING_7BIT = 1;
|
||||||
/** 8-bit encoding scheme (see TS 23.038) */
|
|
||||||
public static final int ENCODING_8BIT = 2;
|
public static final int ENCODING_8BIT = 2;
|
||||||
/** 16-bit encoding scheme (see TS 23.038) */
|
|
||||||
public static final int ENCODING_16BIT = 3;
|
public static final int ENCODING_16BIT = 3;
|
||||||
|
|
||||||
/** The maximum number of payload bytes per message */
|
/** The maximum number of payload bytes per message */
|
||||||
public static final int MAX_USER_DATA_BYTES = 140;
|
public static final int MAX_USER_DATA_BYTES = 140;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO(cleanup): It would be more flexible and less fragile to
|
||||||
|
* rewrite this (meaning get rid of the following constant) such
|
||||||
|
* that an actual UDH is taken into consideration (meaning its
|
||||||
|
* length is measured), allowing for messages that actually
|
||||||
|
* contain other UDH fields... Hence it is actually a shame to
|
||||||
|
* extend the API with this constant. If necessary, maybe define
|
||||||
|
* the size of such a header and let the math for calculating
|
||||||
|
* max_octets/septets be done elsewhere. And, while I am griping,
|
||||||
|
* if we use the word septet, we should use the word octet in
|
||||||
|
* corresponding places, not byte...
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of payload bytes per message if a user data header
|
* The maximum number of payload bytes per message if a user data header
|
||||||
* is present. This assumes the header only contains the
|
* is present. This assumes the header only contains the
|
||||||
@@ -222,6 +249,15 @@ public class SmsMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO(cleanup): It would make some sense if the result of
|
||||||
|
* preprocessing a message to determine the proper encoding (ie
|
||||||
|
* the resulting datastructure from calculateLength) could be
|
||||||
|
* passed as an argument to the actual final encoding function.
|
||||||
|
* This would better ensure that the logic behind size calculation
|
||||||
|
* actually matched the encoding.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the number of SMS's required to encode the message body and
|
* Calculates the number of SMS's required to encode the message body and
|
||||||
* the number of characters remaining until the next message.
|
* the number of characters remaining until the next message.
|
||||||
@@ -232,46 +268,76 @@ public class SmsMessage {
|
|||||||
* space chars. If false, and if the messageBody contains
|
* space chars. If false, and if the messageBody contains
|
||||||
* non-7-bit encodable characters, length is calculated
|
* non-7-bit encodable characters, length is calculated
|
||||||
* using a 16-bit encoding.
|
* using a 16-bit encoding.
|
||||||
* @return an int[4] with int[0] being the number of SMS's required, int[1]
|
* @return an int[4] with int[0] being the number of SMS's
|
||||||
* the number of code units used, and int[2] is the number of code
|
* required, int[1] the number of code units used, and
|
||||||
* units remaining until the next message. int[3] is the encoding
|
* int[2] is the number of code units remaining until the
|
||||||
* type that should be used for the message.
|
* next message. int[3] is an indicator of the encoding
|
||||||
|
* code unit size (see the ENCODING_* definitions in this
|
||||||
|
* class).
|
||||||
*/
|
*/
|
||||||
public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
|
public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
|
||||||
int activePhone = TelephonyManager.getDefault().getPhoneType();
|
int activePhone = TelephonyManager.getDefault().getPhoneType();
|
||||||
|
TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
|
||||||
|
com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly) :
|
||||||
|
com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly);
|
||||||
int ret[] = new int[4];
|
int ret[] = new int[4];
|
||||||
|
ret[0] = ted.msgCount;
|
||||||
|
ret[1] = ted.codeUnitCount;
|
||||||
|
ret[2] = ted.codeUnitsRemaining;
|
||||||
|
ret[3] = ted.codeUnitSize;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int septets = (PHONE_TYPE_CDMA == activePhone) ?
|
/**
|
||||||
com.android.internal.telephony.cdma.SmsMessage.calc7bitEncodedLength(msgBody,
|
* Divide a message text into several fragments, none bigger than
|
||||||
use7bitOnly) :
|
* the maximum SMS message text size.
|
||||||
com.android.internal.telephony.gsm.SmsMessage.calc7bitEncodedLength(msgBody,
|
*
|
||||||
use7bitOnly);
|
* @param text text, must not be null.
|
||||||
if (septets != -1) {
|
* @return an <code>ArrayList</code> of strings that, in order,
|
||||||
ret[1] = septets;
|
* comprise the original msg text
|
||||||
if (septets > MAX_USER_DATA_SEPTETS) {
|
*/
|
||||||
ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
|
public static ArrayList<String> fragmentText(String text) {
|
||||||
ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER
|
int activePhone = TelephonyManager.getDefault().getPhoneType();
|
||||||
- (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
|
TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
|
||||||
} else {
|
com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false) :
|
||||||
ret[0] = 1;
|
com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false);
|
||||||
ret[2] = MAX_USER_DATA_SEPTETS - septets;
|
|
||||||
}
|
// TODO(cleanup): The code here could be rolled into the logic
|
||||||
ret[3] = ENCODING_7BIT;
|
// below cleanly if these MAX_* constants were defined more
|
||||||
|
// flexibly...
|
||||||
|
|
||||||
|
int limit;
|
||||||
|
if (ted.msgCount > 1) {
|
||||||
|
limit = (ted.codeUnitSize == ENCODING_7BIT) ?
|
||||||
|
MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER;
|
||||||
} else {
|
} else {
|
||||||
int octets = msgBody.length() * 2;
|
limit = (ted.codeUnitSize == ENCODING_7BIT) ?
|
||||||
ret[1] = msgBody.length();
|
MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES;
|
||||||
if (octets > MAX_USER_DATA_BYTES) {
|
|
||||||
ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
|
|
||||||
ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER
|
|
||||||
- (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
|
|
||||||
} else {
|
|
||||||
ret[0] = 1;
|
|
||||||
ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
|
|
||||||
}
|
|
||||||
ret[3] = ENCODING_16BIT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
int pos = 0; // Index in code units.
|
||||||
|
int textLen = text.length();
|
||||||
|
ArrayList<String> result = new ArrayList<String>(ted.msgCount);
|
||||||
|
while (pos < textLen) {
|
||||||
|
int nextPos = 0; // Counts code units.
|
||||||
|
if (ted.codeUnitSize == ENCODING_7BIT) {
|
||||||
|
if (PHONE_TYPE_CDMA == activePhone) {
|
||||||
|
nextPos = pos + Math.min(limit, textLen - pos);
|
||||||
|
} else {
|
||||||
|
nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
|
||||||
|
}
|
||||||
|
} else { // Assume unicode.
|
||||||
|
nextPos = pos + Math.min(limit / 2, textLen - pos);
|
||||||
|
}
|
||||||
|
if ((nextPos <= pos) || (nextPos > textLen)) {
|
||||||
|
Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
|
||||||
|
nextPos + " >= " + textLen + ")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result.add(text.substring(pos, nextPos));
|
||||||
|
pos = nextPos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -576,52 +576,6 @@ public class GsmAlphabet {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index into <code>s</code> of the first character
|
|
||||||
* after <code>limit</code> octets have been reached, starting at
|
|
||||||
* index <code>start</code>. This is used when dividing messages
|
|
||||||
* in UCS2 encoding into units within the SMS message size limit.
|
|
||||||
*
|
|
||||||
* @param s source string
|
|
||||||
* @param start index of where to start counting septets
|
|
||||||
* @param limit maximum septets to include,
|
|
||||||
* e.g. <code>MAX_USER_DATA_BYTES</code>
|
|
||||||
* @return index of first character that won't fit, or the length
|
|
||||||
* of the entire string if everything fits
|
|
||||||
*/
|
|
||||||
public static int
|
|
||||||
findUCS2LimitIndex(String s, int start, int limit) {
|
|
||||||
int numCharToBeEncoded = s.length() - start;
|
|
||||||
return ((numCharToBeEncoded*2 > limit)? limit/2: numCharToBeEncoded) + start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index into <code>s</code> of the first character
|
|
||||||
* after <code>limit</code> septets/octets have been reached
|
|
||||||
* according to the <code>encodingType</code>, starting at
|
|
||||||
* index <code>start</code>. This is used when dividing messages
|
|
||||||
* units within the SMS message size limit.
|
|
||||||
*
|
|
||||||
* @param s source string
|
|
||||||
* @param start index of where to start counting septets
|
|
||||||
* @param limit maximum septets to include,
|
|
||||||
* e.g. <code>MAX_USER_DATA_BYTES</code>
|
|
||||||
* @return index of first character that won't fit, or the length
|
|
||||||
* of the entire string if everything fits
|
|
||||||
*/
|
|
||||||
public static int
|
|
||||||
findLimitIndex(String s, int start, int limit, int encodingType) throws EncodeException {
|
|
||||||
if (encodingType == SmsMessage.ENCODING_7BIT) {
|
|
||||||
return findGsmSeptetLimitIndex(s, start, limit);
|
|
||||||
}
|
|
||||||
else if (encodingType == SmsMessage.ENCODING_16BIT) {
|
|
||||||
return findUCS2LimitIndex(s, start, limit);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new EncodeException("Unsupported encoding type: " + encodingType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set in the static initializer
|
// Set in the static initializer
|
||||||
private static int sGsmSpaceChar;
|
private static int sGsmSpaceChar;
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,38 @@ public abstract class SmsMessageBase {
|
|||||||
/** TP-Message-Reference - Message Reference of sent message. @hide */
|
/** TP-Message-Reference - Message Reference of sent message. @hide */
|
||||||
public int messageRef;
|
public int messageRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a specific text string, this object describes protocol
|
||||||
|
* properties of encoding it for transmission as message user
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
public static class TextEncodingDetails {
|
||||||
|
/**
|
||||||
|
*The number of SMS's required to encode the text.
|
||||||
|
*/
|
||||||
|
public int msgCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of code units consumed so far, where code units
|
||||||
|
* are basically characters in the encoding -- for example,
|
||||||
|
* septets for the standard ASCII and GSM encodings, and 16
|
||||||
|
* bits for Unicode.
|
||||||
|
*/
|
||||||
|
public int codeUnitCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many code units are still available without spilling
|
||||||
|
* into an additional message.
|
||||||
|
*/
|
||||||
|
public int codeUnitsRemaining;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The encoding code unit size (specified using
|
||||||
|
* android.telephony.SmsMessage ENCODING_*).
|
||||||
|
*/
|
||||||
|
public int codeUnitSize;
|
||||||
|
}
|
||||||
|
|
||||||
public static abstract class SubmitPduBase {
|
public static abstract class SubmitPduBase {
|
||||||
public byte[] encodedScAddress; // Null if not applicable.
|
public byte[] encodedScAddress; // Null if not applicable.
|
||||||
public byte[] encodedMessage;
|
public byte[] encodedMessage;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.android.internal.telephony.SmsMessageBase;
|
|||||||
import com.android.internal.telephony.SMSDispatcher;
|
import com.android.internal.telephony.SMSDispatcher;
|
||||||
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.telephony.cdma.sms.UserData;
|
||||||
import com.android.internal.util.HexDump;
|
import com.android.internal.util.HexDump;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -302,8 +303,12 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
|
|||||||
deliveryIntent = deliveryIntents.get(i);
|
deliveryIntent = deliveryIntents.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(scAddr, destAddr,
|
UserData uData = new UserData();
|
||||||
parts.get(i), deliveryIntent != null, smsHeader);
|
uData.payloadStr = parts.get(i);
|
||||||
|
uData.userDataHeader = smsHeader;
|
||||||
|
|
||||||
|
SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destAddr,
|
||||||
|
uData, deliveryIntent != null);
|
||||||
|
|
||||||
sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
|
sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,14 +357,18 @@ public class SmsMessage extends SmsMessageBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the number of septets needed to encode the message.
|
* Get an SMS-SUBMIT PDU for a data message to a destination address & port
|
||||||
*
|
*
|
||||||
* @param messageBody the message to encode
|
* @param destAddr the address of the destination for the message
|
||||||
* @param force ignore (but still count) illegal characters if true
|
* @param userDara the data for the message
|
||||||
* @return septet count, or -1 on failure
|
* @param statusReportRequested Indicates whether a report is requested for this message.
|
||||||
|
* @return a <code>SubmitPdu</code> containing the encoded SC
|
||||||
|
* address, if applicable, and the encoded message.
|
||||||
|
* Returns null on encode error.
|
||||||
*/
|
*/
|
||||||
public static int calc7bitEncodedLength(CharSequence msgBody, boolean force) {
|
public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
|
||||||
return BearerData.calc7bitEncodedLength(msgBody.toString(), force);
|
boolean statusReportRequested) {
|
||||||
|
return privateGetSubmitPdu(destAddr, statusReportRequested, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -441,6 +445,18 @@ public class SmsMessage extends SmsMessageBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the number of septets needed to encode the message.
|
||||||
|
*
|
||||||
|
* @param messageBody the message to encode
|
||||||
|
* @param use7bitOnly ignore (but still count) illegal characters if true
|
||||||
|
* @return TextEncodingDetails
|
||||||
|
*/
|
||||||
|
public static TextEncodingDetails calculateLength(CharSequence messageBody,
|
||||||
|
boolean use7bitOnly) {
|
||||||
|
return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the teleservice type of the message.
|
* Returns the teleservice type of the message.
|
||||||
* @return the teleservice:
|
* @return the teleservice:
|
||||||
@@ -627,12 +643,15 @@ public class SmsMessage extends SmsMessageBase {
|
|||||||
bearerData.userData = userData;
|
bearerData.userData = userData;
|
||||||
bearerData.hasUserDataHeader = (userData.userDataHeader != null);
|
bearerData.hasUserDataHeader = (userData.userDataHeader != null);
|
||||||
|
|
||||||
|
int teleservice = bearerData.hasUserDataHeader ?
|
||||||
|
SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
|
||||||
|
|
||||||
byte[] encodedBearerData = BearerData.encode(bearerData);
|
byte[] encodedBearerData = BearerData.encode(bearerData);
|
||||||
if (encodedBearerData == null) return null;
|
if (encodedBearerData == null) return null;
|
||||||
|
|
||||||
SmsEnvelope envelope = new SmsEnvelope();
|
SmsEnvelope envelope = new SmsEnvelope();
|
||||||
envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
|
envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
|
||||||
envelope.teleService = SmsEnvelope.TELESERVICE_WMT;
|
envelope.teleService = teleservice;
|
||||||
envelope.destAddress = destAddr;
|
envelope.destAddress = destAddr;
|
||||||
envelope.bearerReply = RETURN_ACK;
|
envelope.bearerReply = RETURN_ACK;
|
||||||
envelope.bearerData = encodedBearerData;
|
envelope.bearerData = encodedBearerData;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.internal.telephony.cdma.sms;
|
package com.android.internal.telephony.cdma.sms;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
import android.telephony.SmsMessage;
|
import android.telephony.SmsMessage;
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ import com.android.internal.telephony.IccUtils;
|
|||||||
import com.android.internal.telephony.GsmAlphabet;
|
import com.android.internal.telephony.GsmAlphabet;
|
||||||
import com.android.internal.telephony.SmsHeader;
|
import com.android.internal.telephony.SmsHeader;
|
||||||
import com.android.internal.telephony.cdma.sms.UserData;
|
import com.android.internal.telephony.cdma.sms.UserData;
|
||||||
|
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
|
||||||
|
|
||||||
import com.android.internal.util.HexDump;
|
import com.android.internal.util.HexDump;
|
||||||
import com.android.internal.util.BitwiseInputStream;
|
import com.android.internal.util.BitwiseInputStream;
|
||||||
@@ -35,7 +37,7 @@ import com.android.internal.util.BitwiseOutputStream;
|
|||||||
/**
|
/**
|
||||||
* An object to encode and decode CDMA SMS bearer data.
|
* An object to encode and decode CDMA SMS bearer data.
|
||||||
*/
|
*/
|
||||||
public final class BearerData{
|
public final class BearerData {
|
||||||
private final static String LOG_TAG = "SMS";
|
private final static String LOG_TAG = "SMS";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -385,56 +387,61 @@ public final class BearerData{
|
|||||||
outStream.skip(3);
|
outStream.skip(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SeptetData {
|
private static int countAsciiSeptets(CharSequence msg, boolean force) {
|
||||||
byte data[];
|
int msgLen = msg.length();
|
||||||
int septetCount;
|
if (force) return msgLen;
|
||||||
|
for (int i = 0; i < msgLen; i++) {
|
||||||
SeptetData(byte[] data, int septetCount) {
|
if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
|
||||||
this.data = data;
|
return -1;
|
||||||
this.septetCount = septetCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SeptetData encode7bitAscii(String msg, boolean force)
|
|
||||||
throws CodingException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
|
|
||||||
byte[] expandedData = msg.getBytes("US-ASCII");
|
|
||||||
for (int i = 0; i < expandedData.length; i++) {
|
|
||||||
int charCode = expandedData[i];
|
|
||||||
// Test ourselves for ASCII membership, since Java seems not to care.
|
|
||||||
if ((charCode < UserData.PRINTABLE_ASCII_MIN_INDEX) ||
|
|
||||||
(charCode > UserData.PRINTABLE_ASCII_MAX_INDEX)) {
|
|
||||||
if (force) {
|
|
||||||
outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
|
|
||||||
} else {
|
|
||||||
throw new CodingException("illegal ASCII code (" + charCode + ")");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outStream.write(7, expandedData[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new SeptetData(outStream.toByteArray(), expandedData.length);
|
|
||||||
} catch (java.io.UnsupportedEncodingException ex) {
|
|
||||||
throw new CodingException("7bit ASCII encode failed: " + ex);
|
|
||||||
} catch (BitwiseOutputStream.AccessException ex) {
|
|
||||||
throw new CodingException("7bit ASCII encode failed: " + ex);
|
|
||||||
}
|
}
|
||||||
|
return msgLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the number of septets needed to encode the message.
|
* Calculate the message text encoding length, fragmentation, and other details.
|
||||||
*
|
*
|
||||||
* @param force ignore (but still count) illegal characters if true
|
* @param force ignore (but still count) illegal characters if true
|
||||||
* @return septet count, or -1 on failure
|
* @return septet count, or -1 on failure
|
||||||
*/
|
*/
|
||||||
public static int calc7bitEncodedLength(String msg, boolean force) {
|
public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
|
||||||
|
boolean force7BitEncoding) {
|
||||||
|
TextEncodingDetails ted;
|
||||||
|
int septets = countAsciiSeptets(msg, force7BitEncoding);
|
||||||
|
if (septets != -1 && septets <= SmsMessage.MAX_USER_DATA_SEPTETS) {
|
||||||
|
ted = new TextEncodingDetails();
|
||||||
|
ted.msgCount = 1;
|
||||||
|
ted.codeUnitCount = septets;
|
||||||
|
ted.codeUnitsRemaining = SmsMessage.MAX_USER_DATA_SEPTETS - septets;
|
||||||
|
ted.codeUnitSize = SmsMessage.ENCODING_7BIT;
|
||||||
|
} else {
|
||||||
|
ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
|
||||||
|
msg, force7BitEncoding);
|
||||||
|
}
|
||||||
|
return ted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] encode7bitAscii(String msg, boolean force)
|
||||||
|
throws CodingException
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
SeptetData data = encode7bitAscii(msg, force);
|
BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
|
||||||
return data.septetCount;
|
int msgLen = msg.length();
|
||||||
} catch (CodingException ex) {
|
for (int i = 0; i < msgLen; i++) {
|
||||||
return -1;
|
int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
|
||||||
|
if (charCode == -1) {
|
||||||
|
if (force) {
|
||||||
|
outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
|
||||||
|
} else {
|
||||||
|
throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outStream.write(7, charCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outStream.toByteArray();
|
||||||
|
} catch (BitwiseOutputStream.AccessException ex) {
|
||||||
|
throw new CodingException("7bit ASCII encode failed: " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,8 +459,10 @@ public final class BearerData{
|
|||||||
throws CodingException
|
throws CodingException
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
/**
|
/*
|
||||||
* TODO(cleanup): find some way to do this without the copy.
|
* TODO(cleanup): It would be nice if GsmAlphabet provided
|
||||||
|
* an option to produce just the data without prepending
|
||||||
|
* the length.
|
||||||
*/
|
*/
|
||||||
byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg);
|
byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg);
|
||||||
byte []data = new byte[fullData.length - 1];
|
byte []data = new byte[fullData.length - 1];
|
||||||
@@ -470,54 +479,65 @@ public final class BearerData{
|
|||||||
throws CodingException
|
throws CodingException
|
||||||
{
|
{
|
||||||
byte[] headerData = null;
|
byte[] headerData = null;
|
||||||
|
// TODO: if there is a header, meaning EMS mode, we probably
|
||||||
|
// also want the total UD length prior to the UDH length...
|
||||||
if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader);
|
if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader);
|
||||||
int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet
|
int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet
|
||||||
|
|
||||||
byte[] payloadData;
|
byte[] payloadData;
|
||||||
|
int codeUnitCount;
|
||||||
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?
|
|
||||||
payloadData = new byte[0];
|
payloadData = new byte[0];
|
||||||
|
codeUnitCount = 0;
|
||||||
} else {
|
} else {
|
||||||
payloadData = uData.payload;
|
payloadData = uData.payload;
|
||||||
|
codeUnitCount = uData.payload.length;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (uData.payloadStr == null) {
|
if (uData.payloadStr == null) {
|
||||||
Log.e(LOG_TAG, "non-octet user data with null payloadStr");
|
Log.e(LOG_TAG, "non-octet user data with null payloadStr");
|
||||||
// TODO(code_review): reasonable for fail case? or maybe bail on encoding?
|
|
||||||
uData.payloadStr = "";
|
uData.payloadStr = "";
|
||||||
}
|
}
|
||||||
if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
|
if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
|
||||||
payloadData = encode7bitGsm(uData.payloadStr);
|
payloadData = encode7bitGsm(uData.payloadStr);
|
||||||
|
codeUnitCount = (payloadData.length * 8) / 7;
|
||||||
} else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
|
} else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
|
||||||
SeptetData septetData = encode7bitAscii(uData.payloadStr, true);
|
payloadData = encode7bitAscii(uData.payloadStr, true);
|
||||||
payloadData = septetData.data;
|
codeUnitCount = uData.payloadStr.length();
|
||||||
} else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
|
} else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
|
||||||
payloadData = encodeUtf16(uData.payloadStr);
|
payloadData = encodeUtf16(uData.payloadStr);
|
||||||
|
codeUnitCount = uData.payloadStr.length();
|
||||||
} else {
|
} else {
|
||||||
throw new CodingException("unsupported user data encoding (" +
|
throw new CodingException("unsupported user data encoding (" +
|
||||||
uData.msgEncoding + ")");
|
uData.msgEncoding + ")");
|
||||||
}
|
}
|
||||||
uData.numFields = uData.payloadStr.length();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (uData.payloadStr == null) {
|
if (uData.payloadStr == null) {
|
||||||
Log.e(LOG_TAG, "user data with null payloadStr");
|
Log.e(LOG_TAG, "user data with null payloadStr");
|
||||||
// TODO(code_review): reasonable for fail case? or maybe bail on encoding?
|
|
||||||
uData.payloadStr = "";
|
uData.payloadStr = "";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
SeptetData septetData = encode7bitAscii(uData.payloadStr, false);
|
if (headerData == null) {
|
||||||
payloadData = septetData.data;
|
payloadData = encode7bitAscii(uData.payloadStr, false);
|
||||||
uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
|
codeUnitCount = uData.payloadStr.length();
|
||||||
|
uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
|
||||||
|
} else {
|
||||||
|
// If there is a header, we are in EMS mode, in
|
||||||
|
// which case we use GSM encodings.
|
||||||
|
payloadData = encode7bitGsm(uData.payloadStr);
|
||||||
|
codeUnitCount = (payloadData.length * 8) / 7;
|
||||||
|
uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
|
||||||
|
}
|
||||||
} catch (CodingException ex) {
|
} catch (CodingException ex) {
|
||||||
payloadData = encodeUtf16(uData.payloadStr);
|
payloadData = encodeUtf16(uData.payloadStr);
|
||||||
|
codeUnitCount = uData.payloadStr.length();
|
||||||
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
|
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
|
||||||
}
|
}
|
||||||
uData.msgEncodingSet = true;
|
uData.msgEncodingSet = true;
|
||||||
uData.numFields = uData.payloadStr.length();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalLength = payloadData.length + headerDataLen;
|
int totalLength = payloadData.length + headerDataLen;
|
||||||
@@ -526,6 +546,7 @@ public final class BearerData{
|
|||||||
" > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
|
" > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uData.numFields = codeUnitCount;
|
||||||
uData.payload = new byte[totalLength];
|
uData.payload = new byte[totalLength];
|
||||||
if (headerData != null) {
|
if (headerData != null) {
|
||||||
uData.payload[0] = (byte)headerData.length;
|
uData.payload[0] = (byte)headerData.length;
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.internal.telephony.cdma.sms;
|
package com.android.internal.telephony.cdma.sms;
|
||||||
|
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
import com.android.internal.telephony.SmsHeader;
|
import com.android.internal.telephony.SmsHeader;
|
||||||
import com.android.internal.util.HexDump;
|
import com.android.internal.util.HexDump;
|
||||||
|
|
||||||
@@ -40,6 +42,10 @@ public class UserData {
|
|||||||
/**
|
/**
|
||||||
* IA5 data encoding character mappings.
|
* IA5 data encoding character mappings.
|
||||||
* (See CCITT Rec. T.50 Tables 1 and 3)
|
* (See CCITT Rec. T.50 Tables 1 and 3)
|
||||||
|
*
|
||||||
|
* Note this mapping is the the same as for printable ASCII
|
||||||
|
* characters, with a 0x20 offset, meaning that the ASCII SPACE
|
||||||
|
* character occurs with code 0x20.
|
||||||
*/
|
*/
|
||||||
public static final char[] IA5_MAP = {
|
public static final char[] IA5_MAP = {
|
||||||
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
||||||
@@ -61,7 +67,16 @@ public class UserData {
|
|||||||
* Only elements between these indices in the ASCII table are printable.
|
* Only elements between these indices in the ASCII table are printable.
|
||||||
*/
|
*/
|
||||||
public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
|
public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
|
||||||
public static final int PRINTABLE_ASCII_MAX_INDEX = 0x7F;
|
public static final int ASCII_LF_INDEX = 0x0A;
|
||||||
|
public static final int ASCII_CR_INDEX = 0x0D;
|
||||||
|
public static final SparseIntArray charToAscii = new SparseIntArray();
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < IA5_MAP.length; i++) {
|
||||||
|
charToAscii.put(IA5_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
|
||||||
|
}
|
||||||
|
charToAscii.put('\r', ASCII_LF_INDEX);
|
||||||
|
charToAscii.put('\n', ASCII_CR_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping for IA5 values less than 32 are flow control signals
|
* Mapping for IA5 values less than 32 are flow control signals
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.android.internal.telephony.EncodeException;
|
|||||||
import com.android.internal.telephony.GsmAlphabet;
|
import com.android.internal.telephony.GsmAlphabet;
|
||||||
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.SmsMessageBase.TextEncodingDetails;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@@ -742,17 +743,39 @@ public class SmsMessage extends SmsMessageBase{
|
|||||||
/**
|
/**
|
||||||
* Calculate the number of septets needed to encode the message.
|
* Calculate the number of septets needed to encode the message.
|
||||||
*
|
*
|
||||||
* @param messageBody the message to encode
|
* @param msgBody the message to encode
|
||||||
* @param force ignore (but still count) illegal characters if true
|
* @param use7bitOnly ignore (but still count) illegal characters if true
|
||||||
* @return septet count, or -1 on failure
|
* @return TextEncodingDetails
|
||||||
*/
|
*/
|
||||||
public static int calc7bitEncodedLength(CharSequence messageBody, boolean force) {
|
public static TextEncodingDetails calculateLength(CharSequence msgBody,
|
||||||
|
boolean use7bitOnly) {
|
||||||
|
TextEncodingDetails ted = new TextEncodingDetails();
|
||||||
try {
|
try {
|
||||||
return GsmAlphabet.countGsmSeptets(messageBody, !force);
|
int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly);
|
||||||
|
ted.codeUnitCount = septets;
|
||||||
|
if (septets > MAX_USER_DATA_SEPTETS) {
|
||||||
|
ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
|
||||||
|
ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER
|
||||||
|
- (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
|
||||||
|
} else {
|
||||||
|
ted.msgCount = 1;
|
||||||
|
ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
|
||||||
|
}
|
||||||
|
ted.codeUnitSize = ENCODING_7BIT;
|
||||||
} catch (EncodeException ex) {
|
} catch (EncodeException ex) {
|
||||||
/* Just fall through to the -1 error result below. */
|
int octets = msgBody.length() * 2;
|
||||||
|
ted.codeUnitCount = msgBody.length();
|
||||||
|
if (octets > MAX_USER_DATA_BYTES) {
|
||||||
|
ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
|
||||||
|
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
|
||||||
|
- (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
|
||||||
|
} else {
|
||||||
|
ted.msgCount = 1;
|
||||||
|
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
|
||||||
|
}
|
||||||
|
ted.codeUnitSize = ENCODING_16BIT;
|
||||||
}
|
}
|
||||||
return -1;
|
return ted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
|
|||||||
Reference in New Issue
Block a user