• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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