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