Android短信源码分析 --PDU解析过程

原创 2014年12月01日 22:28:49



6.14 PDU解析过程

 

SmsMessage.java,通过createFromPdu开始解析PDU数据。

    public static SmsMessage createFromPdu(byte[] pdu) {

        try {

            SmsMessage msg = new SmsMessage();

            msg.parsePdu(pdu);

            return msg;

        } catch (RuntimeException ex) {

            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);

            return null;

        }

    }

 

依据协议TS 23.040 9.2.3.9,parsePdu 先解析出SCA,再根据mti消息类型决定使用哪个解析函数,对于接收的sms,使用parseSmsDeliver。

 

 

parseSmsDeliver 解析出OA(2字节+号码个数并补偶),PID(1B),dcs(1B),scts(7B),并根据第一个字节判断是否有UDH,最后调用parseUserData。

 

     private void parseSmsDeliver(PduParser p, int firstByte) {

        replyPathPresent = (firstByte & 0x80) == 0x80;

        originatingAddress = p.getAddress();

 

        if (originatingAddress != null) {

            if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "

                    + originatingAddress.address);

        }

 

        protocolIdentifier = p.getByte();

 

        // TP-Data-Coding-Scheme

        // see TS 23.038

        dataCodingScheme = p.getByte();

 

        scTimeMillis = p.getSCTimestampMillis();

 

        if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);

 

        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

 

        parseUserData(p, hasUserDataHeader);

    }

 

parseUserData,先根据协议3GPP TS 23.038 V7.0.0,将DCS分类,解析消息属性。再使用constructUserData创建一个用户实例,分解出用户数据userData和用户数据头userDataHeader,

int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {

            if (hasUserDataHeader) {

                System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);

                userDataHeader = SmsHeader.fromByteArray(udh);

           }

            userData = new byte[bufferLen];

            System.arraycopy(pdu, offset, userData, 0, userData.length);

       }

 

 

对于用户数据头userDataHeader,

SmsHeader fromByteArray会根据协议TS 23.040 9.2.3.24来解析用户数据头信息UDH,先是头信息总长度UDHL,再是具体的数据,每一段的数据格式为:类型ID(1B)+该类型后续数据长度(1B)+数据(x B,决定于长度),有多少个类型就由多少倍上面的数据长度,示例数据:04 01020101,UDHL 4字节,有一条ID=01的记录,其数据长2字节,数据是0101。

 

 

如果文件头带R8编码,SmsHeader fromByteArray里会进行相应解析,并将IEI数据信息存放在languageShiftTable和languageTable里面。

     public static SmsHeader fromByteArray(byte[] data) {

        ByteArrayInputStream inStream = new ByteArrayInputStream(data);

        SmsHeader smsHeader = new SmsHeader();

        while (inStream.available() > 0) {

            int id = inStream.read(); \\读出IEI类型

            int length = inStream.read();\\读出IEI长度

           switch (id) {

            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT: \\使用转换表

                smsHeader.languageShiftTable = inStream.read();\\读出IEI数据

                break;

            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:

                smsHeader.languageTable = inStream.read();\\读出IEI数据

                break;

        }

        return smsHeader;

    }

 

对于用户数据userData,

以ENCODING_7BIT编码方式来分析,

         case ENCODING_7BIT:

            messageBody = p.getUserDataGSM7Bit(count,  //字节个数

                    hasUserDataHeader ? userDataHeader.languageTable : 0, //是否使用编码表

                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);

            break;

gsm7BitPackedToString会完成将PDU里面的数据解压成字符串的过程,存放在PDU里的数据是经过7bit压缩过的转义字符串,不是我们正常阅读的字符串。解压的时候会参考languageShiftTable和languageTable来选择相应的字符表。

     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));

                }//对gsmVal的处理是依次解析出进行过7BIT压缩的每个字符,这些字符目前还是转义过的字符,不是我们通过字符串能正常看到的字符

 

                if (prevCharWasEscape) {//前一个字符是转义字符

                    if (gsmVal == GSM_EXTENDED_ESCAPE) {

                        ret.append(' ');    // display ' ' for reserved double escape sequence 连续2个转义字符表示一个空格

                    } else {

                        char c = shiftTableToChar.charAt(gsmVal);

                        if (c == ' ') { // 转义字符后字符应该在shift表里查找,如果shift表里在存放的是空格(见表定义,shift表里没有定义的字符都是空格),则使用基本语言表的字符

                            ret.append(languageTableToChar.charAt(gsmVal));

                        } else {

                            ret.append(c); // 转义字符后字符应该在shift表里查找

                        }

                    }

                    prevCharWasEscape = false;

                } else if (gsmVal == GSM_EXTENDED_ESCAPE) {

                    prevCharWasEscape = true; //当前字符是转义字符,存起来给下一个字符使用

                } else {

                    ret.append(languageTableToChar.charAt(gsmVal));//正常时,找到语言表里的ascii字符

                }            }        }         }

        return ret.toString();

    }

 

至此,PDU数据解析完成。

 

对接收PDU数据解码总结如下:

分段 含义 说明

SC地址信息的长度n, n个八位字节(包括91)       (长度占1字节)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),SMSC地址要补‘F’凑成偶数个                                           (n字节)

