1f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold/*
2f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * Copyright (C) 2006 The Android Open Source Project
3f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold *
4f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * Licensed under the Apache License, Version 2.0 (the "License");
5f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * you may not use this file except in compliance with the License.
6f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * You may obtain a copy of the License at
7f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold *
8f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold *      http://www.apache.org/licenses/LICENSE-2.0
9f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold *
10f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * Unless required by applicable law or agreed to in writing, software
11f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * distributed under the License is distributed on an "AS IS" BASIS,
12f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * See the License for the specific language governing permissions and
14f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * limitations under the License.
15f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold */
16f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
17f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldpackage com.android.internal.telephony.uicc;
18f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
19f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldimport android.content.res.Resources;
20f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldimport android.content.res.Resources.NotFoundException;
21f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldimport android.graphics.Bitmap;
22f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldimport android.graphics.Color;
23f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldimport android.telephony.Rlog;
24f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
25f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldimport com.android.internal.telephony.GsmAlphabet;
26f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
27f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldimport java.io.UnsupportedEncodingException;
28f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
29f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold/**
30f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold * Various methods, useful for dealing with SIM data.
31f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold */
32f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Haroldpublic class IccUtils {
33f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    static final String LOG_TAG="IccUtils";
34f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
35a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    // A table mapping from a number to a hex character for fast encoding hex strings.
36a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    private static final char[] HEX_CHARS = {
37a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
38a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    };
39a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
40a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
41f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
42f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Many fields in GSM SIM's are stored as nibble-swizzled BCD
43f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
44f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Assumes left-justified field that may be padded right with 0xf
45f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * values.
46f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
47f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Stops on invalid BCD value, returning string so far
48f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
49f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static String
50f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    bcdToString(byte[] data, int offset, int length) {
51f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        StringBuilder ret = new StringBuilder(length*2);
52f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
53f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        for (int i = offset ; i < offset + length ; i++) {
54f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            int v;
55f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
56f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            v = data[i] & 0xf;
57f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (v > 9)  break;
58f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret.append((char)('0' + v));
59f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
60f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            v = (data[i] >> 4) & 0xf;
61f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            // Some PLMNs have 'f' as high nibble, ignore it
62f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (v == 0xf) continue;
63f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (v > 9)  break;
64f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret.append((char)('0' + v));
65f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
66f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
67f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret.toString();
68f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
69f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
70f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
71a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts a bcd byte array to String with offset 0 and byte array length.
72a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
73a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static String bcdToString(byte[] data) {
74a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return bcdToString(data, 0, data.length);
75a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
76a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
77a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
78a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts BCD string to bytes.
79a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
80a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param bcd This should have an even length. If not, an "0" will be appended to the string.
81a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
82a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static byte[] bcdToBytes(String bcd) {
83a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        byte[] output = new byte[(bcd.length() + 1) / 2];
84a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        bcdToBytes(bcd, output);
85a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return output;
86a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
87a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
88a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
89a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts BCD string to bytes and put it into the given byte array.
90a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
91a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param bcd This should have an even length. If not, an "0" will be appended to the string.
92a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param bytes If the array size is less than needed, the rest of the BCD string isn't be
93a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *     converted. If the array size is more than needed, the rest of array remains unchanged.
94a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
95a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static void bcdToBytes(String bcd, byte[] bytes) {
96a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (bcd.length() % 2 != 0) {
97a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            bcd += "0";
98a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
99a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        int size = Math.min(bytes.length * 2, bcd.length());
100a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        for (int i = 0, j = 0; i + 1 < size; i += 2, j++) {
101a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i)));
102a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
103a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
104a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
105a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
106f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3
107f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Returns a concatenated string of MCC+MNC, stripping
1088cd6dad0052a57b16412087581a40b0df80dd15dkun.tang     * all invalid character 'f'
109f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
110f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static String bcdPlmnToString(byte[] data, int offset) {
111f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (offset + 3 > data.length) {
112f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return null;
113f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
114f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        byte[] trans = new byte[3];
115f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        trans[0] = (byte) ((data[0 + offset] << 4) | ((data[0 + offset] >> 4) & 0xF));
116f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        trans[1] = (byte) ((data[1 + offset] << 4) | (data[2 + offset] & 0xF));
117f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF));
118f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        String ret = bytesToHexString(trans);
119f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
1208cd6dad0052a57b16412087581a40b0df80dd15dkun.tang        // For a valid plmn we trim all character 'f'
1218cd6dad0052a57b16412087581a40b0df80dd15dkun.tang        if (ret.contains("f")) {
1228cd6dad0052a57b16412087581a40b0df80dd15dkun.tang            ret = ret.replaceAll("f", "");
123f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
124f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret;
125f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
126f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
127f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
128f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
129f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
130f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static String
131f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    bchToString(byte[] data, int offset, int length) {
132f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        StringBuilder ret = new StringBuilder(length*2);
133f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
134f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        for (int i = offset ; i < offset + length ; i++) {
135f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            int v;
136f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
137f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            v = data[i] & 0xf;
138a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            ret.append(HEX_CHARS[v]);
139f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
140f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            v = (data[i] >> 4) & 0xf;
141a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            ret.append(HEX_CHARS[v]);
142f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
143f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
144f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret.toString();
145f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
146f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
147f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
148f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Decode cdma byte into String.
149f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
150f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static String
151f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    cdmaBcdToString(byte[] data, int offset, int length) {
152f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        StringBuilder ret = new StringBuilder(length);
153f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
154f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int count = 0;
155f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        for (int i = offset; count < length; i++) {
156f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            int v;
157f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            v = data[i] & 0xf;
158f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (v > 9)  v = 0;
159f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret.append((char)('0' + v));
160f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
161f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (++count == length) break;
162f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
163f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            v = (data[i] >> 4) & 0xf;
164f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (v > 9)  v = 0;
165f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret.append((char)('0' + v));
166f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ++count;
167f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
168f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret.toString();
169f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
170f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
171f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
172f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
173f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
174f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * In GSM land, the least significant BCD digit is stored in the most
175f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * significant nibble.
176f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
177f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Out-of-range digits are treated as 0 for the sake of the time stamp,
178f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * because of this:
179f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
180f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * TS 23.040 section 9.2.3.11
181f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * "if the MS receives a non-integer value in the SCTS, it shall
182f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * assume the digit is set to 0 but shall store the entire field
183f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * exactly as received"
184f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
185f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static int
186f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    gsmBcdByteToInt(byte b) {
187f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int ret = 0;
188f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
189f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        // treat out-of-range BCD values as 0
190f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if ((b & 0xf0) <= 0x90) {
191f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret = (b >> 4) & 0xf;
192f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
193f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
194f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if ((b & 0x0f) <= 0x09) {
195f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret +=  (b & 0xf) * 10;
196f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
197f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
198f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret;
199f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
200f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
201f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
202f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but
203f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * opposite nibble format. The least significant BCD digit
204f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * is in the least significant nibble and the most significant
205f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * is in the most significant nibble.
206f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
207f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static int
208f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    cdmaBcdByteToInt(byte b) {
209f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int ret = 0;
210f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
211f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        // treat out-of-range BCD values as 0
212f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if ((b & 0xf0) <= 0x90) {
213f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret = ((b >> 4) & 0xf) * 10;
214f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
215f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
216f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if ((b & 0x0f) <= 0x09) {
217f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret +=  (b & 0xf);
218f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
219f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
220f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret;
221f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
222f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
223f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
224f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Decodes a string field that's formatted like the EF[ADN] alpha
225f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * identifier
226f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
227f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * From TS 51.011 10.5.1:
228f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *   Coding:
229f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *       this alpha tagging shall use either
230f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *      -    the SMS default 7 bit coded alphabet as defined in
231f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          TS 23.038 [12] with bit 8 set to 0. The alpha identifier
232f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          shall be left justified. Unused bytes shall be set to 'FF'; or
233f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *      -    one of the UCS2 coded options as defined in annex B.
234f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
235f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Annex B from TS 11.11 V8.13.0:
236f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *      1)  If the first octet in the alpha string is '80', then the
237f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          remaining octets are 16 bit UCS2 characters ...
238f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *      2)  if the first octet in the alpha string is '81', then the
239f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          second octet contains a value indicating the number of
240f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          characters in the string, and the third octet contains an
241f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          8 bit number which defines bits 15 to 8 of a 16 bit
242f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          base pointer, where bit 16 is set to zero and bits 7 to 1
243f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          are also set to zero.  These sixteen bits constitute a
244f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          base pointer to a "half page" in the UCS2 code space, to be
245f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          used with some or all of the remaining octets in the string.
246f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          The fourth and subsequent octets contain codings as follows:
247f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          If bit 8 of the octet is set to zero, the remaining 7 bits
248f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          of the octet contain a GSM Default Alphabet character,
249f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          whereas if bit 8 of the octet is set to one, then the
250f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          remaining seven bits are an offset value added to the
251f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          16 bit base pointer defined earlier...
252f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *      3)  If the first octet of the alpha string is set to '82', then
253f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          the second octet contains a value indicating the number of
254f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          characters in the string, and the third and fourth octets
255f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          contain a 16 bit number which defines the complete 16 bit
256f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          base pointer to a "half page" in the UCS2 code space...
257f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
258f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static String
259f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    adnStringFieldToString(byte[] data, int offset, int length) {
260f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (length == 0) {
261f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return "";
262f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
263f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (length >= 1) {
264f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (data[offset] == (byte) 0x80) {
265f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                int ucslen = (length - 1) / 2;
266f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                String ret = null;
267f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
268f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                try {
269f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    ret = new String(data, offset + 1, ucslen * 2, "utf-16be");
270f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                } catch (UnsupportedEncodingException ex) {
271f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException",
272f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                          ex);
273f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                }
274f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
275f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                if (ret != null) {
276f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    // trim off trailing FFFF characters
277f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
278f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    ucslen = ret.length();
279f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF')
280f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                        ucslen--;
281f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
282f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    return ret.substring(0, ucslen);
283f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                }
284f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            }
285f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
286f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
287f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        boolean isucs2 = false;
288f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        char base = '\0';
289f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int len = 0;
290f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
291f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (length >= 3 && data[offset] == (byte) 0x81) {
292f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            len = data[offset + 1] & 0xFF;
293f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (len > length - 3)
294f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                len = length - 3;
295f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
296f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            base = (char) ((data[offset + 2] & 0xFF) << 7);
297f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            offset += 3;
298f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            isucs2 = true;
299f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        } else if (length >= 4 && data[offset] == (byte) 0x82) {
300f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            len = data[offset + 1] & 0xFF;
301f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (len > length - 4)
302f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                len = length - 4;
303f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
304f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            base = (char) (((data[offset + 2] & 0xFF) << 8) |
305f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                            (data[offset + 3] & 0xFF));
306f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            offset += 4;
307f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            isucs2 = true;
308f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
309f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
310f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (isucs2) {
311f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            StringBuilder ret = new StringBuilder();
312f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
313f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            while (len > 0) {
314f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                // UCS2 subset case
315f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
316f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                if (data[offset] < 0) {
317f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    ret.append((char) (base + (data[offset] & 0x7F)));
318f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    offset++;
319f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    len--;
320f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                }
321f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
322f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                // GSM character set case
323f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
324f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                int count = 0;
325f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                while (count < len && data[offset + count] >= 0)
326f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    count++;
327f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
328f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                ret.append(GsmAlphabet.gsm8BitUnpackedToString(data,
329f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                           offset, count));
330f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
331f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                offset += count;
332f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                len -= count;
333f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            }
334f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
335f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return ret.toString();
336f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
337f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
338f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        Resources resource = Resources.getSystem();
339f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        String defaultCharset = "";
340f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        try {
341f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            defaultCharset =
342f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset);
343f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        } catch (NotFoundException e) {
344f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            // Ignore Exception and defaultCharset is set to a empty string.
345f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
346f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
347f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
348f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
349a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static int
350f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    hexCharToInt(char c) {
351f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (c >= '0' && c <= '9') return (c - '0');
352f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
353f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
354f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
355f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        throw new RuntimeException ("invalid hex char '" + c + "'");
356f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
357f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
358f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
359f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Converts a hex String to a byte array.
360f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
361f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @param s A string of hexadecimal characters, must be an even number of
362f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *          chars long
363f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
364f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @return byte array representation
365f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
366f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @throws RuntimeException on invalid format
367f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
368f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static byte[]
369f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    hexStringToBytes(String s) {
370f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        byte[] ret;
371f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
372f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (s == null) return null;
373f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
374f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int sz = s.length();
375f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
376f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        ret = new byte[sz/2];
377f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
378f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        for (int i=0 ; i <sz ; i+=2) {
379f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4)
380f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                                | hexCharToInt(s.charAt(i+1)));
381f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
382f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
383f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret;
384f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
385f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
386f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
387f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
388f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Converts a byte array into a String of hexadecimal characters.
389f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
390f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @param bytes an array of bytes
391f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
392f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @return hex string representation of bytes array
393f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
394f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static String
395f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    bytesToHexString(byte[] bytes) {
396f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (bytes == null) return null;
397f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
398f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        StringBuilder ret = new StringBuilder(2*bytes.length);
399f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
400f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        for (int i = 0 ; i < bytes.length ; i++) {
401f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            int b;
402f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
403f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            b = 0x0f & (bytes[i] >> 4);
404f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
405a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            ret.append(HEX_CHARS[b]);
406f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
407f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            b = 0x0f & bytes[i];
408f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
409a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            ret.append(HEX_CHARS[b]);
410f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
411f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
412f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret.toString();
413f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
414f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
415f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
416f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
417f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string
418f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * "offset" points to "octet 3", the coding scheme byte
419f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * empty string returned on decode error
420f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
421f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static String
422f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    networkNameToString(byte[] data, int offset, int length) {
423f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        String ret;
424f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
425f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if ((data[offset] & 0x80) != 0x80 || length < 1) {
426f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return "";
427f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
428f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
429f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        switch ((data[offset] >>> 4) & 0x7) {
430f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            case 0:
431f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                // SMS character set
432f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                int countSeptets;
433f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                int unusedBits = data[offset] & 7;
434f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                countSeptets = (((length - 1) * 8) - unusedBits) / 7 ;
435f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                ret =  GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets);
436f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            break;
437f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            case 1:
438f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                // UCS2
439f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                try {
440f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    ret = new String(data,
441f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                            offset + 1, length - 1, "utf-16");
442f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                } catch (UnsupportedEncodingException ex) {
443f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    ret = "";
444f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    Rlog.e(LOG_TAG,"implausible UnsupportedEncodingException", ex);
445f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                }
446f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            break;
447f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
448f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            // unsupported encoding
449f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            default:
450f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                ret = "";
451f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            break;
452f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
453f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
454f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        // "Add CI"
455f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        // "The MS should add the letters for the Country's Initials and
456f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        //  a separator (e.g. a space) to the text string"
457f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
458f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if ((data[offset] & 0x40) != 0) {
459f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            // FIXME(mkf) add country initials here
460f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
461f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
462f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return ret;
463f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
464f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
465f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
466f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * Convert a TS 131.102 image instance of code scheme '11' into Bitmap
467f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @param data The raw data
468f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @param length The length of image body
469f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @return The bitmap
470f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
471f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static Bitmap parseToBnW(byte[] data, int length){
472f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int valueIndex = 0;
473f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int width = data[valueIndex++] & 0xFF;
474f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int height = data[valueIndex++] & 0xFF;
475f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int numOfPixels = width*height;
476f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
477f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int[] pixels = new int[numOfPixels];
478f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
479f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int pixelIndex = 0;
480f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int bitIndex = 7;
481f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        byte currentByte = 0x00;
482f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        while (pixelIndex < numOfPixels) {
483f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            // reassign data and index for every byte (8 bits).
484f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            if (pixelIndex % 8 == 0) {
485f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                currentByte = data[valueIndex++];
486f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                bitIndex = 7;
487f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            }
488f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01);
489f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
490f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
491f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (pixelIndex != numOfPixels) {
492f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            Rlog.e(LOG_TAG, "parse end and size error");
493f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
494f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
495f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
496f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
497f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    private static int bitToRGB(int bit){
498f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if(bit == 1){
499f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return Color.WHITE;
500f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        } else {
501f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return Color.BLACK;
502f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
503f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
504f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
505f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    /**
506f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * a TS 131.102 image instance of code scheme '11' into color Bitmap
507f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     *
508f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @param data The raw data
509f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @param length the length of image body
510f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @param transparency with or without transparency
511f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     * @return The color bitmap
512f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold     */
513f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    public static Bitmap parseToRGB(byte[] data, int length,
514f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            boolean transparency) {
515f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int valueIndex = 0;
516f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int width = data[valueIndex++] & 0xFF;
517f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int height = data[valueIndex++] & 0xFF;
518f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int bits = data[valueIndex++] & 0xFF;
519f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int colorNumber = data[valueIndex++] & 0xFF;
520f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int clutOffset = ((data[valueIndex++] & 0xFF) << 8)
521f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                | (data[valueIndex++] & 0xFF);
522f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
523f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
524f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (true == transparency) {
525f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            colorIndexArray[colorNumber - 1] = Color.TRANSPARENT;
526f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
527f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
528f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int[] resultArray = null;
529f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (0 == (8 % bits)) {
530f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            resultArray = mapTo2OrderBitColor(data, valueIndex,
531f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    (width * height), colorIndexArray, bits);
532f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        } else {
533f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            resultArray = mapToNon2OrderBitColor(data, valueIndex,
534f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    (width * height), colorIndexArray, bits);
535f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
536f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
537f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return Bitmap.createBitmap(resultArray, width, height,
538f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                Bitmap.Config.RGB_565);
539f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
540f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
541f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex,
542f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            int length, int[] colorArray, int bits) {
543f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (0 != (8 % bits)) {
544f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            Rlog.e(LOG_TAG, "not event number of color");
545f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return mapToNon2OrderBitColor(data, valueIndex, length, colorArray,
546f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    bits);
547f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
548f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
549f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int mask = 0x01;
550f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        switch (bits) {
551f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        case 1:
552f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            mask = 0x01;
553f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            break;
554f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        case 2:
555f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            mask = 0x03;
556f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            break;
557f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        case 4:
558f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            mask = 0x0F;
559f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            break;
560f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        case 8:
561f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            mask = 0xFF;
562f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            break;
563f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
564f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
565f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int[] resultArray = new int[length];
566f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int resultIndex = 0;
567f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int run = 8 / bits;
568f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        while (resultIndex < length) {
569f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            byte tempByte = data[valueIndex++];
570f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            for (int runIndex = 0; runIndex < run; ++runIndex) {
571f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                int offset = run - runIndex - 1;
572f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits))
573f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                        & mask];
574f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            }
575f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
576f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return resultArray;
577f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
578f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
579f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex,
580f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            int length, int[] colorArray, int bits) {
581f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (0 == (8 % bits)) {
582f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            Rlog.e(LOG_TAG, "not odd number of color");
583f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return mapTo2OrderBitColor(data, valueIndex, length, colorArray,
584f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    bits);
585f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
586f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
587f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int[] resultArray = new int[length];
588f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        // TODO fix me:
589f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return resultArray;
590f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
591f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
592f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    private static int[] getCLUT(byte[] rawData, int offset, int number) {
593f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        if (null == rawData) {
594f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            return null;
595f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        }
596f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold
597f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int[] result = new int[number];
598f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int endIndex = offset + (number * 3); // 1 color use 3 bytes
599f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int valueIndex = offset;
600f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int colorIndex = 0;
601f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        int alpha = 0xff << 24;
602f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        do {
603f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold            result[colorIndex++] = alpha
604f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    | ((rawData[valueIndex++] & 0xFF) << 16)
605f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    | ((rawData[valueIndex++] & 0xFF) << 8)
606f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold                    | ((rawData[valueIndex++] & 0xFF));
607f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        } while (valueIndex < endIndex);
608f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold        return result;
609f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold    }
610cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu
611cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu    public static String getDecimalSubstring(String iccId) {
612cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu        int position;
613cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu        for (position = 0; position < iccId.length(); position ++) {
614cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu            if (!Character.isDigit(iccId.charAt(position))) break;
615cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu        }
616cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu        return iccId.substring( 0, position );
617cf94c67dc0e8aff3cdb069d382a64c4e1ea36ec9Yujing Gu    }
618a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
619a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
620a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts a series of bytes to an integer. This method currently only supports positive 32-bit
621a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * integers.
622a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
623a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param src The source bytes.
624a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param offset The position of the first byte of the data to be converted. The data is base
625a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *     256 with the most significant digit first.
626a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param length The length of the data to be converted. It must be <= 4.
627a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code length} is bigger than 4 or {@code src} cannot be
628a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *     parsed as a positive integer.
629a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length}
630a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *     exceeds the bounds of {@code src}.
631a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
632a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static int bytesToInt(byte[] src, int offset, int length) {
633a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (length > 4) {
634a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IllegalArgumentException(
635a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                    "length must be <= 4 (only 32-bit integer supported): " + length);
636a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
637a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (offset < 0 || length < 0 || offset + length > src.length) {
638a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IndexOutOfBoundsException(
639a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                    "Out of the bounds: src=["
640a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + src.length
641a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + "], offset="
642a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + offset
643a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + ", length="
644a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + length);
645a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
646a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        int result = 0;
647a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        for (int i = 0; i < length; i++) {
648a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            result = (result << 8) | (src[offset + i] & 0xFF);
649a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
650a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (result < 0) {
651a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IllegalArgumentException(
652a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                    "src cannot be parsed as a positive integer: " + result);
653a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
654a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return result;
655a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
656a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
657a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
658a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts a series of bytes to a raw long variable which can be both positive and negative.
659a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * This method currently only supports 64-bit long variable.
660a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
661a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param src The source bytes.
662a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param offset The position of the first byte of the data to be converted. The data is base
663a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *     256 with the most significant digit first.
664a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param length The length of the data to be converted. It must be <= 8.
665a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code length} is bigger than 8.
666a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length}
667a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *     exceeds the bounds of {@code src}.
668a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
669a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static long bytesToRawLong(byte[] src, int offset, int length) {
670a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (length > 8) {
671a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IllegalArgumentException(
672a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                    "length must be <= 8 (only 64-bit long supported): " + length);
673a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
674a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (offset < 0 || length < 0 || offset + length > src.length) {
675a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IndexOutOfBoundsException(
676a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                    "Out of the bounds: src=["
677a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + src.length
678a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + "], offset="
679a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + offset
680a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + ", length="
681a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                            + length);
682a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
683a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        long result = 0;
684a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        for (int i = 0; i < length; i++) {
685a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            result = (result << 8) | (src[offset + i] & 0xFF);
686a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
687a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return result;
688a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
689a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
690a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
691a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts an integer to a new byte array with base 256 and the most significant digit first.
692a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
693a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code value} is negative.
694a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
695a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static byte[] unsignedIntToBytes(int value) {
696a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (value < 0) {
697a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IllegalArgumentException("value must be 0 or positive: " + value);
698a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
699a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        byte[] bytes = new byte[byteNumForUnsignedInt(value)];
700a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        unsignedIntToBytes(value, bytes, 0);
701a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return bytes;
702a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
703a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
704a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
705a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts an integer to a new byte array with base 256 and the most significant digit first.
706a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * The first byte's highest bit is used for sign. If the most significant digit is larger than
707a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * 127, an extra byte (0) will be prepended before it. This method currently doesn't support
708a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * negative values.
709a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
710a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code value} is negative.
711a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
712a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static byte[] signedIntToBytes(int value) {
713a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (value < 0) {
714a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IllegalArgumentException("value must be 0 or positive: " + value);
715a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
716a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        byte[] bytes = new byte[byteNumForSignedInt(value)];
717a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        signedIntToBytes(value, bytes, 0);
718a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return bytes;
719a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
720a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
721a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
722a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts an integer to a series of bytes with base 256 and the most significant digit first.
723a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
724a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param value The integer to be converted.
725a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param dest The destination byte array.
726a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @param offset The start offset of the byte array.
727a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @return The number of byte needeed.
728a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code value} is negative.
729a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}.
730a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
731a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static int unsignedIntToBytes(int value, byte[] dest, int offset) {
732a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return intToBytes(value, dest, offset, false);
733a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
734a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
735a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
736a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts an integer to a series of bytes with base 256 and the most significant digit first.
737a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * The first byte's highest bit is used for sign. If the most significant digit is larger than
738a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * 127, an extra byte (0) will be prepended before it. This method currently doesn't support
739a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * negative values.
740a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
741a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code value} is negative.
742a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}.
743a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
744a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static int signedIntToBytes(int value, byte[] dest, int offset) {
745a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return intToBytes(value, dest, offset, true);
746a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
747a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
748a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
749a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Calculates the number of required bytes to represent {@code value}. The bytes will be base
750a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * 256 with the most significant digit first.
751a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
752a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code value} is negative.
753a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
754a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static int byteNumForUnsignedInt(int value) {
755a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return byteNumForInt(value, false);
756a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
757a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
758a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
759a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Calculates the number of required bytes to represent {@code value}. The bytes will be base
760a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * 256 with the most significant digit first. If the most significant digit is larger than 127,
761a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * an extra byte (0) will be prepended before it. This method currently only supports positive
762a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * integers.
763a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     *
764a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * @throws IllegalArgumentException If {@code value} is negative.
765a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
766a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static int byteNumForSignedInt(int value) {
767a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return byteNumForInt(value, true);
768a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
769a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
770a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    private static int intToBytes(int value, byte[] dest, int offset, boolean signed) {
771a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        int l = byteNumForInt(value, signed);
772a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (offset < 0 || offset + l > dest.length) {
773a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IndexOutOfBoundsException("Not enough space to write. Required bytes: " + l);
774a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
775a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        for (int i = l - 1, v = value; i >= 0; i--, v >>>= 8) {
776a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            byte b = (byte) (v & 0xFF);
777a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            dest[offset + i] = b;
778a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
779a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return l;
780a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
781a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
782a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    private static int byteNumForInt(int value, boolean signed) {
783a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (value < 0) {
784a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            throw new IllegalArgumentException("value must be 0 or positive: " + value);
785a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
786a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (signed) {
787a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            if (value <= 0x7F) {
788a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                return 1;
789a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            }
790a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            if (value <= 0x7FFF) {
791a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                return 2;
792a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            }
793a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            if (value <= 0x7FFFFF) {
794a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                return 3;
795a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            }
796a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        } else {
797a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            if (value <= 0xFF) {
798a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                return 1;
799a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            }
800a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            if (value <= 0xFFFF) {
801a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                return 2;
802a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            }
803a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            if (value <= 0xFFFFFF) {
804a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun                return 3;
805a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            }
806a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
807a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return 4;
808a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
809a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
810a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
811a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
812a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Counts the number of trailing zero bits of a byte.
813a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
814a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static byte countTrailingZeros(byte b) {
815a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (b == 0) {
816a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            return 8;
817a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
818a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        int v = b & 0xFF;
819a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        byte c = 7;
820a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if ((v & 0x0F) != 0) {
821a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            c -= 4;
822a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
823a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if ((v & 0x33) != 0) {
824a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            c -= 2;
825a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
826a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if ((v & 0x55) != 0) {
827a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            c -= 1;
828a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
829a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return c;
830a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
831a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
832a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
833a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts a byte to a hex string.
834a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
835a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    public static String byteToHex(byte b) {
836a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return new String(new char[] {HEX_CHARS[(b & 0xFF) >>> 4], HEX_CHARS[b & 0xF]});
837a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
838a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun
839a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    /**
840b40a26c43d30f475682b59dbbe404bae932b68b0Jun Yin     * Strip all the trailing 'F' characters of a string, e.g., an ICCID.
841b40a26c43d30f475682b59dbbe404bae932b68b0Jun Yin     */
842b40a26c43d30f475682b59dbbe404bae932b68b0Jun Yin    public static String stripTrailingFs(String s) {
843b40a26c43d30f475682b59dbbe404bae932b68b0Jun Yin        return s == null ? null : s.replaceAll("(?i)f*$", "");
844b40a26c43d30f475682b59dbbe404bae932b68b0Jun Yin    }
845b40a26c43d30f475682b59dbbe404bae932b68b0Jun Yin
846b40a26c43d30f475682b59dbbe404bae932b68b0Jun Yin    /**
847a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
848a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     * hex number, 0 will be returned.
849a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun     */
850a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    private static byte charToByte(char c) {
851a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        if (c >= 0x30 && c <= 0x39) {
852a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            return (byte) (c - 0x30);
853a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        } else if (c >= 0x41 && c <= 0x46) {
854a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            return (byte) (c - 0x37);
855a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        } else if (c >= 0x61 && c <= 0x66) {
856a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun            return (byte) (c - 0x57);
857a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        }
858a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun        return 0;
859a2b23916c08a3cca55206510fda6e80360d5b4aaHolly Jiuyu Sun    }
860f3e659e2865fe52af5a87efb52299b81005aeb17Nathan Harold}
861