• 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     /**
36      * Many fields in GSM SIM's are stored as nibble-swizzled BCD
37      *
38      * Assumes left-justified field that may be padded right with 0xf
39      * values.
40      *
41      * Stops on invalid BCD value, returning string so far
42      */
43     public static String
bcdToString(byte[] data, int offset, int length)44     bcdToString(byte[] data, int offset, int length) {
45         StringBuilder ret = new StringBuilder(length*2);
46 
47         for (int i = offset ; i < offset + length ; i++) {
48             int v;
49 
50             v = data[i] & 0xf;
51             if (v > 9)  break;
52             ret.append((char)('0' + v));
53 
54             v = (data[i] >> 4) & 0xf;
55             // Some PLMNs have 'f' as high nibble, ignore it
56             if (v == 0xf) continue;
57             if (v > 9)  break;
58             ret.append((char)('0' + v));
59         }
60 
61         return ret.toString();
62     }
63 
64     /**
65      * PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3
66      * Returns a concatenated string of MCC+MNC, stripping
67      * a trailing character for a 2-digit MNC
68      */
bcdPlmnToString(byte[] data, int offset)69     public static String bcdPlmnToString(byte[] data, int offset) {
70         if (offset + 3 > data.length) {
71             return null;
72         }
73         byte[] trans = new byte[3];
74         trans[0] = (byte) ((data[0 + offset] << 4) | ((data[0 + offset] >> 4) & 0xF));
75         trans[1] = (byte) ((data[1 + offset] << 4) | (data[2 + offset] & 0xF));
76         trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF));
77         String ret = bytesToHexString(trans);
78 
79         // For a 2-digit MNC we trim the trailing 'f'
80         if (ret.endsWith("f")) {
81             ret = ret.substring(0, ret.length() - 1);
82         }
83         return ret;
84     }
85 
86     /**
87      * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
88      */
89     public static String
bchToString(byte[] data, int offset, int length)90     bchToString(byte[] data, int offset, int length) {
91         StringBuilder ret = new StringBuilder(length*2);
92 
93         for (int i = offset ; i < offset + length ; i++) {
94             int v;
95 
96             v = data[i] & 0xf;
97             ret.append("0123456789abcdef".charAt(v));
98 
99             v = (data[i] >> 4) & 0xf;
100             ret.append("0123456789abcdef".charAt(v));
101         }
102 
103         return ret.toString();
104     }
105 
106     /**
107      * Decode cdma byte into String.
108      */
109     public static String
cdmaBcdToString(byte[] data, int offset, int length)110     cdmaBcdToString(byte[] data, int offset, int length) {
111         StringBuilder ret = new StringBuilder(length);
112 
113         int count = 0;
114         for (int i = offset; count < length; i++) {
115             int v;
116             v = data[i] & 0xf;
117             if (v > 9)  v = 0;
118             ret.append((char)('0' + v));
119 
120             if (++count == length) break;
121 
122             v = (data[i] >> 4) & 0xf;
123             if (v > 9)  v = 0;
124             ret.append((char)('0' + v));
125             ++count;
126         }
127         return ret.toString();
128     }
129 
130     /**
131      * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
132      *
133      * In GSM land, the least significant BCD digit is stored in the most
134      * significant nibble.
135      *
136      * Out-of-range digits are treated as 0 for the sake of the time stamp,
137      * because of this:
138      *
139      * TS 23.040 section 9.2.3.11
140      * "if the MS receives a non-integer value in the SCTS, it shall
141      * assume the digit is set to 0 but shall store the entire field
142      * exactly as received"
143      */
144     public static int
gsmBcdByteToInt(byte b)145     gsmBcdByteToInt(byte b) {
146         int ret = 0;
147 
148         // treat out-of-range BCD values as 0
149         if ((b & 0xf0) <= 0x90) {
150             ret = (b >> 4) & 0xf;
151         }
152 
153         if ((b & 0x0f) <= 0x09) {
154             ret +=  (b & 0xf) * 10;
155         }
156 
157         return ret;
158     }
159 
160     /**
161      * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but
162      * opposite nibble format. The least significant BCD digit
163      * is in the least significant nibble and the most significant
164      * is in the most significant nibble.
165      */
166     public static int
cdmaBcdByteToInt(byte b)167     cdmaBcdByteToInt(byte b) {
168         int ret = 0;
169 
170         // treat out-of-range BCD values as 0
171         if ((b & 0xf0) <= 0x90) {
172             ret = ((b >> 4) & 0xf) * 10;
173         }
174 
175         if ((b & 0x0f) <= 0x09) {
176             ret +=  (b & 0xf);
177         }
178 
179         return ret;
180     }
181 
182     /**
183      * Decodes a string field that's formatted like the EF[ADN] alpha
184      * identifier
185      *
186      * From TS 51.011 10.5.1:
187      *   Coding:
188      *       this alpha tagging shall use either
189      *      -    the SMS default 7 bit coded alphabet as defined in
190      *          TS 23.038 [12] with bit 8 set to 0. The alpha identifier
191      *          shall be left justified. Unused bytes shall be set to 'FF'; or
192      *      -    one of the UCS2 coded options as defined in annex B.
193      *
194      * Annex B from TS 11.11 V8.13.0:
195      *      1)  If the first octet in the alpha string is '80', then the
196      *          remaining octets are 16 bit UCS2 characters ...
197      *      2)  if the first octet in the alpha string is '81', then the
198      *          second octet contains a value indicating the number of
199      *          characters in the string, and the third octet contains an
200      *          8 bit number which defines bits 15 to 8 of a 16 bit
201      *          base pointer, where bit 16 is set to zero and bits 7 to 1
202      *          are also set to zero.  These sixteen bits constitute a
203      *          base pointer to a "half page" in the UCS2 code space, to be
204      *          used with some or all of the remaining octets in the string.
205      *          The fourth and subsequent octets contain codings as follows:
206      *          If bit 8 of the octet is set to zero, the remaining 7 bits
207      *          of the octet contain a GSM Default Alphabet character,
208      *          whereas if bit 8 of the octet is set to one, then the
209      *          remaining seven bits are an offset value added to the
210      *          16 bit base pointer defined earlier...
211      *      3)  If the first octet of the alpha string is set to '82', then
212      *          the second octet contains a value indicating the number of
213      *          characters in the string, and the third and fourth octets
214      *          contain a 16 bit number which defines the complete 16 bit
215      *          base pointer to a "half page" in the UCS2 code space...
216      */
217     public static String
adnStringFieldToString(byte[] data, int offset, int length)218     adnStringFieldToString(byte[] data, int offset, int length) {
219         if (length == 0) {
220             return "";
221         }
222         if (length >= 1) {
223             if (data[offset] == (byte) 0x80) {
224                 int ucslen = (length - 1) / 2;
225                 String ret = null;
226 
227                 try {
228                     ret = new String(data, offset + 1, ucslen * 2, "utf-16be");
229                 } catch (UnsupportedEncodingException ex) {
230                     Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException",
231                           ex);
232                 }
233 
234                 if (ret != null) {
235                     // trim off trailing FFFF characters
236 
237                     ucslen = ret.length();
238                     while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF')
239                         ucslen--;
240 
241                     return ret.substring(0, ucslen);
242                 }
243             }
244         }
245 
246         boolean isucs2 = false;
247         char base = '\0';
248         int len = 0;
249 
250         if (length >= 3 && data[offset] == (byte) 0x81) {
251             len = data[offset + 1] & 0xFF;
252             if (len > length - 3)
253                 len = length - 3;
254 
255             base = (char) ((data[offset + 2] & 0xFF) << 7);
256             offset += 3;
257             isucs2 = true;
258         } else if (length >= 4 && data[offset] == (byte) 0x82) {
259             len = data[offset + 1] & 0xFF;
260             if (len > length - 4)
261                 len = length - 4;
262 
263             base = (char) (((data[offset + 2] & 0xFF) << 8) |
264                             (data[offset + 3] & 0xFF));
265             offset += 4;
266             isucs2 = true;
267         }
268 
269         if (isucs2) {
270             StringBuilder ret = new StringBuilder();
271 
272             while (len > 0) {
273                 // UCS2 subset case
274 
275                 if (data[offset] < 0) {
276                     ret.append((char) (base + (data[offset] & 0x7F)));
277                     offset++;
278                     len--;
279                 }
280 
281                 // GSM character set case
282 
283                 int count = 0;
284                 while (count < len && data[offset + count] >= 0)
285                     count++;
286 
287                 ret.append(GsmAlphabet.gsm8BitUnpackedToString(data,
288                            offset, count));
289 
290                 offset += count;
291                 len -= count;
292             }
293 
294             return ret.toString();
295         }
296 
297         Resources resource = Resources.getSystem();
298         String defaultCharset = "";
299         try {
300             defaultCharset =
301                     resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset);
302         } catch (NotFoundException e) {
303             // Ignore Exception and defaultCharset is set to a empty string.
304         }
305         return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
306     }
307 
308     static int
hexCharToInt(char c)309     hexCharToInt(char c) {
310         if (c >= '0' && c <= '9') return (c - '0');
311         if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
312         if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
313 
314         throw new RuntimeException ("invalid hex char '" + c + "'");
315     }
316 
317     /**
318      * Converts a hex String to a byte array.
319      *
320      * @param s A string of hexadecimal characters, must be an even number of
321      *          chars long
322      *
323      * @return byte array representation
324      *
325      * @throws RuntimeException on invalid format
326      */
327     public static byte[]
hexStringToBytes(String s)328     hexStringToBytes(String s) {
329         byte[] ret;
330 
331         if (s == null) return null;
332 
333         int sz = s.length();
334 
335         ret = new byte[sz/2];
336 
337         for (int i=0 ; i <sz ; i+=2) {
338             ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4)
339                                 | hexCharToInt(s.charAt(i+1)));
340         }
341 
342         return ret;
343     }
344 
345 
346     /**
347      * Converts a byte array into a String of hexadecimal characters.
348      *
349      * @param bytes an array of bytes
350      *
351      * @return hex string representation of bytes array
352      */
353     public static String
bytesToHexString(byte[] bytes)354     bytesToHexString(byte[] bytes) {
355         if (bytes == null) return null;
356 
357         StringBuilder ret = new StringBuilder(2*bytes.length);
358 
359         for (int i = 0 ; i < bytes.length ; i++) {
360             int b;
361 
362             b = 0x0f & (bytes[i] >> 4);
363 
364             ret.append("0123456789abcdef".charAt(b));
365 
366             b = 0x0f & bytes[i];
367 
368             ret.append("0123456789abcdef".charAt(b));
369         }
370 
371         return ret.toString();
372     }
373 
374 
375     /**
376      * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string
377      * "offset" points to "octet 3", the coding scheme byte
378      * empty string returned on decode error
379      */
380     public static String
networkNameToString(byte[] data, int offset, int length)381     networkNameToString(byte[] data, int offset, int length) {
382         String ret;
383 
384         if ((data[offset] & 0x80) != 0x80 || length < 1) {
385             return "";
386         }
387 
388         switch ((data[offset] >>> 4) & 0x7) {
389             case 0:
390                 // SMS character set
391                 int countSeptets;
392                 int unusedBits = data[offset] & 7;
393                 countSeptets = (((length - 1) * 8) - unusedBits) / 7 ;
394                 ret =  GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets);
395             break;
396             case 1:
397                 // UCS2
398                 try {
399                     ret = new String(data,
400                             offset + 1, length - 1, "utf-16");
401                 } catch (UnsupportedEncodingException ex) {
402                     ret = "";
403                     Rlog.e(LOG_TAG,"implausible UnsupportedEncodingException", ex);
404                 }
405             break;
406 
407             // unsupported encoding
408             default:
409                 ret = "";
410             break;
411         }
412 
413         // "Add CI"
414         // "The MS should add the letters for the Country's Initials and
415         //  a separator (e.g. a space) to the text string"
416 
417         if ((data[offset] & 0x40) != 0) {
418             // FIXME(mkf) add country initials here
419 
420         }
421 
422         return ret;
423     }
424 
425     /**
426      * Convert a TS 131.102 image instance of code scheme '11' into Bitmap
427      * @param data The raw data
428      * @param length The length of image body
429      * @return The bitmap
430      */
parseToBnW(byte[] data, int length)431     public static Bitmap parseToBnW(byte[] data, int length){
432         int valueIndex = 0;
433         int width = data[valueIndex++] & 0xFF;
434         int height = data[valueIndex++] & 0xFF;
435         int numOfPixels = width*height;
436 
437         int[] pixels = new int[numOfPixels];
438 
439         int pixelIndex = 0;
440         int bitIndex = 7;
441         byte currentByte = 0x00;
442         while (pixelIndex < numOfPixels) {
443             // reassign data and index for every byte (8 bits).
444             if (pixelIndex % 8 == 0) {
445                 currentByte = data[valueIndex++];
446                 bitIndex = 7;
447             }
448             pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01);
449         }
450 
451         if (pixelIndex != numOfPixels) {
452             Rlog.e(LOG_TAG, "parse end and size error");
453         }
454         return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
455     }
456 
bitToRGB(int bit)457     private static int bitToRGB(int bit){
458         if(bit == 1){
459             return Color.WHITE;
460         } else {
461             return Color.BLACK;
462         }
463     }
464 
465     /**
466      * a TS 131.102 image instance of code scheme '11' into color Bitmap
467      *
468      * @param data The raw data
469      * @param length the length of image body
470      * @param transparency with or without transparency
471      * @return The color bitmap
472      */
parseToRGB(byte[] data, int length, boolean transparency)473     public static Bitmap parseToRGB(byte[] data, int length,
474             boolean transparency) {
475         int valueIndex = 0;
476         int width = data[valueIndex++] & 0xFF;
477         int height = data[valueIndex++] & 0xFF;
478         int bits = data[valueIndex++] & 0xFF;
479         int colorNumber = data[valueIndex++] & 0xFF;
480         int clutOffset = ((data[valueIndex++] & 0xFF) << 8)
481                 | (data[valueIndex++] & 0xFF);
482 
483         int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
484         if (true == transparency) {
485             colorIndexArray[colorNumber - 1] = Color.TRANSPARENT;
486         }
487 
488         int[] resultArray = null;
489         if (0 == (8 % bits)) {
490             resultArray = mapTo2OrderBitColor(data, valueIndex,
491                     (width * height), colorIndexArray, bits);
492         } else {
493             resultArray = mapToNon2OrderBitColor(data, valueIndex,
494                     (width * height), colorIndexArray, bits);
495         }
496 
497         return Bitmap.createBitmap(resultArray, width, height,
498                 Bitmap.Config.RGB_565);
499     }
500 
mapTo2OrderBitColor(byte[] data, int valueIndex, int length, int[] colorArray, int bits)501     private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex,
502             int length, int[] colorArray, int bits) {
503         if (0 != (8 % bits)) {
504             Rlog.e(LOG_TAG, "not event number of color");
505             return mapToNon2OrderBitColor(data, valueIndex, length, colorArray,
506                     bits);
507         }
508 
509         int mask = 0x01;
510         switch (bits) {
511         case 1:
512             mask = 0x01;
513             break;
514         case 2:
515             mask = 0x03;
516             break;
517         case 4:
518             mask = 0x0F;
519             break;
520         case 8:
521             mask = 0xFF;
522             break;
523         }
524 
525         int[] resultArray = new int[length];
526         int resultIndex = 0;
527         int run = 8 / bits;
528         while (resultIndex < length) {
529             byte tempByte = data[valueIndex++];
530             for (int runIndex = 0; runIndex < run; ++runIndex) {
531                 int offset = run - runIndex - 1;
532                 resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits))
533                         & mask];
534             }
535         }
536         return resultArray;
537     }
538 
mapToNon2OrderBitColor(byte[] data, int valueIndex, int length, int[] colorArray, int bits)539     private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex,
540             int length, int[] colorArray, int bits) {
541         if (0 == (8 % bits)) {
542             Rlog.e(LOG_TAG, "not odd number of color");
543             return mapTo2OrderBitColor(data, valueIndex, length, colorArray,
544                     bits);
545         }
546 
547         int[] resultArray = new int[length];
548         // TODO fix me:
549         return resultArray;
550     }
551 
getCLUT(byte[] rawData, int offset, int number)552     private static int[] getCLUT(byte[] rawData, int offset, int number) {
553         if (null == rawData) {
554             return null;
555         }
556 
557         int[] result = new int[number];
558         int endIndex = offset + (number * 3); // 1 color use 3 bytes
559         int valueIndex = offset;
560         int colorIndex = 0;
561         int alpha = 0xff << 24;
562         do {
563             result[colorIndex++] = alpha
564                     | ((rawData[valueIndex++] & 0xFF) << 16)
565                     | ((rawData[valueIndex++] & 0xFF) << 8)
566                     | ((rawData[valueIndex++] & 0xFF));
567         } while (valueIndex < endIndex);
568         return result;
569     }
570 }
571