基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址,回复地址数字个数m, 共m个十进制数(不包括91和‘F’)                      (x字节)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),回复地址(TP-RA),补‘F’凑成偶数个                                        ((m+1)/2 字节)

协议标识(TP-PID), 00是普通GSM类型,点到点方式  (1字节)

用户信息编码方式(TP-DCS) ,08是UCS2编码         (1字节)

时间戳(TP-SCTS) 80是 +8时区                       (7字节)

用户信息长度(TP-UDL) UCS2编码是字节数,7BIT编码 是字符个数      (长度占1字节)

用户信息(TP-UD)                                    (y字节)

 

举例:

分段 含义 说明

08 地址信息的长度 个八位字节(包括91)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 08 20 05 05 F0 SMSC地址 8613800250500,补‘F’凑成偶数个

84 基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址

0D 回复地址数字个数 共13个十进制数(不包括91和‘F’)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 96 03 29 30 F0 回复地址(TP-RA) 8613693092030,补‘F’凑成偶数个

00 协议标识(TP-PID) 是普通GSM类型,点到点方式

08 用户信息编码方式(TP-DCS) UCS2编码

30 30 21 80 63 54 80 时间戳(TP-SCTS) 2003-3-12 08:36:45  +8时区

06 用户信息长度(TP-UDL) 实际长度6个字节

4F 60 59 7D 00 21 用户信息(TP-UD) “你好!”

6.14 PDU解析过程

 

SmsMessage.java,通过createFromPdu开始解析PDU数据。

    public static SmsMessage createFromPdu(byte[] pdu) {

        try {

            SmsMessage msg = new SmsMessage();

            msg.parsePdu(pdu);

            return msg;

        } catch (RuntimeException ex) {

            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);

            return null;

        }

    }

 

依据协议TS 23.040 9.2.3.9,parsePdu 先解析出SCA,再根据mti消息类型决定使用哪个解析函数,对于接收的sms,使用parseSmsDeliver。

 

 

parseSmsDeliver 解析出OA(2字节+号码个数并补偶),PID(1B),dcs(1B),scts(7B),并根据第一个字节判断是否有UDH,最后调用parseUserData。

 

     private void parseSmsDeliver(PduParser p, int firstByte) {

        replyPathPresent = (firstByte & 0x80) == 0x80;

        originatingAddress = p.getAddress();

 

        if (originatingAddress != null) {

            if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "

                    + originatingAddress.address);

        }

 

        protocolIdentifier = p.getByte();

 

        // TP-Data-Coding-Scheme

        // see TS 23.038

        dataCodingScheme = p.getByte();

 

        scTimeMillis = p.getSCTimestampMillis();

 

        if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);

 

        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

 

        parseUserData(p, hasUserDataHeader);

    }

 

parseUserData,先根据协议3GPP TS 23.038 V7.0.0,将DCS分类,解析消息属性。再使用constructUserData创建一个用户实例,分解出用户数据userData和用户数据头userDataHeader,

int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {

            if (hasUserDataHeader) {

                System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);

                userDataHeader = SmsHeader.fromByteArray(udh);

           }

            userData = new byte[bufferLen];

            System.arraycopy(pdu, offset, userData, 0, userData.length);

       }

 

 

对于用户数据头userDataHeader,

SmsHeader fromByteArray会根据协议TS 23.040 9.2.3.24来解析用户数据头信息UDH,先是头信息总长度UDHL,再是具体的数据,每一段的数据格式为:类型ID(1B)+该类型后续数据长度(1B)+数据(x B,决定于长度),有多少个类型就由多少倍上面的数据长度,示例数据:04 01020101,UDHL 4字节,有一条ID=01的记录,其数据长2字节,数据是0101。

 

 

如果文件头带R8编码,SmsHeader fromByteArray里会进行相应解析,并将IEI数据信息存放在languageShiftTable和languageTable里面。

     public static SmsHeader fromByteArray(byte[] data) {

        ByteArrayInputStream inStream = new ByteArrayInputStream(data);

        SmsHeader smsHeader = new SmsHeader();

        while (inStream.available() > 0) {

            int id = inStream.read(); \\读出IEI类型

            int length = inStream.read();\\读出IEI长度

           switch (id) {

            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT: \\使用转换表

                smsHeader.languageShiftTable = inStream.read();\\读出IEI数据

                break;

            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:

                smsHeader.languageTable = inStream.read();\\读出IEI数据

                break;

        }

        return smsHeader;

    }

 

对于用户数据userData,

以ENCODING_7BIT编码方式来分析,

         case ENCODING_7BIT:

            messageBody = p.getUserDataGSM7Bit(count,  //字节个数

                    hasUserDataHeader ? userDataHeader.languageTable : 0, //是否使用编码表

                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);

            break;

gsm7BitPackedToString会完成将PDU里面的数据解压成字符串的过程,存放在PDU里的数据是经过7bit压缩过的转义字符串,不是我们正常阅读的字符串。解压的时候会参考languageShiftTable和languageTable来选择相应的字符表。

     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));

                }//对gsmVal的处理是依次解析出进行过7BIT压缩的每个字符,这些字符目前还是转义过的字符,不是我们通过字符串能正常看到的字符

 

                if (prevCharWasEscape) {//前一个字符是转义字符

                    if (gsmVal == GSM_EXTENDED_ESCAPE) {

                        ret.append(' ');    // display ' ' for reserved double escape sequence 连续2个转义字符表示一个空格

                    } else {

                        char c = shiftTableToChar.charAt(gsmVal);

                        if (c == ' ') { // 转义字符后字符应该在shift表里查找,如果shift表里在存放的是空格(见表定义,shift表里没有定义的字符都是空格),则使用基本语言表的字符

                            ret.append(languageTableToChar.charAt(gsmVal));

                        } else {

                            ret.append(c); // 转义字符后字符应该在shift表里查找

                        }

                    }

                    prevCharWasEscape = false;

                } else if (gsmVal == GSM_EXTENDED_ESCAPE) {

                    prevCharWasEscape = true; //当前字符是转义字符,存起来给下一个字符使用

                } else {

                    ret.append(languageTableToChar.charAt(gsmVal));//正常时,找到语言表里的ascii字符

                }            }        }         }

        return ret.toString();

    }

 

