GsmAlphabet.java revision b55df4471ed55a0e91dee79304f3b1209ffa4b35
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 android.text.TextUtils; 20import android.util.SparseIntArray; 21 22import android.util.Log; 23 24import java.nio.ByteBuffer; 25import java.nio.charset.Charset; 26 27/** 28 * This class implements the character set mapping between 29 * the GSM SMS 7-bit alphabet specified in TS 23.038 6.2.1 30 * and UTF-16 31 * 32 * {@hide} 33 */ 34public class GsmAlphabet { 35 static final String LOG_TAG = "GSM"; 36 37 38 39 //***** Constants 40 41 /** 42 * This escapes extended characters, and when present indicates that the 43 * following character should 44 * be looked up in the "extended" table 45 * 46 * gsmToChar(GSM_EXTENDED_ESCAPE) returns 0xffff 47 */ 48 49 public static final byte GSM_EXTENDED_ESCAPE = 0x1B; 50 51 52 /** 53 * char to GSM alphabet char 54 * Returns ' ' in GSM alphabet if there's no possible match 55 * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table 56 * In this case, you must call charToGsmExtended() for the value that 57 * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string 58 */ 59 public static int 60 charToGsm(char c) { 61 try { 62 return charToGsm(c, false); 63 } catch (EncodeException ex) { 64 // this should never happen 65 return sGsmSpaceChar; 66 } 67 } 68 69 /** 70 * char to GSM alphabet char 71 * @param throwException If true, throws EncodeException on invalid char. 72 * If false, returns GSM alphabet ' ' char. 73 * 74 * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table 75 * In this case, you must call charToGsmExtended() for the value that 76 * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string 77 */ 78 79 public static int 80 charToGsm(char c, boolean throwException) throws EncodeException { 81 int ret; 82 83 ret = charToGsm.get(c, -1); 84 85 if (ret == -1) { 86 ret = charToGsmExtended.get(c, -1); 87 88 if (ret == -1) { 89 if (throwException) { 90 throw new EncodeException(c); 91 } else { 92 return sGsmSpaceChar; 93 } 94 } else { 95 return GSM_EXTENDED_ESCAPE; 96 } 97 } 98 99 return ret; 100 101 } 102 103 104 /** 105 * char to extended GSM alphabet char 106 * 107 * Extended chars should be escaped with GSM_EXTENDED_ESCAPE 108 * 109 * Returns ' ' in GSM alphabet if there's no possible match 110 * 111 */ 112 public static int 113 charToGsmExtended(char c) { 114 int ret; 115 116 ret = charToGsmExtended.get(c, -1); 117 118 if (ret == -1) { 119 return sGsmSpaceChar; 120 } 121 122 return ret; 123 } 124 125 /** 126 * Converts a character in the GSM alphabet into a char 127 * 128 * if GSM_EXTENDED_ESCAPE is passed, 0xffff is returned. In this case, 129 * the following character in the stream should be decoded with 130 * gsmExtendedToChar() 131 * 132 * If an unmappable value is passed (one greater than 127), ' ' is returned 133 */ 134 135 public static char 136 gsmToChar(int gsmChar) { 137 return (char)gsmToChar.get(gsmChar, ' '); 138 } 139 140 /** 141 * Converts a character in the extended GSM alphabet into a char 142 * 143 * if GSM_EXTENDED_ESCAPE is passed, ' ' is returned since no second 144 * extension page has yet been defined (see Note 1 in table 6.2.1.1 of 145 * TS 23.038 v7.00) 146 * 147 * If an unmappable value is passed , ' ' is returned 148 */ 149 150 public static char 151 gsmExtendedToChar(int gsmChar) { 152 int ret; 153 154 ret = gsmExtendedToChar.get(gsmChar, -1); 155 156 if (ret == -1) { 157 return ' '; 158 } 159 160 return (char)ret; 161 } 162 163 /** 164 * Converts a String into a byte array containing the 7-bit packed 165 * GSM Alphabet representation of the string. If a header is provided, 166 * this is included in the returned byte array and padded to a septet 167 * boundary. 168 * 169 * Unencodable chars are encoded as spaces 170 * 171 * Byte 0 in the returned byte array is the count of septets used, 172 * including the header and header padding. The returned byte array is 173 * the minimum size required to store the packed septets. The returned 174 * array cannot contain more than 255 septets. 175 * 176 * @param data The text string to encode. 177 * @param header Optional header (including length byte) that precedes 178 * the encoded data, padded to septet boundary. 179 * @return Byte array containing header and encoded data. 180 */ 181 public static byte[] stringToGsm7BitPackedWithHeader(String data, byte[] header) 182 throws EncodeException { 183 184 if (header == null || header.length == 0) { 185 return stringToGsm7BitPacked(data); 186 } 187 188 int headerBits = (header.length + 1) * 8; 189 int headerSeptets = (headerBits + 6) / 7; 190 191 byte[] ret = stringToGsm7BitPacked(data, headerSeptets, true); 192 193 // Paste in the header 194 ret[1] = (byte)header.length; 195 System.arraycopy(header, 0, ret, 2, header.length); 196 return ret; 197 } 198 199 /** 200 * Converts a String into a byte array containing 201 * the 7-bit packed GSM Alphabet representation of the string. 202 * 203 * Unencodable chars are encoded as spaces 204 * 205 * Byte 0 in the returned byte array is the count of septets used 206 * The returned byte array is the minimum size required to store 207 * the packed septets. The returned array cannot contain more than 255 208 * septets. 209 * 210 * @param data the data string to encode 211 * @throws EncodeException if String is too large to encode 212 */ 213 public static byte[] stringToGsm7BitPacked(String data) 214 throws EncodeException { 215 return stringToGsm7BitPacked(data, 0, true); 216 } 217 218 /** 219 * Converts a String into a byte array containing 220 * the 7-bit packed GSM Alphabet representation of the string. 221 * 222 * Byte 0 in the returned byte array is the count of septets used 223 * The returned byte array is the minimum size required to store 224 * the packed septets. The returned array cannot contain more than 255 225 * septets. 226 * 227 * @param data the text to convert to septets 228 * @param startingSeptetOffset the number of padding septets to put before 229 * the character data at the beginning of the array 230 * @param throwException If true, throws EncodeException on invalid char. 231 * If false, replaces unencodable char with GSM alphabet space char. 232 * 233 * @throws EncodeException if String is too large to encode 234 */ 235 public static byte[] stringToGsm7BitPacked(String data, int startingSeptetOffset, 236 boolean throwException) throws EncodeException { 237 int dataLen = data.length(); 238 int septetCount = countGsmSeptets(data, throwException) + startingSeptetOffset; 239 if (septetCount > 255) { 240 throw new EncodeException("Payload cannot exceed 255 septets"); 241 } 242 int byteCount = ((septetCount * 7) + 7) / 8; 243 byte[] ret = new byte[byteCount + 1]; // Include space for one byte length prefix. 244 for (int i = 0, septets = startingSeptetOffset, bitOffset = startingSeptetOffset * 7; 245 i < dataLen && septets < septetCount; 246 i++, bitOffset += 7) { 247 char c = data.charAt(i); 248 int v = GsmAlphabet.charToGsm(c, throwException); 249 if (v == GSM_EXTENDED_ESCAPE) { 250 v = GsmAlphabet.charToGsmExtended(c); // Lookup the extended char. 251 packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE); 252 bitOffset += 7; 253 septets++; 254 } 255 packSmsChar(ret, bitOffset, v); 256 septets++; 257 } 258 ret[0] = (byte) (septetCount); // Validated by check above. 259 return ret; 260 } 261 262 /** 263 * Pack a 7-bit char into its appropriate place in a byte array 264 * 265 * @param bitOffset the bit offset that the septet should be packed at 266 * (septet index * 7) 267 */ 268 private static void 269 packSmsChar(byte[] packedChars, int bitOffset, int value) { 270 int byteOffset = bitOffset / 8; 271 int shift = bitOffset % 8; 272 273 packedChars[++byteOffset] |= value << shift; 274 275 if (shift > 1) { 276 packedChars[++byteOffset] = (byte)(value >> (8 - shift)); 277 } 278 } 279 280 /** 281 * Convert a GSM alphabet 7 bit packed string (SMS string) into a 282 * {@link java.lang.String}. 283 * 284 * See TS 23.038 6.1.2.1 for SMS Character Packing 285 * 286 * @param pdu the raw data from the pdu 287 * @param offset the byte offset of 288 * @param lengthSeptets string length in septets, not bytes 289 * @return String representation or null on decoding exception 290 */ 291 public static String gsm7BitPackedToString(byte[] pdu, int offset, 292 int lengthSeptets) { 293 return gsm7BitPackedToString(pdu, offset, lengthSeptets, 0); 294 } 295 296 /** 297 * Convert a GSM alphabet 7 bit packed string (SMS string) into a 298 * {@link java.lang.String}. 299 * 300 * See TS 23.038 6.1.2.1 for SMS Character Packing 301 * 302 * @param pdu the raw data from the pdu 303 * @param offset the byte offset of 304 * @param lengthSeptets string length in septets, not bytes 305 * @param numPaddingBits the number of padding bits before the start of the 306 * string in the first byte 307 * @return String representation or null on decoding exception 308 */ 309 public static String gsm7BitPackedToString(byte[] pdu, int offset, 310 int lengthSeptets, int numPaddingBits) { 311 StringBuilder ret = new StringBuilder(lengthSeptets); 312 boolean prevCharWasEscape; 313 314 try { 315 prevCharWasEscape = false; 316 317 for (int i = 0 ; i < lengthSeptets ; i++) { 318 int bitOffset = (7 * i) + numPaddingBits; 319 320 int byteOffset = bitOffset / 8; 321 int shift = bitOffset % 8; 322 int gsmVal; 323 324 gsmVal = (0x7f & (pdu[offset + byteOffset] >> shift)); 325 326 // if it crosses a byte boundary 327 if (shift > 1) { 328 // set msb bits to 0 329 gsmVal &= 0x7f >> (shift - 1); 330 331 gsmVal |= 0x7f & (pdu[offset + byteOffset + 1] << (8 - shift)); 332 } 333 334 if (prevCharWasEscape) { 335 ret.append(GsmAlphabet.gsmExtendedToChar(gsmVal)); 336 prevCharWasEscape = false; 337 } else if (gsmVal == GSM_EXTENDED_ESCAPE) { 338 prevCharWasEscape = true; 339 } else { 340 ret.append(GsmAlphabet.gsmToChar(gsmVal)); 341 } 342 } 343 } catch (RuntimeException ex) { 344 Log.e(LOG_TAG, "Error GSM 7 bit packed: ", ex); 345 return null; 346 } 347 348 return ret.toString(); 349 } 350 351 352 /** 353 * Convert a GSM alphabet string that's stored in 8-bit unpacked 354 * format (as it often appears in SIM records) into a String 355 * 356 * Field may be padded with trailing 0xff's. The decode stops 357 * at the first 0xff encountered. 358 */ 359 public static String 360 gsm8BitUnpackedToString(byte[] data, int offset, int length) { 361 return gsm8BitUnpackedToString(data, offset, length, ""); 362 } 363 364 /** 365 * Convert a GSM alphabet string that's stored in 8-bit unpacked 366 * format (as it often appears in SIM records) into a String 367 * 368 * Field may be padded with trailing 0xff's. The decode stops 369 * at the first 0xff encountered. 370 * 371 * Additionally, in some country(ex. Korea), there are non-ASCII or MBCS characters. 372 * If a character set is given, characters in data are treat as MBCS. 373 */ 374 public static String 375 gsm8BitUnpackedToString(byte[] data, int offset, int length, String characterset) { 376 boolean isMbcs = false; 377 Charset charset = null; 378 ByteBuffer mbcsBuffer = null; 379 380 if (!TextUtils.isEmpty(characterset) 381 && !characterset.equalsIgnoreCase("us-ascii") 382 && Charset.isSupported(characterset)) { 383 isMbcs = true; 384 charset = Charset.forName(characterset); 385 mbcsBuffer = ByteBuffer.allocate(2); 386 } 387 boolean prevWasEscape; 388 StringBuilder ret = new StringBuilder(length); 389 390 prevWasEscape = false; 391 for (int i = offset ; i < offset + length ; i++) { 392 // Never underestimate the pain that can be caused 393 // by signed bytes 394 int c = data[i] & 0xff; 395 396 if (c == 0xff) { 397 break; 398 } else if (c == GSM_EXTENDED_ESCAPE) { 399 if (prevWasEscape) { 400 // Two escape chars in a row 401 // We treat this as a space 402 // See Note 1 in table 6.2.1.1 of TS 23.038 v7.00 403 ret.append(' '); 404 prevWasEscape = false; 405 } else { 406 prevWasEscape = true; 407 } 408 } else { 409 if (prevWasEscape) { 410 ret.append((char)gsmExtendedToChar.get(c, ' ')); 411 } else { 412 if (!isMbcs || c < 0x80 || i + 1 >= offset + length) { 413 ret.append((char)gsmToChar.get(c, ' ')); 414 } else { 415 // isMbcs must be true. So both mbcsBuffer and charset are initialized. 416 mbcsBuffer.clear(); 417 mbcsBuffer.put(data, i++, 2); 418 mbcsBuffer.flip(); 419 ret.append(charset.decode(mbcsBuffer).toString()); 420 } 421 } 422 prevWasEscape = false; 423 } 424 } 425 426 return ret.toString(); 427 } 428 429 /** 430 * Convert a string into an 8-bit unpacked GSM alphabet byte 431 * array 432 */ 433 public static byte[] 434 stringToGsm8BitPacked(String s) { 435 byte[] ret; 436 437 int septets = 0; 438 439 septets = countGsmSeptets(s); 440 441 // Enough for all the septets and the length byte prefix 442 ret = new byte[septets]; 443 444 stringToGsm8BitUnpackedField(s, ret, 0, ret.length); 445 446 return ret; 447 } 448 449 450 /** 451 * Write a String into a GSM 8-bit unpacked field of 452 * @param length size at @param offset in @param dest 453 * 454 * Field is padded with 0xff's, string is truncated if necessary 455 */ 456 457 public static void 458 stringToGsm8BitUnpackedField(String s, byte dest[], int offset, int length) { 459 int outByteIndex = offset; 460 461 // Septets are stored in byte-aligned octets 462 for (int i = 0, sz = s.length() 463 ; i < sz && (outByteIndex - offset) < length 464 ; i++ 465 ) { 466 char c = s.charAt(i); 467 468 int v = GsmAlphabet.charToGsm(c); 469 470 if (v == GSM_EXTENDED_ESCAPE) { 471 // make sure we can fit an escaped char 472 if (! (outByteIndex + 1 - offset < length)) { 473 break; 474 } 475 476 dest[outByteIndex++] = GSM_EXTENDED_ESCAPE; 477 478 v = GsmAlphabet.charToGsmExtended(c); 479 } 480 481 dest[outByteIndex++] = (byte)v; 482 } 483 484 // pad with 0xff's 485 while((outByteIndex - offset) < length) { 486 dest[outByteIndex++] = (byte)0xff; 487 } 488 } 489 490 /** 491 * Returns the count of 7-bit GSM alphabet characters 492 * needed to represent this character. Counts unencodable char as 1 septet. 493 */ 494 public static int 495 countGsmSeptets(char c) { 496 try { 497 return countGsmSeptets(c, false); 498 } catch (EncodeException ex) { 499 // This should never happen. 500 return 0; 501 } 502 } 503 504 /** 505 * Returns the count of 7-bit GSM alphabet characters 506 * needed to represent this character 507 * @param throwsException If true, throws EncodeException if unencodable 508 * char. Otherwise, counts invalid char as 1 septet 509 */ 510 public static int 511 countGsmSeptets(char c, boolean throwsException) throws EncodeException { 512 if (charToGsm.get(c, -1) != -1) { 513 return 1; 514 } 515 516 if (charToGsmExtended.get(c, -1) != -1) { 517 return 2; 518 } 519 520 if (throwsException) { 521 throw new EncodeException(c); 522 } else { 523 // count as a space char 524 return 1; 525 } 526 } 527 528 /** 529 * Returns the count of 7-bit GSM alphabet characters 530 * needed to represent this string. Counts unencodable char as 1 septet. 531 */ 532 public static int 533 countGsmSeptets(CharSequence s) { 534 try { 535 return countGsmSeptets(s, false); 536 } catch (EncodeException ex) { 537 // this should never happen 538 return 0; 539 } 540 } 541 542 /** 543 * Returns the count of 7-bit GSM alphabet characters 544 * needed to represent this string. 545 * @param throwsException If true, throws EncodeException if unencodable 546 * char. Otherwise, counts invalid char as 1 septet 547 */ 548 public static int 549 countGsmSeptets(CharSequence s, boolean throwsException) throws EncodeException { 550 int charIndex = 0; 551 int sz = s.length(); 552 int count = 0; 553 554 while (charIndex < sz) { 555 count += countGsmSeptets(s.charAt(charIndex), throwsException); 556 charIndex++; 557 } 558 559 return count; 560 } 561 562 /** 563 * Returns the index into <code>s</code> of the first character 564 * after <code>limit</code> septets have been reached, starting at 565 * index <code>start</code>. This is used when dividing messages 566 * into units within the SMS message size limit. 567 * 568 * @param s source string 569 * @param start index of where to start counting septets 570 * @param limit maximum septets to include, 571 * e.g. <code>MAX_USER_DATA_SEPTETS</code> 572 * @return index of first character that won't fit, or the length 573 * of the entire string if everything fits 574 */ 575 public static int 576 findGsmSeptetLimitIndex(String s, int start, int limit) { 577 int accumulator = 0; 578 int size = s.length(); 579 580 for (int i = start; i < size; i++) { 581 accumulator += countGsmSeptets(s.charAt(i)); 582 if (accumulator > limit) { 583 return i; 584 } 585 } 586 return size; 587 } 588 589 // Set in the static initializer 590 private static int sGsmSpaceChar; 591 592 private static final SparseIntArray charToGsm = new SparseIntArray(); 593 private static final SparseIntArray gsmToChar = new SparseIntArray(); 594 private static final SparseIntArray charToGsmExtended = new SparseIntArray(); 595 private static final SparseIntArray gsmExtendedToChar = new SparseIntArray(); 596 597 static { 598 int i = 0; 599 600 charToGsm.put('@', i++); 601 charToGsm.put('\u00a3', i++); 602 charToGsm.put('$', i++); 603 charToGsm.put('\u00a5', i++); 604 charToGsm.put('\u00e8', i++); 605 charToGsm.put('\u00e9', i++); 606 charToGsm.put('\u00f9', i++); 607 charToGsm.put('\u00ec', i++); 608 charToGsm.put('\u00f2', i++); 609 charToGsm.put('\u00c7', i++); 610 charToGsm.put('\n', i++); 611 charToGsm.put('\u00d8', i++); 612 charToGsm.put('\u00f8', i++); 613 charToGsm.put('\r', i++); 614 charToGsm.put('\u00c5', i++); 615 charToGsm.put('\u00e5', i++); 616 617 charToGsm.put('\u0394', i++); 618 charToGsm.put('_', i++); 619 charToGsm.put('\u03a6', i++); 620 charToGsm.put('\u0393', i++); 621 charToGsm.put('\u039b', i++); 622 charToGsm.put('\u03a9', i++); 623 charToGsm.put('\u03a0', i++); 624 charToGsm.put('\u03a8', i++); 625 charToGsm.put('\u03a3', i++); 626 charToGsm.put('\u0398', i++); 627 charToGsm.put('\u039e', i++); 628 charToGsm.put('\uffff', i++); 629 charToGsm.put('\u00c6', i++); 630 charToGsm.put('\u00e6', i++); 631 charToGsm.put('\u00df', i++); 632 charToGsm.put('\u00c9', i++); 633 634 charToGsm.put(' ', i++); 635 charToGsm.put('!', i++); 636 charToGsm.put('"', i++); 637 charToGsm.put('#', i++); 638 charToGsm.put('\u00a4', i++); 639 charToGsm.put('%', i++); 640 charToGsm.put('&', i++); 641 charToGsm.put('\'', i++); 642 charToGsm.put('(', i++); 643 charToGsm.put(')', i++); 644 charToGsm.put('*', i++); 645 charToGsm.put('+', i++); 646 charToGsm.put(',', i++); 647 charToGsm.put('-', i++); 648 charToGsm.put('.', i++); 649 charToGsm.put('/', i++); 650 651 charToGsm.put('0', i++); 652 charToGsm.put('1', i++); 653 charToGsm.put('2', i++); 654 charToGsm.put('3', i++); 655 charToGsm.put('4', i++); 656 charToGsm.put('5', i++); 657 charToGsm.put('6', i++); 658 charToGsm.put('7', i++); 659 charToGsm.put('8', i++); 660 charToGsm.put('9', i++); 661 charToGsm.put(':', i++); 662 charToGsm.put(';', i++); 663 charToGsm.put('<', i++); 664 charToGsm.put('=', i++); 665 charToGsm.put('>', i++); 666 charToGsm.put('?', i++); 667 668 charToGsm.put('\u00a1', i++); 669 charToGsm.put('A', i++); 670 charToGsm.put('B', i++); 671 charToGsm.put('C', i++); 672 charToGsm.put('D', i++); 673 charToGsm.put('E', i++); 674 charToGsm.put('F', i++); 675 charToGsm.put('G', i++); 676 charToGsm.put('H', i++); 677 charToGsm.put('I', i++); 678 charToGsm.put('J', i++); 679 charToGsm.put('K', i++); 680 charToGsm.put('L', i++); 681 charToGsm.put('M', i++); 682 charToGsm.put('N', i++); 683 charToGsm.put('O', i++); 684 685 charToGsm.put('P', i++); 686 charToGsm.put('Q', i++); 687 charToGsm.put('R', i++); 688 charToGsm.put('S', i++); 689 charToGsm.put('T', i++); 690 charToGsm.put('U', i++); 691 charToGsm.put('V', i++); 692 charToGsm.put('W', i++); 693 charToGsm.put('X', i++); 694 charToGsm.put('Y', i++); 695 charToGsm.put('Z', i++); 696 charToGsm.put('\u00c4', i++); 697 charToGsm.put('\u00d6', i++); 698 charToGsm.put('\u00d1', i++); 699 charToGsm.put('\u00dc', i++); 700 charToGsm.put('\u00a7', i++); 701 702 charToGsm.put('\u00bf', i++); 703 charToGsm.put('a', i++); 704 charToGsm.put('b', i++); 705 charToGsm.put('c', i++); 706 charToGsm.put('d', i++); 707 charToGsm.put('e', i++); 708 charToGsm.put('f', i++); 709 charToGsm.put('g', i++); 710 charToGsm.put('h', i++); 711 charToGsm.put('i', i++); 712 charToGsm.put('j', i++); 713 charToGsm.put('k', i++); 714 charToGsm.put('l', i++); 715 charToGsm.put('m', i++); 716 charToGsm.put('n', i++); 717 charToGsm.put('o', i++); 718 719 charToGsm.put('p', i++); 720 charToGsm.put('q', i++); 721 charToGsm.put('r', i++); 722 charToGsm.put('s', i++); 723 charToGsm.put('t', i++); 724 charToGsm.put('u', i++); 725 charToGsm.put('v', i++); 726 charToGsm.put('w', i++); 727 charToGsm.put('x', i++); 728 charToGsm.put('y', i++); 729 charToGsm.put('z', i++); 730 charToGsm.put('\u00e4', i++); 731 charToGsm.put('\u00f6', i++); 732 charToGsm.put('\u00f1', i++); 733 charToGsm.put('\u00fc', i++); 734 charToGsm.put('\u00e0', i++); 735 736 737 charToGsmExtended.put('\f', 10); 738 charToGsmExtended.put('^', 20); 739 charToGsmExtended.put('{', 40); 740 charToGsmExtended.put('}', 41); 741 charToGsmExtended.put('\\', 47); 742 charToGsmExtended.put('[', 60); 743 charToGsmExtended.put('~', 61); 744 charToGsmExtended.put(']', 62); 745 charToGsmExtended.put('|', 64); 746 charToGsmExtended.put('\u20ac', 101); 747 748 int size = charToGsm.size(); 749 for (int j=0; j<size; j++) { 750 gsmToChar.put(charToGsm.valueAt(j), charToGsm.keyAt(j)); 751 } 752 753 size = charToGsmExtended.size(); 754 for (int j=0; j<size; j++) { 755 gsmExtendedToChar.put(charToGsmExtended.valueAt(j), charToGsmExtended.keyAt(j)); 756 } 757 758 759 sGsmSpaceChar = charToGsm.get(' '); 760 } 761 762 763} 764