am 4d53cb02: Merge "Enable support for SMS national language shift tables." into gingerbread
* commit '4d53cb02168fe35104d7e644dd9b3efd8ca4c91b': Enable support for SMS national language shift tables.
This commit is contained in:
@@ -426,6 +426,46 @@
|
||||
option to enable/disable read reports is removed in the Messaging app. -->
|
||||
<bool name="config_mms_read_reports_support">true</bool>
|
||||
|
||||
<!-- National Language Identifier codes for the following two config items.
|
||||
(from 3GPP TS 23.038 V9.1.1 Table 6.2.1.2.4.1):
|
||||
0 - reserved
|
||||
1 - Turkish
|
||||
2 - Spanish (single shift table only)
|
||||
3 - Portuguese
|
||||
4 - Bengali
|
||||
5 - Gujarati
|
||||
6 - Hindi
|
||||
7 - Kannada
|
||||
8 - Malayalam
|
||||
9 - Oriya
|
||||
10 - Punjabi
|
||||
11 - Tamil
|
||||
12 - Telugu
|
||||
13 - Urdu
|
||||
14+ - reserved -->
|
||||
|
||||
<!-- National language single shift tables to enable for SMS encoding.
|
||||
Decoding is always enabled. 3GPP TS 23.038 states that this feature
|
||||
should not be enabled until a formal request is issued by the relevant
|
||||
national regulatory body. Array elements are codes from the table above.
|
||||
Example 1: devices sold in Turkey must include table 1 to conform with
|
||||
By-Law Number 27230. (http://www.btk.gov.tr/eng/pdf/2009/BY-LAW_SMS.pdf)
|
||||
Example 2: devices sold in India should include tables 4 through 13
|
||||
to enable use of the new Release 9 tables for Indic languages. -->
|
||||
<integer-array name="config_sms_enabled_single_shift_tables"></integer-array>
|
||||
|
||||
<!-- National language locking shift tables to enable for SMS encoding.
|
||||
Decoding is always enabled. 3GPP TS 23.038 states that this feature
|
||||
should not be enabled until a formal request is issued by the relevant
|
||||
national regulatory body. Array elements are codes from the table above.
|
||||
Example 1: devices sold in Turkey must include table 1 after the
|
||||
Turkish Telecommunication Authority requires locking shift encoding
|
||||
to be enabled (est. July 2012). (http://www.btk.gov.tr/eng/pdf/2009/BY-LAW_SMS.pdf)
|
||||
See also: http://www.mobitech.com.tr/tr/ersanozturkblog_en/index.php?entry=entry090223-160014
|
||||
Example 2: devices sold in India should include tables 4 through 13
|
||||
to enable use of the new Release 9 tables for Indic languages. -->
|
||||
<integer-array name="config_sms_enabled_locking_shift_tables"></integer-array>
|
||||
|
||||
<!-- Set and Unsets WiMAX -->
|
||||
<bool name="config_wimaxEnabled">false</bool>
|
||||
<!-- Location of the wimax framwork jar location -->
|
||||
|
||||
@@ -47,30 +47,25 @@ import android.os.SystemClock;
|
||||
import android.os.WorkSource;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Telephony.Sms.Intents;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.gsm.GsmCellLocation;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import com.android.internal.app.IBatteryStats;
|
||||
import com.android.internal.telephony.Phone;
|
||||
import com.android.internal.location.GpsNetInitiatedHandler;
|
||||
import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.util.HexDump;
|
||||
import com.android.internal.telephony.Phone;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringBufferInputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
|
||||
@@ -314,7 +314,8 @@ public class SmsMessage {
|
||||
nextPos = pos + Math.min(limit, textLen - pos);
|
||||
} else {
|
||||
// For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
|
||||
nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
|
||||
nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit,
|
||||
ted.languageTable, ted.languageShiftTable);
|
||||
}
|
||||
} else { // Assume unicode.
|
||||
nextPos = pos + Math.min(limit / 2, textLen - pos);
|
||||
@@ -370,7 +371,8 @@ public class SmsMessage {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get an SMS-SUBMIT PDU for a destination address and a message
|
||||
* Get an SMS-SUBMIT PDU for a destination address and a message.
|
||||
* This method will not attempt to use any GSM national language 7 bit encodings.
|
||||
*
|
||||
* @param scAddress Service Centre address. Null means use default.
|
||||
* @return a <code>SubmitPdu</code> containing the encoded SC
|
||||
@@ -397,7 +399,8 @@ public class SmsMessage {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SMS-SUBMIT PDU for a destination address and a message
|
||||
* Get an SMS-SUBMIT PDU for a destination address and a message.
|
||||
* This method will not attempt to use any GSM national language 7 bit encodings.
|
||||
*
|
||||
* @param scAddress Service Centre address. Null means use default.
|
||||
* @return a <code>SubmitPdu</code> containing the encoded SC
|
||||
@@ -421,7 +424,8 @@ public class SmsMessage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This method will not attempt to use any GSM national language 7 bit encodings.
|
||||
*
|
||||
* @param scAddress Service Centre address. null == use default
|
||||
* @param destinationAddress the address of the destination for the message
|
||||
|
||||
@@ -297,37 +297,14 @@ public class SmsMessage {
|
||||
*/
|
||||
@Deprecated
|
||||
public static int[] calculateLength(CharSequence messageBody, boolean use7bitOnly) {
|
||||
SmsMessageBase.TextEncodingDetails ted =
|
||||
com.android.internal.telephony.gsm.SmsMessage
|
||||
.calculateLength(messageBody, use7bitOnly);
|
||||
int ret[] = new int[4];
|
||||
|
||||
try {
|
||||
// Try GSM alphabet
|
||||
int septets = GsmAlphabet.countGsmSeptets(messageBody, !use7bitOnly);
|
||||
ret[1] = septets;
|
||||
if (septets > MAX_USER_DATA_SEPTETS) {
|
||||
ret[0] = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
|
||||
MAX_USER_DATA_SEPTETS_WITH_HEADER;
|
||||
ret[2] = (ret[0] * MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
|
||||
} else {
|
||||
ret[0] = 1;
|
||||
ret[2] = MAX_USER_DATA_SEPTETS - septets;
|
||||
}
|
||||
ret[3] = ENCODING_7BIT;
|
||||
} catch (EncodeException ex) {
|
||||
// fall back to UCS-2
|
||||
int octets = messageBody.length() * 2;
|
||||
ret[1] = messageBody.length();
|
||||
if (octets > MAX_USER_DATA_BYTES) {
|
||||
// 6 is the size of the user data header
|
||||
ret[0] = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
|
||||
MAX_USER_DATA_BYTES_WITH_HEADER;
|
||||
ret[2] = ((ret[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
|
||||
} else {
|
||||
ret[0] = 1;
|
||||
ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
|
||||
}
|
||||
ret[3] = ENCODING_16BIT;
|
||||
}
|
||||
|
||||
ret[0] = ted.msgCount;
|
||||
ret[1] = ted.codeUnitCount;
|
||||
ret[2] = ted.codeUnitsRemaining;
|
||||
ret[3] = ted.codeUnitSize;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class SmsHeader {
|
||||
|
||||
// TODO(cleanup): this datastructure is generally referred to as
|
||||
// TODO(cleanup): this data structure is generally referred to as
|
||||
// the 'user data header' or UDH, and so the class name should
|
||||
// change to reflect this...
|
||||
|
||||
@@ -66,6 +66,8 @@ public class SmsHeader {
|
||||
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 ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT = 0x24;
|
||||
public static final int ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT = 0x25;
|
||||
|
||||
public static final int PORT_WAP_PUSH = 2948;
|
||||
public static final int PORT_WAP_WSP = 9200;
|
||||
@@ -96,6 +98,12 @@ public class SmsHeader {
|
||||
public ConcatRef concatRef;
|
||||
public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>();
|
||||
|
||||
/** 7 bit national language locking shift table, or 0 for GSM default 7 bit alphabet. */
|
||||
public int languageTable;
|
||||
|
||||
/** 7 bit national language single shift table, or 0 for GSM default 7 bit extension table. */
|
||||
public int languageShiftTable;
|
||||
|
||||
public SmsHeader() {}
|
||||
|
||||
/**
|
||||
@@ -157,6 +165,12 @@ public class SmsHeader {
|
||||
portAddrs.areEightBits = false;
|
||||
smsHeader.portAddrs = portAddrs;
|
||||
break;
|
||||
case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT:
|
||||
smsHeader.languageShiftTable = inStream.read();
|
||||
break;
|
||||
case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:
|
||||
smsHeader.languageTable = inStream.read();
|
||||
break;
|
||||
default:
|
||||
MiscElt miscElt = new MiscElt();
|
||||
miscElt.id = id;
|
||||
@@ -212,6 +226,16 @@ public class SmsHeader {
|
||||
outStream.write(portAddrs.origPort & 0x00FF);
|
||||
}
|
||||
}
|
||||
if (smsHeader.languageShiftTable != 0) {
|
||||
outStream.write(ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT);
|
||||
outStream.write(1);
|
||||
outStream.write(smsHeader.languageShiftTable);
|
||||
}
|
||||
if (smsHeader.languageTable != 0) {
|
||||
outStream.write(ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT);
|
||||
outStream.write(1);
|
||||
outStream.write(smsHeader.languageTable);
|
||||
}
|
||||
for (MiscElt miscElt : smsHeader.miscEltList) {
|
||||
outStream.write(miscElt.id);
|
||||
outStream.write(miscElt.data.length);
|
||||
@@ -243,6 +267,12 @@ public class SmsHeader {
|
||||
builder.append(", areEightBits=" + portAddrs.areEightBits);
|
||||
builder.append(" }");
|
||||
}
|
||||
if (languageShiftTable != 0) {
|
||||
builder.append(", languageShiftTable=" + languageShiftTable);
|
||||
}
|
||||
if (languageTable != 0) {
|
||||
builder.append(", languageTable=" + languageTable);
|
||||
}
|
||||
for (MiscElt miscElt : miscEltList) {
|
||||
builder.append(", MiscElt ");
|
||||
builder.append("{ id=" + miscElt.id);
|
||||
|
||||
@@ -118,6 +118,16 @@ public abstract class SmsMessageBase {
|
||||
*/
|
||||
public int codeUnitSize;
|
||||
|
||||
/**
|
||||
* The GSM national language table to use, or 0 for the default 7-bit alphabet.
|
||||
*/
|
||||
public int languageTable;
|
||||
|
||||
/**
|
||||
* The GSM national language shift table to use, or 0 for the default 7-bit extension table.
|
||||
*/
|
||||
public int languageShiftTable;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TextEncodingDetails " +
|
||||
@@ -125,6 +135,8 @@ public abstract class SmsMessageBase {
|
||||
", codeUnitCount=" + codeUnitCount +
|
||||
", codeUnitsRemaining=" + codeUnitsRemaining +
|
||||
", codeUnitSize=" + codeUnitSize +
|
||||
", languageTable=" + languageTable +
|
||||
", languageShiftTable=" + languageShiftTable +
|
||||
" }";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class GetInkeyInputResponseData extends ResponseData {
|
||||
int size = mInData.length();
|
||||
|
||||
byte[] tempData = GsmAlphabet
|
||||
.stringToGsm7BitPacked(mInData);
|
||||
.stringToGsm7BitPacked(mInData, 0, 0);
|
||||
data = new byte[size];
|
||||
// Since stringToGsm7BitPacked() set byte 0 in the
|
||||
// returned byte array to the count of septets used...
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
|
||||
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import android.telephony.SmsMessage;
|
||||
|
||||
@@ -30,10 +29,8 @@ import android.text.format.Time;
|
||||
import com.android.internal.telephony.IccUtils;
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
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.BitwiseInputStream;
|
||||
import com.android.internal.util.BitwiseOutputStream;
|
||||
|
||||
@@ -504,7 +501,7 @@ public final class BearerData {
|
||||
* stringToGsm7BitPacked, and potentially directly support
|
||||
* access to the main bitwise stream from encode/decode.
|
||||
*/
|
||||
byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force);
|
||||
byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0);
|
||||
Gsm7bitCodingResult result = new Gsm7bitCodingResult();
|
||||
result.data = new byte[fullData.length - 1];
|
||||
System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
|
||||
@@ -927,7 +924,7 @@ public final class BearerData {
|
||||
private static String decodeUtf16(byte[] data, int offset, int numFields)
|
||||
throws CodingException
|
||||
{
|
||||
// Start reading from the next 16-bit aligned boundry after offset.
|
||||
// Start reading from the next 16-bit aligned boundary after offset.
|
||||
int padding = offset % 2;
|
||||
numFields -= (offset + padding) / 2;
|
||||
try {
|
||||
@@ -973,12 +970,13 @@ public final class BearerData {
|
||||
private static String decode7bitGsm(byte[] data, int offset, int numFields)
|
||||
throws CodingException
|
||||
{
|
||||
// Start reading from the next 7-bit aligned boundry after offset.
|
||||
// Start reading from the next 7-bit aligned boundary after offset.
|
||||
int offsetBits = offset * 8;
|
||||
int offsetSeptets = (offsetBits + 6) / 7;
|
||||
numFields -= offsetSeptets;
|
||||
int paddingBits = (offsetSeptets * 7) - offsetBits;
|
||||
String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits);
|
||||
String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
|
||||
0, 0);
|
||||
if (result == null) {
|
||||
throw new CodingException("7bit GSM decoding failed");
|
||||
}
|
||||
|
||||
@@ -69,9 +69,8 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
String pduString = (String) ar.result;
|
||||
SmsMessage sms = SmsMessage.newFromCDS(pduString);
|
||||
|
||||
int tpStatus = sms.getStatus();
|
||||
|
||||
if (sms != null) {
|
||||
int tpStatus = sms.getStatus();
|
||||
int messageRef = sms.messageRef;
|
||||
for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
|
||||
SmsTracker tracker = deliveryPendingList.get(i);
|
||||
@@ -190,6 +189,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|
||||
mRemainingMessages = msgCount;
|
||||
|
||||
TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
|
||||
for (int i = 0; i < msgCount; i++) {
|
||||
TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
|
||||
if (encoding != details.codeUnitSize
|
||||
@@ -197,6 +197,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|| encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
|
||||
encoding = details.codeUnitSize;
|
||||
}
|
||||
encodingForParts[i] = details;
|
||||
}
|
||||
|
||||
for (int i = 0; i < msgCount; i++) {
|
||||
@@ -213,6 +214,10 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
concatRef.isEightBits = true;
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.concatRef = concatRef;
|
||||
if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
|
||||
smsHeader.languageTable = encodingForParts[i].languageTable;
|
||||
smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
|
||||
}
|
||||
|
||||
PendingIntent sentIntent = null;
|
||||
if (sentIntents != null && sentIntents.size() > i) {
|
||||
@@ -226,7 +231,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|
||||
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
|
||||
parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
|
||||
encoding);
|
||||
encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
|
||||
|
||||
sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
|
||||
}
|
||||
@@ -281,6 +286,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|
||||
mRemainingMessages = msgCount;
|
||||
|
||||
TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
|
||||
for (int i = 0; i < msgCount; i++) {
|
||||
TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
|
||||
if (encoding != details.codeUnitSize
|
||||
@@ -288,6 +294,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|| encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
|
||||
encoding = details.codeUnitSize;
|
||||
}
|
||||
encodingForParts[i] = details;
|
||||
}
|
||||
|
||||
for (int i = 0; i < msgCount; i++) {
|
||||
@@ -298,6 +305,10 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
concatRef.isEightBits = false;
|
||||
SmsHeader smsHeader = new SmsHeader();
|
||||
smsHeader.concatRef = concatRef;
|
||||
if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
|
||||
smsHeader.languageTable = encodingForParts[i].languageTable;
|
||||
smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
|
||||
}
|
||||
|
||||
PendingIntent sentIntent = null;
|
||||
if (sentIntents != null && sentIntents.size() > i) {
|
||||
@@ -311,7 +322,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
|
||||
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
|
||||
parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
|
||||
encoding);
|
||||
encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("smsc", pdus.encodedScAddress);
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.SimRegionCache;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.SmsMessageBase;
|
||||
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -39,7 +38,6 @@ import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -221,9 +219,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
*/
|
||||
public static int getTPLayerLengthForPDU(String pdu) {
|
||||
int len = pdu.length() / 2;
|
||||
int smscLen = 0;
|
||||
|
||||
smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
|
||||
int smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
|
||||
|
||||
return len - smscLen - 1;
|
||||
}
|
||||
@@ -241,7 +237,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
String destinationAddress, String message,
|
||||
boolean statusReportRequested, byte[] header) {
|
||||
return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
|
||||
ENCODING_UNKNOWN);
|
||||
ENCODING_UNKNOWN, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -251,6 +247,8 @@ public class SmsMessage extends SmsMessageBase{
|
||||
*
|
||||
* @param scAddress Service Centre address. Null means use default.
|
||||
* @param encoding Encoding defined by constants in android.telephony.SmsMessage.ENCODING_*
|
||||
* @param languageTable
|
||||
* @param languageShiftTable
|
||||
* @return a <code>SubmitPdu</code> containing the encoded SC
|
||||
* address, if applicable, and the encoded message.
|
||||
* Returns null on encode error.
|
||||
@@ -258,7 +256,8 @@ public class SmsMessage extends SmsMessageBase{
|
||||
*/
|
||||
public static SubmitPdu getSubmitPdu(String scAddress,
|
||||
String destinationAddress, String message,
|
||||
boolean statusReportRequested, byte[] header, int encoding) {
|
||||
boolean statusReportRequested, byte[] header, int encoding,
|
||||
int languageTable, int languageShiftTable) {
|
||||
|
||||
// Perform null parameter checks.
|
||||
if (message == null || destinationAddress == null) {
|
||||
@@ -279,7 +278,8 @@ public class SmsMessage extends SmsMessageBase{
|
||||
}
|
||||
try {
|
||||
if (encoding == ENCODING_7BIT) {
|
||||
userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header);
|
||||
userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
|
||||
languageTable, languageShiftTable);
|
||||
} else { //assume UCS-2
|
||||
try {
|
||||
userData = encodeUCS2(message, header);
|
||||
@@ -384,7 +384,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
* @param destinationAddress the address of the destination for the message
|
||||
* @param destinationPort the port to deliver the message to at the
|
||||
* destination
|
||||
* @param data the dat for the message
|
||||
* @param data the data for the message
|
||||
* @return a <code>SubmitPdu</code> containing the encoded SC
|
||||
* address, if applicable, and the encoded message.
|
||||
* Returns null on encode error.
|
||||
@@ -580,7 +580,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
int second = IccUtils.gsmBcdByteToInt(pdu[cur++]);
|
||||
|
||||
// For the timezone, the most significant bit of the
|
||||
// least signficant nibble is the sign byte
|
||||
// least significant nibble is the sign byte
|
||||
// (meaning the max range of this field is 79 quarter-hours,
|
||||
// which is more than enough)
|
||||
|
||||
@@ -639,7 +639,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
/*
|
||||
* Here we just create the user data length to be the remainder of
|
||||
* the pdu minus the user data header, since userDataLength means
|
||||
* the number of uncompressed sepets.
|
||||
* the number of uncompressed septets.
|
||||
*/
|
||||
bufferLen = pdu.length - offset;
|
||||
} else {
|
||||
@@ -697,70 +697,19 @@ public class SmsMessage extends SmsMessageBase{
|
||||
return userDataHeader;
|
||||
}
|
||||
|
||||
/*
|
||||
XXX Not sure what this one is supposed to be doing, and no one is using
|
||||
it.
|
||||
String getUserDataGSM8bit() {
|
||||
// System.out.println("remainder of pud:" +
|
||||
// HexDump.dumpHexString(pdu, cur, pdu.length - cur));
|
||||
int count = pdu[cur++] & 0xff;
|
||||
int size = pdu[cur++];
|
||||
|
||||
// skip over header for now
|
||||
cur += size;
|
||||
|
||||
if (pdu[cur - 1] == 0x01) {
|
||||
int tid = pdu[cur++] & 0xff;
|
||||
int type = pdu[cur++] & 0xff;
|
||||
|
||||
size = pdu[cur++] & 0xff;
|
||||
|
||||
int i = cur;
|
||||
|
||||
while (pdu[i++] != '\0') {
|
||||
}
|
||||
|
||||
int length = i - cur;
|
||||
String mimeType = new String(pdu, cur, length);
|
||||
|
||||
cur += length;
|
||||
|
||||
if (false) {
|
||||
System.out.println("tid = 0x" + HexDump.toHexString(tid));
|
||||
System.out.println("type = 0x" + HexDump.toHexString(type));
|
||||
System.out.println("header size = " + size);
|
||||
System.out.println("mimeType = " + mimeType);
|
||||
System.out.println("remainder of header:" +
|
||||
HexDump.dumpHexString(pdu, cur, (size - mimeType.length())));
|
||||
}
|
||||
|
||||
cur += size - mimeType.length();
|
||||
|
||||
// System.out.println("data count = " + count + " cur = " + cur
|
||||
// + " :" + HexDump.dumpHexString(pdu, cur, pdu.length - cur));
|
||||
|
||||
MMSMessage msg = MMSMessage.parseEncoding(mContext, pdu, cur,
|
||||
pdu.length - cur);
|
||||
} else {
|
||||
System.out.println(new String(pdu, cur, pdu.length - cur - 1));
|
||||
}
|
||||
|
||||
return IccUtils.bytesToHexString(pdu);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interprets the user data payload as pack GSM 7bit characters, and
|
||||
* Interprets the user data payload as packed GSM 7bit characters, and
|
||||
* decodes them into a String.
|
||||
*
|
||||
* @param septetCount the number of septets in the user data payload
|
||||
* @return a String with the decoded characters
|
||||
*/
|
||||
String getUserDataGSM7Bit(int septetCount) {
|
||||
String getUserDataGSM7Bit(int septetCount, int languageTable,
|
||||
int languageShiftTable) {
|
||||
String ret;
|
||||
|
||||
ret = GsmAlphabet.gsm7BitPackedToString(pdu, cur, septetCount,
|
||||
mUserDataSeptetPadding);
|
||||
mUserDataSeptetPadding, languageTable, languageShiftTable);
|
||||
|
||||
cur += (septetCount * 7) / 8;
|
||||
|
||||
@@ -824,21 +773,9 @@ public class SmsMessage extends SmsMessageBase{
|
||||
*/
|
||||
public static TextEncodingDetails calculateLength(CharSequence msgBody,
|
||||
boolean use7bitOnly) {
|
||||
TextEncodingDetails ted = new TextEncodingDetails();
|
||||
try {
|
||||
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)) /
|
||||
MAX_USER_DATA_SEPTETS_WITH_HEADER;
|
||||
ted.codeUnitsRemaining = (ted.msgCount *
|
||||
MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
|
||||
} else {
|
||||
ted.msgCount = 1;
|
||||
ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
|
||||
}
|
||||
ted.codeUnitSize = ENCODING_7BIT;
|
||||
} catch (EncodeException ex) {
|
||||
TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(msgBody, use7bitOnly);
|
||||
if (ted == null) {
|
||||
ted = new TextEncodingDetails();
|
||||
int octets = msgBody.length() * 2;
|
||||
ted.codeUnitCount = msgBody.length();
|
||||
if (octets > MAX_USER_DATA_BYTES) {
|
||||
@@ -875,7 +812,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isMWIClearMessage() {
|
||||
if (isMwi && (mwiSense == false)) {
|
||||
if (isMwi && !mwiSense) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -885,7 +822,7 @@ public class SmsMessage extends SmsMessageBase{
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isMWISetMessage() {
|
||||
if (isMwi && (mwiSense == true)) {
|
||||
if (isMwi && mwiSense) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -931,13 +868,13 @@ public class SmsMessage extends SmsMessageBase{
|
||||
* TS 27.005 3.1, <pdu> definition "In the case of SMS: 3GPP TS 24.011 [6]
|
||||
* SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
|
||||
* ME/TA converts each octet of TP data unit into two IRA character long
|
||||
* hexad number (e.g. octet with integer value 42 is presented to TE as two
|
||||
* hex number (e.g. octet with integer value 42 is presented to TE as two
|
||||
* characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
|
||||
* something else...
|
||||
*/
|
||||
private void parsePdu(byte[] pdu) {
|
||||
mPdu = pdu;
|
||||
// Log.d(LOG_TAG, "raw sms mesage:");
|
||||
// Log.d(LOG_TAG, "raw sms message:");
|
||||
// Log.d(LOG_TAG, s);
|
||||
|
||||
PduParser p = new PduParser(pdu);
|
||||
@@ -1158,7 +1095,9 @@ public class SmsMessage extends SmsMessageBase{
|
||||
break;
|
||||
|
||||
case ENCODING_7BIT:
|
||||
messageBody = p.getUserDataGSM7Bit(count);
|
||||
messageBody = p.getUserDataGSM7Bit(count,
|
||||
hasUserDataHeader ? userDataHeader.languageTable : 0,
|
||||
hasUserDataHeader ? userDataHeader.languageShiftTable : 0);
|
||||
break;
|
||||
|
||||
case ENCODING_16BIT:
|
||||
|
||||
@@ -20,7 +20,6 @@ import junit.framework.TestCase;
|
||||
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.test.suitebuilder.annotation.Suppress;
|
||||
|
||||
public class GsmAlphabetTest extends TestCase {
|
||||
|
||||
@@ -38,20 +37,21 @@ public class GsmAlphabetTest extends TestCase {
|
||||
|
||||
String message = "aaaaaaaaaabbbbbbbbbbcccccccccc";
|
||||
byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message,
|
||||
SmsHeader.toByteArray(header));
|
||||
int septetCount = GsmAlphabet.countGsmSeptets(message, false);
|
||||
SmsHeader.toByteArray(header), 0, 0);
|
||||
int septetCount = GsmAlphabet.countGsmSeptetsUsingTables(message, true, 0, 0);
|
||||
String parsedMessage = GsmAlphabet.gsm7BitPackedToString(
|
||||
userData, SmsHeader.toByteArray(header).length+2, septetCount, 1);
|
||||
userData, SmsHeader.toByteArray(header).length+2, septetCount, 1, 0, 0);
|
||||
assertEquals(message, parsedMessage);
|
||||
}
|
||||
|
||||
// TODO: This method should *really* be a series of individual test methods.
|
||||
@LargeTest
|
||||
// However, it's a SmallTest because it executes quickly.
|
||||
@SmallTest
|
||||
public void testBasic() throws Exception {
|
||||
// '@' maps to char 0
|
||||
assertEquals(0, GsmAlphabet.charToGsm('@'));
|
||||
|
||||
// `a (a with grave accent) maps to last GSM charater
|
||||
// `a (a with grave accent) maps to last GSM character
|
||||
assertEquals(0x7f, GsmAlphabet.charToGsm('\u00e0'));
|
||||
|
||||
//
|
||||
@@ -97,7 +97,7 @@ public class GsmAlphabetTest extends TestCase {
|
||||
|
||||
assertEquals('@', GsmAlphabet.gsmToChar(0));
|
||||
|
||||
// `a (a with grave accent) maps to last GSM charater
|
||||
// `a (a with grave accent) maps to last GSM character
|
||||
assertEquals('\u00e0', GsmAlphabet.gsmToChar(0x7f));
|
||||
|
||||
assertEquals('\uffff',
|
||||
@@ -116,8 +116,12 @@ public class GsmAlphabetTest extends TestCase {
|
||||
assertEquals(' ', GsmAlphabet.gsmExtendedToChar(
|
||||
GsmAlphabet.GSM_EXTENDED_ESCAPE));
|
||||
|
||||
// Unmappable
|
||||
assertEquals(' ', GsmAlphabet.gsmExtendedToChar(0));
|
||||
// Reserved for extension to extension table (mapped to space)
|
||||
assertEquals(' ', GsmAlphabet.gsmExtendedToChar(GsmAlphabet.GSM_EXTENDED_ESCAPE));
|
||||
|
||||
// Unmappable (mapped to character in default or national locking shift table)
|
||||
assertEquals('@', GsmAlphabet.gsmExtendedToChar(0));
|
||||
assertEquals('\u00e0', GsmAlphabet.gsmExtendedToChar(0x7f));
|
||||
|
||||
//
|
||||
// stringTo7BitPacked, gsm7BitPackedToString
|
||||
@@ -128,7 +132,7 @@ public class GsmAlphabetTest extends TestCase {
|
||||
|
||||
// Check all alignment cases
|
||||
for (int i = 0; i < 9; i++, testString.append('@')) {
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
|
||||
assertEquals(testString.toString(),
|
||||
GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
|
||||
}
|
||||
@@ -149,7 +153,7 @@ public class GsmAlphabetTest extends TestCase {
|
||||
assertEquals(1, GsmAlphabet.countGsmSeptets(c));
|
||||
}
|
||||
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
|
||||
assertEquals(testString.toString(),
|
||||
GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
|
||||
|
||||
@@ -164,7 +168,7 @@ public class GsmAlphabetTest extends TestCase {
|
||||
|
||||
}
|
||||
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
|
||||
assertEquals(testString.toString(),
|
||||
GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
|
||||
|
||||
@@ -175,7 +179,7 @@ public class GsmAlphabetTest extends TestCase {
|
||||
testString.append('@');
|
||||
}
|
||||
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
|
||||
assertEquals(testString.toString(),
|
||||
GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
|
||||
|
||||
@@ -183,7 +187,7 @@ public class GsmAlphabetTest extends TestCase {
|
||||
testString.append('@');
|
||||
|
||||
try {
|
||||
GsmAlphabet.stringToGsm7BitPacked(testString.toString());
|
||||
GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
|
||||
fail("expected exception");
|
||||
} catch (EncodeException ex) {
|
||||
// exception expected
|
||||
@@ -196,7 +200,7 @@ public class GsmAlphabetTest extends TestCase {
|
||||
testString.append('{');
|
||||
}
|
||||
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
|
||||
packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
|
||||
assertEquals(testString.toString(),
|
||||
GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
|
||||
|
||||
@@ -204,17 +208,29 @@ public class GsmAlphabetTest extends TestCase {
|
||||
testString.append('{');
|
||||
|
||||
try {
|
||||
GsmAlphabet.stringToGsm7BitPacked(testString.toString());
|
||||
GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
|
||||
fail("expected exception");
|
||||
} catch (EncodeException ex) {
|
||||
// exception expected
|
||||
}
|
||||
|
||||
// Reserved for extension to extension table (mapped to space)
|
||||
packed = new byte[]{(byte)(0x1b | 0x80), 0x1b >> 1};
|
||||
assertEquals(" ", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
|
||||
|
||||
// Unmappable (mapped to character in default alphabet table)
|
||||
packed[0] = 0x1b;
|
||||
packed[1] = 0x00;
|
||||
assertEquals("@", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
|
||||
packed[0] = (byte)(0x1b | 0x80);
|
||||
packed[1] = (byte)(0x7f >> 1);
|
||||
assertEquals("\u00e0", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
|
||||
|
||||
//
|
||||
// 8 bit unpacked format
|
||||
//
|
||||
// Note: we compare hex strings here
|
||||
// because Assert doesnt have array-comparisons
|
||||
// because Assert doesn't have array comparisons
|
||||
|
||||
byte unpacked[];
|
||||
|
||||
@@ -306,5 +322,16 @@ public class GsmAlphabetTest extends TestCase {
|
||||
|
||||
assertEquals("a",
|
||||
GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
|
||||
|
||||
// Reserved for extension to extension table (mapped to space)
|
||||
unpacked[0] = 0x1b;
|
||||
unpacked[1] = 0x1b;
|
||||
assertEquals(" ", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
|
||||
|
||||
// Unmappable (mapped to character in default or national locking shift table)
|
||||
unpacked[1] = 0x00;
|
||||
assertEquals("@", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
|
||||
unpacked[1] = 0x7f;
|
||||
assertEquals("\u00e0", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,12 @@
|
||||
|
||||
package com.android.internal.telephony;
|
||||
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.gsm.SmsMessage;
|
||||
import com.android.internal.util.HexDump;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class GsmSmsTest extends AndroidTestCase {
|
||||
|
||||
@SmallTest
|
||||
@@ -211,8 +207,38 @@ public class GsmSmsTest extends AndroidTestCase {
|
||||
sms.getMessageBody());
|
||||
}
|
||||
|
||||
// GSM 7 bit tables in String form, Escape (0x1B) replaced with '@'
|
||||
private static final String[] sBasicTables = {
|
||||
// GSM 7 bit default alphabet
|
||||
"@\u00a3$\u00a5\u00e8\u00e9\u00f9\u00ec\u00f2\u00c7\n\u00d8\u00f8\r\u00c5\u00e5\u0394_"
|
||||
+ "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e@\u00c6\u00e6\u00df\u00c9"
|
||||
+ " !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u00a1ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c4\u00d6"
|
||||
+ "\u00d1\u00dc\u00a7\u00bfabcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
|
||||
|
||||
// Turkish locking shift table
|
||||
"@\u00a3$\u00a5\u20ac\u00e9\u00f9\u0131\u00f2\u00c7\n\u011e\u011f\r\u00c5\u00e5\u0394_"
|
||||
+ "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e@\u015e\u015f\u00df\u00c9"
|
||||
+ " !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u0130ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c4\u00d6"
|
||||
+ "\u00d1\u00dc\u00a7\u00e7abcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
|
||||
|
||||
// no locking shift table defined for Spanish
|
||||
"",
|
||||
|
||||
// Portuguese locking shift table
|
||||
"@\u00a3$\u00a5\u00ea\u00e9\u00fa\u00ed\u00f3\u00e7\n\u00d4\u00f4\r\u00c1\u00e1\u0394_"
|
||||
+ "\u00aa\u00c7\u00c0\u221e^\\\u20ac\u00d3|@\u00c2\u00e2\u00ca\u00c9 !\"#\u00ba%&'()"
|
||||
+ "*+,-./0123456789:;<=>?\u00cdABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c3\u00d5\u00da\u00dc"
|
||||
+ "\u00a7~abcdefghijklmnopqrstuvwxyz\u00e3\u00f5`\u00fc\u00e0"
|
||||
};
|
||||
|
||||
@SmallTest
|
||||
public void testDecode() throws Exception {
|
||||
decodeSingle(0); // default table
|
||||
decodeSingle(1); // Turkish locking shift table
|
||||
decodeSingle(3); // Portuguese locking shift table
|
||||
}
|
||||
|
||||
private void decodeSingle(int language) throws Exception {
|
||||
byte[] septets = new byte[(7 * 128 + 7) / 8];
|
||||
|
||||
int bitOffset = 0;
|
||||
@@ -238,15 +264,168 @@ public class GsmSmsTest extends AndroidTestCase {
|
||||
bitOffset += 7;
|
||||
}
|
||||
|
||||
String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, 128);
|
||||
byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded);
|
||||
String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, 128, 0, language, 0);
|
||||
byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded, language, 0);
|
||||
|
||||
assertEquals(sBasicTables[language], decoded);
|
||||
|
||||
// reEncoded has the count septets byte at the front
|
||||
assertEquals(reEncoded.length, septets.length + 1);
|
||||
assertEquals(septets.length + 1, reEncoded.length);
|
||||
|
||||
for (int i = 0; i < septets.length; i++) {
|
||||
assertEquals(reEncoded[i + 1], septets[i]);
|
||||
assertEquals(septets[i], reEncoded[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int GSM_ESCAPE_CHARACTER = 0x1b;
|
||||
|
||||
private static final String[] sExtendedTables = {
|
||||
// GSM 7 bit default alphabet extension table
|
||||
"\f^{}\\[~]|\u20ac",
|
||||
|
||||
// Turkish single shift extension table
|
||||
"\f^{}\\[~]|\u011e\u0130\u015e\u00e7\u20ac\u011f\u0131\u015f",
|
||||
|
||||
// Spanish single shift extension table
|
||||
"\u00e7\f^{}\\[~]|\u00c1\u00cd\u00d3\u00da\u00e1\u20ac\u00ed\u00f3\u00fa",
|
||||
|
||||
// Portuguese single shift extension table
|
||||
"\u00ea\u00e7\f\u00d4\u00f4\u00c1\u00e1\u03a6\u0393^\u03a9\u03a0\u03a8\u03a3\u0398\u00ca"
|
||||
+ "{}\\[~]|\u00c0\u00cd\u00d3\u00da\u00c3\u00d5\u00c2\u20ac\u00ed\u00f3\u00fa\u00e3"
|
||||
+ "\u00f5\u00e2"
|
||||
};
|
||||
|
||||
private static final int[][] sExtendedTableIndexes = {
|
||||
{0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x65},
|
||||
{0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x47, 0x49, 0x53, 0x63,
|
||||
0x65, 0x67, 0x69, 0x73},
|
||||
{0x09, 0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x49, 0x4f,
|
||||
0x55, 0x61, 0x65, 0x69, 0x6f, 0x75},
|
||||
{0x05, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1f, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x49,
|
||||
0x4f, 0x55, 0x5b, 0x5c, 0x61, 0x65, 0x69, 0x6f, 0x75, 0x7b, 0x7c, 0x7f}
|
||||
};
|
||||
|
||||
@SmallTest
|
||||
public void testDecodeExtended() throws Exception {
|
||||
for (int language = 0; language < 3; language++) {
|
||||
int[] tableIndex = sExtendedTableIndexes[language];
|
||||
int numSeptets = tableIndex.length * 2; // two septets per extended char
|
||||
byte[] septets = new byte[(7 * numSeptets + 7) / 8];
|
||||
|
||||
int bitOffset = 0;
|
||||
|
||||
for (int v : tableIndex) {
|
||||
// escape character
|
||||
int byteOffset = bitOffset / 8;
|
||||
int shift = bitOffset % 8;
|
||||
|
||||
septets[byteOffset] |= GSM_ESCAPE_CHARACTER << shift;
|
||||
|
||||
if (shift > 1) {
|
||||
septets[byteOffset + 1] = (byte) (GSM_ESCAPE_CHARACTER >> (8 - shift));
|
||||
}
|
||||
|
||||
bitOffset += 7;
|
||||
|
||||
// extended table index
|
||||
byteOffset = bitOffset / 8;
|
||||
shift = bitOffset % 8;
|
||||
|
||||
septets[byteOffset] |= v << shift;
|
||||
|
||||
if (shift > 1) {
|
||||
septets[byteOffset + 1] = (byte) (v >> (8 - shift));
|
||||
}
|
||||
|
||||
bitOffset += 7;
|
||||
}
|
||||
|
||||
String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0,
|
||||
0, language);
|
||||
byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded, 0, language);
|
||||
|
||||
assertEquals(sExtendedTables[language], decoded);
|
||||
|
||||
// reEncoded has the count septets byte at the front
|
||||
assertEquals(septets.length + 1, reEncoded.length);
|
||||
|
||||
for (int i = 0; i < septets.length; i++) {
|
||||
assertEquals(septets[i], reEncoded[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testDecodeExtendedFallback() throws Exception {
|
||||
// verify that unmapped characters in extension table fall back to locking shift table
|
||||
for (int language = 0; language < 3; language++) {
|
||||
int[] tableIndex = sExtendedTableIndexes[language];
|
||||
int numChars = 128 - tableIndex.length;
|
||||
int numSeptets = numChars * 2; // two septets per extended char
|
||||
byte[] septets = new byte[(7 * numSeptets + 7) / 8];
|
||||
|
||||
int tableOffset = 0;
|
||||
int bitOffset = 0;
|
||||
|
||||
StringBuilder defaultTable = new StringBuilder(128);
|
||||
StringBuilder turkishTable = new StringBuilder(128);
|
||||
StringBuilder portugueseTable = new StringBuilder(128);
|
||||
|
||||
for (char c = 0; c < 128; c++) {
|
||||
// skip characters that are present in the current extension table
|
||||
if (tableOffset < tableIndex.length && tableIndex[tableOffset] == c) {
|
||||
tableOffset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// escape character
|
||||
int byteOffset = bitOffset / 8;
|
||||
int shift = bitOffset % 8;
|
||||
|
||||
septets[byteOffset] |= GSM_ESCAPE_CHARACTER << shift;
|
||||
|
||||
if (shift > 1) {
|
||||
septets[byteOffset + 1] = (byte) (GSM_ESCAPE_CHARACTER >> (8 - shift));
|
||||
}
|
||||
|
||||
bitOffset += 7;
|
||||
|
||||
// extended table index
|
||||
byteOffset = bitOffset / 8;
|
||||
shift = bitOffset % 8;
|
||||
|
||||
septets[byteOffset] |= c << shift;
|
||||
|
||||
if (shift > 1) {
|
||||
septets[byteOffset + 1] = (byte) (c >> (8 - shift));
|
||||
}
|
||||
|
||||
bitOffset += 7;
|
||||
|
||||
if (c == GsmAlphabet.GSM_EXTENDED_ESCAPE) {
|
||||
// double Escape maps to space character
|
||||
defaultTable.append(' ');
|
||||
turkishTable.append(' ');
|
||||
portugueseTable.append(' ');
|
||||
} else {
|
||||
// other unmapped chars map to the default or locking shift table
|
||||
defaultTable.append(sBasicTables[0].charAt(c));
|
||||
turkishTable.append(sBasicTables[1].charAt(c));
|
||||
portugueseTable.append(sBasicTables[3].charAt(c));
|
||||
}
|
||||
}
|
||||
|
||||
String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0,
|
||||
0, language);
|
||||
|
||||
assertEquals(defaultTable.toString(), decoded);
|
||||
|
||||
decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0, 1, language);
|
||||
assertEquals(turkishTable.toString(), decoded);
|
||||
|
||||
decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0, 3, language);
|
||||
assertEquals(portugueseTable.toString(), decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,21 +19,57 @@ package com.android.internal.telephony;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Test cases to verify selection of the optimal 7 bit encoding tables
|
||||
* (for all combinations of enabled national language tables) for messages
|
||||
* containing Turkish, Spanish, Portuguese, Greek, and other symbols
|
||||
* present in the GSM default and national language tables defined in
|
||||
* 3GPP TS 23.038. Also verifies correct SMS encoding for CDMA, which only
|
||||
* supports the GSM 7 bit default alphabet, ASCII 8 bit, and UCS-2.
|
||||
* Tests both encoding variations: unsupported characters mapped to space,
|
||||
* and unsupported characters force entire message to UCS-2.
|
||||
*/
|
||||
public class SmsMessageBodyTest extends AndroidTestCase {
|
||||
private static final String TAG = "SmsMessageBodyTest";
|
||||
|
||||
// ASCII chars in the GSM 7 bit default alphabet
|
||||
private static final String sAsciiChars = "@$_ !\"#%&'()*+,-./0123456789" +
|
||||
":;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\r";
|
||||
private static final String sGsmBasicChars = "\u00a3\u00a5\u00e8\u00e9" +
|
||||
"\u00f9\u00ec\u00f2\u00c7\u00d8\u00f8\u00c5\u00e5\u0394\u03a6" +
|
||||
"\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u00c6\u00e6" +
|
||||
"\u00df\u00c9\u00a4\u00a1\u00c4\u00d6\u00d1\u00dc\u00a7\u00bf" +
|
||||
"\u00e4\u00f6\u00f1\u00fc\u00e0";
|
||||
private static final String sGsmExtendedAsciiChars = "{|}\\[~]^\f";
|
||||
|
||||
// Unicode chars in the GSM 7 bit default alphabet and both locking shift tables
|
||||
private static final String sGsmDefaultChars = "\u00a3\u00a5\u00e9\u00c7\u0394\u00c9" +
|
||||
"\u00dc\u00a7\u00fc\u00e0";
|
||||
|
||||
// Unicode chars in the GSM 7 bit default table and Turkish locking shift tables
|
||||
private static final String sGsmDefaultAndTurkishTables = "\u00f9\u00f2\u00c5\u00e5\u00df" +
|
||||
"\u00a4\u00c4\u00d6\u00d1\u00e4\u00f6\u00f1";
|
||||
|
||||
// Unicode chars in the GSM 7 bit default table but not the locking shift tables
|
||||
private static final String sGsmDefaultTableOnly = "\u00e8\u00ec\u00d8\u00f8\u00c6\u00e6" +
|
||||
"\u00a1\u00bf";
|
||||
|
||||
// ASCII chars in the GSM default extension table
|
||||
private static final String sGsmExtendedAsciiChars = "{}[]\f";
|
||||
|
||||
// chars in GSM default extension table and Portuguese locking shift table
|
||||
private static final String sGsmExtendedPortugueseLocking = "^\\|~";
|
||||
|
||||
// Euro currency symbol
|
||||
private static final String sGsmExtendedEuroSymbol = "\u20ac";
|
||||
|
||||
// CJK ideographs, Hiragana, Katakana, full width letters, Cyrillic, etc.
|
||||
private static final String sUnicodeChars = "\u4e00\u4e01\u4e02\u4e03" +
|
||||
"\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d" +
|
||||
"\u4e0e\u4e0f\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048" +
|
||||
@@ -43,6 +79,86 @@ public class SmsMessageBodyTest extends AndroidTestCase {
|
||||
"\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408" +
|
||||
"\u00a2\u00a9\u00ae\u2122";
|
||||
|
||||
// chars in Turkish single shift and locking shift tables
|
||||
private static final String sTurkishChars = "\u0131\u011e\u011f\u015e\u015f\u0130";
|
||||
|
||||
// chars in Spanish single shift table and Portuguese single and locking shift tables
|
||||
private static final String sPortugueseAndSpanishChars = "\u00c1\u00e1\u00cd\u00ed"
|
||||
+ "\u00d3\u00f3\u00da\u00fa";
|
||||
|
||||
// chars in all national language tables but not in the standard GSM alphabets
|
||||
private static final String sNationalLanguageTablesOnly = "\u00e7";
|
||||
|
||||
// chars in Portuguese single shift and locking shift tables
|
||||
private static final String sPortugueseChars = "\u00ea\u00d4\u00f4\u00c0\u00c2\u00e2"
|
||||
+ "\u00ca\u00c3\u00d5\u00e3\u00f5";
|
||||
|
||||
// chars in Portuguese locking shift table only
|
||||
private static final String sPortugueseLockingShiftChars = "\u00aa\u221e\u00ba`";
|
||||
|
||||
// Greek letters in GSM alphabet missing from Portuguese locking and single shift tables
|
||||
private static final String sGreekLettersNotInPortugueseTables = "\u039b\u039e";
|
||||
|
||||
// Greek letters in GSM alphabet and Portuguese single shift (but not locking shift) table
|
||||
private static final String sGreekLettersInPortugueseShiftTable =
|
||||
"\u03a6\u0393\u03a9\u03a0\u03a8\u03a3\u0398";
|
||||
|
||||
// List of classes of characters in SMS tables
|
||||
private static final String[] sCharacterClasses = {
|
||||
sGsmExtendedAsciiChars,
|
||||
sGsmExtendedPortugueseLocking,
|
||||
sGsmDefaultChars,
|
||||
sGsmDefaultAndTurkishTables,
|
||||
sGsmDefaultTableOnly,
|
||||
sGsmExtendedEuroSymbol,
|
||||
sUnicodeChars,
|
||||
sTurkishChars,
|
||||
sPortugueseChars,
|
||||
sPortugueseLockingShiftChars,
|
||||
sPortugueseAndSpanishChars,
|
||||
sGreekLettersNotInPortugueseTables,
|
||||
sGreekLettersInPortugueseShiftTable,
|
||||
sNationalLanguageTablesOnly,
|
||||
sAsciiChars
|
||||
};
|
||||
|
||||
private static final int sNumCharacterClasses = sCharacterClasses.length;
|
||||
|
||||
// For each character class, whether it is present in a particular char table.
|
||||
// First three entries are locking shift tables, followed by four single shift tables
|
||||
private static final boolean[][] sCharClassPresenceInTables = {
|
||||
// ASCII chars in all GSM extension tables
|
||||
{false, false, false, true, true, true, true},
|
||||
// ASCII chars in all GSM extension tables and Portuguese locking shift table
|
||||
{false, false, true, true, true, true, true},
|
||||
// non-ASCII chars in GSM default alphabet and all locking tables
|
||||
{true, true, true, false, false, false, false},
|
||||
// non-ASCII chars in GSM default alphabet and Turkish locking shift table
|
||||
{true, true, false, false, false, false, false},
|
||||
// non-ASCII chars in GSM default alphabet table only
|
||||
{true, false, false, false, false, false, false},
|
||||
// Euro symbol is present in several tables
|
||||
{false, true, true, true, true, true, true},
|
||||
// Unicode characters not present in any 7 bit tables
|
||||
{false, false, false, false, false, false, false},
|
||||
// Characters specific to Turkish language
|
||||
{false, true, false, false, true, false, false},
|
||||
// Characters in Portuguese single shift and locking shift tables
|
||||
{false, false, true, false, false, false, true},
|
||||
// Characters in Portuguese locking shift table only
|
||||
{false, false, true, false, false, false, false},
|
||||
// Chars in Spanish single shift and Portuguese single and locking shift tables
|
||||
{false, false, true, false, false, true, true},
|
||||
// Greek letters in GSM default alphabet missing from Portuguese tables
|
||||
{true, true, false, false, false, false, false},
|
||||
// Greek letters in GSM alphabet and Portuguese single shift table
|
||||
{true, true, false, false, false, false, true},
|
||||
// Chars in all national language tables but not the standard GSM tables
|
||||
{false, true, true, false, true, true, true},
|
||||
// ASCII chars in GSM default alphabet
|
||||
{true, true, true, false, false, false, false}
|
||||
};
|
||||
|
||||
private static final int sTestLengthCount = 12;
|
||||
|
||||
private static final int[] sSeptetTestLengths =
|
||||
@@ -60,11 +176,92 @@ public class SmsMessageBodyTest extends AndroidTestCase {
|
||||
private static final int[] sUnicodeUnitsRemaining =
|
||||
{ 70, 69, 68, 35, 1, 0, 63, 34, 1, 0, 66, 41};
|
||||
|
||||
// Combinations of enabled GSM national language single shift tables
|
||||
private static final int[][] sEnabledSingleShiftTables = {
|
||||
{}, // GSM default alphabet only
|
||||
{1}, // Turkish (single shift only)
|
||||
{1}, // Turkish (single and locking shift)
|
||||
{2}, // Spanish
|
||||
{3}, // Portuguese (single shift only)
|
||||
{3}, // Portuguese (single and locking shift)
|
||||
{1, 2}, // Turkish + Spanish (single shift only)
|
||||
{1, 2}, // Turkish + Spanish (single and locking shift)
|
||||
{1, 3}, // Turkish + Portuguese (single shift only)
|
||||
{1, 3}, // Turkish + Portuguese (single and locking shift)
|
||||
{2, 3}, // Spanish + Portuguese (single shift only)
|
||||
{2, 3}, // Spanish + Portuguese (single and locking shift)
|
||||
{1, 2, 3}, // Turkish, Spanish, Portuguese (single shift only)
|
||||
{1, 2, 3}, // Turkish, Spanish, Portuguese (single and locking shift)
|
||||
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} // all language tables
|
||||
};
|
||||
|
||||
// Combinations of enabled GSM national language locking shift tables
|
||||
private static final int[][] sEnabledLockingShiftTables = {
|
||||
{}, // GSM default alphabet only
|
||||
{}, // Turkish (single shift only)
|
||||
{1}, // Turkish (single and locking shift)
|
||||
{}, // Spanish (no locking shift table)
|
||||
{}, // Portuguese (single shift only)
|
||||
{3}, // Portuguese (single and locking shift)
|
||||
{}, // Turkish + Spanish (single shift only)
|
||||
{1}, // Turkish + Spanish (single and locking shift)
|
||||
{}, // Turkish + Portuguese (single shift only)
|
||||
{1, 3}, // Turkish + Portuguese (single and locking shift)
|
||||
{}, // Spanish + Portuguese (single shift only)
|
||||
{3}, // Spanish + Portuguese (single and locking shift)
|
||||
{}, // Turkish, Spanish, Portuguese (single shift only)
|
||||
{1, 3}, // Turkish, Spanish, Portuguese (single and locking shift)
|
||||
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} // all language tables
|
||||
};
|
||||
|
||||
// LanguagePair counter indexes to check for each entry above
|
||||
private static final int[][] sLanguagePairIndexesByEnabledIndex = {
|
||||
{0}, // default tables only
|
||||
{0, 1}, // Turkish (single shift only)
|
||||
{0, 1, 4, 5}, // Turkish (single and locking shift)
|
||||
{0, 2}, // Spanish
|
||||
{0, 3}, // Portuguese (single shift only)
|
||||
{0, 3, 8, 11}, // Portuguese (single and locking shift)
|
||||
{0, 1, 2}, // Turkish + Spanish (single shift only)
|
||||
{0, 1, 2, 4, 5, 6}, // Turkish + Spanish (single and locking shift)
|
||||
{0, 1, 3}, // Turkish + Portuguese (single shift only)
|
||||
{0, 1, 3, 4, 5, 7, 8, 9, 11}, // Turkish + Portuguese (single and locking shift)
|
||||
{0, 2, 3}, // Spanish + Portuguese (single shift only)
|
||||
{0, 2, 3, 8, 10, 11}, // Spanish + Portuguese (single and locking shift)
|
||||
{0, 1, 2, 3}, // all languages (single shift only)
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, // all languages (single and locking shift)
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} // all languages (no Indic chars in test)
|
||||
};
|
||||
|
||||
/**
|
||||
* User data header requires one octet for length. Count as one septet, because
|
||||
* all combinations of header elements below will have at least one free bit
|
||||
* when padding to the nearest septet boundary.
|
||||
*/
|
||||
private static final int UDH_SEPTET_COST_LENGTH = 1;
|
||||
|
||||
/**
|
||||
* Using a non-default language locking shift table OR single shift table
|
||||
* requires a user data header of 3 octets, or 4 septets, plus UDH length.
|
||||
*/
|
||||
private static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4;
|
||||
|
||||
/**
|
||||
* Using a non-default language locking shift table AND single shift table
|
||||
* requires a user data header of 6 octets, or 7 septets, plus UDH length.
|
||||
*/
|
||||
private static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7;
|
||||
|
||||
/**
|
||||
* Multi-part messages require a user data header of 5 octets, or 6 septets,
|
||||
* plus UDH length.
|
||||
*/
|
||||
private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
|
||||
|
||||
@SmallTest
|
||||
public void testCalcLengthAscii() throws Exception {
|
||||
StringBuilder sb = new StringBuilder(320);
|
||||
int[] values = {0, 0, 0, SmsMessage.ENCODING_7BIT};
|
||||
int[] values = {0, 0, 0, SmsMessage.ENCODING_7BIT, 0, 0};
|
||||
int startPos = 0;
|
||||
int asciiCharsLen = sAsciiChars.length();
|
||||
|
||||
@@ -93,21 +290,11 @@ public class SmsMessageBodyTest extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCalcLength7bitGsm() throws Exception {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCalcLength7bitGsmExtended() throws Exception {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCalcLengthUnicode() throws Exception {
|
||||
StringBuilder sb = new StringBuilder(160);
|
||||
int[] values = {0, 0, 0, SmsMessage.ENCODING_16BIT};
|
||||
int[] values7bit = {1, 0, 0, SmsMessage.ENCODING_7BIT};
|
||||
int[] values = {0, 0, 0, SmsMessage.ENCODING_16BIT, 0, 0};
|
||||
int[] values7bit = {1, 0, 0, SmsMessage.ENCODING_7BIT, 0, 0};
|
||||
int startPos = 0;
|
||||
int unicodeCharsLen = sUnicodeChars.length();
|
||||
|
||||
@@ -139,6 +326,229 @@ public class SmsMessageBodyTest extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static class LanguagePair {
|
||||
// index is 2 for Portuguese locking shift because there is no Spanish locking shift table
|
||||
private final int langTableIndex;
|
||||
private final int langShiftTableIndex;
|
||||
int length;
|
||||
int missingChars7bit;
|
||||
|
||||
LanguagePair(int langTable, int langShiftTable) {
|
||||
langTableIndex = langTable;
|
||||
langShiftTableIndex = langShiftTable;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
length = 0;
|
||||
missingChars7bit = 0;
|
||||
}
|
||||
|
||||
void addChar(boolean[] charClassTableRow) {
|
||||
if (charClassTableRow[langTableIndex]) {
|
||||
length++;
|
||||
} else if (charClassTableRow[3 + langShiftTableIndex]) {
|
||||
length += 2;
|
||||
} else {
|
||||
length++; // use ' ' for unmapped char in 7 bit only mode
|
||||
missingChars7bit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class CounterHelper {
|
||||
LanguagePair[] mCounters;
|
||||
int[] mStatsCounters;
|
||||
int mUnicodeCounter;
|
||||
|
||||
CounterHelper() {
|
||||
mCounters = new LanguagePair[12];
|
||||
mStatsCounters = new int[12];
|
||||
for (int i = 0; i < 12; i++) {
|
||||
mCounters[i] = new LanguagePair(i/4, i%4);
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
// Note: don't clear stats counters
|
||||
for (int i = 0; i < 12; i++) {
|
||||
mCounters[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void addChar(int charClass) {
|
||||
boolean[] charClassTableRow = sCharClassPresenceInTables[charClass];
|
||||
for (int i = 0; i < 12; i++) {
|
||||
mCounters[i].addChar(charClassTableRow);
|
||||
}
|
||||
}
|
||||
|
||||
void fillData(int enabledLangsIndex, boolean use7bitOnly, int[] values, int length) {
|
||||
int[] languagePairs = sLanguagePairIndexesByEnabledIndex[enabledLangsIndex];
|
||||
int minNumSeptets = Integer.MAX_VALUE;
|
||||
int minNumSeptetsWithHeader = Integer.MAX_VALUE;
|
||||
int minNumMissingChars = Integer.MAX_VALUE;
|
||||
int langIndex = -1;
|
||||
int langShiftIndex = -1;
|
||||
for (int i : languagePairs) {
|
||||
LanguagePair pair = mCounters[i];
|
||||
int udhLength = 0;
|
||||
if (i != 0) {
|
||||
udhLength = UDH_SEPTET_COST_LENGTH;
|
||||
if (i < 4 || i % 4 == 0) {
|
||||
udhLength += UDH_SEPTET_COST_ONE_SHIFT_TABLE;
|
||||
} else {
|
||||
udhLength += UDH_SEPTET_COST_TWO_SHIFT_TABLES;
|
||||
}
|
||||
}
|
||||
int numSeptetsWithHeader;
|
||||
if (pair.length > (MAX_USER_DATA_SEPTETS - udhLength)) {
|
||||
if (udhLength == 0) {
|
||||
udhLength = UDH_SEPTET_COST_LENGTH;
|
||||
}
|
||||
udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE;
|
||||
int septetsPerPart = MAX_USER_DATA_SEPTETS - udhLength;
|
||||
int msgCount = (pair.length + septetsPerPart - 1) / septetsPerPart;
|
||||
numSeptetsWithHeader = udhLength * msgCount + pair.length;
|
||||
} else {
|
||||
numSeptetsWithHeader = udhLength + pair.length;
|
||||
}
|
||||
|
||||
if (use7bitOnly) {
|
||||
if (pair.missingChars7bit < minNumMissingChars || (pair.missingChars7bit ==
|
||||
minNumMissingChars && numSeptetsWithHeader < minNumSeptetsWithHeader)) {
|
||||
minNumSeptets = pair.length;
|
||||
minNumSeptetsWithHeader = numSeptetsWithHeader;
|
||||
minNumMissingChars = pair.missingChars7bit;
|
||||
langIndex = pair.langTableIndex;
|
||||
langShiftIndex = pair.langShiftTableIndex;
|
||||
}
|
||||
} else {
|
||||
if (pair.missingChars7bit == 0 && numSeptetsWithHeader < minNumSeptetsWithHeader) {
|
||||
minNumSeptets = pair.length;
|
||||
minNumSeptetsWithHeader = numSeptetsWithHeader;
|
||||
langIndex = pair.langTableIndex;
|
||||
langShiftIndex = pair.langShiftTableIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (langIndex == -1) {
|
||||
// nothing matches, use values for Unicode
|
||||
int byteCount = length * 2;
|
||||
if (byteCount > MAX_USER_DATA_BYTES) {
|
||||
values[0] = (byteCount + MAX_USER_DATA_BYTES_WITH_HEADER - 1) /
|
||||
MAX_USER_DATA_BYTES_WITH_HEADER;
|
||||
values[2] = ((values[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - byteCount) / 2;
|
||||
} else {
|
||||
values[0] = 1;
|
||||
values[2] = (MAX_USER_DATA_BYTES - byteCount) / 2;
|
||||
}
|
||||
values[1] = length;
|
||||
values[3] = SmsMessage.ENCODING_16BIT;
|
||||
values[4] = 0;
|
||||
values[5] = 0;
|
||||
mUnicodeCounter++;
|
||||
} else {
|
||||
int udhLength = 0;
|
||||
if (langIndex != 0 || langShiftIndex != 0) {
|
||||
udhLength = UDH_SEPTET_COST_LENGTH;
|
||||
if (langIndex == 0 || langShiftIndex == 0) {
|
||||
udhLength += UDH_SEPTET_COST_ONE_SHIFT_TABLE;
|
||||
} else {
|
||||
udhLength += UDH_SEPTET_COST_TWO_SHIFT_TABLES;
|
||||
}
|
||||
}
|
||||
int msgCount;
|
||||
if (minNumSeptets > (MAX_USER_DATA_SEPTETS - udhLength)) {
|
||||
if (udhLength == 0) {
|
||||
udhLength = UDH_SEPTET_COST_LENGTH;
|
||||
}
|
||||
udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE;
|
||||
int septetsPerPart = MAX_USER_DATA_SEPTETS - udhLength;
|
||||
msgCount = (minNumSeptets + septetsPerPart - 1) / septetsPerPart;
|
||||
} else {
|
||||
msgCount = 1;
|
||||
}
|
||||
values[0] = msgCount;
|
||||
values[1] = minNumSeptets;
|
||||
values[2] = (values[0] * (MAX_USER_DATA_SEPTETS - udhLength)) - minNumSeptets;
|
||||
values[3] = SmsMessage.ENCODING_7BIT;
|
||||
values[4] = (langIndex == 2 ? 3 : langIndex); // Portuguese is code 3, index 2
|
||||
values[5] = langShiftIndex;
|
||||
assertEquals("minNumSeptetsWithHeader", minNumSeptetsWithHeader,
|
||||
udhLength * msgCount + minNumSeptets);
|
||||
mStatsCounters[langIndex * 4 + langShiftIndex]++;
|
||||
}
|
||||
}
|
||||
|
||||
void printStats() {
|
||||
Log.d(TAG, "Unicode selection count: " + mUnicodeCounter);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Log.d(TAG, "Language pair index " + i + " count: " + mStatsCounters[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testCalcLengthMixed7bit() throws Exception {
|
||||
StringBuilder sb = new StringBuilder(320);
|
||||
CounterHelper ch = new CounterHelper();
|
||||
Random r = new Random(0x4321); // use the same seed for reproducibility
|
||||
int[] expectedValues = new int[6];
|
||||
int[] origLockingShiftTables = GsmAlphabet.getEnabledLockingShiftTables();
|
||||
int[] origSingleShiftTables = GsmAlphabet.getEnabledSingleShiftTables();
|
||||
int enabledLanguagesTestCases = sEnabledSingleShiftTables.length;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// Repeat for 10 test runs
|
||||
for (int run = 0; run < 10; run++) {
|
||||
sb.setLength(0);
|
||||
ch.clear();
|
||||
int unicodeOnlyCount = 0;
|
||||
|
||||
// Test incrementally from 1 to 320 character random messages
|
||||
for (int i = 1; i < 320; i++) {
|
||||
// 1% chance to add from each special character class, else add an ASCII char
|
||||
int charClass = r.nextInt(100);
|
||||
if (charClass >= sNumCharacterClasses) {
|
||||
charClass = sNumCharacterClasses - 1; // last class is ASCII
|
||||
}
|
||||
int classLength = sCharacterClasses[charClass].length();
|
||||
char nextChar = sCharacterClasses[charClass].charAt(r.nextInt(classLength));
|
||||
sb.append(nextChar);
|
||||
ch.addChar(charClass);
|
||||
|
||||
// if (i % 20 == 0) {
|
||||
// Log.d(TAG, "test string: " + sb);
|
||||
// }
|
||||
|
||||
// Test string against all combinations of enabled languages
|
||||
boolean unicodeOnly = true;
|
||||
for (int j = 0; j < enabledLanguagesTestCases; j++) {
|
||||
GsmAlphabet.setEnabledSingleShiftTables(sEnabledSingleShiftTables[j]);
|
||||
GsmAlphabet.setEnabledLockingShiftTables(sEnabledLockingShiftTables[j]);
|
||||
ch.fillData(j, false, expectedValues, i);
|
||||
if (expectedValues[3] == SmsMessage.ENCODING_7BIT) {
|
||||
unicodeOnly = false;
|
||||
}
|
||||
callGsmLengthMethods(sb, false, expectedValues);
|
||||
// test 7 bit only mode
|
||||
ch.fillData(j, true, expectedValues, i);
|
||||
callGsmLengthMethods(sb, true, expectedValues);
|
||||
}
|
||||
// after 10 iterations with a Unicode-only string, skip to next test string
|
||||
// so we can spend more time testing strings that do encode into 7 bits.
|
||||
if (unicodeOnly && ++unicodeOnlyCount == 10) {
|
||||
// Log.d(TAG, "Unicode only: skipping to next test string");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ch.printStats();
|
||||
Log.d(TAG, "Completed in " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
GsmAlphabet.setEnabledLockingShiftTables(origLockingShiftTables);
|
||||
GsmAlphabet.setEnabledSingleShiftTables(origSingleShiftTables);
|
||||
}
|
||||
|
||||
private void callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly,
|
||||
int[] expectedValues)
|
||||
{
|
||||
@@ -164,6 +574,8 @@ public class SmsMessageBodyTest extends AndroidTestCase {
|
||||
assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
|
||||
assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
|
||||
assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
|
||||
assertEquals("languageTable", expectedValues[4], ted.languageTable);
|
||||
assertEquals("languageShiftTable", expectedValues[5], ted.languageShiftTable);
|
||||
}
|
||||
|
||||
private void callCdmaLengthMethods(CharSequence msgBody, boolean use7bitOnly,
|
||||
|
||||
Reference in New Issue
Block a user