至此,PDU数据解析完成。

 

对接收PDU数据解码总结如下:

分段 含义 说明

SC地址信息的长度n, n个八位字节(包括91)       (长度占1字节)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),SMSC地址要补‘F’凑成偶数个                                           (n字节)

基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址,回复地址数字个数m, 共m个十进制数(不包括91和‘F’)                      (x字节)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),回复地址(TP-RA),补‘F’凑成偶数个                                        ((m+1)/2 字节)

协议标识(TP-PID), 00是普通GSM类型,点到点方式  (1字节)

用户信息编码方式(TP-DCS) ,08是UCS2编码         (1字节)

时间戳(TP-SCTS) 80是 +8时区                       (7字节)

用户信息长度(TP-UDL) UCS2编码是字节数,7BIT编码 是字符个数      (长度占1字节)

用户信息(TP-UD)                                    (y字节)

 

举例:

分段 含义 说明

08 地址信息的长度 个八位字节(包括91)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 08 20 05 05 F0 SMSC地址 8613800250500,补‘F’凑成偶数个

84 基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址

0D 回复地址数字个数 共13个十进制数(不包括91和‘F’)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 96 03 29 30 F0 回复地址(TP-RA) 8613693092030,补‘F’凑成偶数个

00 协议标识(TP-PID) 是普通GSM类型,点到点方式

08 用户信息编码方式(TP-DCS) UCS2编码

30 30 21 80 63 54 80 时间戳(TP-SCTS) 2003-3-12 08:36:45  +8时区

06 用户信息长度(TP-UDL) 实际长度6个字节

4F 60 59 7D 00 21 用户信息(TP-UD) “你好!”




6.14 PDU解析过程

 

SmsMessage.java,通过createFromPdu开始解析PDU数据。

    public static SmsMessage createFromPdu(byte[] pdu) {

        try {

            SmsMessage msg = new SmsMessage();

            msg.parsePdu(pdu);

            return msg;

        } catch (RuntimeException ex) {

            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);

            return null;

        }

    }

 

依据协议TS 23.040 9.2.3.9,parsePdu 先解析出SCA,再根据mti消息类型决定使用哪个解析函数,对于接收的sms,使用parseSmsDeliver。

 

 

parseSmsDeliver 解析出OA(2字节+号码个数并补偶),PID(1B),dcs(1B),scts(7B),并根据第一个字节判断是否有UDH,最后调用parseUserData。

 

     private void parseSmsDeliver(PduParser p, int firstByte) {

        replyPathPresent = (firstByte & 0x80) == 0x80;

        originatingAddress = p.getAddress();

 

        if (originatingAddress != null) {

            if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "

                    + originatingAddress.address);

        }

 

        protocolIdentifier = p.getByte();

 

        // TP-Data-Coding-Scheme

        // see TS 23.038

        dataCodingScheme = p.getByte();

 

        scTimeMillis = p.getSCTimestampMillis();

 

        if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);

 

        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

 

        parseUserData(p, hasUserDataHeader);

    }

 

parseUserData,先根据协议3GPP TS 23.038 V7.0.0,将DCS分类,解析消息属性。再使用constructUserData创建一个用户实例,分解出用户数据userData和用户数据头userDataHeader,

int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {

            if (hasUserDataHeader) {

                System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);

                userDataHeader = SmsHeader.fromByteArray(udh);

           }

            userData = new byte[bufferLen];

            System.arraycopy(pdu, offset, userData, 0, userData.length);

       }

 

 

对于用户数据头userDataHeader,

SmsHeader fromByteArray会根据协议TS 23.040 9.2.3.24来解析用户数据头信息UDH,先是头信息总长度UDHL,再是具体的数据,每一段的数据格式为:类型ID(1B)+该类型后续数据长度(1B)+数据(x B,决定于长度),有多少个类型就由多少倍上面的数据长度,示例数据:04 01020101,UDHL 4字节,有一条ID=01的记录,其数据长2字节,数据是0101。

 

 

