2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony;
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;
25 import com.android.internal.telephony.GsmAlphabet;
26 import java.io.UnsupportedEncodingException;
27 import java.nio.ByteBuffer;
28 import java.nio.charset.Charset;
31 * Various methods, useful for dealing with SIM data.
33 public class IccUtils {
34 static final String LOG_TAG="IccUtils";
37 * Many fields in GSM SIM's are stored as nibble-swizzled BCD
39 * Assumes left-justified field that may be padded right with 0xf
42 * Stops on invalid BCD value, returning string so far
45 bcdToString(byte[] data, int offset, int length) {
46 StringBuilder ret = new StringBuilder(length*2);
48 for (int i = offset ; i < offset + length ; i++) {
54 ret.append((char)('0' + v));
56 v = (data[i] >> 4) & 0xf;
57 // Some PLMNs have 'f' as high nibble, ignore it
58 if (v == 0xf) continue;
60 ret.append((char)('0' + v));
63 return ret.toString();
67 * Decode cdma byte into String.
70 cdmaBcdToString(byte[] data, int offset, int length) {
71 StringBuilder ret = new StringBuilder(length);
74 for (int i = offset; count < length; i++) {
78 ret.append((char)('0' + v));
80 if (++count == length) break;
82 v = (data[i] >> 4) & 0xf;
84 ret.append((char)('0' + v));
87 return ret.toString();
91 * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
93 * In GSM land, the least significant BCD digit is stored in the most
96 * Out-of-range digits are treated as 0 for the sake of the time stamp,
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"
105 gsmBcdByteToInt(byte b) {
108 // treat out-of-range BCD values as 0
109 if ((b & 0xf0) <= 0x90) {
110 ret = (b >> 4) & 0xf;
113 if ((b & 0x0f) <= 0x09) {
114 ret += (b & 0xf) * 10;
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.
127 cdmaBcdByteToInt(byte b) {
130 // treat out-of-range BCD values as 0
131 if ((b & 0xf0) <= 0x90) {
132 ret = ((b >> 4) & 0xf) * 10;
135 if ((b & 0x0f) <= 0x09) {
143 * Decodes a string field that's formatted like the EF[ADN] alpha
146 * From TS 51.011 10.5.1:
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.
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...
178 adnStringFieldToString(byte[] data, int offset, int length) {
183 if (data[offset] == (byte) 0x80) {
184 int ucslen = (length - 1) / 2;
188 ret = new String(data, offset + 1, ucslen * 2, "utf-16be");
189 } catch (UnsupportedEncodingException ex) {
190 Log.e(LOG_TAG, "implausible UnsupportedEncodingException",
195 // trim off trailing FFFF characters
197 ucslen = ret.length();
198 while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF')
201 return ret.substring(0, ucslen);
206 boolean isucs2 = false;
210 if (length >= 3 && data[offset] == (byte) 0x81) {
211 len = data[offset + 1] & 0xFF;
212 if (len > length - 3)
215 base = (char) ((data[offset + 2] & 0xFF) << 7);
218 } else if (length >= 4 && data[offset] == (byte) 0x82) {
219 len = data[offset + 1] & 0xFF;
220 if (len > length - 4)
223 base = (char) (((data[offset + 2] & 0xFF) << 8) |
224 (data[offset + 3] & 0xFF));
230 StringBuilder ret = new StringBuilder();
235 if (data[offset] < 0) {
236 ret.append((char) (base + (data[offset] & 0x7F)));
241 // GSM character set case
244 while (count < len && data[offset + count] >= 0)
247 ret.append(GsmAlphabet.gsm8BitUnpackedToString(data,
254 return ret.toString();
257 Resources resource = Resources.getSystem();
258 String 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.
265 return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
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);
274 throw new RuntimeException ("invalid hex char '" + c + "'");
278 * Converts a hex String to a byte array.
280 * @param s A string of hexadecimal characters, must be an even number of
283 * @return byte array representation
285 * @throws RuntimeException on invalid format
288 hexStringToBytes(String s) {
291 if (s == null) return null;
295 ret = new byte[sz/2];
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)));
307 * Converts a byte array into a String of hexadecimal characters.
309 * @param bytes an array of bytes
311 * @return hex string representation of bytes array
314 bytesToHexString(byte[] bytes) {
315 if (bytes == null) return null;
317 StringBuilder ret = new StringBuilder(2*bytes.length);
319 for (int i = 0 ; i < bytes.length ; i++) {
322 b = 0x0f & (bytes[i] >> 4);
324 ret.append("0123456789abcdef".charAt(b));
328 ret.append("0123456789abcdef".charAt(b));
331 return ret.toString();
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
341 networkNameToString(byte[] data, int offset, int length) {
344 if ((data[offset] & 0x80) != 0x80 || length < 1) {
348 switch ((data[offset] >>> 4) & 0x7) {
352 int unusedBits = data[offset] & 7;
353 countSeptets = (((length - 1) * 8) - unusedBits) / 7 ;
354 ret = GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets);
359 ret = new String(data,
360 offset + 1, length - 1, "utf-16");
361 } catch (UnsupportedEncodingException ex) {
363 Log.e(LOG_TAG,"implausible UnsupportedEncodingException", ex);
367 // unsupported encoding
374 // "The MS should add the letters for the Country's Initials and
375 // a separator (e.g. a space) to the text string"
377 if ((data[offset] & 0x40) != 0) {
378 // FIXME(mkf) add country initials here
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
391 public static Bitmap parseToBnW(byte[] data, int length){
393 int width = data[valueIndex++] & 0xFF;
394 int height = data[valueIndex++] & 0xFF;
395 int numOfPixels = width*height;
397 int[] pixels = new int[numOfPixels];
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++];
408 pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01);
411 if (pixelIndex != numOfPixels) {
412 Log.e(LOG_TAG, "parse end and size error");
414 return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
417 private static int bitToRGB(int bit){
426 * a TS 131.102 image instance of code scheme '11' into color Bitmap
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
433 public static Bitmap parseToRGB(byte[] data, int length,
434 boolean transparency) {
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);
444 int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
445 if (true == transparency) {
446 colorIndexArray[colorNumber - 1] = Color.TRANSPARENT;
449 int[] resultArray = null;
450 if (0 == (8 % bits)) {
451 resultArray = mapTo2OrderBitColor(data, valueIndex,
452 (width * height), colorIndexArray, bits);
454 resultArray = mapToNon2OrderBitColor(data, valueIndex,
455 (width * height), colorIndexArray, bits);
458 return Bitmap.createBitmap(resultArray, width, height,
459 Bitmap.Config.RGB_565);
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,
486 int[] resultArray = new int[length];
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))
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,
508 int[] resultArray = new int[length];
513 private static int[] getCLUT(byte[] rawData, int offset, int number) {
514 if (null == rawData) {
518 int[] result = new int[number];
519 int endIndex = offset + (number * 3); // 1 color use 3 bytes
520 int valueIndex = offset;
522 int alpha = 0xff << 24;
524 result[colorIndex++] = alpha
525 | ((rawData[valueIndex++] & 0xFF) << 16)
526 | ((rawData[valueIndex++] & 0xFF) << 8)
527 | ((rawData[valueIndex++] & 0xFF));
528 } while (valueIndex < endIndex);