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