如果文件头带R8编码,SmsHeader fromByteArray里会进行相应解析,并将IEI数据信息存放在languageShiftTable和languageTable里面。

     public static SmsHeader fromByteArray(byte[] data) {

        ByteArrayInputStream inStream = new ByteArrayInputStream(data);

        SmsHeader smsHeader = new SmsHeader();

        while (inStream.available() > 0) {

            int id = inStream.read(); \\读出IEI类型

            int length = inStream.read();\\读出IEI长度

           switch (id) {

            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT: \\使用转换表

                smsHeader.languageShiftTable = inStream.read();\\读出IEI数据

                break;

            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:

                smsHeader.languageTable = inStream.read();\\读出IEI数据

                break;

        }

        return smsHeader;

    }

 

对于用户数据userData,

以ENCODING_7BIT编码方式来分析,

         case ENCODING_7BIT:

            messageBody = p.getUserDataGSM7Bit(count,  //字节个数

                    hasUserDataHeader ? userDataHeader.languageTable : 0, //是否使用编码表

                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);

            break;

gsm7BitPackedToString会完成将PDU里面的数据解压成字符串的过程,存放在PDU里的数据是经过7bit压缩过的转义字符串,不是我们正常阅读的字符串。解压的时候会参考languageShiftTable和languageTable来选择相应的字符表。

     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));

                }//对gsmVal的处理是依次解析出进行过7BIT压缩的每个字符,这些字符目前还是转义过的字符,不是我们通过字符串能正常看到的字符

 

                if (prevCharWasEscape) {//前一个字符是转义字符

                    if (gsmVal == GSM_EXTENDED_ESCAPE) {

                        ret.append(' ');    // display ' ' for reserved double escape sequence 连续2个转义字符表示一个空格

                    } else {

                        char c = shiftTableToChar.charAt(gsmVal);

                        if (c == ' ') { // 转义字符后字符应该在shift表里查找,如果shift表里在存放的是空格(见表定义,shift表里没有定义的字符都是空格),则使用基本语言表的字符

                            ret.append(languageTableToChar.charAt(gsmVal));

                        } else {

                            ret.append(c); // 转义字符后字符应该在shift表里查找

                        }

                    }

                    prevCharWasEscape = false;

                } else if (gsmVal == GSM_EXTENDED_ESCAPE) {

                    prevCharWasEscape = true; //当前字符是转义字符,存起来给下一个字符使用

                } else {

                    ret.append(languageTableToChar.charAt(gsmVal));//正常时,找到语言表里的ascii字符

                }            }        }         }

        return ret.toString();

    }

 

至此,PDU数据解析完成。

 

对接收PDU数据解码总结如下:

分段 含义 说明

SC地址信息的长度n, n个八位字节(包括91)       (长度占1字节)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),SMSC地址要补‘F’凑成偶数个                                           (n字节)

基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址,回复地址数字个数m, 共m个十进制数(不包括91和‘F’)                      (x字节)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),回复地址(TP-RA),补‘F’凑成偶数个                                        ((m+1)/2 字节)

协议标识(TP-PID), 00是普通GSM类型,点到点方式  (1字节)

用户信息编码方式(TP-DCS) ,08是UCS2编码         (1字节)

时间戳(TP-SCTS) 80是 +8时区                       (7字节)

用户信息长度(TP-UDL) UCS2编码是字节数,7BIT编码 是字符个数      (长度占1字节)

用户信息(TP-UD)                                    (y字节)

 

举例:

分段 含义 说明

08 地址信息的长度 个八位字节(包括91)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 08 20 05 05 F0 SMSC地址 8613800250500,补‘F’凑成偶数个

84 基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址

0D 回复地址数字个数 共13个十进制数(不包括91和‘F’)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 96 03 29 30 F0 回复地址(TP-RA) 8613693092030,补‘F’凑成偶数个

00 协议标识(TP-PID) 是普通GSM类型,点到点方式

08 用户信息编码方式(TP-DCS) UCS2编码

30 30 21 80 63 54 80 时间戳(TP-SCTS) 2003-3-12 08:36:45  +8时区

06 用户信息长度(TP-UDL) 实际长度6个字节

4F 60 59 7D 00 21 用户信息(TP-UD) “你好!”



6.14 PDU解析过程

 

SmsMessage.java,通过createFromPdu开始解析PDU数据。

    public static SmsMessage createFromPdu(byte[] pdu) {

        try {

            SmsMessage msg = new SmsMessage();

            msg.parsePdu(pdu);

            return msg;

        } catch (RuntimeException ex) {

            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);

            return null;

        }

    }

 

依据协议TS 23.040 9.2.3.9,parsePdu 先解析出SCA,再根据mti消息类型决定使用哪个解析函数,对于接收的sms,使用parseSmsDeliver。

 

 

parseSmsDeliver 解析出OA(2字节+号码个数并补偶),PID(1B),dcs(1B),scts(7B),并根据第一个字节判断是否有UDH,最后调用parseUserData。

 

     private void parseSmsDeliver(PduParser p, int firstByte) {

        replyPathPresent = (firstByte & 0x80) == 0x80;

        originatingAddress = p.getAddress();

 

        if (originatingAddress != null) {

            if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "

                    + originatingAddress.address);

        }

 

        protocolIdentifier = p.getByte();

 

        // TP-Data-Coding-Scheme

        // see TS 23.038

        dataCodingScheme = p.getByte();

 

        scTimeMillis = p.getSCTimestampMillis();

 

        if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);

 

        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

 

        parseUserData(p, hasUserDataHeader);

    }

 

parseUserData,先根据协议3GPP TS 23.038 V7.0.0,将DCS分类,解析消息属性。再使用constructUserData创建一个用户实例,分解出用户数据userData和用户数据头userDataHeader,

