/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.uicc;

import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.telephony.Rlog;

import com.android.internal.telephony.GsmAlphabet;

import java.io.UnsupportedEncodingException;

Various methods, useful for dealing with SIM data.
/** * Various methods, useful for dealing with SIM data. */
public class IccUtils { static final String LOG_TAG="IccUtils"; // A table mapping from a number to a hex character for fast encoding hex strings. private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
Many fields in GSM SIM's are stored as nibble-swizzled BCD Assumes left-justified field that may be padded right with 0xf values. Stops on invalid BCD value, returning string so far
/** * Many fields in GSM SIM's are stored as nibble-swizzled BCD * * Assumes left-justified field that may be padded right with 0xf * values. * * Stops on invalid BCD value, returning string so far */
public static String bcdToString(byte[] data, int offset, int length) { StringBuilder ret = new StringBuilder(length*2); for (int i = offset ; i < offset + length ; i++) { int v; v = data[i] & 0xf; if (v > 9) break; ret.append((char)('0' + v)); v = (data[i] >> 4) & 0xf; // Some PLMNs have 'f' as high nibble, ignore it if (v == 0xf) continue; if (v > 9) break; ret.append((char)('0' + v)); } return ret.toString(); }
Converts a bcd byte array to String with offset 0 and byte array length.
/** * Converts a bcd byte array to String with offset 0 and byte array length. */
public static String bcdToString(byte[] data) { return bcdToString(data, 0, data.length); }
Converts BCD string to bytes.
Params:
  • bcd – This should have an even length. If not, an "0" will be appended to the string.
/** * Converts BCD string to bytes. * * @param bcd This should have an even length. If not, an "0" will be appended to the string. */
public static byte[] bcdToBytes(String bcd) { byte[] output = new byte[(bcd.length() + 1) / 2]; bcdToBytes(bcd, output); return output; }
Converts BCD string to bytes and put it into the given byte array.
Params:
  • bcd – This should have an even length. If not, an "0" will be appended to the string.
  • bytes – If the array size is less than needed, the rest of the BCD string isn't be converted. If the array size is more than needed, the rest of array remains unchanged.
/** * Converts BCD string to bytes and put it into the given byte array. * * @param bcd This should have an even length. If not, an "0" will be appended to the string. * @param bytes If the array size is less than needed, the rest of the BCD string isn't be * converted. If the array size is more than needed, the rest of array remains unchanged. */
public static void bcdToBytes(String bcd, byte[] bytes) { if (bcd.length() % 2 != 0) { bcd += "0"; } int size = Math.min(bytes.length * 2, bcd.length()); for (int i = 0, j = 0; i + 1 < size; i += 2, j++) { bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i))); } }
PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3 Returns a concatenated string of MCC+MNC, stripping all invalid character 'F'
/** * PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3 * Returns a concatenated string of MCC+MNC, stripping * all invalid character 'F' */
public static String bcdPlmnToString(byte[] data, int offset) { if (offset + 3 > data.length) { return null; } byte[] trans = new byte[3]; trans[0] = (byte) ((data[0 + offset] << 4) | ((data[0 + offset] >> 4) & 0xF)); trans[1] = (byte) ((data[1 + offset] << 4) | (data[2 + offset] & 0xF)); trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF)); String ret = bytesToHexString(trans); // For a valid plmn we trim all character 'F' if (ret.contains("F")) { ret = ret.replaceAll("F", ""); } return ret; }
Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
/** * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH */
public static String bchToString(byte[] data, int offset, int length) { StringBuilder ret = new StringBuilder(length*2); for (int i = offset ; i < offset + length ; i++) { int v; v = data[i] & 0xf; ret.append(HEX_CHARS[v]); v = (data[i] >> 4) & 0xf; ret.append(HEX_CHARS[v]); } return ret.toString(); }
Decode cdma byte into String.
/** * Decode cdma byte into String. */
public static String cdmaBcdToString(byte[] data, int offset, int length) { StringBuilder ret = new StringBuilder(length); int count = 0; for (int i = offset; count < length; i++) { int v; v = data[i] & 0xf; if (v > 9) v = 0; ret.append((char)('0' + v)); if (++count == length) break; v = (data[i] >> 4) & 0xf; if (v > 9) v = 0; ret.append((char)('0' + v)); ++count; } return ret.toString(); }
Decodes a GSM-style BCD byte, returning an int ranging from 0-99. In GSM land, the least significant BCD digit is stored in the most significant nibble. Out-of-range digits are treated as 0 for the sake of the time stamp, because of this: TS 23.040 section 9.2.3.11 "if the MS receives a non-integer value in the SCTS, it shall assume the digit is set to 0 but shall store the entire field exactly as received"
/** * Decodes a GSM-style BCD byte, returning an int ranging from 0-99. * * In GSM land, the least significant BCD digit is stored in the most * significant nibble. * * Out-of-range digits are treated as 0 for the sake of the time stamp, * because of this: * * TS 23.040 section 9.2.3.11 * "if the MS receives a non-integer value in the SCTS, it shall * assume the digit is set to 0 but shall store the entire field * exactly as received" */
public static int gsmBcdByteToInt(byte b) { int ret = 0; // treat out-of-range BCD values as 0 if ((b & 0xf0) <= 0x90) { ret = (b >> 4) & 0xf; } if ((b & 0x0f) <= 0x09) { ret += (b & 0xf) * 10; } return ret; }
Decodes a CDMA style BCD byte like gsmBcdByteToInt, but opposite nibble format. The least significant BCD digit is in the least significant nibble and the most significant is in the most significant nibble.
/** * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but * opposite nibble format. The least significant BCD digit * is in the least significant nibble and the most significant * is in the most significant nibble. */
public static int cdmaBcdByteToInt(byte b) { int ret = 0; // treat out-of-range BCD values as 0 if ((b & 0xf0) <= 0x90) { ret = ((b >> 4) & 0xf) * 10; } if ((b & 0x0f) <= 0x09) { ret += (b & 0xf); } return ret; }
Decodes a string field that's formatted like the EF[ADN] alpha identifier From TS 51.011 10.5.1: Coding: this alpha tagging shall use either - the SMS default 7 bit coded alphabet as defined in TS 23.038 [12] with bit 8 set to 0. The alpha identifier shall be left justified. Unused bytes shall be set to 'FF'; or - one of the UCS2 coded options as defined in annex B. Annex B from TS 11.11 V8.13.0: 1) If the first octet in the alpha string is '80', then the remaining octets are 16 bit UCS2 characters ... 2) if the first octet in the alpha string is '81', then the second octet contains a value indicating the number of characters in the string, and the third octet contains an 8 bit number which defines bits 15 to 8 of a 16 bit base pointer, where bit 16 is set to zero and bits 7 to 1 are also set to zero. These sixteen bits constitute a base pointer to a "half page" in the UCS2 code space, to be used with some or all of the remaining octets in the string. The fourth and subsequent octets contain codings as follows: If bit 8 of the octet is set to zero, the remaining 7 bits of the octet contain a GSM Default Alphabet character, whereas if bit 8 of the octet is set to one, then the remaining seven bits are an offset value added to the 16 bit base pointer defined earlier... 3) If the first octet of the alpha string is set to '82', then the second octet contains a value indicating the number of characters in the string, and the third and fourth octets contain a 16 bit number which defines the complete 16 bit base pointer to a "half page" in the UCS2 code space...
/** * Decodes a string field that's formatted like the EF[ADN] alpha * identifier * * From TS 51.011 10.5.1: * Coding: * this alpha tagging shall use either * - the SMS default 7 bit coded alphabet as defined in * TS 23.038 [12] with bit 8 set to 0. The alpha identifier * shall be left justified. Unused bytes shall be set to 'FF'; or * - one of the UCS2 coded options as defined in annex B. * * Annex B from TS 11.11 V8.13.0: * 1) If the first octet in the alpha string is '80', then the * remaining octets are 16 bit UCS2 characters ... * 2) if the first octet in the alpha string is '81', then the * second octet contains a value indicating the number of * characters in the string, and the third octet contains an * 8 bit number which defines bits 15 to 8 of a 16 bit * base pointer, where bit 16 is set to zero and bits 7 to 1 * are also set to zero. These sixteen bits constitute a * base pointer to a "half page" in the UCS2 code space, to be * used with some or all of the remaining octets in the string. * The fourth and subsequent octets contain codings as follows: * If bit 8 of the octet is set to zero, the remaining 7 bits * of the octet contain a GSM Default Alphabet character, * whereas if bit 8 of the octet is set to one, then the * remaining seven bits are an offset value added to the * 16 bit base pointer defined earlier... * 3) If the first octet of the alpha string is set to '82', then * the second octet contains a value indicating the number of * characters in the string, and the third and fourth octets * contain a 16 bit number which defines the complete 16 bit * base pointer to a "half page" in the UCS2 code space... */
public static String adnStringFieldToString(byte[] data, int offset, int length) { if (length == 0) { return ""; } if (length >= 1) { if (data[offset] == (byte) 0x80) { int ucslen = (length - 1) / 2; String ret = null; try { ret = new String(data, offset + 1, ucslen * 2, "utf-16be"); } catch (UnsupportedEncodingException ex) { Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex); } if (ret != null) { // trim off trailing FFFF characters ucslen = ret.length(); while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF') ucslen--; return ret.substring(0, ucslen); } } } boolean isucs2 = false; char base = '\0'; int len = 0; if (length >= 3 && data[offset] == (byte) 0x81) { len = data[offset + 1] & 0xFF; if (len > length - 3) len = length - 3; base = (char) ((data[offset + 2] & 0xFF) << 7); offset += 3; isucs2 = true; } else if (length >= 4 && data[offset] == (byte) 0x82) { len = data[offset + 1] & 0xFF; if (len > length - 4) len = length - 4; base = (char) (((data[offset + 2] & 0xFF) << 8) | (data[offset + 3] & 0xFF)); offset += 4; isucs2 = true; } if (isucs2) { StringBuilder ret = new StringBuilder(); while (len > 0) { // UCS2 subset case if (data[offset] < 0) { ret.append((char) (base + (data[offset] & 0x7F))); offset++; len--; } // GSM character set case int count = 0; while (count < len && data[offset + count] >= 0) count++; ret.append(GsmAlphabet.gsm8BitUnpackedToString(data, offset, count)); offset += count; len -= count; } return ret.toString(); } Resources resource = Resources.getSystem(); String defaultCharset = ""; try { defaultCharset = resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset); } catch (NotFoundException e) { // Ignore Exception and defaultCharset is set to a empty string. } return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim()); } public static int hexCharToInt(char c) { if (c >= '0' && c <= '9') return (c - '0'); if (c >= 'A' && c <= 'F') return (c - 'A' + 10); if (c >= 'a' && c <= 'f') return (c - 'a' + 10); throw new RuntimeException ("invalid hex char '" + c + "'"); }
Converts a hex String to a byte array.
Params:
  • s – A string of hexadecimal characters, must be an even number of chars long
Throws:
Returns:byte array representation
/** * Converts a hex String to a byte array. * * @param s A string of hexadecimal characters, must be an even number of * chars long * * @return byte array representation * * @throws RuntimeException on invalid format */
public static byte[] hexStringToBytes(String s) { byte[] ret; if (s == null) return null; int sz = s.length(); ret = new byte[sz/2]; for (int i=0 ; i <sz ; i+=2) { ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4) | hexCharToInt(s.charAt(i+1))); } return ret; }
Converts a byte array into a String of hexadecimal characters.
Params:
  • bytes – an array of bytes
Returns:hex string representation of bytes array
/** * Converts a byte array into a String of hexadecimal characters. * * @param bytes an array of bytes * * @return hex string representation of bytes array */
public static String bytesToHexString(byte[] bytes) { if (bytes == null) return null; StringBuilder ret = new StringBuilder(2*bytes.length); for (int i = 0 ; i < bytes.length ; i++) { int b; b = 0x0f & (bytes[i] >> 4); ret.append(HEX_CHARS[b]); b = 0x0f & bytes[i]; ret.append(HEX_CHARS[b]); } return ret.toString(); }
Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string "offset" points to "octet 3", the coding scheme byte empty string returned on decode error
/** * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string * "offset" points to "octet 3", the coding scheme byte * empty string returned on decode error */
public static String networkNameToString(byte[] data, int offset, int length) { String ret; if ((data[offset] & 0x80) != 0x80 || length < 1) { return ""; } switch ((data[offset] >>> 4) & 0x7) { case 0: // SMS character set int countSeptets; int unusedBits = data[offset] & 7; countSeptets = (((length - 1) * 8) - unusedBits) / 7 ; ret = GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets); break; case 1: // UCS2 try { ret = new String(data, offset + 1, length - 1, "utf-16"); } catch (UnsupportedEncodingException ex) { ret = ""; Rlog.e(LOG_TAG,"implausible UnsupportedEncodingException", ex); } break; // unsupported encoding default: ret = ""; break; } // "Add CI" // "The MS should add the letters for the Country's Initials and // a separator (e.g. a space) to the text string" if ((data[offset] & 0x40) != 0) { // FIXME(mkf) add country initials here } return ret; }
Convert a TS 131.102 image instance of code scheme '11' into Bitmap
Params:
  • data – The raw data
  • length – The length of image body
Returns:The bitmap
/** * Convert a TS 131.102 image instance of code scheme '11' into Bitmap * @param data The raw data * @param length The length of image body * @return The bitmap */
public static Bitmap parseToBnW(byte[] data, int length){ int valueIndex = 0; int width = data[valueIndex++] & 0xFF; int height = data[valueIndex++] & 0xFF; int numOfPixels = width*height; int[] pixels = new int[numOfPixels]; int pixelIndex = 0; int bitIndex = 7; byte currentByte = 0x00; while (pixelIndex < numOfPixels) { // reassign data and index for every byte (8 bits). if (pixelIndex % 8 == 0) { currentByte = data[valueIndex++]; bitIndex = 7; } pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01); } if (pixelIndex != numOfPixels) { Rlog.e(LOG_TAG, "parse end and size error"); } return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); } private static int bitToRGB(int bit){ if(bit == 1){ return Color.WHITE; } else { return Color.BLACK; } }
a TS 131.102 image instance of code scheme '11' into color Bitmap
Params:
  • data – The raw data
  • length – the length of image body
  • transparency – with or without transparency
Returns:The color bitmap
/** * a TS 131.102 image instance of code scheme '11' into color Bitmap * * @param data The raw data * @param length the length of image body * @param transparency with or without transparency * @return The color bitmap */
public static Bitmap parseToRGB(byte[] data, int length, boolean transparency) { int valueIndex = 0; int width = data[valueIndex++] & 0xFF; int height = data[valueIndex++] & 0xFF; int bits = data[valueIndex++] & 0xFF; int colorNumber = data[valueIndex++] & 0xFF; int clutOffset = ((data[valueIndex++] & 0xFF) << 8) | (data[valueIndex++] & 0xFF); int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber); if (true == transparency) { colorIndexArray[colorNumber - 1] = Color.TRANSPARENT; } int[] resultArray = null; if (0 == (8 % bits)) { resultArray = mapTo2OrderBitColor(data, valueIndex, (width * height), colorIndexArray, bits); } else { resultArray = mapToNon2OrderBitColor(data, valueIndex, (width * height), colorIndexArray, bits); } return Bitmap.createBitmap(resultArray, width, height, Bitmap.Config.RGB_565); } private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex, int length, int[] colorArray, int bits) { if (0 != (8 % bits)) { Rlog.e(LOG_TAG, "not event number of color"); return mapToNon2OrderBitColor(data, valueIndex, length, colorArray, bits); } int mask = 0x01; switch (bits) { case 1: mask = 0x01; break; case 2: mask = 0x03; break; case 4: mask = 0x0F; break; case 8: mask = 0xFF; break; } int[] resultArray = new int[length]; int resultIndex = 0; int run = 8 / bits; while (resultIndex < length) { byte tempByte = data[valueIndex++]; for (int runIndex = 0; runIndex < run; ++runIndex) { int offset = run - runIndex - 1; resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits)) & mask]; } } return resultArray; } private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex, int length, int[] colorArray, int bits) { if (0 == (8 % bits)) { Rlog.e(LOG_TAG, "not odd number of color"); return mapTo2OrderBitColor(data, valueIndex, length, colorArray, bits); } int[] resultArray = new int[length]; // TODO fix me: return resultArray; } private static int[] getCLUT(byte[] rawData, int offset, int number) { if (null == rawData) { return null; } int[] result = new int[number]; int endIndex = offset + (number * 3); // 1 color use 3 bytes int valueIndex = offset; int colorIndex = 0; int alpha = 0xff << 24; do { result[colorIndex++] = alpha | ((rawData[valueIndex++] & 0xFF) << 16) | ((rawData[valueIndex++] & 0xFF) << 8) | ((rawData[valueIndex++] & 0xFF)); } while (valueIndex < endIndex); return result; } public static String getDecimalSubstring(String iccId) { int position; for (position = 0; position < iccId.length(); position ++) { if (!Character.isDigit(iccId.charAt(position))) break; } return iccId.substring( 0, position ); }
Converts a series of bytes to an integer. This method currently only supports positive 32-bit integers.
Params:
  • src – The source bytes.
  • offset – The position of the first byte of the data to be converted. The data is base 256 with the most significant digit first.
  • length – The length of the data to be converted. It must be <= 4.
Throws:
/** * Converts a series of bytes to an integer. This method currently only supports positive 32-bit * integers. * * @param src The source bytes. * @param offset The position of the first byte of the data to be converted. The data is base * 256 with the most significant digit first. * @param length The length of the data to be converted. It must be <= 4. * @throws IllegalArgumentException If {@code length} is bigger than 4 or {@code src} cannot be * parsed as a positive integer. * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length} * exceeds the bounds of {@code src}. */
public static int bytesToInt(byte[] src, int offset, int length) { if (length > 4) { throw new IllegalArgumentException( "length must be <= 4 (only 32-bit integer supported): " + length); } if (offset < 0 || length < 0 || offset + length > src.length) { throw new IndexOutOfBoundsException( "Out of the bounds: src=[" + src.length + "], offset=" + offset + ", length=" + length); } int result = 0; for (int i = 0; i < length; i++) { result = (result << 8) | (src[offset + i] & 0xFF); } if (result < 0) { throw new IllegalArgumentException( "src cannot be parsed as a positive integer: " + result); } return result; }
Converts a series of bytes to a raw long variable which can be both positive and negative. This method currently only supports 64-bit long variable.
Params:
  • src – The source bytes.
  • offset – The position of the first byte of the data to be converted. The data is base 256 with the most significant digit first.
  • length – The length of the data to be converted. It must be <= 8.
Throws:
/** * Converts a series of bytes to a raw long variable which can be both positive and negative. * This method currently only supports 64-bit long variable. * * @param src The source bytes. * @param offset The position of the first byte of the data to be converted. The data is base * 256 with the most significant digit first. * @param length The length of the data to be converted. It must be <= 8. * @throws IllegalArgumentException If {@code length} is bigger than 8. * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length} * exceeds the bounds of {@code src}. */
public static long bytesToRawLong(byte[] src, int offset, int length) { if (length > 8) { throw new IllegalArgumentException( "length must be <= 8 (only 64-bit long supported): " + length); } if (offset < 0 || length < 0 || offset + length > src.length) { throw new IndexOutOfBoundsException( "Out of the bounds: src=[" + src.length + "], offset=" + offset + ", length=" + length); } long result = 0; for (int i = 0; i < length; i++) { result = (result << 8) | (src[offset + i] & 0xFF); } return result; }
Converts an integer to a new byte array with base 256 and the most significant digit first.
Throws:
  • IllegalArgumentException – If value is negative.
/** * Converts an integer to a new byte array with base 256 and the most significant digit first. * * @throws IllegalArgumentException If {@code value} is negative. */
public static byte[] unsignedIntToBytes(int value) { if (value < 0) { throw new IllegalArgumentException("value must be 0 or positive: " + value); } byte[] bytes = new byte[byteNumForUnsignedInt(value)]; unsignedIntToBytes(value, bytes, 0); return bytes; }
Converts an integer to a new byte array with base 256 and the most significant digit first. The first byte's highest bit is used for sign. If the most significant digit is larger than 127, an extra byte (0) will be prepended before it. This method currently doesn't support negative values.
Throws:
  • IllegalArgumentException – If value is negative.
/** * Converts an integer to a new byte array with base 256 and the most significant digit first. * The first byte's highest bit is used for sign. If the most significant digit is larger than * 127, an extra byte (0) will be prepended before it. This method currently doesn't support * negative values. * * @throws IllegalArgumentException If {@code value} is negative. */
public static byte[] signedIntToBytes(int value) { if (value < 0) { throw new IllegalArgumentException("value must be 0 or positive: " + value); } byte[] bytes = new byte[byteNumForSignedInt(value)]; signedIntToBytes(value, bytes, 0); return bytes; }
Converts an integer to a series of bytes with base 256 and the most significant digit first.
Params:
  • value – The integer to be converted.
  • dest – The destination byte array.
  • offset – The start offset of the byte array.
Throws:
Returns:The number of byte needeed.
/** * Converts an integer to a series of bytes with base 256 and the most significant digit first. * * @param value The integer to be converted. * @param dest The destination byte array. * @param offset The start offset of the byte array. * @return The number of byte needeed. * @throws IllegalArgumentException If {@code value} is negative. * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}. */
public static int unsignedIntToBytes(int value, byte[] dest, int offset) { return intToBytes(value, dest, offset, false); }
Converts an integer to a series of bytes with base 256 and the most significant digit first. The first byte's highest bit is used for sign. If the most significant digit is larger than 127, an extra byte (0) will be prepended before it. This method currently doesn't support negative values.
Throws:
/** * Converts an integer to a series of bytes with base 256 and the most significant digit first. * The first byte's highest bit is used for sign. If the most significant digit is larger than * 127, an extra byte (0) will be prepended before it. This method currently doesn't support * negative values. * * @throws IllegalArgumentException If {@code value} is negative. * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}. */
public static int signedIntToBytes(int value, byte[] dest, int offset) { return intToBytes(value, dest, offset, true); }
Calculates the number of required bytes to represent value. The bytes will be base 256 with the most significant digit first.
Throws:
/** * Calculates the number of required bytes to represent {@code value}. The bytes will be base * 256 with the most significant digit first. * * @throws IllegalArgumentException If {@code value} is negative. */
public static int byteNumForUnsignedInt(int value) { return byteNumForInt(value, false); }
Calculates the number of required bytes to represent value. The bytes will be base 256 with the most significant digit first. If the most significant digit is larger than 127, an extra byte (0) will be prepended before it. This method currently only supports positive integers.
Throws:
/** * Calculates the number of required bytes to represent {@code value}. The bytes will be base * 256 with the most significant digit first. If the most significant digit is larger than 127, * an extra byte (0) will be prepended before it. This method currently only supports positive * integers. * * @throws IllegalArgumentException If {@code value} is negative. */
public static int byteNumForSignedInt(int value) { return byteNumForInt(value, true); } private static int intToBytes(int value, byte[] dest, int offset, boolean signed) { int l = byteNumForInt(value, signed); if (offset < 0 || offset + l > dest.length) { throw new IndexOutOfBoundsException("Not enough space to write. Required bytes: " + l); } for (int i = l - 1, v = value; i >= 0; i--, v >>>= 8) { byte b = (byte) (v & 0xFF); dest[offset + i] = b; } return l; } private static int byteNumForInt(int value, boolean signed) { if (value < 0) { throw new IllegalArgumentException("value must be 0 or positive: " + value); } if (signed) { if (value <= 0x7F) { return 1; } if (value <= 0x7FFF) { return 2; } if (value <= 0x7FFFFF) { return 3; } } else { if (value <= 0xFF) { return 1; } if (value <= 0xFFFF) { return 2; } if (value <= 0xFFFFFF) { return 3; } } return 4; }
Counts the number of trailing zero bits of a byte.
/** * Counts the number of trailing zero bits of a byte. */
public static byte countTrailingZeros(byte b) { if (b == 0) { return 8; } int v = b & 0xFF; byte c = 7; if ((v & 0x0F) != 0) { c -= 4; } if ((v & 0x33) != 0) { c -= 2; } if ((v & 0x55) != 0) { c -= 1; } return c; }
Converts a byte to a hex string.
/** * Converts a byte to a hex string. */
public static String byteToHex(byte b) { return new String(new char[] {HEX_CHARS[(b & 0xFF) >>> 4], HEX_CHARS[b & 0xF]}); }
Strip all the trailing 'F' characters of a string, e.g., an ICCID.
/** * Strip all the trailing 'F' characters of a string, e.g., an ICCID. */
public static String stripTrailingFs(String s) { return s == null ? null : s.replaceAll("(?i)f*$", ""); }
Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a hex number, 0 will be returned.
/** * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a * hex number, 0 will be returned. */
private static byte charToByte(char c) { if (c >= 0x30 && c <= 0x39) { return (byte) (c - 0x30); } else if (c >= 0x41 && c <= 0x46) { return (byte) (c - 0x37); } else if (c >= 0x61 && c <= 0x66) { return (byte) (c - 0x57); } return 0; } }