/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import android.telephony.SmsMessage; import android.telephony.TelephonyManager; import android.telephony.Rlog; 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 com.android.internal.telephony.SmsConstants; import java.util.Random; /** * 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"; // 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" + "\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8" + "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18" + "\uff70\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78" + "\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 = { 0, 1, 2, 80, 159, 160, 161, 240, 305, 306, 307, 320}; private static final int[] sUnicodeTestLengths = { 0, 1, 2, 35, 69, 70, 71, 100, 133, 134, 135, 160}; private static final int[] sTestMsgCounts = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3}; private static final int[] sSeptetUnitsRemaining = {160, 159, 158, 80, 1, 0, 145, 66, 1, 0, 152, 139}; 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, SmsConstants.ENCODING_7BIT, 0, 0}; int startPos = 0; int asciiCharsLen = sAsciiChars.length(); for (int i = 0; i < sTestLengthCount; i++) { int len = sSeptetTestLengths[i]; assertTrue(sb.length() <= len); while (sb.length() < len) { int addCount = len - sb.length(); int endPos = (asciiCharsLen - startPos > addCount) ? (startPos + addCount) : asciiCharsLen; sb.append(sAsciiChars, startPos, endPos); startPos = (endPos == asciiCharsLen) ? 0 : endPos; } assertEquals(len, sb.length()); String testStr = sb.toString(); values[0] = sTestMsgCounts[i]; values[1] = len; values[2] = sSeptetUnitsRemaining[i]; callGsmLengthMethods(testStr, false, values); callGsmLengthMethods(testStr, true, values); callCdmaLengthMethods(testStr, false, values); callCdmaLengthMethods(testStr, true, values); } } @SmallTest public void testCalcLengthUnicode() throws Exception { StringBuilder sb = new StringBuilder(160); int[] values = {0, 0, 0, SmsConstants.ENCODING_16BIT, 0, 0}; int[] values7bit = {1, 0, 0, SmsConstants.ENCODING_7BIT, 0, 0}; int startPos = 0; int unicodeCharsLen = sUnicodeChars.length(); // start with length 1: empty string uses ENCODING_7BIT for (int i = 1; i < sTestLengthCount; i++) { int len = sUnicodeTestLengths[i]; assertTrue(sb.length() <= len); while (sb.length() < len) { int addCount = len - sb.length(); int endPos = (unicodeCharsLen - startPos > addCount) ? (startPos + addCount) : unicodeCharsLen; sb.append(sUnicodeChars, startPos, endPos); startPos = (endPos == unicodeCharsLen) ? 0 : endPos; } assertEquals(len, sb.length()); String testStr = sb.toString(); values[0] = sTestMsgCounts[i]; values[1] = len; values[2] = sUnicodeUnitsRemaining[i]; values7bit[1] = len; values7bit[2] = SmsConstants.MAX_USER_DATA_SEPTETS - len; callGsmLengthMethods(testStr, false, values); callCdmaLengthMethods(testStr, false, values); callGsmLengthMethods(testStr, true, values7bit); callCdmaLengthMethods(testStr, true, values7bit); } } 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 > (SmsConstants.MAX_USER_DATA_SEPTETS - udhLength)) { if (udhLength == 0) { udhLength = UDH_SEPTET_COST_LENGTH; } udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE; int septetsPerPart = SmsConstants.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 > SmsConstants.MAX_USER_DATA_BYTES) { values[0] = (byteCount + SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER - 1) / SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER; values[2] = ((values[0] * SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER) - byteCount) / 2; } else { values[0] = 1; values[2] = (SmsConstants.MAX_USER_DATA_BYTES - byteCount) / 2; } values[1] = length; values[3] = SmsConstants.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 > (SmsConstants.MAX_USER_DATA_SEPTETS - udhLength)) { if (udhLength == 0) { udhLength = UDH_SEPTET_COST_LENGTH; } udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE; int septetsPerPart = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength; msgCount = (minNumSeptets + septetsPerPart - 1) / septetsPerPart; } else { msgCount = 1; } values[0] = msgCount; values[1] = minNumSeptets; values[2] = (values[0] * (SmsConstants.MAX_USER_DATA_SEPTETS - udhLength)) - minNumSeptets; values[3] = SmsConstants.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() { Rlog.d(TAG, "Unicode selection count: " + mUnicodeCounter); for (int i = 0; i < 12; i++) { Rlog.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) { // Rlog.d(TAG, "test string: " + sb); // } // Test string against all combinations of enabled languages boolean unicodeOnly = true; for (int j = 0; j < enabledLanguagesTestCases; j++) { Log.d(TAG, "testCalcLengthMixed7bit: " + run + " " + i + " " + j); GsmAlphabet.setEnabledSingleShiftTables(sEnabledSingleShiftTables[j]); GsmAlphabet.setEnabledLockingShiftTables(sEnabledLockingShiftTables[j]); ch.fillData(j, false, expectedValues, i); if (expectedValues[3] == SmsConstants.ENCODING_7BIT) { unicodeOnly = false; } callGsmLengthMethods(sb, false, expectedValues); // test 7 bit only mode ch.fillData(j, true, expectedValues, i); callGsmLengthMethods(sb, true, expectedValues); Log.d(TAG, "testCalcLengthMixed7bit: " + run + " " + i + " " + j); } // 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) { // Rlog.d(TAG, "Unicode only: skipping to next test string"); break; } } } ch.printStats(); Rlog.d(TAG, "Completed in " + (System.currentTimeMillis() - startTime) + " ms"); GsmAlphabet.setEnabledLockingShiftTables(origLockingShiftTables); GsmAlphabet.setEnabledSingleShiftTables(origSingleShiftTables); }*/ private void callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly, int[] expectedValues) { // deprecated GSM-specific method int[] values = android.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly); assertEquals("msgCount", expectedValues[0], values[0]); assertEquals("codeUnitCount", expectedValues[1], values[1]); assertEquals("codeUnitsRemaining", expectedValues[2], values[2]); assertEquals("codeUnitSize", expectedValues[3], values[3]); int activePhone = TelephonyManager.getDefault().getPhoneType(); if (TelephonyManager.PHONE_TYPE_GSM == activePhone) { values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly); assertEquals("msgCount", expectedValues[0], values[0]); assertEquals("codeUnitCount", expectedValues[1], values[1]); assertEquals("codeUnitsRemaining", expectedValues[2], values[2]); assertEquals("codeUnitSize", expectedValues[3], values[3]); } GsmAlphabet.TextEncodingDetails ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly); assertEquals("msgCount", expectedValues[0], ted.msgCount); 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, int[] expectedValues) { int activePhone = TelephonyManager.getDefault().getPhoneType(); if (TelephonyManager.PHONE_TYPE_CDMA == activePhone) { int[] values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly); assertEquals("msgCount", expectedValues[0], values[0]); assertEquals("codeUnitCount", expectedValues[1], values[1]); assertEquals("codeUnitsRemaining", expectedValues[2], values[2]); assertEquals("codeUnitSize", expectedValues[3], values[3]); } GsmAlphabet.TextEncodingDetails ted = com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly, true); assertEquals("msgCount", expectedValues[0], ted.msgCount); assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount); assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining); assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize); ted = com.android.internal.telephony.cdma.sms.BearerData.calcTextEncodingDetails(msgBody, use7bitOnly, true); assertEquals("msgCount", expectedValues[0], ted.msgCount); assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount); assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining); assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize); } }