int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {

            if (hasUserDataHeader) {

                System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);

                userDataHeader = SmsHeader.fromByteArray(udh);

           }

            userData = new byte[bufferLen];

            System.arraycopy(pdu, offset, userData, 0, userData.length);

       }

 

 

对于用户数据头userDataHeader,

SmsHeader fromByteArray会根据协议TS 23.040 9.2.3.24来解析用户数据头信息UDH,先是头信息总长度UDHL,再是具体的数据,每一段的数据格式为:类型ID(1B)+该类型后续数据长度(1B)+数据(x B,决定于长度),有多少个类型就由多少倍上面的数据长度,示例数据:04 01020101,UDHL 4字节,有一条ID=01的记录,其数据长2字节,数据是0101。

 

 

如果文件头带R8编码,SmsHeader fromByteArray里会进行相应解析,并将IEI数据信息存放在languageShiftTable和languageTable里面。

     public static SmsHeader fromByteArray(byte[] data) {

        ByteArrayInputStream inStream = new ByteArrayInputStream(data);

        SmsHeader smsHeader = new SmsHeader();

        while (inStream.available() > 0) {

            int id = inStream.read(); \\读出IEI类型

            int length = inStream.read();\\读出IEI长度

           switch (id) {

            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT: \\使用转换表

                smsHeader.languageShiftTable = inStream.read();\\读出IEI数据

                break;

            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:

                smsHeader.languageTable = inStream.read();\\读出IEI数据

                break;

        }

        return smsHeader;

    }

 

对于用户数据userData,

以ENCODING_7BIT编码方式来分析,

         case ENCODING_7BIT:

            messageBody = p.getUserDataGSM7Bit(count,  //字节个数

                    hasUserDataHeader ? userDataHeader.languageTable : 0, //是否使用编码表

                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);

            break;

gsm7BitPackedToString会完成将PDU里面的数据解压成字符串的过程,存放在PDU里的数据是经过7bit压缩过的转义字符串,不是我们正常阅读的字符串。解压的时候会参考languageShiftTable和languageTable来选择相应的字符表。

     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));

                }//对gsmVal的处理是依次解析出进行过7BIT压缩的每个字符,这些字符目前还是转义过的字符,不是我们通过字符串能正常看到的字符

 

                if (prevCharWasEscape) {//前一个字符是转义字符

                    if (gsmVal == GSM_EXTENDED_ESCAPE) {

                        ret.append(' ');    // display ' ' for reserved double escape sequence 连续2个转义字符表示一个空格

                    } else {

                        char c = shiftTableToChar.charAt(gsmVal);

                        if (c == ' ') { // 转义字符后字符应该在shift表里查找,如果shift表里在存放的是空格(见表定义,shift表里没有定义的字符都是空格),则使用基本语言表的字符

                            ret.append(languageTableToChar.charAt(gsmVal));

                        } else {

                            ret.append(c); // 转义字符后字符应该在shift表里查找

                        }

                    }

                    prevCharWasEscape = false;

                } else if (gsmVal == GSM_EXTENDED_ESCAPE) {

                    prevCharWasEscape = true; //当前字符是转义字符,存起来给下一个字符使用

                } else {

                    ret.append(languageTableToChar.charAt(gsmVal));//正常时,找到语言表里的ascii字符

                }            }        }         }

        return ret.toString();

    }

 

至此,PDU数据解析完成。

 

对接收PDU数据解码总结如下:

分段 含义 说明

SC地址信息的长度n, n个八位字节(包括91)       (长度占1字节)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),SMSC地址要补‘F’凑成偶数个                                           (n字节)

基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址,回复地址数字个数m, 共m个十进制数(不包括91和‘F’)                      (x字节)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’),回复地址(TP-RA),补‘F’凑成偶数个                                        ((m+1)/2 字节)

协议标识(TP-PID), 00是普通GSM类型,点到点方式  (1字节)

用户信息编码方式(TP-DCS) ,08是UCS2编码         (1字节)

时间戳(TP-SCTS) 80是 +8时区                       (7字节)

用户信息长度(TP-UDL) UCS2编码是字节数,7BIT编码 是字符个数      (长度占1字节)

用户信息(TP-UD)                                    (y字节)

 

举例:

分段 含义 说明

08 地址信息的长度 个八位字节(包括91)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 08 20 05 05 F0 SMSC地址 8613800250500,补‘F’凑成偶数个

84 基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址

0D 回复地址数字个数 共13个十进制数(不包括91和‘F’)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)

68 31 96 03 29 30 F0 回复地址(TP-RA) 8613693092030,补‘F’凑成偶数个

00 协议标识(TP-PID) 是普通GSM类型,点到点方式

08 用户信息编码方式(TP-DCS) UCS2编码

30 30 21 80 63 54 80 时间戳(TP-SCTS) 2003-3-12 08:36:45  +8时区

06 用户信息长度(TP-UDL) 实际长度6个字节

4F 60 59 7D 00 21 用户信息(TP-UD) “你好!”

6.14 PDU解析过程

 

SmsMessage.java,通过createFromPdu开始解析PDU数据。

    public static SmsMessage createFromPdu(byte[] pdu) {

        try {

            SmsMessage msg = new SmsMessage();

            msg.parsePdu(pdu);

            return msg;

        } catch (RuntimeException ex) {

            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);

            return null;

        }

    }

 

