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.uicc; 18 19 import android.content.res.Resources; 20 import android.content.res.Resources.NotFoundException; 21 import android.graphics.Bitmap; 22 import android.graphics.Color; 23 import android.telephony.Rlog; 24 25 import com.android.internal.telephony.GsmAlphabet; 26 27 import java.io.UnsupportedEncodingException; 28 29 /** 30 * Various methods, useful for dealing with SIM data. 31 */ 32 public class IccUtils { 33 static final String LOG_TAG="IccUtils"; 34 35 // A table mapping from a number to a hex character for fast encoding hex strings. 36 private static final char[] HEX_CHARS = { 37 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 38 }; 39 40 41 /** 42 * Many fields in GSM SIM's are stored as nibble-swizzled BCD 43 * 44 * Assumes left-justified field that may be padded right with 0xf 45 * values. 46 * 47 * Stops on invalid BCD value, returning string so far 48 */ 49 public static String bcdToString(byte[] data, int offset, int length)50 bcdToString(byte[] data, int offset, int length) { 51 StringBuilder ret = new StringBuilder(length*2); 52 53 for (int i = offset ; i < offset + length ; i++) { 54 int v; 55 56 v = data[i] & 0xf; 57 if (v > 9) break; 58 ret.append((char)('0' + v)); 59 60 v = (data[i] >> 4) & 0xf; 61 // Some PLMNs have 'f' as high nibble, ignore it 62 if (v == 0xf) continue; 63 if (v > 9) break; 64 ret.append((char)('0' + v)); 65 } 66 67 return ret.toString(); 68 } 69 70 /** 71 * Converts a bcd byte array to String with offset 0 and byte array length. 72 */ bcdToString(byte[] data)73 public static String bcdToString(byte[] data) { 74 return bcdToString(data, 0, data.length); 75 } 76 77 /** 78 * Converts BCD string to bytes. 79 * 80 * @param bcd This should have an even length. If not, an "0" will be appended to the string. 81 */ bcdToBytes(String bcd)82 public static byte[] bcdToBytes(String bcd) { 83 byte[] output = new byte[(bcd.length() + 1) / 2]; 84 bcdToBytes(bcd, output); 85 return output; 86 } 87 88 /** 89 * Converts BCD string to bytes and put it into the given byte array. 90 * 91 * @param bcd This should have an even length. If not, an "0" will be appended to the string. 92 * @param bytes If the array size is less than needed, the rest of the BCD string isn't be 93 * converted. If the array size is more than needed, the rest of array remains unchanged. 94 */ bcdToBytes(String bcd, byte[] bytes)95 public static void bcdToBytes(String bcd, byte[] bytes) { 96 if (bcd.length() % 2 != 0) { 97 bcd += "0"; 98 } 99 int size = Math.min(bytes.length * 2, bcd.length()); 100 for (int i = 0, j = 0; i + 1 < size; i += 2, j++) { 101 bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i))); 102 } 103 } 104 105 /** 106 * PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3 107 * Returns a concatenated string of MCC+MNC, stripping 108 * all invalid character 'f' 109 */ bcdPlmnToString(byte[] data, int offset)110 public static String bcdPlmnToString(byte[] data, int offset) { 111 if (offset + 3 > data.length) { 112 return null; 113 } 114 byte[] trans = new byte[3]; 115 trans[0] = (byte) ((data[0 + offset] << 4) | ((data[0 + offset] >> 4) & 0xF)); 116 trans[1] = (byte) ((data[1 + offset] << 4) | (data[2 + offset] & 0xF)); 117 trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF)); 118 String ret = bytesToHexString(trans); 119 120 // For a valid plmn we trim all character 'f' 121 if (ret.contains("f")) { 122 ret = ret.replaceAll("f", ""); 123 } 124 return ret; 125 } 126 127 /** 128 * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH 129 */ 130 public static String bchToString(byte[] data, int offset, int length)131 bchToString(byte[] data, int offset, int length) { 132 StringBuilder ret = new StringBuilder(length*2); 133 134 for (int i = offset ; i < offset + length ; i++) { 135 int v; 136 137 v = data[i] & 0xf; 138 ret.append(HEX_CHARS[v]); 139 140 v = (data[i] >> 4) & 0xf; 141 ret.append(HEX_CHARS[v]); 142 } 143 144 return ret.toString(); 145 } 146 147 /** 148 * Decode cdma byte into String. 149 */ 150 public static String cdmaBcdToString(byte[] data, int offset, int length)151 cdmaBcdToString(byte[] data, int offset, int length) { 152 StringBuilder ret = new StringBuilder(length); 153 154 int count = 0; 155 for (int i = offset; count < length; i++) { 156 int v; 157 v = data[i] & 0xf; 158 if (v > 9) v = 0; 159 ret.append((char)('0' + v)); 160 161 if (++count == length) break; 162 163 v = (data[i] >> 4) & 0xf; 164 if (v > 9) v = 0; 165 ret.append((char)('0' + v)); 166 ++count; 167 } 168 return ret.toString(); 169 } 170 171 /** 172 * Decodes a GSM-style BCD byte, returning an int ranging from 0-99. 173 * 174 * In GSM land, the least significant BCD digit is stored in the most 175 * significant nibble. 176 * 177 * Out-of-range digits are treated as 0 for the sake of the time stamp, 178 * because of this: 179 * 180 * TS 23.040 section 9.2.3.11 181 * "if the MS receives a non-integer value in the SCTS, it shall 182 * assume the digit is set to 0 but shall store the entire field 183 * exactly as received" 184 */ 185 public static int gsmBcdByteToInt(byte b)186 gsmBcdByteToInt(byte b) { 187 int ret = 0; 188 189 // treat out-of-range BCD values as 0 190 if ((b & 0xf0) <= 0x90) { 191 ret = (b >> 4) & 0xf; 192 } 193 194 if ((b & 0x0f) <= 0x09) { 195 ret += (b & 0xf) * 10; 196 } 197 198 return ret; 199 } 200 201 /** 202 * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but 203 * opposite nibble format. The least significant BCD digit 204 * is in the least significant nibble and the most significant 205 * is in the most significant nibble. 206 */ 207 public static int cdmaBcdByteToInt(byte b)208 cdmaBcdByteToInt(byte b) { 209 int ret = 0; 210 211 // treat out-of-range BCD values as 0 212 if ((b & 0xf0) <= 0x90) { 213 ret = ((b >> 4) & 0xf) * 10; 214 } 215 216 if ((b & 0x0f) <= 0x09) { 217 ret += (b & 0xf); 218 } 219 220 return ret; 221 } 222 223 /** 224 * Decodes a string field that's formatted like the EF[ADN] alpha 225 * identifier 226 * 227 * From TS 51.011 10.5.1: 228 * Coding: 229 * this alpha tagging shall use either 230 * - the SMS default 7 bit coded alphabet as defined in 231 * TS 23.038 [12] with bit 8 set to 0. The alpha identifier 232 * shall be left justified. Unused bytes shall be set to 'FF'; or 233 * - one of the UCS2 coded options as defined in annex B. 234 * 235 * Annex B from TS 11.11 V8.13.0: 236 * 1) If the first octet in the alpha string is '80', then the 237 * remaining octets are 16 bit UCS2 characters ... 238 * 2) if the first octet in the alpha string is '81', then the 239 * second octet contains a value indicating the number of 240 * characters in the string, and the third octet contains an 241 * 8 bit number which defines bits 15 to 8 of a 16 bit 242 * base pointer, where bit 16 is set to zero and bits 7 to 1 243 * are also set to zero. These sixteen bits constitute a 244 * base pointer to a "half page" in the UCS2 code space, to be 245 * used with some or all of the remaining octets in the string. 246 * The fourth and subsequent octets contain codings as follows: 247 * If bit 8 of the octet is set to zero, the remaining 7 bits 248 * of the octet contain a GSM Default Alphabet character, 249 * whereas if bit 8 of the octet is set to one, then the 250 * remaining seven bits are an offset value added to the 251 * 16 bit base pointer defined earlier... 252 * 3) If the first octet of the alpha string is set to '82', then 253 * the second octet contains a value indicating the number of 254 * characters in the string, and the third and fourth octets 255 * contain a 16 bit number which defines the complete 16 bit 256 * base pointer to a "half page" in the UCS2 code space... 257 */ 258 public static String adnStringFieldToString(byte[] data, int offset, int length)259 adnStringFieldToString(byte[] data, int offset, int length) { 260 if (length == 0) { 261 return ""; 262 } 263 if (length >= 1) { 264 if (data[offset] == (byte) 0x80) { 265 int ucslen = (length - 1) / 2; 266 String ret = null; 267 268 try { 269 ret = new String(data, offset + 1, ucslen * 2, "utf-16be"); 270 } catch (UnsupportedEncodingException ex) { 271 Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", 272 ex); 273 } 274 275 if (ret != null) { 276 // trim off trailing FFFF characters 277 278 ucslen = ret.length(); 279 while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF') 280 ucslen--; 281 282 return ret.substring(0, ucslen); 283 } 284 } 285 } 286 287 boolean isucs2 = false; 288 char base = '\0'; 289 int len = 0; 290 291 if (length >= 3 && data[offset] == (byte) 0x81) { 292 len = data[offset + 1] & 0xFF; 293 if (len > length - 3) 294 len = length - 3; 295 296 base = (char) ((data[offset + 2] & 0xFF) << 7); 297 offset += 3; 298 isucs2 = true; 299 } else if (length >= 4 && data[offset] == (byte) 0x82) { 300 len = data[offset + 1] & 0xFF; 301 if (len > length - 4) 302 len = length - 4; 303 304 base = (char) (((data[offset + 2] & 0xFF) << 8) | 305 (data[offset + 3] & 0xFF)); 306 offset += 4; 307 isucs2 = true; 308 } 309 310 if (isucs2) { 311 StringBuilder ret = new StringBuilder(); 312 313 while (len > 0) { 314 // UCS2 subset case 315 316 if (data[offset] < 0) { 317 ret.append((char) (base + (data[offset] & 0x7F))); 318 offset++; 319 len--; 320 } 321 322 // GSM character set case 323 324 int count = 0; 325 while (count < len && data[offset + count] >= 0) 326 count++; 327 328 ret.append(GsmAlphabet.gsm8BitUnpackedToString(data, 329 offset, count)); 330 331 offset += count; 332 len -= count; 333 } 334 335 return ret.toString(); 336 } 337 338 Resources resource = Resources.getSystem(); 339 String defaultCharset = ""; 340 try { 341 defaultCharset = 342 resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset); 343 } catch (NotFoundException e) { 344 // Ignore Exception and defaultCharset is set to a empty string. 345 } 346 return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim()); 347 } 348 349 public static int hexCharToInt(char c)350 hexCharToInt(char c) { 351 if (c >= '0' && c <= '9') return (c - '0'); 352 if (c >= 'A' && c <= 'F') return (c - 'A' + 10); 353 if (c >= 'a' && c <= 'f') return (c - 'a' + 10); 354 355 throw new RuntimeException ("invalid hex char '" + c + "'"); 356 } 357 358 /** 359 * Converts a hex String to a byte array. 360 * 361 * @param s A string of hexadecimal characters, must be an even number of 362 * chars long 363 * 364 * @return byte array representation 365 * 366 * @throws RuntimeException on invalid format 367 */ 368 public static byte[] hexStringToBytes(String s)369 hexStringToBytes(String s) { 370 byte[] ret; 371 372 if (s == null) return null; 373 374 int sz = s.length(); 375 376 ret = new byte[sz/2]; 377 378 for (int i=0 ; i <sz ; i+=2) { 379 ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4) 380 | hexCharToInt(s.charAt(i+1))); 381 } 382 383 return ret; 384 } 385 386 387 /** 388 * Converts a byte array into a String of hexadecimal characters. 389 * 390 * @param bytes an array of bytes 391 * 392 * @return hex string representation of bytes array 393 */ 394 public static String bytesToHexString(byte[] bytes)395 bytesToHexString(byte[] bytes) { 396 if (bytes == null) return null; 397 398 StringBuilder ret = new StringBuilder(2*bytes.length); 399 400 for (int i = 0 ; i < bytes.length ; i++) { 401 int b; 402 403 b = 0x0f & (bytes[i] >> 4); 404 405 ret.append(HEX_CHARS[b]); 406 407 b = 0x0f & bytes[i]; 408 409 ret.append(HEX_CHARS[b]); 410 } 411 412 return ret.toString(); 413 } 414 415 416 /** 417 * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string 418 * "offset" points to "octet 3", the coding scheme byte 419 * empty string returned on decode error 420 */ 421 public static String networkNameToString(byte[] data, int offset, int length)422 networkNameToString(byte[] data, int offset, int length) { 423 String ret; 424 425 if ((data[offset] & 0x80) != 0x80 || length < 1) { 426 return ""; 427 } 428 429 switch ((data[offset] >>> 4) & 0x7) { 430 case 0: 431 // SMS character set 432 int countSeptets; 433 int unusedBits = data[offset] & 7; 434 countSeptets = (((length - 1) * 8) - unusedBits) / 7 ; 435 ret = GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets); 436 break; 437 case 1: 438 // UCS2 439 try { 440 ret = new String(data, 441 offset + 1, length - 1, "utf-16"); 442 } catch (UnsupportedEncodingException ex) { 443 ret = ""; 444 Rlog.e(LOG_TAG,"implausible UnsupportedEncodingException", ex); 445 } 446 break; 447 448 // unsupported encoding 449 default: 450 ret = ""; 451 break; 452 } 453 454 // "Add CI" 455 // "The MS should add the letters for the Country's Initials and 456 // a separator (e.g. a space) to the text string" 457 458 if ((data[offset] & 0x40) != 0) { 459 // FIXME(mkf) add country initials here 460 } 461 462 return ret; 463 } 464 465 /** 466 * Convert a TS 131.102 image instance of code scheme '11' into Bitmap 467 * @param data The raw data 468 * @param length The length of image body 469 * @return The bitmap 470 */ parseToBnW(byte[] data, int length)471 public static Bitmap parseToBnW(byte[] data, int length){ 472 int valueIndex = 0; 473 int width = data[valueIndex++] & 0xFF; 474 int height = data[valueIndex++] & 0xFF; 475 int numOfPixels = width*height; 476 477 int[] pixels = new int[numOfPixels]; 478 479 int pixelIndex = 0; 480 int bitIndex = 7; 481 byte currentByte = 0x00; 482 while (pixelIndex < numOfPixels) { 483 // reassign data and index for every byte (8 bits). 484 if (pixelIndex % 8 == 0) { 485 currentByte = data[valueIndex++]; 486 bitIndex = 7; 487 } 488 pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01); 489 } 490 491 if (pixelIndex != numOfPixels) { 492 Rlog.e(LOG_TAG, "parse end and size error"); 493 } 494 return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); 495 } 496 bitToRGB(int bit)497 private static int bitToRGB(int bit){ 498 if(bit == 1){ 499 return Color.WHITE; 500 } else { 501 return Color.BLACK; 502 } 503 } 504 505 /** 506 * a TS 131.102 image instance of code scheme '11' into color Bitmap 507 * 508 * @param data The raw data 509 * @param length the length of image body 510 * @param transparency with or without transparency 511 * @return The color bitmap 512 */ parseToRGB(byte[] data, int length, boolean transparency)513 public static Bitmap parseToRGB(byte[] data, int length, 514 boolean transparency) { 515 int valueIndex = 0; 516 int width = data[valueIndex++] & 0xFF; 517 int height = data[valueIndex++] & 0xFF; 518 int bits = data[valueIndex++] & 0xFF; 519 int colorNumber = data[valueIndex++] & 0xFF; 520 int clutOffset = ((data[valueIndex++] & 0xFF) << 8) 521 | (data[valueIndex++] & 0xFF); 522 523 int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber); 524 if (true == transparency) { 525 colorIndexArray[colorNumber - 1] = Color.TRANSPARENT; 526 } 527 528 int[] resultArray = null; 529 if (0 == (8 % bits)) { 530 resultArray = mapTo2OrderBitColor(data, valueIndex, 531 (width * height), colorIndexArray, bits); 532 } else { 533 resultArray = mapToNon2OrderBitColor(data, valueIndex, 534 (width * height), colorIndexArray, bits); 535 } 536 537 return Bitmap.createBitmap(resultArray, width, height, 538 Bitmap.Config.RGB_565); 539 } 540 mapTo2OrderBitColor(byte[] data, int valueIndex, int length, int[] colorArray, int bits)541 private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex, 542 int length, int[] colorArray, int bits) { 543 if (0 != (8 % bits)) { 544 Rlog.e(LOG_TAG, "not event number of color"); 545 return mapToNon2OrderBitColor(data, valueIndex, length, colorArray, 546 bits); 547 } 548 549 int mask = 0x01; 550 switch (bits) { 551 case 1: 552 mask = 0x01; 553 break; 554 case 2: 555 mask = 0x03; 556 break; 557 case 4: 558 mask = 0x0F; 559 break; 560 case 8: 561 mask = 0xFF; 562 break; 563 } 564 565 int[] resultArray = new int[length]; 566 int resultIndex = 0; 567 int run = 8 / bits; 568 while (resultIndex < length) { 569 byte tempByte = data[valueIndex++]; 570 for (int runIndex = 0; runIndex < run; ++runIndex) { 571 int offset = run - runIndex - 1; 572 resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits)) 573 & mask]; 574 } 575 } 576 return resultArray; 577 } 578 mapToNon2OrderBitColor(byte[] data, int valueIndex, int length, int[] colorArray, int bits)579 private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex, 580 int length, int[] colorArray, int bits) { 581 if (0 == (8 % bits)) { 582 Rlog.e(LOG_TAG, "not odd number of color"); 583 return mapTo2OrderBitColor(data, valueIndex, length, colorArray, 584 bits); 585 } 586 587 int[] resultArray = new int[length]; 588 // TODO fix me: 589 return resultArray; 590 } 591 getCLUT(byte[] rawData, int offset, int number)592 private static int[] getCLUT(byte[] rawData, int offset, int number) { 593 if (null == rawData) { 594 return null; 595 } 596 597 int[] result = new int[number]; 598 int endIndex = offset + (number * 3); // 1 color use 3 bytes 599 int valueIndex = offset; 600 int colorIndex = 0; 601 int alpha = 0xff << 24; 602 do { 603 result[colorIndex++] = alpha 604 | ((rawData[valueIndex++] & 0xFF) << 16) 605 | ((rawData[valueIndex++] & 0xFF) << 8) 606 | ((rawData[valueIndex++] & 0xFF)); 607 } while (valueIndex < endIndex); 608 return result; 609 } 610 getDecimalSubstring(String iccId)611 public static String getDecimalSubstring(String iccId) { 612 int position; 613 for (position = 0; position < iccId.length(); position ++) { 614 if (!Character.isDigit(iccId.charAt(position))) break; 615 } 616 return iccId.substring( 0, position ); 617 } 618 619 /** 620 * Converts a series of bytes to an integer. This method currently only supports positive 32-bit 621 * integers. 622 * 623 * @param src The source bytes. 624 * @param offset The position of the first byte of the data to be converted. The data is base 625 * 256 with the most significant digit first. 626 * @param length The length of the data to be converted. It must be <= 4. 627 * @throws IllegalArgumentException If {@code length} is bigger than 4 or {@code src} cannot be 628 * parsed as a positive integer. 629 * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length} 630 * exceeds the bounds of {@code src}. 631 */ bytesToInt(byte[] src, int offset, int length)632 public static int bytesToInt(byte[] src, int offset, int length) { 633 if (length > 4) { 634 throw new IllegalArgumentException( 635 "length must be <= 4 (only 32-bit integer supported): " + length); 636 } 637 if (offset < 0 || length < 0 || offset + length > src.length) { 638 throw new IndexOutOfBoundsException( 639 "Out of the bounds: src=[" 640 + src.length 641 + "], offset=" 642 + offset 643 + ", length=" 644 + length); 645 } 646 int result = 0; 647 for (int i = 0; i < length; i++) { 648 result = (result << 8) | (src[offset + i] & 0xFF); 649 } 650 if (result < 0) { 651 throw new IllegalArgumentException( 652 "src cannot be parsed as a positive integer: " + result); 653 } 654 return result; 655 } 656 657 /** 658 * Converts a series of bytes to a raw long variable which can be both positive and negative. 659 * This method currently only supports 64-bit long variable. 660 * 661 * @param src The source bytes. 662 * @param offset The position of the first byte of the data to be converted. The data is base 663 * 256 with the most significant digit first. 664 * @param length The length of the data to be converted. It must be <= 8. 665 * @throws IllegalArgumentException If {@code length} is bigger than 8. 666 * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length} 667 * exceeds the bounds of {@code src}. 668 */ bytesToRawLong(byte[] src, int offset, int length)669 public static long bytesToRawLong(byte[] src, int offset, int length) { 670 if (length > 8) { 671 throw new IllegalArgumentException( 672 "length must be <= 8 (only 64-bit long supported): " + length); 673 } 674 if (offset < 0 || length < 0 || offset + length > src.length) { 675 throw new IndexOutOfBoundsException( 676 "Out of the bounds: src=[" 677 + src.length 678 + "], offset=" 679 + offset 680 + ", length=" 681 + length); 682 } 683 long result = 0; 684 for (int i = 0; i < length; i++) { 685 result = (result << 8) | (src[offset + i] & 0xFF); 686 } 687 return result; 688 } 689 690 /** 691 * Converts an integer to a new byte array with base 256 and the most significant digit first. 692 * 693 * @throws IllegalArgumentException If {@code value} is negative. 694 */ unsignedIntToBytes(int value)695 public static byte[] unsignedIntToBytes(int value) { 696 if (value < 0) { 697 throw new IllegalArgumentException("value must be 0 or positive: " + value); 698 } 699 byte[] bytes = new byte[byteNumForUnsignedInt(value)]; 700 unsignedIntToBytes(value, bytes, 0); 701 return bytes; 702 } 703 704 /** 705 * Converts an integer to a new byte array with base 256 and the most significant digit first. 706 * The first byte's highest bit is used for sign. If the most significant digit is larger than 707 * 127, an extra byte (0) will be prepended before it. This method currently doesn't support 708 * negative values. 709 * 710 * @throws IllegalArgumentException If {@code value} is negative. 711 */ signedIntToBytes(int value)712 public static byte[] signedIntToBytes(int value) { 713 if (value < 0) { 714 throw new IllegalArgumentException("value must be 0 or positive: " + value); 715 } 716 byte[] bytes = new byte[byteNumForSignedInt(value)]; 717 signedIntToBytes(value, bytes, 0); 718 return bytes; 719 } 720 721 /** 722 * Converts an integer to a series of bytes with base 256 and the most significant digit first. 723 * 724 * @param value The integer to be converted. 725 * @param dest The destination byte array. 726 * @param offset The start offset of the byte array. 727 * @return The number of byte needeed. 728 * @throws IllegalArgumentException If {@code value} is negative. 729 * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}. 730 */ unsignedIntToBytes(int value, byte[] dest, int offset)731 public static int unsignedIntToBytes(int value, byte[] dest, int offset) { 732 return intToBytes(value, dest, offset, false); 733 } 734 735 /** 736 * Converts an integer to a series of bytes with base 256 and the most significant digit first. 737 * The first byte's highest bit is used for sign. If the most significant digit is larger than 738 * 127, an extra byte (0) will be prepended before it. This method currently doesn't support 739 * negative values. 740 * 741 * @throws IllegalArgumentException If {@code value} is negative. 742 * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}. 743 */ signedIntToBytes(int value, byte[] dest, int offset)744 public static int signedIntToBytes(int value, byte[] dest, int offset) { 745 return intToBytes(value, dest, offset, true); 746 } 747 748 /** 749 * Calculates the number of required bytes to represent {@code value}. The bytes will be base 750 * 256 with the most significant digit first. 751 * 752 * @throws IllegalArgumentException If {@code value} is negative. 753 */ byteNumForUnsignedInt(int value)754 public static int byteNumForUnsignedInt(int value) { 755 return byteNumForInt(value, false); 756 } 757 758 /** 759 * Calculates the number of required bytes to represent {@code value}. The bytes will be base 760 * 256 with the most significant digit first. If the most significant digit is larger than 127, 761 * an extra byte (0) will be prepended before it. This method currently only supports positive 762 * integers. 763 * 764 * @throws IllegalArgumentException If {@code value} is negative. 765 */ byteNumForSignedInt(int value)766 public static int byteNumForSignedInt(int value) { 767 return byteNumForInt(value, true); 768 } 769 intToBytes(int value, byte[] dest, int offset, boolean signed)770 private static int intToBytes(int value, byte[] dest, int offset, boolean signed) { 771 int l = byteNumForInt(value, signed); 772 if (offset < 0 || offset + l > dest.length) { 773 throw new IndexOutOfBoundsException("Not enough space to write. Required bytes: " + l); 774 } 775 for (int i = l - 1, v = value; i >= 0; i--, v >>>= 8) { 776 byte b = (byte) (v & 0xFF); 777 dest[offset + i] = b; 778 } 779 return l; 780 } 781 byteNumForInt(int value, boolean signed)782 private static int byteNumForInt(int value, boolean signed) { 783 if (value < 0) { 784 throw new IllegalArgumentException("value must be 0 or positive: " + value); 785 } 786 if (signed) { 787 if (value <= 0x7F) { 788 return 1; 789 } 790 if (value <= 0x7FFF) { 791 return 2; 792 } 793 if (value <= 0x7FFFFF) { 794 return 3; 795 } 796 } else { 797 if (value <= 0xFF) { 798 return 1; 799 } 800 if (value <= 0xFFFF) { 801 return 2; 802 } 803 if (value <= 0xFFFFFF) { 804 return 3; 805 } 806 } 807 return 4; 808 } 809 810 811 /** 812 * Counts the number of trailing zero bits of a byte. 813 */ countTrailingZeros(byte b)814 public static byte countTrailingZeros(byte b) { 815 if (b == 0) { 816 return 8; 817 } 818 int v = b & 0xFF; 819 byte c = 7; 820 if ((v & 0x0F) != 0) { 821 c -= 4; 822 } 823 if ((v & 0x33) != 0) { 824 c -= 2; 825 } 826 if ((v & 0x55) != 0) { 827 c -= 1; 828 } 829 return c; 830 } 831 832 /** 833 * Converts a byte to a hex string. 834 */ byteToHex(byte b)835 public static String byteToHex(byte b) { 836 return new String(new char[] {HEX_CHARS[(b & 0xFF) >>> 4], HEX_CHARS[b & 0xF]}); 837 } 838 839 /** 840 * Strip all the trailing 'F' characters of a string, e.g., an ICCID. 841 */ stripTrailingFs(String s)842 public static String stripTrailingFs(String s) { 843 return s == null ? null : s.replaceAll("(?i)f*$", ""); 844 } 845 846 /** 847 * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a 848 * hex number, 0 will be returned. 849 */ charToByte(char c)850 private static byte charToByte(char c) { 851 if (c >= 0x30 && c <= 0x39) { 852 return (byte) (c - 0x30); 853 } else if (c >= 0x41 && c <= 0x46) { 854 return (byte) (c - 0x37); 855 } else if (c >= 0x61 && c <= 0x66) { 856 return (byte) (c - 0x57); 857 } 858 return 0; 859 } 860 } 861