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