依据协议TS 23.040 9.2.3.9parsePdu 先解析出SCA,再根据mti消息类型决定使用哪个解析函数,对于接收的sms,使用parseSmsDeliver

 

 

parseSmsDeliver 解析出OA2字节+号码个数并补偶),PID(1B)dcs1B),scts7B),并根据第一个字节判断是否有UDH,最后调用parseUserData

 

     private void parseSmsDeliver(PduParser p, int firstByte) {

        replyPathPresent = (firstByte & 0x80) == 0x80;

        originatingAddress = p.getAddress();

 

        if (originatingAddress != null) {

            if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "

                    + originatingAddress.address);

        }

 

        protocolIdentifier = p.getByte();

 

        // TP-Data-Coding-Scheme

        // see TS 23.038

        dataCodingScheme = p.getByte();

 

        scTimeMillis = p.getSCTimestampMillis();

 

        if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);

 

        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

 

        parseUserData(p, hasUserDataHeader);

    }

 

parseUserData,先根据协议3GPP TS 23.038 V7.0.0,将DCS分类,解析消息属性。再使用constructUserData创建一个用户实例,分解出用户数据userData和用户数据头userDataHeader

int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {

            if (hasUserDataHeader) {

                System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);

                userDataHeader = SmsHeader.fromByteArray(udh);

           }

            userData = new byte[bufferLen];

            System.arraycopy(pdu, offset, userData, 0, userData.length);

       }

 

 

对于用户数据头userDataHeader

SmsHeader fromByteArray会根据协议TS 23.040 9.2.3.24来解析用户数据头信息UDH,先是头信息总长度UDHL,再是具体的数据,每一段的数据格式为:类型ID1B+该类型后续数据长度(1B+数据(x B,决定于长度),有多少个类型就由多少倍上面的数据长度,示例数据:04 01020101UDHL 4字节,有一条ID=01的记录,其数据长2字节,数据是0101

 

 

如果文件头带R8编码,SmsHeader fromByteArray里会进行相应解析,并将IEI数据信息存放在languageShiftTablelanguageTable里面。

     public static SmsHeader fromByteArray(byte[] data) {

        ByteArrayInputStream inStream = new ByteArrayInputStream(data);

        SmsHeader smsHeader = new SmsHeader();

        while (inStream.available() > 0) {

            int id = inStream.read(); \\读出IEI类型

            int length = inStream.read();\\读出IEI长度

           switch (id) {

            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT: \\使用转换表

                smsHeader.languageShiftTable = inStream.read();\\读出IEI数据

                break;

            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:

                smsHeader.languageTable = inStream.read();\\读出IEI数据

                break;

        }

        return smsHeader;

    }

 

对于用户数据userData

ENCODING_7BIT编码方式来分析,

         case ENCODING_7BIT:

            messageBody = p.getUserDataGSM7Bit(count,  //字节个数

                    hasUserDataHeader ? userDataHeader.languageTable : 0, //是否使用编码表

                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);

            break;

gsm7BitPackedToString会完成将PDU里面的数据解压成字符串的过程,存放在PDU里的数据是经过7bit压缩过的转义字符串,不是我们正常阅读的字符串。解压的时候会参考languageShiftTablelanguageTable来选择相应的字符表。

     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));

                }//gsmVal的处理是依次解析出进行过7BIT压缩的每个字符,这些字符目前还是转义过的字符,不是我们通过字符串能正常看到的字符

 

                if (prevCharWasEscape) {//前一个字符是转义字符

                    if (gsmVal == GSM_EXTENDED_ESCAPE) {

                        ret.append(' ');    // display ' ' for reserved double escape sequence 连续2个转义字符表示一个空格

                    } else {

                        char c = shiftTableToChar.charAt(gsmVal);

                        if (c == ' ') { // 转义字符后字符应该在shift表里查找,如果shift表里在存放的是空格(见表定义,shift表里没有定义的字符都是空格),则使用基本语言表的字符

                            ret.append(languageTableToChar.charAt(gsmVal));

                        } else {

                            ret.append(c); // 转义字符后字符应该在shift表里查找

                        }

                    }

                    prevCharWasEscape = false;

                } else if (gsmVal == GSM_EXTENDED_ESCAPE) {

                    prevCharWasEscape = true; //当前字符是转义字符,存起来给下一个字符使用

                } else {

                    ret.append(languageTableToChar.charAt(gsmVal));//正常时,找到语言表里的ascii字符

                }            }        }         }

        return ret.toString();

    }

 

至此,PDU数据解析完成。

 

对接收PDU数据解码总结如下:

分段 含义 说明

SC地址信息的长度n n个八位字节(包括91)       (长度占1字节)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+)SMSC地址要补‘F’凑成偶数个                                           n字节)

基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址,回复地址数字个数m m个十进制数(不包括91和‘F)                      x字节)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+),回复地址(TP-RA),补‘F’凑成偶数个                                        ((m+1/2 字节)

协议标识(TP-PID) 00是普通GSM类型,点到点方式  (1字节)

用户信息编码方式(TP-DCS) 08UCS2编码         (1字节)

时间戳(TP-SCTS) 80 +8时区                       (7字节)

用户信息长度(TP-UDL) UCS2编码是字节数,7BIT编码 是字符个数      (长度占1字节)

用户信息(TP-UD)                                    y字节)

 

