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
17package com.android.internal.telephony;
18
19import com.android.internal.telephony.GsmAlphabet;
20import com.android.internal.telephony.uicc.IccUtils;
21
22import junit.framework.TestCase;
23
24import android.test.suitebuilder.annotation.LargeTest;
25import android.test.suitebuilder.annotation.SmallTest;
26
27public class GsmAlphabetTest extends TestCase {
28
29    private static final String sGsmExtendedChars = "{|}\\[~]\f\u20ac";
30
31    @SmallTest
32    public void test7bitWithHeader() throws Exception {
33        SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
34        concatRef.refNumber = 1;
35        concatRef.seqNumber = 2;
36        concatRef.msgCount = 2;
37        concatRef.isEightBits = true;
38        SmsHeader header = new SmsHeader();
39        header.concatRef = concatRef;
40
41        String message = "aaaaaaaaaabbbbbbbbbbcccccccccc";
42        byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message,
43                SmsHeader.toByteArray(header), 0, 0);
44        int septetCount = GsmAlphabet.countGsmSeptetsUsingTables(message, true, 0, 0);
45        String parsedMessage = GsmAlphabet.gsm7BitPackedToString(
46                userData, SmsHeader.toByteArray(header).length+2, septetCount, 1, 0, 0);
47        assertEquals(message, parsedMessage);
48    }
49
50    // TODO: This method should *really* be a series of individual test methods.
51    // However, it's a SmallTest because it executes quickly.
52    @SmallTest
53    public void testBasic() throws Exception {
54        // '@' maps to char 0
55        assertEquals(0, GsmAlphabet.charToGsm('@'));
56
57        // `a (a with grave accent) maps to last GSM character
58        assertEquals(0x7f, GsmAlphabet.charToGsm('\u00e0'));
59
60        //
61        // These are the extended chars
62        // They should all return GsmAlphabet.GSM_EXTENDED_ESCAPE
63        //
64
65        for (int i = 0, s = sGsmExtendedChars.length(); i < s; i++) {
66            assertEquals(GsmAlphabet.GSM_EXTENDED_ESCAPE,
67                    GsmAlphabet.charToGsm(sGsmExtendedChars.charAt(i)));
68
69        }
70
71        // euro symbol
72        assertEquals(GsmAlphabet.GSM_EXTENDED_ESCAPE,
73                GsmAlphabet.charToGsm('\u20ac'));
74
75        // An unmappable char (the 'cent' char) maps to a space
76        assertEquals(GsmAlphabet.charToGsm(' '),
77                GsmAlphabet.charToGsm('\u00a2'));
78
79        // unmappable = space = 1 septet
80        assertEquals(1, GsmAlphabet.countGsmSeptets('\u00a2'));
81
82        //
83        // Test extended table
84        //
85
86        for (int i = 0, s = sGsmExtendedChars.length(); i < s; i++) {
87            assertEquals(sGsmExtendedChars.charAt(i),
88                    GsmAlphabet.gsmExtendedToChar(
89                            GsmAlphabet.charToGsmExtended(sGsmExtendedChars.charAt(i))));
90
91        }
92
93        // Unmappable extended char
94        assertEquals(GsmAlphabet.charToGsm(' '),
95                GsmAlphabet.charToGsmExtended('@'));
96
97        //
98        // gsmToChar()
99        //
100
101        assertEquals('@', GsmAlphabet.gsmToChar(0));
102
103        // `a (a with grave accent) maps to last GSM character
104        assertEquals('\u00e0', GsmAlphabet.gsmToChar(0x7f));
105
106        assertEquals('\uffff',
107                GsmAlphabet.gsmToChar(GsmAlphabet.GSM_EXTENDED_ESCAPE));
108
109        // Out-of-range/unmappable value
110        assertEquals(' ', GsmAlphabet.gsmToChar(0x80));
111
112        //
113        // gsmExtendedToChar()
114        //
115
116        assertEquals('{', GsmAlphabet.gsmExtendedToChar(0x28));
117
118        // No double-escapes
119        assertEquals(' ', GsmAlphabet.gsmExtendedToChar(
120                GsmAlphabet.GSM_EXTENDED_ESCAPE));
121
122        // Reserved for extension to extension table (mapped to space)
123        assertEquals(' ', GsmAlphabet.gsmExtendedToChar(GsmAlphabet.GSM_EXTENDED_ESCAPE));
124
125        // Unmappable (mapped to character in default or national locking shift table)
126        assertEquals('@', GsmAlphabet.gsmExtendedToChar(0));
127        assertEquals('\u00e0', GsmAlphabet.gsmExtendedToChar(0x7f));
128
129        //
130        // stringTo7BitPacked, gsm7BitPackedToString
131        //
132
133        byte[] packed;
134        StringBuilder testString = new StringBuilder(300);
135
136        // Check all alignment cases
137        for (int i = 0; i < 9; i++, testString.append('@')) {
138            packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
139            assertEquals(testString.toString(),
140                    GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
141        }
142
143        // Check full non-extended alphabet
144        for (int i = 0; i < 0x80; i++) {
145            char c;
146
147            if (i == GsmAlphabet.GSM_EXTENDED_ESCAPE) {
148                continue;
149            }
150
151            c = GsmAlphabet.gsmToChar(i);
152            testString.append(c);
153
154            // These are all non-extended chars, so it should be
155            // one septet per char
156            assertEquals(1, GsmAlphabet.countGsmSeptets(c));
157        }
158
159        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
160        assertEquals(testString.toString(),
161                GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
162
163        // Test extended chars too
164
165        testString.append(sGsmExtendedChars);
166
167        for (int i = 0, s = sGsmExtendedChars.length(); i < s; i++) {
168            // These are all extended chars, so it should be
169            // two septets per char
170            assertEquals(2, GsmAlphabet.countGsmSeptets(sGsmExtendedChars.charAt(i)));
171
172        }
173
174        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
175        assertEquals(testString.toString(),
176                GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
177
178        // stringTo7BitPacked handles up to 255 septets
179
180        testString.setLength(0);
181        for (int i = 0; i < 255; i++) {
182            testString.append('@');
183        }
184
185        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
186        assertEquals(testString.toString(),
187                GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
188
189        // > 255 septets throws runtime exception
190        testString.append('@');
191
192        try {
193            GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
194            fail("expected exception");
195        } catch (EncodeException ex) {
196            // exception expected
197        }
198
199        // Try 254 septets with 127 extended chars
200
201        testString.setLength(0);
202        for (int i = 0; i < (255 / 2); i++) {
203            testString.append('{');
204        }
205
206        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
207        assertEquals(testString.toString(),
208                GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
209
210        // > 255 septets throws runtime exception
211        testString.append('{');
212
213        try {
214            GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
215            fail("expected exception");
216        } catch (EncodeException ex) {
217            // exception expected
218        }
219
220        // Reserved for extension to extension table (mapped to space)
221        packed = new byte[]{(byte)(0x1b | 0x80), 0x1b >> 1};
222        assertEquals(" ", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
223
224        // Unmappable (mapped to character in default alphabet table)
225        packed[0] = 0x1b;
226        packed[1] = 0x00;
227        assertEquals("@", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
228        packed[0] = (byte)(0x1b | 0x80);
229        packed[1] = (byte)(0x7f >> 1);
230        assertEquals("\u00e0", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
231
232        //
233        // 8 bit unpacked format
234        //
235        // Note: we compare hex strings here
236        // because Assert doesn't have array comparisons
237
238        byte unpacked[];
239
240        unpacked = IccUtils.hexStringToBytes("566F696365204D61696C");
241        assertEquals("Voice Mail",
242                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length));
243
244        assertEquals(IccUtils.bytesToHexString(unpacked),
245                IccUtils.bytesToHexString(
246                        GsmAlphabet.stringToGsm8BitPacked("Voice Mail")));
247
248        unpacked = GsmAlphabet.stringToGsm8BitPacked(sGsmExtendedChars);
249        // two bytes for every extended char
250        assertEquals(2 * sGsmExtendedChars.length(), unpacked.length);
251        assertEquals(sGsmExtendedChars,
252                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length));
253
254        // should be two bytes per extended char
255        assertEquals(2 * sGsmExtendedChars.length(), unpacked.length);
256
257        // Test truncation of unaligned extended chars
258        unpacked = new byte[3];
259        GsmAlphabet.stringToGsm8BitUnpackedField(sGsmExtendedChars, unpacked,
260                0, unpacked.length);
261
262        // Should be one extended char and an 0xff at the end
263
264        assertEquals(0xff, 0xff & unpacked[2]);
265        assertEquals(sGsmExtendedChars.substring(0, 1),
266                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length));
267
268        // Test truncation of normal chars
269        unpacked = new byte[3];
270        GsmAlphabet.stringToGsm8BitUnpackedField("abcd", unpacked,
271                0, unpacked.length);
272
273        assertEquals("abc",
274                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length));
275
276        // Test truncation of mixed normal and extended chars
277        unpacked = new byte[3];
278        GsmAlphabet.stringToGsm8BitUnpackedField("a{cd", unpacked,
279                0, unpacked.length);
280
281        assertEquals("a{",
282                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length));
283
284        // Test padding after normal char
285        unpacked = new byte[3];
286        GsmAlphabet.stringToGsm8BitUnpackedField("a", unpacked,
287                0, unpacked.length);
288
289        assertEquals("a",
290                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length));
291
292        assertEquals(0xff, 0xff & unpacked[1]);
293        assertEquals(0xff, 0xff & unpacked[2]);
294
295        // Test malformed input -- escape char followed by end of field
296        unpacked[0] = 0;
297        unpacked[1] = 0;
298        unpacked[2] = GsmAlphabet.GSM_EXTENDED_ESCAPE;
299
300        assertEquals("@@",
301                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length));
302
303        // non-zero offset
304        assertEquals("@",
305                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
306
307        // test non-zero offset
308        unpacked[0] = 0;
309        GsmAlphabet.stringToGsm8BitUnpackedField("abcd", unpacked,
310                1, unpacked.length - 1);
311
312
313        assertEquals(0, unpacked[0]);
314
315        assertEquals("ab",
316                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
317
318        // test non-zero offset with truncated extended char
319        unpacked[0] = 0;
320
321        GsmAlphabet.stringToGsm8BitUnpackedField("a{", unpacked,
322                1, unpacked.length - 1);
323
324        assertEquals(0, unpacked[0]);
325
326        assertEquals("a",
327                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
328
329        // Reserved for extension to extension table (mapped to space)
330        unpacked[0] = 0x1b;
331        unpacked[1] = 0x1b;
332        assertEquals(" ", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
333
334        // Unmappable (mapped to character in default or national locking shift table)
335        unpacked[1] = 0x00;
336        assertEquals("@", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
337        unpacked[1] = 0x7f;
338        assertEquals("\u00e0", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
339    }
340
341    @SmallTest
342    public void testGsm8BitUpackedWithEuckr() throws Exception {
343        // Some feature phones in Korea store contacts as euc-kr.
344        // Test this situations.
345        byte unpacked[];
346
347        // Test general alphabet strings.
348        unpacked = IccUtils.hexStringToBytes("61626320646566FF");
349        assertEquals("abc def",
350                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length, "euc-kr"));
351
352        // Test korean strings.
353        unpacked = IccUtils.hexStringToBytes("C5D7BDBAC6AEFF");
354        assertEquals("\uD14C\uC2A4\uD2B8",
355                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length, "euc-kr"));
356
357        // Test gsm Extented Characters.
358        unpacked = GsmAlphabet.stringToGsm8BitPacked(sGsmExtendedChars);
359        assertEquals(sGsmExtendedChars,
360                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length, "euc-kr"));
361    }
362}
363