OSDN Git Service

am 409b9a4c: Merge "Revert "cherrypick from hc mr1 Change-Id: I23143ecaa0eeab68d55d00...
[android-x86/frameworks-base.git] / telephony / java / com / android / internal / telephony / IccUtils.java
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
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
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
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
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
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
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[]
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
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
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      */
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
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      */
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         length = length - 6;
443
444         int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
445         if (true == transparency) {
446             colorIndexArray[colorNumber - 1] = Color.TRANSPARENT;
447         }
448
449         int[] resultArray = null;
450         if (0 == (8 % bits)) {
451             resultArray = mapTo2OrderBitColor(data, valueIndex,
452                     (width * height), colorIndexArray, bits);
453         } else {
454             resultArray = mapToNon2OrderBitColor(data, valueIndex,
455                     (width * height), colorIndexArray, bits);
456         }
457
458         return Bitmap.createBitmap(resultArray, width, height,
459                 Bitmap.Config.RGB_565);
460     }
461
462     private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex,
463             int length, int[] colorArray, int bits) {
464         if (0 != (8 % bits)) {
465             Log.e(LOG_TAG, "not event number of color");
466             return mapToNon2OrderBitColor(data, valueIndex, length, colorArray,
467                     bits);
468         }
469
470         int mask = 0x01;
471         switch (bits) {
472         case 1:
473             mask = 0x01;
474             break;
475         case 2:
476             mask = 0x03;
477             break;
478         case 4:
479             mask = 0x0F;
480             break;
481         case 8:
482             mask = 0xFF;
483             break;
484         }
485
486         int[] resultArray = new int[length];
487         int resultIndex = 0;
488         int run = 8 / bits;
489         while (resultIndex < length) {
490             byte tempByte = data[valueIndex++];
491             for (int runIndex = 0; runIndex < run; ++runIndex) {
492                 int offset = run - runIndex - 1;
493                 resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits))
494                         & mask];
495             }
496         }
497         return resultArray;
498     }
499
500     private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex,
501             int length, int[] colorArray, int bits) {
502         if (0 == (8 % bits)) {
503             Log.e(LOG_TAG, "not odd number of color");
504             return mapTo2OrderBitColor(data, valueIndex, length, colorArray,
505                     bits);
506         }
507
508         int[] resultArray = new int[length];
509         // TODO fix me:
510         return resultArray;
511     }
512
513     private static int[] getCLUT(byte[] rawData, int offset, int number) {
514         if (null == rawData) {
515             return null;
516         }
517
518         int[] result = new int[number];
519         int endIndex = offset + (number * 3); // 1 color use 3 bytes
520         int valueIndex = offset;
521         int colorIndex = 0;
522         int alpha = 0xff << 24;
523         do {
524             result[colorIndex++] = alpha
525                     | ((rawData[valueIndex++] & 0xFF) << 16)
526                     | ((rawData[valueIndex++] & 0xFF) << 8)
527                     | ((rawData[valueIndex++] & 0xFF));
528         } while (valueIndex < endIndex);
529         return result;
530     }
531 }