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