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.content.res.Resources; 20import android.content.res.Resources.NotFoundException; 21import android.graphics.Bitmap; 22import android.graphics.Color; 23import android.telephony.Rlog; 24 25import com.android.internal.telephony.GsmAlphabet; 26import java.io.UnsupportedEncodingException; 27 28/** 29 * Various methods, useful for dealing with SIM data. 30 */ 31public class IccUtils { 32 static final String LOG_TAG="IccUtils"; 33 34 /** 35 * Many fields in GSM SIM's are stored as nibble-swizzled BCD 36 * 37 * Assumes left-justified field that may be padded right with 0xf 38 * values. 39 * 40 * Stops on invalid BCD value, returning string so far 41 */ 42 public static String 43 bcdToString(byte[] data, int offset, int length) { 44 StringBuilder ret = new StringBuilder(length*2); 45 46 for (int i = offset ; i < offset + length ; i++) { 47 byte b; 48 int v; 49 50 v = data[i] & 0xf; 51 if (v > 9) break; 52 ret.append((char)('0' + v)); 53 54 v = (data[i] >> 4) & 0xf; 55 // Some PLMNs have 'f' as high nibble, ignore it 56 if (v == 0xf) continue; 57 if (v > 9) break; 58 ret.append((char)('0' + v)); 59 } 60 61 return ret.toString(); 62 } 63 64 /** 65 * Decode cdma byte into String. 66 */ 67 public static String 68 cdmaBcdToString(byte[] data, int offset, int length) { 69 StringBuilder ret = new StringBuilder(length); 70 71 int count = 0; 72 for (int i = offset; count < length; i++) { 73 int v; 74 v = data[i] & 0xf; 75 if (v > 9) v = 0; 76 ret.append((char)('0' + v)); 77 78 if (++count == length) break; 79 80 v = (data[i] >> 4) & 0xf; 81 if (v > 9) v = 0; 82 ret.append((char)('0' + v)); 83 ++count; 84 } 85 return ret.toString(); 86 } 87 88 /** 89 * Decodes a GSM-style BCD byte, returning an int ranging from 0-99. 90 * 91 * In GSM land, the least significant BCD digit is stored in the most 92 * significant nibble. 93 * 94 * Out-of-range digits are treated as 0 for the sake of the time stamp, 95 * because of this: 96 * 97 * TS 23.040 section 9.2.3.11 98 * "if the MS receives a non-integer value in the SCTS, it shall 99 * assume the digit is set to 0 but shall store the entire field 100 * exactly as received" 101 */ 102 public static int 103 gsmBcdByteToInt(byte b) { 104 int ret = 0; 105 106 // treat out-of-range BCD values as 0 107 if ((b & 0xf0) <= 0x90) { 108 ret = (b >> 4) & 0xf; 109 } 110 111 if ((b & 0x0f) <= 0x09) { 112 ret += (b & 0xf) * 10; 113 } 114 115 return ret; 116 } 117 118 /** 119 * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but 120 * opposite nibble format. The least significant BCD digit 121 * is in the least significant nibble and the most significant 122 * is in the most significant nibble. 123 */ 124 public static int 125 cdmaBcdByteToInt(byte b) { 126 int ret = 0; 127 128 // treat out-of-range BCD values as 0 129 if ((b & 0xf0) <= 0x90) { 130 ret = ((b >> 4) & 0xf) * 10; 131 } 132 133 if ((b & 0x0f) <= 0x09) { 134 ret += (b & 0xf); 135 } 136 137 return ret; 138 } 139 140 /** 141 * Decodes a string field that's formatted like the EF[ADN] alpha 142 * identifier 143 * 144 * From TS 51.011 10.5.1: 145 * Coding: 146 * this alpha tagging shall use either 147 * - the SMS default 7 bit coded alphabet as defined in 148 * TS 23.038 [12] with bit 8 set to 0. The alpha identifier 149 * shall be left justified. Unused bytes shall be set to 'FF'; or 150 * - one of the UCS2 coded options as defined in annex B. 151 * 152 * Annex B from TS 11.11 V8.13.0: 153 * 1) If the first octet in the alpha string is '80', then the 154 * remaining octets are 16 bit UCS2 characters ... 155 * 2) if the first octet in the alpha string is '81', then the 156 * second octet contains a value indicating the number of 157 * characters in the string, and the third octet contains an 158 * 8 bit number which defines bits 15 to 8 of a 16 bit 159 * base pointer, where bit 16 is set to zero and bits 7 to 1 160 * are also set to zero. These sixteen bits constitute a 161 * base pointer to a "half page" in the UCS2 code space, to be 162 * used with some or all of the remaining octets in the string. 163 * The fourth and subsequent octets contain codings as follows: 164 * If bit 8 of the octet is set to zero, the remaining 7 bits 165 * of the octet contain a GSM Default Alphabet character, 166 * whereas if bit 8 of the octet is set to one, then the 167 * remaining seven bits are an offset value added to the 168 * 16 bit base pointer defined earlier... 169 * 3) If the first octet of the alpha string is set to '82', then 170 * the second octet contains a value indicating the number of 171 * characters in the string, and the third and fourth octets 172 * contain a 16 bit number which defines the complete 16 bit 173 * base pointer to a "half page" in the UCS2 code space... 174 */ 175 public static String 176 adnStringFieldToString(byte[] data, int offset, int length) { 177 if (length == 0) { 178 return ""; 179 } 180 if (length >= 1) { 181 if (data[offset] == (byte) 0x80) { 182 int ucslen = (length - 1) / 2; 183 String ret = null; 184 185 try { 186 ret = new String(data, offset + 1, ucslen * 2, "utf-16be"); 187 } catch (UnsupportedEncodingException ex) { 188 Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", 189 ex); 190 } 191 192 if (ret != null) { 193 // trim off trailing FFFF characters 194 195 ucslen = ret.length(); 196 while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF') 197 ucslen--; 198 199 return ret.substring(0, ucslen); 200 } 201 } 202 } 203 204 boolean isucs2 = false; 205 char base = '\0'; 206 int len = 0; 207 208 if (length >= 3 && data[offset] == (byte) 0x81) { 209 len = data[offset + 1] & 0xFF; 210 if (len > length - 3) 211 len = length - 3; 212 213 base = (char) ((data[offset + 2] & 0xFF) << 7); 214 offset += 3; 215 isucs2 = true; 216 } else if (length >= 4 && data[offset] == (byte) 0x82) { 217 len = data[offset + 1] & 0xFF; 218 if (len > length - 4) 219 len = length - 4; 220 221 base = (char) (((data[offset + 2] & 0xFF) << 8) | 222 (data[offset + 3] & 0xFF)); 223 offset += 4; 224 isucs2 = true; 225 } 226 227 if (isucs2) { 228 StringBuilder ret = new StringBuilder(); 229 230 while (len > 0) { 231 // UCS2 subset case 232 233 if (data[offset] < 0) { 234 ret.append((char) (base + (data[offset] & 0x7F))); 235 offset++; 236 len--; 237 } 238 239 // GSM character set case 240 241 int count = 0; 242 while (count < len && data[offset + count] >= 0) 243 count++; 244 245 ret.append(GsmAlphabet.gsm8BitUnpackedToString(data, 246 offset, count)); 247 248 offset += count; 249 len -= count; 250 } 251 252 return ret.toString(); 253 } 254 255 Resources resource = Resources.getSystem(); 256 String defaultCharset = ""; 257 try { 258 defaultCharset = 259 resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset); 260 } catch (NotFoundException e) { 261 // Ignore Exception and defaultCharset is set to a empty string. 262 } 263 return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim()); 264 } 265 266 static int 267 hexCharToInt(char c) { 268 if (c >= '0' && c <= '9') return (c - '0'); 269 if (c >= 'A' && c <= 'F') return (c - 'A' + 10); 270 if (c >= 'a' && c <= 'f') return (c - 'a' + 10); 271 272 throw new RuntimeException ("invalid hex char '" + c + "'"); 273 } 274 275 /** 276 * Converts a hex String to a byte array. 277 * 278 * @param s A string of hexadecimal characters, must be an even number of 279 * chars long 280 * 281 * @return byte array representation 282 * 283 * @throws RuntimeException on invalid format 284 */ 285 public static byte[] 286 hexStringToBytes(String s) { 287 byte[] ret; 288 289 if (s == null) return null; 290 291 int sz = s.length(); 292 293 ret = new byte[sz/2]; 294 295 for (int i=0 ; i <sz ; i+=2) { 296 ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4) 297 | hexCharToInt(s.charAt(i+1))); 298 } 299 300 return ret; 301 } 302 303 304 /** 305 * Converts a byte array into a String of hexadecimal characters. 306 * 307 * @param bytes an array of bytes 308 * 309 * @return hex string representation of bytes array 310 */ 311 public static String 312 bytesToHexString(byte[] bytes) { 313 if (bytes == null) return null; 314 315 StringBuilder ret = new StringBuilder(2*bytes.length); 316 317 for (int i = 0 ; i < bytes.length ; i++) { 318 int b; 319 320 b = 0x0f & (bytes[i] >> 4); 321 322 ret.append("0123456789abcdef".charAt(b)); 323 324 b = 0x0f & bytes[i]; 325 326 ret.append("0123456789abcdef".charAt(b)); 327 } 328 329 return ret.toString(); 330 } 331 332 333 /** 334 * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string 335 * "offset" points to "octet 3", the coding scheme byte 336 * empty string returned on decode error 337 */ 338 public static String 339 networkNameToString(byte[] data, int offset, int length) { 340 String ret; 341 342 if ((data[offset] & 0x80) != 0x80 || length < 1) { 343 return ""; 344 } 345 346 switch ((data[offset] >>> 4) & 0x7) { 347 case 0: 348 // SMS character set 349 int countSeptets; 350 int unusedBits = data[offset] & 7; 351 countSeptets = (((length - 1) * 8) - unusedBits) / 7 ; 352 ret = GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets); 353 break; 354 case 1: 355 // UCS2 356 try { 357 ret = new String(data, 358 offset + 1, length - 1, "utf-16"); 359 } catch (UnsupportedEncodingException ex) { 360 ret = ""; 361 Rlog.e(LOG_TAG,"implausible UnsupportedEncodingException", ex); 362 } 363 break; 364 365 // unsupported encoding 366 default: 367 ret = ""; 368 break; 369 } 370 371 // "Add CI" 372 // "The MS should add the letters for the Country's Initials and 373 // a separator (e.g. a space) to the text string" 374 375 if ((data[offset] & 0x40) != 0) { 376 // FIXME(mkf) add country initials here 377 378 } 379 380 return ret; 381 } 382 383 /** 384 * Convert a TS 131.102 image instance of code scheme '11' into Bitmap 385 * @param data The raw data 386 * @param length The length of image body 387 * @return The bitmap 388 */ 389 public static Bitmap parseToBnW(byte[] data, int length){ 390 int valueIndex = 0; 391 int width = data[valueIndex++] & 0xFF; 392 int height = data[valueIndex++] & 0xFF; 393 int numOfPixels = width*height; 394 395 int[] pixels = new int[numOfPixels]; 396 397 int pixelIndex = 0; 398 int bitIndex = 7; 399 byte currentByte = 0x00; 400 while (pixelIndex < numOfPixels) { 401 // reassign data and index for every byte (8 bits). 402 if (pixelIndex % 8 == 0) { 403 currentByte = data[valueIndex++]; 404 bitIndex = 7; 405 } 406 pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01); 407 } 408 409 if (pixelIndex != numOfPixels) { 410 Rlog.e(LOG_TAG, "parse end and size error"); 411 } 412 return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); 413 } 414 415 private static int bitToRGB(int bit){ 416 if(bit == 1){ 417 return Color.WHITE; 418 } else { 419 return Color.BLACK; 420 } 421 } 422 423 /** 424 * a TS 131.102 image instance of code scheme '11' into color Bitmap 425 * 426 * @param data The raw data 427 * @param length the length of image body 428 * @param transparency with or without transparency 429 * @return The color bitmap 430 */ 431 public static Bitmap parseToRGB(byte[] data, int length, 432 boolean transparency) { 433 int valueIndex = 0; 434 int width = data[valueIndex++] & 0xFF; 435 int height = data[valueIndex++] & 0xFF; 436 int bits = data[valueIndex++] & 0xFF; 437 int colorNumber = data[valueIndex++] & 0xFF; 438 int clutOffset = ((data[valueIndex++] & 0xFF) << 8) 439 | (data[valueIndex++] & 0xFF); 440 441 int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber); 442 if (true == transparency) { 443 colorIndexArray[colorNumber - 1] = Color.TRANSPARENT; 444 } 445 446 int[] resultArray = null; 447 if (0 == (8 % bits)) { 448 resultArray = mapTo2OrderBitColor(data, valueIndex, 449 (width * height), colorIndexArray, bits); 450 } else { 451 resultArray = mapToNon2OrderBitColor(data, valueIndex, 452 (width * height), colorIndexArray, bits); 453 } 454 455 return Bitmap.createBitmap(resultArray, width, height, 456 Bitmap.Config.RGB_565); 457 } 458 459 private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex, 460 int length, int[] colorArray, int bits) { 461 if (0 != (8 % bits)) { 462 Rlog.e(LOG_TAG, "not event number of color"); 463 return mapToNon2OrderBitColor(data, valueIndex, length, colorArray, 464 bits); 465 } 466 467 int mask = 0x01; 468 switch (bits) { 469 case 1: 470 mask = 0x01; 471 break; 472 case 2: 473 mask = 0x03; 474 break; 475 case 4: 476 mask = 0x0F; 477 break; 478 case 8: 479 mask = 0xFF; 480 break; 481 } 482 483 int[] resultArray = new int[length]; 484 int resultIndex = 0; 485 int run = 8 / bits; 486 while (resultIndex < length) { 487 byte tempByte = data[valueIndex++]; 488 for (int runIndex = 0; runIndex < run; ++runIndex) { 489 int offset = run - runIndex - 1; 490 resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits)) 491 & mask]; 492 } 493 } 494 return resultArray; 495 } 496 497 private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex, 498 int length, int[] colorArray, int bits) { 499 if (0 == (8 % bits)) { 500 Rlog.e(LOG_TAG, "not odd number of color"); 501 return mapTo2OrderBitColor(data, valueIndex, length, colorArray, 502 bits); 503 } 504 505 int[] resultArray = new int[length]; 506 // TODO fix me: 507 return resultArray; 508 } 509 510 private static int[] getCLUT(byte[] rawData, int offset, int number) { 511 if (null == rawData) { 512 return null; 513 } 514 515 int[] result = new int[number]; 516 int endIndex = offset + (number * 3); // 1 color use 3 bytes 517 int valueIndex = offset; 518 int colorIndex = 0; 519 int alpha = 0xff << 24; 520 do { 521 result[colorIndex++] = alpha 522 | ((rawData[valueIndex++] & 0xFF) << 16) 523 | ((rawData[valueIndex++] & 0xFF) << 8) 524 | ((rawData[valueIndex++] & 0xFF)); 525 } while (valueIndex < endIndex); 526 return result; 527 } 528} 529