Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2006 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.TelephonyManager;
     20 import android.test.AndroidTestCase;
     21 import android.test.suitebuilder.annotation.SmallTest;
     22 
     23 import com.android.internal.telephony.gsm.SmsMessage;
     24 import com.android.internal.util.HexDump;
     25 
     26 import java.util.ArrayList;
     27 
     28 public class GsmSmsTest extends AndroidTestCase {
     29 
     30     @SmallTest
     31     public void testAddressing() throws Exception {
     32         String pdu = "07914151551512f2040B916105551511f100006060605130308A04D4F29C0E";
     33         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
     34         assertEquals("+14155551212", sms.getServiceCenterAddress());
     35         assertEquals("+16505551111", sms.getOriginatingAddress());
     36         assertEquals("Test", sms.getMessageBody());
     37 
     38         pdu = "07914151551512f2040B916105551511f100036060924180008A0DA"
     39                 + "8695DAC2E8FE9296A794E07";
     40         sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
     41         assertEquals("+14155551212", sms.getServiceCenterAddress());
     42         assertEquals("+16505551111", sms.getOriginatingAddress());
     43         assertEquals("(Subject)Test", sms.getMessageBody());
     44     }
     45 
     46     @SmallTest
     47     public void testUdh() throws Exception {
     48         String pdu = "07914140279510F6440A8111110301003BF56080207130138A8C0B05040B8423F"
     49                 + "000032A02010106276170706C69636174696F6E2F766E642E7761702E6D6D732D"
     50                 + "6D65737361676500AF848D0185B4848C8298524E453955304A6D7135514141426"
     51                 + "66C414141414D7741414236514141414141008D908918802B3135313232393737"
     52                 + "3638332F545950453D504C4D4E008A808E022B918805810306977F83687474703"
     53                 + "A2F2F36";
     54         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
     55         SmsHeader header = sms.getUserDataHeader();
     56         assertNotNull(header);
     57         assertNotNull(header.concatRef);
     58         assertEquals(header.concatRef.refNumber, 42);
     59         assertEquals(header.concatRef.msgCount, 2);
     60         assertEquals(header.concatRef.seqNumber, 1);
     61         assertEquals(header.concatRef.isEightBits, true);
     62         assertNotNull(header.portAddrs);
     63         assertEquals(header.portAddrs.destPort, 2948);
     64         assertEquals(header.portAddrs.origPort, 9200);
     65         assertEquals(header.portAddrs.areEightBits, false);
     66 
     67         pdu = "07914140279510F6440A8111110301003BF56080207130238A3B0B05040B8423F"
     68                 + "000032A0202362E3130322E3137312E3135302F524E453955304A6D7135514141"
     69                 + "42666C414141414D774141423651414141414100";
     70         sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
     71         header = sms.getUserDataHeader();
     72         assertNotNull(header);
     73         assertNotNull(header.concatRef);
     74         assertEquals(header.concatRef.refNumber, 42);
     75         assertEquals(header.concatRef.msgCount, 2);
     76         assertEquals(header.concatRef.seqNumber, 2);
     77         assertEquals(header.concatRef.isEightBits, true);
     78         assertNotNull(header.portAddrs);
     79         assertEquals(header.portAddrs.destPort, 2948);
     80         assertEquals(header.portAddrs.origPort, 9200);
     81         assertEquals(header.portAddrs.areEightBits, false);
     82     }
     83 
     84     @SmallTest
     85     public void testUcs2() throws Exception {
     86         String pdu = "07912160130300F4040B914151245584F600087010807121352B1021220"
     87                 + "0A900AE00680065006C006C006F";
     88         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
     89         assertEquals("\u2122\u00a9\u00aehello", sms.getMessageBody());
     90     }
     91 
     92     @SmallTest
     93     public void testMultipart() throws Exception {
     94         /*
     95          * Multi-part text SMS with septet data.
     96          */
     97         String pdu = "07916163838408F6440B816105224431F700007060217175830AA0050003"
     98                 + "00020162B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
     99                 + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
    100                 + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
    101                 + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
    102                 + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562";
    103         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    104         assertEquals(sms.getMessageBody(),
    105                 "1111111111111111111111111111111111111111"
    106                 + "1111111111111111111111111111111111111111"
    107                 + "1111111111111111111111111111111111111111"
    108                 + "111111111111111111111111111111111");
    109 
    110         pdu = "07916163838408F6440B816105224431F700007060217185000A23050003"
    111                 + "00020262B1582C168BC96432994C2693C96432994C2693C96432990C";
    112         sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    113         assertEquals("1111111222222222222222222222", sms.getMessageBody());
    114     }
    115 
    116     @SmallTest
    117     public void testCPHSVoiceMail() throws Exception {
    118         // "set MWI flag"
    119 
    120         String pdu = "07912160130310F20404D0110041006060627171118A0120";
    121 
    122         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    123 
    124         assertTrue(sms.isReplace());
    125         assertEquals("_@", sms.getOriginatingAddress());
    126         assertEquals(" ", sms.getMessageBody());
    127         assertTrue(sms.isMWISetMessage());
    128 
    129         // "clear mwi flag"
    130 
    131         pdu = "07912160130310F20404D0100041006021924193352B0120";
    132 
    133         sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    134 
    135         assertTrue(sms.isMWIClearMessage());
    136 
    137         // "clear MWI flag"
    138 
    139         pdu = "07912160130310F20404D0100041006060627161058A0120";
    140 
    141         sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    142 
    143         assertTrue(sms.isReplace());
    144         assertEquals("\u0394@", sms.getOriginatingAddress());
    145         assertEquals(" ", sms.getMessageBody());
    146         assertTrue(sms.isMWIClearMessage());
    147     }
    148 
    149     @SmallTest
    150     public void testCingularVoiceMail() throws Exception {
    151         // "set MWI flag"
    152 
    153         String pdu = "07912180958750F84401800500C87020026195702B06040102000200";
    154         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    155 
    156         assertTrue(sms.isMWISetMessage());
    157         assertTrue(sms.isMwiDontStore());
    158 
    159         // "clear mwi flag"
    160 
    161         pdu = "07912180958750F84401800500C07020027160112B06040102000000";
    162         sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    163 
    164         assertTrue(sms.isMWIClearMessage());
    165         assertTrue(sms.isMwiDontStore());
    166     }
    167 
    168     @SmallTest
    169     public void testEmailGateway() throws Exception {
    170         String pdu = "07914151551512f204038105f300007011103164638a28e6f71b50c687db" +
    171                 "7076d9357eb7412f7a794e07cdeb6275794c07bde8e5391d247e93f3";
    172 
    173         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    174 
    175         assertEquals("+14155551212", sms.getServiceCenterAddress());
    176         assertTrue(sms.isEmail());
    177         assertEquals("foo (at) example.com", sms.getEmailFrom());
    178         assertEquals("foo (at) example.com", sms.getDisplayOriginatingAddress());
    179         // As of https://android-git.corp.google.com/g/#change,9324
    180         // getPseudoSubject will always be empty, and any subject is not extracted.
    181         assertEquals("", sms.getPseudoSubject());
    182         assertEquals("test subject /test body", sms.getDisplayMessageBody());
    183         assertEquals("test subject /test body", sms.getEmailBody());
    184 
    185         // email gateway sms test, including gsm extended character set.
    186         pdu = "07914151551512f204038105f400007011103105458a29e6f71b50c687db" +
    187                 "7076d9357eb741af0d0a442fcfe9c23739bfe16d289bdee6b5f1813629";
    188 
    189         sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    190 
    191         assertEquals("+14155551212", sms.getServiceCenterAddress());
    192         assertTrue(sms.isEmail());
    193         assertEquals("foo (at) example.com", sms.getDisplayOriginatingAddress());
    194         assertEquals("foo (at) example.com", sms.getEmailFrom());
    195         assertEquals("{ testBody[^~\\] }", sms.getDisplayMessageBody());
    196         assertEquals("{ testBody[^~\\] }", sms.getEmailBody());
    197     }
    198 
    199     @SmallTest
    200     public void testExtendedCharacterTable() throws Exception {
    201         String pdu = "07914151551512f2040B916105551511f100006080615131728A44D4F29C0E2" +
    202                 "AE3E96537B94C068DD16179784C2FCB41F4B0985D06B958ADD00FB0E94536AF9749" +
    203                 "74DA6D281BA00E95E26D509B946FC3DBF87A25D56A04";
    204 
    205         SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
    206 
    207         assertEquals("+14155551212", sms.getServiceCenterAddress());
    208         assertEquals("+16505551111", sms.getOriginatingAddress());
    209         assertEquals("Test extended character table .,-!?@~_\\/&\"';^|:()<{}>[]=%*+#",
    210                 sms.getMessageBody());
    211     }
    212 
    213     // GSM 7 bit tables in String form, Escape (0x1B) replaced with '@'
    214     private static final String[] sBasicTables = {
    215         // GSM 7 bit default alphabet
    216         "@\u00a3$\u00a5\u00e8\u00e9\u00f9\u00ec\u00f2\u00c7\n\u00d8\u00f8\r\u00c5\u00e5\u0394_"
    217             + "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e@\u00c6\u00e6\u00df\u00c9"
    218             + " !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u00a1ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c4\u00d6"
    219             + "\u00d1\u00dc\u00a7\u00bfabcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
    220 
    221         // Turkish locking shift table
    222         "@\u00a3$\u00a5\u20ac\u00e9\u00f9\u0131\u00f2\u00c7\n\u011e\u011f\r\u00c5\u00e5\u0394_"
    223             + "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e@\u015e\u015f\u00df\u00c9"
    224             + " !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u0130ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c4\u00d6"
    225             + "\u00d1\u00dc\u00a7\u00e7abcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
    226 
    227         // no locking shift table defined for Spanish
    228         "",
    229 
    230         // Portuguese locking shift table
    231         "@\u00a3$\u00a5\u00ea\u00e9\u00fa\u00ed\u00f3\u00e7\n\u00d4\u00f4\r\u00c1\u00e1\u0394_"
    232             + "\u00aa\u00c7\u00c0\u221e^\\\u20ac\u00d3|@\u00c2\u00e2\u00ca\u00c9 !\"#\u00ba%&'()"
    233             + "*+,-./0123456789:;<=>?\u00cdABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c3\u00d5\u00da\u00dc"
    234             + "\u00a7~abcdefghijklmnopqrstuvwxyz\u00e3\u00f5`\u00fc\u00e0"
    235     };
    236 
    237     @SmallTest
    238     public void testFragmentText() throws Exception {
    239         boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
    240                 TelephonyManager.PHONE_TYPE_GSM);
    241 
    242         // Valid 160 character 7-bit text.
    243         String text = "123456789012345678901234567890123456789012345678901234567890" +
    244                 "1234567890123456789012345678901234567890123456789012345678901234567890" +
    245                 "123456789012345678901234567890";
    246         GsmAlphabet.TextEncodingDetails ted = SmsMessage.calculateLength(text, false);
    247         assertEquals(1, ted.msgCount);
    248         assertEquals(160, ted.codeUnitCount);
    249         assertEquals(1, ted.codeUnitSize);
    250         assertEquals(0, ted.languageTable);
    251         assertEquals(0, ted.languageShiftTable);
    252         if (isGsmPhone) {
    253             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
    254             assertEquals(1, fragments.size());
    255         }
    256 
    257         // Valid 161 character 7-bit text.
    258         text = "123456789012345678901234567890123456789012345678901234567890" +
    259                 "1234567890123456789012345678901234567890123456789012345678901234567890" +
    260                 "1234567890123456789012345678901";
    261         ted = SmsMessage.calculateLength(text, false);
    262         assertEquals(2, ted.msgCount);
    263         assertEquals(161, ted.codeUnitCount);
    264         assertEquals(1, ted.codeUnitSize);
    265         assertEquals(0, ted.languageTable);
    266         assertEquals(0, ted.languageShiftTable);
    267         if (isGsmPhone) {
    268             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
    269             assertEquals(2, fragments.size());
    270             assertEquals(text, fragments.get(0) + fragments.get(1));
    271             assertEquals(153, fragments.get(0).length());
    272             assertEquals(8, fragments.get(1).length());
    273         }
    274     }
    275 
    276     @SmallTest
    277     public void testFragmentTurkishText() throws Exception {
    278         boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
    279                 TelephonyManager.PHONE_TYPE_GSM);
    280 
    281         int[] oldTables = GsmAlphabet.getEnabledSingleShiftTables();
    282         int[] turkishTable = { 1 };
    283         GsmAlphabet.setEnabledSingleShiftTables(turkishTable);
    284 
    285         // Valid 77 character text with Turkish characters.
    286         String text = "" +
    287                 "";
    288         GsmAlphabet.TextEncodingDetails ted = SmsMessage.calculateLength(text, false);
    289         assertEquals(1, ted.msgCount);
    290         assertEquals(154, ted.codeUnitCount);
    291         assertEquals(1, ted.codeUnitSize);
    292         assertEquals(0, ted.languageTable);
    293         assertEquals(1, ted.languageShiftTable);
    294         if (isGsmPhone) {
    295             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
    296             assertEquals(1, fragments.size());
    297             assertEquals(text, fragments.get(0));
    298             assertEquals(77, fragments.get(0).length());
    299         }
    300 
    301         // Valid 78 character text with Turkish characters.
    302         text = "" +
    303                 "";
    304         ted = SmsMessage.calculateLength(text, false);
    305         assertEquals(2, ted.msgCount);
    306         assertEquals(156, ted.codeUnitCount);
    307         assertEquals(1, ted.codeUnitSize);
    308         assertEquals(0, ted.languageTable);
    309         assertEquals(1, ted.languageShiftTable);
    310         if (isGsmPhone) {
    311             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
    312             assertEquals(2, fragments.size());
    313             assertEquals(text, fragments.get(0) + fragments.get(1));
    314             assertEquals(74, fragments.get(0).length());
    315             assertEquals(4, fragments.get(1).length());
    316         }
    317 
    318         // Valid 160 character text with Turkish characters.
    319         text = "" +
    320                 "" +
    321                 "";
    322         ted = SmsMessage.calculateLength(text, false);
    323         assertEquals(3, ted.msgCount);
    324         assertEquals(320, ted.codeUnitCount);
    325         assertEquals(1, ted.codeUnitSize);
    326         assertEquals(0, ted.languageTable);
    327         assertEquals(1, ted.languageShiftTable);
    328         if (isGsmPhone) {
    329             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
    330             assertEquals(3, fragments.size());
    331             assertEquals(text, fragments.get(0) + fragments.get(1) + fragments.get(2));
    332             assertEquals(74, fragments.get(0).length());
    333             assertEquals(74, fragments.get(1).length());
    334             assertEquals(12, fragments.get(2).length());
    335         }
    336 
    337         GsmAlphabet.setEnabledSingleShiftTables(oldTables);
    338     }
    339 
    340 
    341     @SmallTest
    342     public void testDecode() throws Exception {
    343         decodeSingle(0);    // default table
    344         decodeSingle(1);    // Turkish locking shift table
    345         decodeSingle(3);    // Portuguese locking shift table
    346     }
    347 
    348     private void decodeSingle(int language) throws Exception {
    349         byte[] septets = new byte[(7 * 128 + 7) / 8];
    350 
    351         int bitOffset = 0;
    352 
    353         for (int i = 0; i < 128; i++) {
    354             int v;
    355             if (i == 0x1b) {
    356                 // extended escape char
    357                 v = 0;
    358             } else {
    359                 v = i;
    360             }
    361 
    362             int byteOffset = bitOffset / 8;
    363             int shift = bitOffset % 8;
    364 
    365             septets[byteOffset] |= v << shift;
    366 
    367             if (shift > 1) {
    368                 septets[byteOffset + 1] = (byte) (v >> (8 - shift));
    369             }
    370 
    371             bitOffset += 7;
    372         }
    373 
    374         String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, 128, 0, language, 0);
    375         byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded, language, 0);
    376 
    377         assertEquals(sBasicTables[language], decoded);
    378 
    379         // reEncoded has the count septets byte at the front
    380         assertEquals(septets.length + 1, reEncoded.length);
    381 
    382         for (int i = 0; i < septets.length; i++) {
    383             assertEquals(septets[i], reEncoded[i + 1]);
    384         }
    385     }
    386 
    387     private static final int GSM_ESCAPE_CHARACTER = 0x1b;
    388 
    389     private static final String[] sExtendedTables = {
    390         // GSM 7 bit default alphabet extension table
    391         "\f^{}\\[~]|\u20ac",
    392 
    393         // Turkish single shift extension table
    394         "\f^{}\\[~]|\u011e\u0130\u015e\u00e7\u20ac\u011f\u0131\u015f",
    395 
    396         // Spanish single shift extension table
    397         "\u00e7\f^{}\\[~]|\u00c1\u00cd\u00d3\u00da\u00e1\u20ac\u00ed\u00f3\u00fa",
    398 
    399         // Portuguese single shift extension table
    400         "\u00ea\u00e7\f\u00d4\u00f4\u00c1\u00e1\u03a6\u0393^\u03a9\u03a0\u03a8\u03a3\u0398\u00ca"
    401             + "{}\\[~]|\u00c0\u00cd\u00d3\u00da\u00c3\u00d5\u00c2\u20ac\u00ed\u00f3\u00fa\u00e3"
    402             + "\u00f5\u00e2"
    403     };
    404 
    405     private static final int[][] sExtendedTableIndexes = {
    406         {0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x65},
    407         {0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x47, 0x49, 0x53, 0x63,
    408                 0x65, 0x67, 0x69, 0x73},
    409         {0x09, 0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x49, 0x4f,
    410                 0x55, 0x61, 0x65, 0x69, 0x6f, 0x75},
    411         {0x05, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    412                 0x18, 0x19, 0x1f, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x49,
    413                 0x4f, 0x55, 0x5b, 0x5c, 0x61, 0x65, 0x69, 0x6f, 0x75, 0x7b, 0x7c, 0x7f}
    414     };
    415 
    416     @SmallTest
    417     public void testDecodeExtended() throws Exception {
    418         for (int language = 0; language < 3; language++) {
    419             int[] tableIndex = sExtendedTableIndexes[language];
    420             int numSeptets = tableIndex.length * 2;  // two septets per extended char
    421             byte[] septets = new byte[(7 * numSeptets + 7) / 8];
    422 
    423             int bitOffset = 0;
    424 
    425             for (int v : tableIndex) {
    426                 // escape character
    427                 int byteOffset = bitOffset / 8;
    428                 int shift = bitOffset % 8;
    429 
    430                 septets[byteOffset] |= GSM_ESCAPE_CHARACTER << shift;
    431 
    432                 if (shift > 1) {
    433                     septets[byteOffset + 1] = (byte) (GSM_ESCAPE_CHARACTER >> (8 - shift));
    434                 }
    435 
    436                 bitOffset += 7;
    437 
    438                 // extended table index
    439                 byteOffset = bitOffset / 8;
    440                 shift = bitOffset % 8;
    441 
    442                 septets[byteOffset] |= v << shift;
    443 
    444                 if (shift > 1) {
    445                     septets[byteOffset + 1] = (byte) (v >> (8 - shift));
    446                 }
    447 
    448                 bitOffset += 7;
    449             }
    450 
    451             String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0,
    452                     0, language);
    453             byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded, 0, language);
    454 
    455             assertEquals(sExtendedTables[language], decoded);
    456 
    457             // reEncoded has the count septets byte at the front
    458             assertEquals(septets.length + 1, reEncoded.length);
    459 
    460             for (int i = 0; i < septets.length; i++) {
    461                 assertEquals(septets[i], reEncoded[i + 1]);
    462             }
    463         }
    464     }
    465 
    466     @SmallTest
    467     public void testDecodeExtendedFallback() throws Exception {
    468         // verify that unmapped characters in extension table fall back to locking shift table
    469         for (int language = 0; language < 3; language++) {
    470             int[] tableIndex = sExtendedTableIndexes[language];
    471             int numChars = 128 - tableIndex.length;
    472             int numSeptets = numChars * 2;  // two septets per extended char
    473             byte[] septets = new byte[(7 * numSeptets + 7) / 8];
    474 
    475             int tableOffset = 0;
    476             int bitOffset = 0;
    477 
    478             StringBuilder defaultTable = new StringBuilder(128);
    479             StringBuilder turkishTable = new StringBuilder(128);
    480             StringBuilder portugueseTable = new StringBuilder(128);
    481 
    482             for (char c = 0; c < 128; c++) {
    483                 // skip characters that are present in the current extension table
    484                 if (tableOffset < tableIndex.length && tableIndex[tableOffset] == c) {
    485                     tableOffset++;
    486                     continue;
    487                 }
    488 
    489                 // escape character
    490                 int byteOffset = bitOffset / 8;
    491                 int shift = bitOffset % 8;
    492 
    493                 septets[byteOffset] |= GSM_ESCAPE_CHARACTER << shift;
    494 
    495                 if (shift > 1) {
    496                     septets[byteOffset + 1] = (byte) (GSM_ESCAPE_CHARACTER >> (8 - shift));
    497                 }
    498 
    499                 bitOffset += 7;
    500 
    501                 // extended table index
    502                 byteOffset = bitOffset / 8;
    503                 shift = bitOffset % 8;
    504 
    505                 septets[byteOffset] |= c << shift;
    506 
    507                 if (shift > 1) {
    508                     septets[byteOffset + 1] = (byte) (c >> (8 - shift));
    509                 }
    510 
    511                 bitOffset += 7;
    512 
    513                 if (c == GsmAlphabet.GSM_EXTENDED_ESCAPE) {
    514                     // double Escape maps to space character
    515                     defaultTable.append(' ');
    516                     turkishTable.append(' ');
    517                     portugueseTable.append(' ');
    518                 } else {
    519                     // other unmapped chars map to the default or locking shift table
    520                     defaultTable.append(sBasicTables[0].charAt(c));
    521                     turkishTable.append(sBasicTables[1].charAt(c));
    522                     portugueseTable.append(sBasicTables[3].charAt(c));
    523                 }
    524             }
    525 
    526             String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0,
    527                     0, language);
    528 
    529             assertEquals(defaultTable.toString(), decoded);
    530 
    531             decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0, 1, language);
    532             assertEquals(turkishTable.toString(), decoded);
    533 
    534             decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0, 3, language);
    535             assertEquals(portugueseTable.toString(), decoded);
    536         }
    537     }
    538 }
    539