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