举例:

分段 含义 说明

08 地址信息的长度 个八位字节(包括91)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+)

68 31 08 20 05 05 F0 SMSC地址 8613800250500,补‘F’凑成偶数个

84 基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址

0D 回复地址数字个数 13个十进制数(不包括91和‘F)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+)

68 31 96 03 29 30 F0 回复地址(TP-RA) 8613693092030,补‘F’凑成偶数个

00 协议标识(TP-PID) 是普通GSM类型,点到点方式

08 用户信息编码方式(TP-DCS) UCS2编码

30 30 21 80 63 54 80 时间戳(TP-SCTS) 2003-3-12 08:36:45  +8时区

06 用户信息长度(TP-UDL) 实际长度6个字节

4F 60 59 7D 00 21 用户信息(TP-UD) “你好!

6.14 PDU解析过程

 

SmsMessage.java,通过createFromPdu开始解析PDU数据。

    public static SmsMessage createFromPdu(byte[] pdu) {

        try {

            SmsMessage msg = new SmsMessage();

            msg.parsePdu(pdu);

            return msg;

        } catch (RuntimeException ex) {

            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);

            return null;

        }

    }

 

依据协议TS 23.040 9.2.3.9parsePdu 先解析出SCA,再根据mti消息类型决定使用哪个解析函数,对于接收的sms,使用parseSmsDeliver

 

 

parseSmsDeliver 解析出OA2字节+号码个数并补偶),PID(1B)dcs1B),scts7B),并根据第一个字节判断是否有UDH,最后调用parseUserData

 

     private void parseSmsDeliver(PduParser p, int firstByte) {

        replyPathPresent = (firstByte & 0x80) == 0x80;

        originatingAddress = p.getAddress();

 

        if (originatingAddress != null) {

            if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "

                    + originatingAddress.address);

        }

 

        protocolIdentifier = p.getByte();

 

        // TP-Data-Coding-Scheme

        // see TS 23.038

        dataCodingScheme = p.getByte();

 

        scTimeMillis = p.getSCTimestampMillis();

 

        if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);

 

        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

 

        parseUserData(p, hasUserDataHeader);

    }

 

parseUserData,先根据协议3GPP TS 23.038 V7.0.0,将DCS分类,解析消息属性。再使用constructUserData创建一个用户实例,分解出用户数据userData和用户数据头userDataHeader

int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {

            if (hasUserDataHeader) {

                System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);

                userDataHeader = SmsHeader.fromByteArray(udh);

           }

            userData = new byte[bufferLen];

            System.arraycopy(pdu, offset, userData, 0, userData.length);

       }

 

 

对于用户数据头userDataHeader

SmsHeader fromByteArray会根据协议TS 23.040 9.2.3.24来解析用户数据头信息UDH,先是头信息总长度UDHL,再是具体的数据,每一段的数据格式为:类型ID1B+该类型后续数据长度(1B+数据(x B,决定于长度),有多少个类型就由多少倍上面的数据长度,示例数据:04 01020101UDHL 4字节,有一条ID=01的记录,其数据长2字节,数据是0101

 

 

如果文件头带R8编码,SmsHeader fromByteArray里会进行相应解析,并将IEI数据信息存放在languageShiftTablelanguageTable里面。

     public static SmsHeader fromByteArray(byte[] data) {

        ByteArrayInputStream inStream = new ByteArrayInputStream(data);

        SmsHeader smsHeader = new SmsHeader();

        while (inStream.available() > 0) {

            int id = inStream.read(); \\读出IEI类型

            int length = inStream.read();\\读出IEI长度

           switch (id) {

            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT: \\使用转换表

                smsHeader.languageShiftTable = inStream.read();\\读出IEI数据

                break;

            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:

                smsHeader.languageTable = inStream.read();\\读出IEI数据

                break;

        }

        return smsHeader;

    }

 

对于用户数据userData

ENCODING_7BIT编码方式来分析,

         case ENCODING_7BIT:

            messageBody = p.getUserDataGSM7Bit(count,  //字节个数

                    hasUserDataHeader ? userDataHeader.languageTable : 0, //是否使用编码表

                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);

            break;

gsm7BitPackedToString会完成将PDU里面的数据解压成字符串的过程,存放在PDU里的数据是经过7bit压缩过的转义字符串,不是我们正常阅读的字符串。解压的时候会参考languageShiftTablelanguageTable来选择相应的字符表。

     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));

                }//gsmVal的处理是依次解析出进行过7BIT压缩的每个字符,这些字符目前还是转义过的字符,不是我们通过字符串能正常看到的字符

 

                if (prevCharWasEscape) {//前一个字符是转义字符

                    if (gsmVal == GSM_EXTENDED_ESCAPE) {

                        ret.append(' ');    // display ' ' for reserved double escape sequence 连续2个转义字符表示一个空格

                    } else {

                        char c = shiftTableToChar.charAt(gsmVal);

                        if (c == ' ') { // 转义字符后字符应该在shift表里查找,如果shift表里在存放的是空格(见表定义,shift表里没有定义的字符都是空格),则使用基本语言表的字符

                            ret.append(languageTableToChar.charAt(gsmVal));

                        } else {

                            ret.append(c); // 转义字符后字符应该在shift表里查找

                        }

                    }

                    prevCharWasEscape = false;

                } else if (gsmVal == GSM_EXTENDED_ESCAPE) {

                    prevCharWasEscape = true; //当前字符是转义字符,存起来给下一个字符使用

                } else {

                    ret.append(languageTableToChar.charAt(gsmVal));//正常时,找到语言表里的ascii字符

                }            }        }         }

        return ret.toString();

    }

 

