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