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