至此,PDU数据解析完成。

 

对接收PDU数据解码总结如下:

分段 含义 说明

SC地址信息的长度n n个八位字节(包括91)       (长度占1字节)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+)SMSC地址要补‘F’凑成偶数个                                           n字节)

基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址,回复地址数字个数m m个十进制数(不包括91和‘F)                      x字节)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+),回复地址(TP-RA),补‘F’凑成偶数个                                        ((m+1/2 字节)

协议标识(TP-PID) 00是普通GSM类型,点到点方式  (1字节)

用户信息编码方式(TP-DCS) 08UCS2编码         (1字节)

时间戳(TP-SCTS) 80 +8时区                       (7字节)

用户信息长度(TP-UDL) UCS2编码是字节数,7BIT编码 是字符个数      (长度占1字节)

用户信息(TP-UD)                                    y字节)

 

举例:

分段 含义 说明

08 地址信息的长度 个八位字节(包括91)

91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+)

68 31 08 20 05 05 F0 SMSC地址 8613800250500,补‘F’凑成偶数个

84 基本参数(TP-MTI/MMS/RP) 接收,无更多消息,有回复地址

0D 回复地址数字个数 13个十进制数(不包括91和‘F)

91 回复地址格式(TON/NPI) 用国际格式号码(在前面加‘+)

68 31 96 03 29 30 F0 回复地址(TP-RA) 8613693092030,补‘F’凑成偶数个

00 协议标识(TP-PID) 是普通GSM类型,点到点方式

08 用户信息编码方式(TP-DCS) UCS2编码

30 30 21 80 63 54 80 时间戳(TP-SCTS) 2003-3-12 08:36:45  +8时区

06 用户信息长度(TP-UDL) 实际长度6个字节

4F 60 59 7D 00 21 用户信息(TP-UD) “你好!

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android Mms专题之:PDU介绍

Android当中的Mms对MMS(Multimedia Messaging Service)的操作关乎MMS协议部分都是通过Frameworks中提供的API来完成的:com.google.andr...
  • hitlion2008
  • hitlion2008
  • 2012年03月29日 20:23
  • 11813

Android短信接收过程源码分析(原)

本文主要讨论RILJ接收到一条普通短消息时是如何把它转换成Broadcast发送到系统中的。 一、GsmSMSDispatcher注册监听过程         在《Framework层中的...
  • u010961631
  • u010961631
  • 2013年10月14日 10:53
  • 5104

android CDMA短信pdu数据包解析

在短信发送过程中,短信内容,发送时间等信息会压缩成“数字”字符串,这个字符串就是我们的pdu包,这个过程也叫做pdu的打包。pdu包在AT命令的帮助下到达moderm,由moderm将它发送到移动基站...
  • hailushijie
  • hailushijie
  • 2015年08月10日 15:04
  • 1060

Android接收彩信时解析PDU的过程记录

从http中获得的字节流中解析出彩信内容
  • UDBuilder
  • UDBuilder
  • 2015年11月19日 22:04
  • 547

PDU格式短信解析

AT指令收发短信主要有两种模式:Text模式和PDU(Protocol Data Unit,协议数据单元)模式。使用Text模式收发短信代码简单,很容易实现,最大缺点不支持中文短信。PDU模式不仅能发...
  • u010871058
  • u010871058
  • 2017年04月21日 15:03
  • 285

SMS短信PDU编码详细解析

以一个例子来详细解析: 01 08 91 683110300405F1 60 05 A1 0110F0 00 08 31808061349523 04 4F60597D(1)01-短信状态; [01...
  • hlx156
  • hlx156
  • 2017年01月03日 17:38
  • 1125

短信PDU编码解析

短信PDU编码解析 一 概述   问题:接收飞信或者配合终端发送的长短信(两三百个字)或者接收运营商发送的话费信息,长短信总是接收不完整。其中会有某些段有丢失。 原因分析: 1...
  • canghai1129
  • canghai1129
  • 2014年11月17日 15:46
  • 1030

android的SMS监听

转自http://www.cnblogs.com/xirihanlin/archive/2009/10/22/1588356.html 当设备接收到一条新的SMS消息时,就会广播一个包含了and...
  • fancylovejava
  • fancylovejava
  • 2012年10月14日 21:45
  • 11082

Android入门:广播接收者应用(短信窃听器)

一、短信窃听器原理介绍 短信窃听器的目的是窃听某人发送的短信,比如我们在A的手机中安装了此应用想要看B发送给A的短信; 而怎么样才能够获得短信息呢?如果通过短信方式发送给第三方,则...
  • xiazdong
  • xiazdong
  • 2012年07月21日 06:47
  • 10420

Android接收彩信时解析PDU的过程记录

从http中获得的字节流中解析出彩信内容
  • UDBuilder
  • UDBuilder
  • 2015年11月19日 22:04
  • 547
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android短信源码分析 --PDU解析过程
举报原因:
原因补充:

(最多只允许输入30个字)