package com.android.internal.telephony.uicc; import android.telephony.Rlog; /** * UICC TLV Data Parser according to ETSI TS 102 221 spec. */ public class UiccTlvData { private static final String LOG_TAG = "UiccTlvData"; private static final int TLV_FORMAT_ID = 0x62; private static final int TAG_FILE_DESCRIPTOR = 0x82; private static final int TAG_FILE_IDENTIFIER = 0x83; private static final int TAG_PROPRIETARY_INFO = 0xA5; private static final int TAG_LIFECYCLE_STATUS = 0x8A; private static final int TAG_SECURITY_ATTR_1 = 0x8B; private static final int TAG_SECURITY_ATTR_2 = 0x8C; private static final int TAG_SECURITY_ATTR_3 = 0xAB; private static final int TAG_FILE_SIZE = 0x80; private static final int TAG_TOTAL_FILE_SIZE = 0x81; private static final int TAG_SHORT_FILE_IDENTIFIER = 0x88; private static final int TYPE_5 = 5; private static final int TYPE_2 = 2; int mRecordSize; int mFileSize; int mNumRecords; boolean mIsDataEnough; private int mFileType = -1; private UiccTlvData() { mNumRecords = -1; mFileSize = -1; mRecordSize = -1; } public boolean isIncomplete() { return mNumRecords == -1 || mFileSize == -1 || mRecordSize == -1 || mFileType == -1; } public static boolean isUiccTlvData(byte[] data) { if(data != null && data.length > 0 && TLV_FORMAT_ID == (data[0] & 0xFF)) { return true; } return false; } public static UiccTlvData parse(byte[] data) throws IccFileTypeMismatch{ UiccTlvData parsedData = new UiccTlvData(); if (data == null || data.length == 0 || TLV_FORMAT_ID != (data[0] & 0xFF)) { throw new IccFileTypeMismatch(); } try { int currentLocation = 2; //Ignore FCP size int currentTag; while (currentLocation < data.length) { currentTag = data[currentLocation++] & 0xFF; switch (currentTag) { case TAG_FILE_DESCRIPTOR: currentLocation = parsedData.parseFileDescriptor(data, currentLocation); break; case TAG_FILE_SIZE: currentLocation = parsedData.parseFileSize(data, currentLocation); break; case TAG_FILE_IDENTIFIER: case TAG_PROPRIETARY_INFO: case TAG_LIFECYCLE_STATUS: case TAG_SECURITY_ATTR_1: case TAG_SECURITY_ATTR_2: case TAG_SECURITY_ATTR_3: case TAG_TOTAL_FILE_SIZE: case TAG_SHORT_FILE_IDENTIFIER: currentLocation = parsedData.parseSomeTag(data, currentLocation); break; default: Rlog.d(LOG_TAG, "Unknown tag 0x" + String.format("%02X", currentTag)); currentLocation = parsedData.parseSomeTag(data, currentLocation); break; } } } catch (ArrayIndexOutOfBoundsException e) { //We might be looking at incomplete data but we might have what we need. //Ignore this and let caller handle it by checking isIncomplete } return parsedData; } private int parseFileSize(byte[] data, int currentLocation) { int length = data[currentLocation++] & 0xFF; int fileSize = 0; for (int i = 0; i < length; i++) { fileSize += ((data[currentLocation + i] & 0xFF) << ( 8 * (length - i - 1))); } mFileSize = fileSize; if (mFileType == TYPE_2) { mRecordSize = fileSize; } return currentLocation + length; } private int parseSomeTag(byte[] data, int currentLocation) { //Just skip unwanted tags; int length = data[currentLocation++] & 0xFF; return currentLocation + length; } private int parseFileDescriptor(byte[] data, int currentLocation) throws IccFileTypeMismatch { int length = data[currentLocation++] & 0xFF; if (length == 5) { mRecordSize = ((data[currentLocation + 2] & 0xFF) << 8) + (data[currentLocation + 3] & 0xFF); // Length of 1 record mNumRecords = data[currentLocation + 4] & 0xFF; // Number of records mFileSize = mRecordSize * mNumRecords; mFileType = TYPE_5; return currentLocation + 5; } else if (length == 2) { int descriptorByte = data[currentLocation + 1] & 0xFF; //Ignore descriptorByte for now mNumRecords = 1; mFileType = TYPE_2; return currentLocation + 2; } else { throw new IccFileTypeMismatch(); } } }