Last active
August 29, 2015 14:19
-
-
Save wjx/c85c7a2cc45fecc5ec29 to your computer and use it in GitHub Desktop.
Android sms parse
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
frameworks/base/telephony/java/com/android/internal/telephony/GsmAlphabet.java | |
static { | |
int numTables = sLanguageTables.length; | |
sCharsToGsmTables = new SparseIntArray[numTables]; | |
for (int i = 0; i < numTables; i++) { | |
String table = sLanguageTables[i]; | |
int tableLen = table.length(); | |
if (tableLen != 0 && tableLen != 128) { | |
Rlog.e(TAG, "Error: language tables index " + i + | |
" length " + tableLen + " (expected 128 or 0)"); | |
} | |
SparseIntArray charToGsmTable = new SparseIntArray(tableLen); | |
sCharsToGsmTables[i] = charToGsmTable; | |
for (int j = 0; j < tableLen; j++) { | |
char c = table.charAt(j); | |
charToGsmTable.put(c, j); | |
} | |
} | |
} | |
/** | |
* Converts a String into a byte array containing | |
* the 7-bit packed GSM Alphabet representation of the string. | |
* | |
* Byte 0 in the returned byte array is the count of septets used | |
* The returned byte array is the minimum size required to store | |
* the packed septets. The returned array cannot contain more than 255 | |
* septets. | |
* | |
* @param data the text to convert to septets | |
* @param startingSeptetOffset the number of padding septets to put before | |
* the character data at the beginning of the array | |
* @param throwException If true, throws EncodeException on invalid char. | |
* If false, replaces unencodable char with GSM alphabet space char. | |
* @param languageTable the 7 bit language table, or 0 for the default GSM alphabet | |
* @param languageShiftTable the 7 bit single shift language table, or 0 for the default | |
* GSM extension table | |
* @return the encoded message | |
* | |
* @throws EncodeException if String is too large to encode | |
*/ | |
public static byte[] stringToGsm7BitPacked(String data, int startingSeptetOffset, | |
boolean throwException, int languageTable, int languageShiftTable) | |
throws EncodeException { | |
int dataLen = data.length(); | |
int septetCount = countGsmSeptetsUsingTables(data, !throwException, | |
languageTable, languageShiftTable); | |
int byteCount = ((septetCount * 7) + 7) / 8; | |
byte[] ret = new byte[byteCount + 1]; // Include space for one byte length prefix. | |
SparseIntArray charToLanguageTable = sCharsToGsmTables[languageTable]; | |
SparseIntArray charToShiftTable = sCharsToShiftTables[languageShiftTable]; | |
for (int i = 0, septets = startingSeptetOffset, bitOffset = startingSeptetOffset * 7; | |
i < dataLen && septets < septetCount; | |
i++, bitOffset += 7) { | |
char c = data.charAt(i); | |
int v = charToLanguageTable.get(c, -1); | |
if (v == -1) { | |
v = charToShiftTable.get(c, -1); // Lookup the extended char. | |
if (v == -1) { | |
if (throwException) { | |
throw new EncodeException("stringToGsm7BitPacked(): unencodable char"); | |
} else { | |
v = charToLanguageTable.get(' ', ' '); // should return ASCII space | |
} | |
} else { | |
packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE); | |
bitOffset += 7; | |
septets++; | |
} | |
} | |
packSmsChar(ret, bitOffset, v); | |
septets++; | |
} | |
ret[0] = (byte) (septetCount); // Validated by check above. | |
return ret; | |
} | |
/** | |
* Pack a 7-bit char into its appropriate place in a byte array | |
* | |
* @param packedChars the destination byte array | |
* @param bitOffset the bit offset that the septet should be packed at | |
* (septet index * 7) | |
* @param value the 7-bit character to store | |
*/ | |
private static void | |
packSmsChar(byte[] packedChars, int bitOffset, int value) { | |
int byteOffset = bitOffset / 8; | |
int shift = bitOffset % 8; | |
packedChars[++byteOffset] |= value << shift; | |
if (shift > 1) { | |
packedChars[++byteOffset] = (byte)(value >> (8 - shift)); | |
} | |
} | |
/*----------------------------------------------------------------------*/ | |
/** | |
* Convert a GSM alphabet 7 bit packed string (SMS string) into a | |
* {@link java.lang.String}. | |
* | |
* See TS 23.038 6.1.2.1 for SMS Character Packing | |
* | |
* @param pdu the raw data from the pdu | |
* @param offset the byte offset of | |
* @param lengthSeptets string length in septets, not bytes | |
* @param numPaddingBits the number of padding bits before the start of the | |
* string in the first byte | |
* @param languageTable the 7 bit language table, or 0 for the default GSM alphabet | |
* @param shiftTable the 7 bit single shift language table, or 0 for the default | |
* GSM extension table | |
* @return String representation or null on decoding exception | |
*/ | |
public static String gsm7BitPackedToString(byte[] pdu, int offset, | |
int lengthSeptets, int numPaddingBits, int languageTable, int shiftTable) { | |
StringBuilder ret = new StringBuilder(lengthSeptets); | |
try { | |
boolean prevCharWasEscape = false; | |
String languageTableToChar = sLanguageTables[languageTable]; | |
String shiftTableToChar = sLanguageShiftTables[shiftTable]; | |
for (int i = 0 ; i < lengthSeptets ; i++) { | |
int bitOffset = (7 * i) + numPaddingBits; | |
int byteOffset = bitOffset / 8; | |
int shift = bitOffset % 8; | |
int gsmVal; | |
gsmVal = (0x7f & (pdu[offset + byteOffset] >> shift)); | |
// if it crosses a byte boundary | |
if (shift > 1) { | |
// set msb bits to 0 | |
gsmVal &= 0x7f >> (shift - 1); | |
gsmVal |= 0x7f & (pdu[offset + byteOffset + 1] << (8 - shift)); | |
} | |
if (prevCharWasEscape) { | |
if (gsmVal == GSM_EXTENDED_ESCAPE) { | |
ret.append(' '); // display ' ' for reserved double escape sequence | |
} else { | |
char c = shiftTableToChar.charAt(gsmVal); | |
if (c == ' ') { | |
ret.append(languageTableToChar.charAt(gsmVal)); | |
} else { | |
ret.append(c); | |
} | |
} | |
prevCharWasEscape = false; | |
} else if (gsmVal == GSM_EXTENDED_ESCAPE) { | |
prevCharWasEscape = true; | |
} else { | |
ret.append(languageTableToChar.charAt(gsmVal)); | |
} | |
} | |
} catch (RuntimeException ex) { | |
Rlog.e(TAG, "Error GSM 7 bit packed: ", ex); | |
return null; | |
} | |
return ret.toString(); | |
} | |
/** | |
* GSM default 7 bit alphabet plus national language locking shift character tables. | |
* Comment lines above strings indicate the lower four bits of the table position. | |
*/ | |
private static final String[] sLanguageTables = { | |
/* 3GPP TS 23.038 V9.1.1 section 6.2.1 - GSM 7 bit Default Alphabet | |
01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....0.....1 */ | |
"@\u00a3$\u00a5\u00e8\u00e9\u00f9\u00ec\u00f2\u00c7\n\u00d8\u00f8\r\u00c5\u00e5\u0394_" | |
// 2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E..... | |
+ "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e\uffff\u00c6\u00e6\u00df" | |
// F.....012.34.....56789ABCDEF0123456789ABCDEF0.....123456789ABCDEF0123456789A | |
+ "\u00c9 !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u00a1ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
// B.....C.....D.....E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D..... | |
+ "\u00c4\u00d6\u00d1\u00dc\u00a7\u00bfabcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1" | |
// E.....F..... | |
+ "\u00fc\u00e0", | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SmsMessage.java | |
/** | |
* TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the | |
* +CMT unsolicited response (PDU mode, of course) | |
* +CMT: [<alpha>],<length><CR><LF><pdu> | |
* | |
* Only public for debugging | |
* | |
* {@hide} | |
*/ | |
public static SmsMessage newFromCMT(String[] lines) { | |
try { | |
SmsMessage msg = new SmsMessage(); | |
msg.parsePdu(IccUtils.hexStringToBytes(lines[1])); | |
return msg; | |
} catch (RuntimeException ex) { | |
Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex); | |
return null; | |
} | |
} | |
/** | |
* TS 27.005 3.1, <pdu> definition "In the case of SMS: 3GPP TS 24.011 [6] | |
* SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format: | |
* ME/TA converts each octet of TP data unit into two IRA character long | |
* hex number (e.g. octet with integer value 42 is presented to TE as two | |
* characters 2A (IRA 50 and 65))" ...in the case of cell broadcast, | |
* something else... | |
*/ | |
private void parsePdu(byte[] pdu) { | |
mPdu = pdu; | |
// Rlog.d(LOG_TAG, "raw sms message:"); | |
// Rlog.d(LOG_TAG, s); | |
PduParser p = new PduParser(pdu); | |
mScAddress = p.getSCAddress(); | |
if (mScAddress != null) { | |
if (VDBG) Rlog.d(LOG_TAG, "SMS SC address: " + mScAddress); | |
} | |
// TODO(mkf) support reply path, user data header indicator | |
// TP-Message-Type-Indicator | |
// 9.2.3 | |
int firstByte = p.getByte(); | |
mMti = firstByte & 0x3; | |
switch (mMti) { | |
// TP-Message-Type-Indicator | |
// 9.2.3 | |
case 0: | |
case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved. | |
//This should be processed in the same way as MTI == 0 (Deliver) | |
parseSmsDeliver(p, firstByte); | |
break; | |
case 1: | |
parseSmsSubmit(p, firstByte); | |
break; | |
case 2: | |
parseSmsStatusReport(p, firstByte); | |
break; | |
default: | |
// TODO(mkf) the rest of these | |
throw new RuntimeException("Unsupported message type"); | |
} | |
} | |
private void parseSmsDeliver(PduParser p, int firstByte) { | |
mReplyPathPresent = (firstByte & 0x80) == 0x80; | |
mOriginatingAddress = p.getAddress(); | |
if (mOriginatingAddress != null) { | |
if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: " | |
+ mOriginatingAddress.address); | |
} | |
// TP-Protocol-Identifier (TP-PID) | |
// TS 23.040 9.2.3.9 | |
mProtocolIdentifier = p.getByte(); | |
// TP-Data-Coding-Scheme | |
// see TS 23.038 | |
mDataCodingScheme = p.getByte(); | |
if (VDBG) { | |
Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier | |
+ " data coding scheme: " + mDataCodingScheme); | |
} | |
mScTimeMillis = p.getSCTimestampMillis(); | |
if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis); | |
boolean hasUserDataHeader = (firstByte & 0x40) == 0x40; | |
parseUserData(p, hasUserDataHeader); | |
} | |
private static class PduParser { | |
byte mPdu[]; | |
int mCur; | |
SmsHeader mUserDataHeader; | |
byte[] mUserData; | |
int mUserDataSeptetPadding; | |
PduParser(byte[] pdu) { | |
mPdu = pdu; | |
mCur = 0; | |
mUserDataSeptetPadding = 0; | |
} | |
/** | |
* returns non-sign-extended byte value | |
*/ | |
int getByte() { | |
return mPdu[mCur++] & 0xff; | |
} | |
/** | |
* Interprets the user data payload as packed GSM 7bit characters, and | |
* decodes them into a String. | |
* | |
* @param septetCount the number of septets in the user data payload | |
* @return a String with the decoded characters | |
*/ | |
String getUserDataGSM7Bit(int septetCount, int languageTable, | |
int languageShiftTable) { | |
String ret; | |
ret = GsmAlphabet.gsm7BitPackedToString(mPdu, mCur, septetCount, | |
mUserDataSeptetPadding, languageTable, languageShiftTable); | |
mCur += (septetCount * 7) / 8; | |
return ret; | |
} | |
/** | |
* Interprets the user data payload as UCS2 characters, and | |
* decodes them into a String. | |
* | |
* @param byteCount the number of bytes in the user data payload | |
* @return a String with the decoded characters | |
*/ | |
String getUserDataUCS2(int byteCount) { | |
String ret; | |
try { | |
ret = new String(mPdu, mCur, byteCount, "utf-16"); | |
} catch (UnsupportedEncodingException ex) { | |
ret = ""; | |
Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex); | |
} | |
mCur += byteCount; | |
return ret; | |
} | |
boolean moreDataPresent() { | |
return (mPdu.length > mCur); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment