1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony;
18 
19 import android.telephony.SmsMessage;
20 import android.telephony.TelephonyManager;
21 import android.telephony.Rlog;
22 import android.test.AndroidTestCase;
23 import android.test.suitebuilder.annotation.LargeTest;
24 import android.test.suitebuilder.annotation.MediumTest;
25 import android.test.suitebuilder.annotation.SmallTest;
26 import android.util.Log;
27 
28 import com.android.internal.telephony.SmsConstants;
29 
30 import java.util.Random;
31 
32 /**
33  * Test cases to verify selection of the optimal 7 bit encoding tables
34  * (for all combinations of enabled national language tables) for messages
35  * containing Turkish, Spanish, Portuguese, Greek, and other symbols
36  * present in the GSM default and national language tables defined in
37  * 3GPP TS 23.038. Also verifies correct SMS encoding for CDMA, which only
38  * supports the GSM 7 bit default alphabet, ASCII 8 bit, and UCS-2.
39  * Tests both encoding variations: unsupported characters mapped to space,
40  * and unsupported characters force entire message to UCS-2.
41  */
42 public class SmsMessageBodyTest extends AndroidTestCase {
43     private static final String TAG = "SmsMessageBodyTest";
44 
45     // ASCII chars in the GSM 7 bit default alphabet
46     private static final String sAsciiChars = "@$_ !\"#%&'()*+,-./0123456789" +
47             ":;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\r";
48 
49     // Unicode chars in the GSM 7 bit default alphabet and both locking shift tables
50     private static final String sGsmDefaultChars = "\u00a3\u00a5\u00e9\u00c7\u0394\u00c9" +
51             "\u00dc\u00a7\u00fc\u00e0";
52 
53     // Unicode chars in the GSM 7 bit default table and Turkish locking shift tables
54     private static final String sGsmDefaultAndTurkishTables = "\u00f9\u00f2\u00c5\u00e5\u00df" +
55             "\u00a4\u00c4\u00d6\u00d1\u00e4\u00f6\u00f1";
56 
57     // Unicode chars in the GSM 7 bit default table but not the locking shift tables
58     private static final String sGsmDefaultTableOnly = "\u00e8\u00ec\u00d8\u00f8\u00c6\u00e6" +
59             "\u00a1\u00bf";
60 
61     // ASCII chars in the GSM default extension table
62     private static final String sGsmExtendedAsciiChars = "{}[]\f";
63 
64     // chars in GSM default extension table and Portuguese locking shift table
65     private static final String sGsmExtendedPortugueseLocking = "^\\|~";
66 
67     // Euro currency symbol
68     private static final String sGsmExtendedEuroSymbol = "\u20ac";
69 
70     // CJK ideographs, Hiragana, Katakana, full width letters, Cyrillic, etc.
71     private static final String sUnicodeChars = "\u4e00\u4e01\u4e02\u4e03" +
72             "\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d" +
73             "\u4e0e\u4e0f\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048" +
74             "\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8" +
75             "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18" +
76             "\uff70\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78" +
77             "\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408" +
78             "\u00a2\u00a9\u00ae\u2122";
79 
80     // chars in Turkish single shift and locking shift tables
81     private static final String sTurkishChars = "\u0131\u011e\u011f\u015e\u015f\u0130";
82 
83     // chars in Spanish single shift table and Portuguese single and locking shift tables
84     private static final String sPortugueseAndSpanishChars = "\u00c1\u00e1\u00cd\u00ed"
85             + "\u00d3\u00f3\u00da\u00fa";
86 
87     // chars in all national language tables but not in the standard GSM alphabets
88     private static final String sNationalLanguageTablesOnly = "\u00e7";
89 
90     // chars in Portuguese single shift and locking shift tables
91     private static final String sPortugueseChars = "\u00ea\u00d4\u00f4\u00c0\u00c2\u00e2"
92             + "\u00ca\u00c3\u00d5\u00e3\u00f5";
93 
94     // chars in Portuguese locking shift table only
95     private static final String sPortugueseLockingShiftChars = "\u00aa\u221e\u00ba`";
96 
97     // Greek letters in GSM alphabet missing from Portuguese locking and single shift tables
98     private static final String sGreekLettersNotInPortugueseTables = "\u039b\u039e";
99 
100     // Greek letters in GSM alphabet and Portuguese single shift (but not locking shift) table
101     private static final String sGreekLettersInPortugueseShiftTable =
102             "\u03a6\u0393\u03a9\u03a0\u03a8\u03a3\u0398";
103 
104     // List of classes of characters in SMS tables
105     private static final String[] sCharacterClasses = {
106             sGsmExtendedAsciiChars,
107             sGsmExtendedPortugueseLocking,
108             sGsmDefaultChars,
109             sGsmDefaultAndTurkishTables,
110             sGsmDefaultTableOnly,
111             sGsmExtendedEuroSymbol,
112             sUnicodeChars,
113             sTurkishChars,
114             sPortugueseChars,
115             sPortugueseLockingShiftChars,
116             sPortugueseAndSpanishChars,
117             sGreekLettersNotInPortugueseTables,
118             sGreekLettersInPortugueseShiftTable,
119             sNationalLanguageTablesOnly,
120             sAsciiChars
121     };
122 
123     private static final int sNumCharacterClasses = sCharacterClasses.length;
124 
125     // For each character class, whether it is present in a particular char table.
126     // First three entries are locking shift tables, followed by four single shift tables
127     private static final boolean[][] sCharClassPresenceInTables = {
128             // ASCII chars in all GSM extension tables
129             {false, false, false, true, true, true, true},
130             // ASCII chars in all GSM extension tables and Portuguese locking shift table
131             {false, false, true, true, true, true, true},
132             // non-ASCII chars in GSM default alphabet and all locking tables
133             {true, true, true, false, false, false, false},
134             // non-ASCII chars in GSM default alphabet and Turkish locking shift table
135             {true, true, false, false, false, false, false},
136             // non-ASCII chars in GSM default alphabet table only
137             {true, false, false, false, false, false, false},
138             // Euro symbol is present in several tables
139             {false, true, true, true, true, true, true},
140             // Unicode characters not present in any 7 bit tables
141             {false, false, false, false, false, false, false},
142             // Characters specific to Turkish language
143             {false, true, false, false, true, false, false},
144             // Characters in Portuguese single shift and locking shift tables
145             {false, false, true, false, false, false, true},
146             // Characters in Portuguese locking shift table only
147             {false, false, true, false, false, false, false},
148             // Chars in Spanish single shift and Portuguese single and locking shift tables
149             {false, false, true, false, false, true, true},
150             // Greek letters in GSM default alphabet missing from Portuguese tables
151             {true, true, false, false, false, false, false},
152             // Greek letters in GSM alphabet and Portuguese single shift table
153             {true, true, false, false, false, false, true},
154             // Chars in all national language tables but not the standard GSM tables
155             {false, true, true, false, true, true, true},
156             // ASCII chars in GSM default alphabet
157             {true, true, true, false, false, false, false}
158     };
159 
160     private static final int sTestLengthCount = 12;
161 
162     private static final int[] sSeptetTestLengths =
163             {  0,   1,   2, 80, 159, 160, 161, 240, 305, 306, 307, 320};
164 
165     private static final int[] sUnicodeTestLengths =
166             {  0,   1,   2, 35,  69,  70,  71, 100, 133, 134, 135, 160};
167 
168     private static final int[] sTestMsgCounts =
169             {  1,   1,   1,  1,   1,   1,   2,   2,   2,   2,   3,   3};
170 
171     private static final int[] sSeptetUnitsRemaining =
172             {160, 159, 158, 80,   1,   0, 145,  66,   1,   0, 152, 139};
173 
174     private static final int[] sUnicodeUnitsRemaining =
175             { 70,  69,  68, 35,   1,   0,  63,  34,   1,   0,  66,  41};
176 
177     // Combinations of enabled GSM national language single shift tables
178     private static final int[][] sEnabledSingleShiftTables = {
179             {},         // GSM default alphabet only
180             {1},        // Turkish (single shift only)
181             {1},        // Turkish (single and locking shift)
182             {2},        // Spanish
183             {3},        // Portuguese (single shift only)
184             {3},        // Portuguese (single and locking shift)
185             {1, 2},     // Turkish + Spanish (single shift only)
186             {1, 2},     // Turkish + Spanish (single and locking shift)
187             {1, 3},     // Turkish + Portuguese (single shift only)
188             {1, 3},     // Turkish + Portuguese (single and locking shift)
189             {2, 3},     // Spanish + Portuguese (single shift only)
190             {2, 3},     // Spanish + Portuguese (single and locking shift)
191             {1, 2, 3},  // Turkish, Spanish, Portuguese (single shift only)
192             {1, 2, 3},  // Turkish, Spanish, Portuguese (single and locking shift)
193             {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} // all language tables
194     };
195 
196     // Combinations of enabled GSM national language locking shift tables
197     private static final int[][] sEnabledLockingShiftTables = {
198             {},         // GSM default alphabet only
199             {},         // Turkish (single shift only)
200             {1},        // Turkish (single and locking shift)
201             {},         // Spanish (no locking shift table)
202             {},         // Portuguese (single shift only)
203             {3},        // Portuguese (single and locking shift)
204             {},         // Turkish + Spanish (single shift only)
205             {1},        // Turkish + Spanish (single and locking shift)
206             {},         // Turkish + Portuguese (single shift only)
207             {1, 3},     // Turkish + Portuguese (single and locking shift)
208             {},         // Spanish + Portuguese (single shift only)
209             {3},        // Spanish + Portuguese (single and locking shift)
210             {},         // Turkish, Spanish, Portuguese (single shift only)
211             {1, 3},     // Turkish, Spanish, Portuguese (single and locking shift)
212             {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} // all language tables
213     };
214 
215     // LanguagePair counter indexes to check for each entry above
216     private static final int[][] sLanguagePairIndexesByEnabledIndex = {
217             {0},                            // default tables only
218             {0, 1},                         // Turkish (single shift only)
219             {0, 1, 4, 5},                   // Turkish (single and locking shift)
220             {0, 2},                         // Spanish
221             {0, 3},                         // Portuguese (single shift only)
222             {0, 3, 8, 11},                  // Portuguese (single and locking shift)
223             {0, 1, 2},                      // Turkish + Spanish (single shift only)
224             {0, 1, 2, 4, 5, 6},             // Turkish + Spanish (single and locking shift)
225             {0, 1, 3},                      // Turkish + Portuguese (single shift only)
226             {0, 1, 3, 4, 5, 7, 8, 9, 11},   // Turkish + Portuguese (single and locking shift)
227             {0, 2, 3},                      // Spanish + Portuguese (single shift only)
228             {0, 2, 3, 8, 10, 11},           // Spanish + Portuguese (single and locking shift)
229             {0, 1, 2, 3},                   // all languages (single shift only)
230             {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, // all languages (single and locking shift)
231             {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}  // all languages (no Indic chars in test)
232     };
233 
234     /**
235      * User data header requires one octet for length. Count as one septet, because
236      * all combinations of header elements below will have at least one free bit
237      * when padding to the nearest septet boundary.
238      */
239     private static final int UDH_SEPTET_COST_LENGTH = 1;
240 
241     /**
242      * Using a non-default language locking shift table OR single shift table
243      * requires a user data header of 3 octets, or 4 septets, plus UDH length.
244      */
245     private static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4;
246 
247     /**
248      * Using a non-default language locking shift table AND single shift table
249      * requires a user data header of 6 octets, or 7 septets, plus UDH length.
250      */
251     private static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7;
252 
253     /**
254      * Multi-part messages require a user data header of 5 octets, or 6 septets,
255      * plus UDH length.
256      */
257     private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
258 
259     @SmallTest
testCalcLengthAscii()260     public void testCalcLengthAscii() throws Exception {
261         StringBuilder sb = new StringBuilder(320);
262         int[] values = {0, 0, 0, SmsConstants.ENCODING_7BIT, 0, 0};
263         int startPos = 0;
264         int asciiCharsLen = sAsciiChars.length();
265 
266         for (int i = 0; i < sTestLengthCount; i++) {
267             int len = sSeptetTestLengths[i];
268             assertTrue(sb.length() <= len);
269 
270             while (sb.length() < len) {
271                 int addCount = len - sb.length();
272                 int endPos = (asciiCharsLen - startPos > addCount) ?
273                         (startPos + addCount) : asciiCharsLen;
274                 sb.append(sAsciiChars, startPos, endPos);
275                 startPos = (endPos == asciiCharsLen) ? 0 : endPos;
276             }
277             assertEquals(len, sb.length());
278 
279             String testStr = sb.toString();
280             values[0] = sTestMsgCounts[i];
281             values[1] = len;
282             values[2] = sSeptetUnitsRemaining[i];
283 
284             callGsmLengthMethods(testStr, false, values);
285             callGsmLengthMethods(testStr, true, values);
286             callCdmaLengthMethods(testStr, false, values);
287             callCdmaLengthMethods(testStr, true, values);
288         }
289     }
290 
291     @SmallTest
testCalcLengthUnicode()292     public void testCalcLengthUnicode() throws Exception {
293         StringBuilder sb = new StringBuilder(160);
294         int[] values = {0, 0, 0, SmsConstants.ENCODING_16BIT, 0, 0};
295         int[] values7bit = {1, 0, 0, SmsConstants.ENCODING_7BIT, 0, 0};
296         int startPos = 0;
297         int unicodeCharsLen = sUnicodeChars.length();
298 
299         // start with length 1: empty string uses ENCODING_7BIT
300         for (int i = 1; i < sTestLengthCount; i++) {
301             int len = sUnicodeTestLengths[i];
302             assertTrue(sb.length() <= len);
303 
304             while (sb.length() < len) {
305                 int addCount = len - sb.length();
306                 int endPos = (unicodeCharsLen - startPos > addCount) ?
307                         (startPos + addCount) : unicodeCharsLen;
308                 sb.append(sUnicodeChars, startPos, endPos);
309                 startPos = (endPos == unicodeCharsLen) ? 0 : endPos;
310             }
311             assertEquals(len, sb.length());
312 
313             String testStr = sb.toString();
314             values[0] = sTestMsgCounts[i];
315             values[1] = len;
316             values[2] = sUnicodeUnitsRemaining[i];
317             values7bit[1] = len;
318             values7bit[2] = SmsConstants.MAX_USER_DATA_SEPTETS - len;
319 
320             callGsmLengthMethods(testStr, false, values);
321             callCdmaLengthMethods(testStr, false, values);
322             callGsmLengthMethods(testStr, true, values7bit);
323             callCdmaLengthMethods(testStr, true, values7bit);
324         }
325     }
326 
327     private static class LanguagePair {
328         // index is 2 for Portuguese locking shift because there is no Spanish locking shift table
329         private final int langTableIndex;
330         private final int langShiftTableIndex;
331         int length;
332         int missingChars7bit;
333 
LanguagePair(int langTable, int langShiftTable)334         LanguagePair(int langTable, int langShiftTable) {
335             langTableIndex = langTable;
336             langShiftTableIndex = langShiftTable;
337         }
338 
clear()339         void clear() {
340             length = 0;
341             missingChars7bit = 0;
342         }
343 
addChar(boolean[] charClassTableRow)344         void addChar(boolean[] charClassTableRow) {
345             if (charClassTableRow[langTableIndex]) {
346                 length++;
347             } else if (charClassTableRow[3 + langShiftTableIndex]) {
348                 length += 2;
349             } else {
350                 length++;    // use ' ' for unmapped char in 7 bit only mode
351                 missingChars7bit++;
352             }
353         }
354     }
355 
356     private static class CounterHelper {
357         LanguagePair[] mCounters;
358         int[] mStatsCounters;
359         int mUnicodeCounter;
360 
CounterHelper()361         CounterHelper() {
362             mCounters = new LanguagePair[12];
363             mStatsCounters = new int[12];
364             for (int i = 0; i < 12; i++) {
365                 mCounters[i] = new LanguagePair(i/4, i%4);
366             }
367         }
368 
clear()369         void clear() {
370             // Note: don't clear stats counters
371             for (int i = 0; i < 12; i++) {
372                 mCounters[i].clear();
373             }
374         }
375 
addChar(int charClass)376         void addChar(int charClass) {
377             boolean[] charClassTableRow = sCharClassPresenceInTables[charClass];
378             for (int i = 0; i < 12; i++) {
379                 mCounters[i].addChar(charClassTableRow);
380             }
381         }
382 
fillData(int enabledLangsIndex, boolean use7bitOnly, int[] values, int length)383         void fillData(int enabledLangsIndex, boolean use7bitOnly, int[] values, int length) {
384             int[] languagePairs = sLanguagePairIndexesByEnabledIndex[enabledLangsIndex];
385             int minNumSeptets = Integer.MAX_VALUE;
386             int minNumSeptetsWithHeader = Integer.MAX_VALUE;
387             int minNumMissingChars = Integer.MAX_VALUE;
388             int langIndex = -1;
389             int langShiftIndex = -1;
390             for (int i : languagePairs) {
391                 LanguagePair pair = mCounters[i];
392                 int udhLength = 0;
393                 if (i != 0) {
394                     udhLength = UDH_SEPTET_COST_LENGTH;
395                     if (i < 4 || i % 4 == 0) {
396                         udhLength += UDH_SEPTET_COST_ONE_SHIFT_TABLE;
397                     } else {
398                         udhLength += UDH_SEPTET_COST_TWO_SHIFT_TABLES;
399                     }
400                 }
401                 int numSeptetsWithHeader;
402                 if (pair.length > (SmsConstants.MAX_USER_DATA_SEPTETS - udhLength)) {
403                     if (udhLength == 0) {
404                         udhLength = UDH_SEPTET_COST_LENGTH;
405                     }
406                     udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE;
407                     int septetsPerPart = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
408                     int msgCount = (pair.length + septetsPerPart - 1) / septetsPerPart;
409                     numSeptetsWithHeader = udhLength * msgCount + pair.length;
410                 } else {
411                     numSeptetsWithHeader = udhLength + pair.length;
412                 }
413 
414                 if (use7bitOnly) {
415                     if (pair.missingChars7bit < minNumMissingChars || (pair.missingChars7bit ==
416                             minNumMissingChars && numSeptetsWithHeader < minNumSeptetsWithHeader)) {
417                         minNumSeptets = pair.length;
418                         minNumSeptetsWithHeader = numSeptetsWithHeader;
419                         minNumMissingChars = pair.missingChars7bit;
420                         langIndex = pair.langTableIndex;
421                         langShiftIndex = pair.langShiftTableIndex;
422                     }
423                 } else {
424                     if (pair.missingChars7bit == 0 && numSeptetsWithHeader < minNumSeptetsWithHeader) {
425                         minNumSeptets = pair.length;
426                         minNumSeptetsWithHeader = numSeptetsWithHeader;
427                         langIndex = pair.langTableIndex;
428                         langShiftIndex = pair.langShiftTableIndex;
429                     }
430                 }
431             }
432             if (langIndex == -1) {
433                 // nothing matches, use values for Unicode
434                 int byteCount = length * 2;
435                 if (byteCount > SmsConstants.MAX_USER_DATA_BYTES) {
436                     values[0] = (byteCount + SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER - 1) /
437                             SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
438                     values[2] = ((values[0] * SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER) -
439                             byteCount) / 2;
440                 } else {
441                     values[0] = 1;
442                     values[2] = (SmsConstants.MAX_USER_DATA_BYTES - byteCount) / 2;
443                 }
444                 values[1] = length;
445                 values[3] = SmsConstants.ENCODING_16BIT;
446                 values[4] = 0;
447                 values[5] = 0;
448                 mUnicodeCounter++;
449             } else {
450                 int udhLength = 0;
451                 if (langIndex != 0 || langShiftIndex != 0) {
452                     udhLength = UDH_SEPTET_COST_LENGTH;
453                     if (langIndex == 0 || langShiftIndex == 0) {
454                         udhLength += UDH_SEPTET_COST_ONE_SHIFT_TABLE;
455                     } else {
456                         udhLength += UDH_SEPTET_COST_TWO_SHIFT_TABLES;
457                     }
458                 }
459                 int msgCount;
460                 if (minNumSeptets > (SmsConstants.MAX_USER_DATA_SEPTETS - udhLength)) {
461                     if (udhLength == 0) {
462                         udhLength = UDH_SEPTET_COST_LENGTH;
463                     }
464                     udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE;
465                     int septetsPerPart = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
466                     msgCount = (minNumSeptets + septetsPerPart - 1) / septetsPerPart;
467                 } else {
468                     msgCount = 1;
469                 }
470                 values[0] = msgCount;
471                 values[1] = minNumSeptets;
472                 values[2] = (values[0] * (SmsConstants.MAX_USER_DATA_SEPTETS - udhLength)) -
473                         minNumSeptets;
474                 values[3] = SmsConstants.ENCODING_7BIT;
475                 values[4] = (langIndex == 2 ? 3 : langIndex); // Portuguese is code 3, index 2
476                 values[5] = langShiftIndex;
477                 assertEquals("minNumSeptetsWithHeader", minNumSeptetsWithHeader,
478                         udhLength * msgCount + minNumSeptets);
479                 mStatsCounters[langIndex * 4 + langShiftIndex]++;
480             }
481         }
482 
printStats()483         void printStats() {
484             Rlog.d(TAG, "Unicode selection count: " + mUnicodeCounter);
485             for (int i = 0; i < 12; i++) {
486                 Rlog.d(TAG, "Language pair index " + i + " count: " + mStatsCounters[i]);
487             }
488         }
489     }
490 
491     //@LargeTest
492     /*public void testCalcLengthMixed7bit() throws Exception {
493         StringBuilder sb = new StringBuilder(320);
494         CounterHelper ch = new CounterHelper();
495         Random r = new Random(0x4321);  // use the same seed for reproducibility
496         int[] expectedValues = new int[6];
497         int[] origLockingShiftTables = GsmAlphabet.getEnabledLockingShiftTables();
498         int[] origSingleShiftTables = GsmAlphabet.getEnabledSingleShiftTables();
499         int enabledLanguagesTestCases = sEnabledSingleShiftTables.length;
500         long startTime = System.currentTimeMillis();
501 
502         // Repeat for 10 test runs
503         for (int run = 0; run < 10; run++) {
504             sb.setLength(0);
505             ch.clear();
506             int unicodeOnlyCount = 0;
507 
508             // Test incrementally from 1 to 320 character random messages
509             for (int i = 1; i < 320; i++) {
510                 // 1% chance to add from each special character class, else add an ASCII char
511                 int charClass = r.nextInt(100);
512                 if (charClass >= sNumCharacterClasses) {
513                     charClass = sNumCharacterClasses - 1;   // last class is ASCII
514                 }
515                 int classLength = sCharacterClasses[charClass].length();
516                 char nextChar = sCharacterClasses[charClass].charAt(r.nextInt(classLength));
517                 sb.append(nextChar);
518                 ch.addChar(charClass);
519 
520 //                if (i % 20 == 0) {
521 //                    Rlog.d(TAG, "test string: " + sb);
522 //                }
523 
524                 // Test string against all combinations of enabled languages
525                 boolean unicodeOnly = true;
526                 for (int j = 0; j < enabledLanguagesTestCases; j++) {
527                     Log.d(TAG, "testCalcLengthMixed7bit: " + run + " " + i + " " + j);
528                     GsmAlphabet.setEnabledSingleShiftTables(sEnabledSingleShiftTables[j]);
529                     GsmAlphabet.setEnabledLockingShiftTables(sEnabledLockingShiftTables[j]);
530                     ch.fillData(j, false, expectedValues, i);
531                     if (expectedValues[3] == SmsConstants.ENCODING_7BIT) {
532                         unicodeOnly = false;
533                     }
534                     callGsmLengthMethods(sb, false, expectedValues);
535                     // test 7 bit only mode
536                     ch.fillData(j, true, expectedValues, i);
537                     callGsmLengthMethods(sb, true, expectedValues);
538                     Log.d(TAG, "testCalcLengthMixed7bit: " + run + " " + i + " " + j);
539                 }
540                 // after 10 iterations with a Unicode-only string, skip to next test string
541                 // so we can spend more time testing strings that do encode into 7 bits.
542                 if (unicodeOnly && ++unicodeOnlyCount == 10) {
543 //                    Rlog.d(TAG, "Unicode only: skipping to next test string");
544                     break;
545                 }
546             }
547         }
548         ch.printStats();
549         Rlog.d(TAG, "Completed in " + (System.currentTimeMillis() - startTime) + " ms");
550         GsmAlphabet.setEnabledLockingShiftTables(origLockingShiftTables);
551         GsmAlphabet.setEnabledSingleShiftTables(origSingleShiftTables);
552     }*/
553 
callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly, int[] expectedValues)554     private void callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly,
555             int[] expectedValues)
556     {
557         // deprecated GSM-specific method
558         int[] values = android.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
559         assertEquals("msgCount",           expectedValues[0], values[0]);
560         assertEquals("codeUnitCount",      expectedValues[1], values[1]);
561         assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
562         assertEquals("codeUnitSize",       expectedValues[3], values[3]);
563 
564         int activePhone = TelephonyManager.getDefault().getPhoneType();
565         if (TelephonyManager.PHONE_TYPE_GSM == activePhone) {
566             values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
567             assertEquals("msgCount",           expectedValues[0], values[0]);
568             assertEquals("codeUnitCount",      expectedValues[1], values[1]);
569             assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
570             assertEquals("codeUnitSize",       expectedValues[3], values[3]);
571         }
572 
573         GsmAlphabet.TextEncodingDetails ted =
574                 com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
575         assertEquals("msgCount",           expectedValues[0], ted.msgCount);
576         assertEquals("codeUnitCount",      expectedValues[1], ted.codeUnitCount);
577         assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
578         assertEquals("codeUnitSize",       expectedValues[3], ted.codeUnitSize);
579         assertEquals("languageTable",      expectedValues[4], ted.languageTable);
580         assertEquals("languageShiftTable", expectedValues[5], ted.languageShiftTable);
581     }
582 
callCdmaLengthMethods(CharSequence msgBody, boolean use7bitOnly, int[] expectedValues)583     private void callCdmaLengthMethods(CharSequence msgBody, boolean use7bitOnly,
584             int[] expectedValues)
585     {
586         int activePhone = TelephonyManager.getDefault().getPhoneType();
587         if (TelephonyManager.PHONE_TYPE_CDMA == activePhone) {
588             int[] values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
589             assertEquals("msgCount",           expectedValues[0], values[0]);
590             assertEquals("codeUnitCount",      expectedValues[1], values[1]);
591             assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
592             assertEquals("codeUnitSize",       expectedValues[3], values[3]);
593         }
594 
595         GsmAlphabet.TextEncodingDetails ted =
596                 com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly, true);
597         assertEquals("msgCount",           expectedValues[0], ted.msgCount);
598         assertEquals("codeUnitCount",      expectedValues[1], ted.codeUnitCount);
599         assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
600         assertEquals("codeUnitSize",       expectedValues[3], ted.codeUnitSize);
601 
602         ted = com.android.internal.telephony.cdma.sms.BearerData.calcTextEncodingDetails(msgBody, use7bitOnly, true);
603         assertEquals("msgCount",           expectedValues[0], ted.msgCount);
604         assertEquals("codeUnitCount",      expectedValues[1], ted.codeUnitCount);
605         assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
606         assertEquals("codeUnitSize",       expectedValues[3], ted.codeUnitSize);
607     }
608 }
609