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