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

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。

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

 

如果文件头带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

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

 

 

如果文件头带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

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

 

 

如果文件头带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源码分析
    原文地址: https://blog.csdn.net/xiashaohua/article/details/40555945
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