Android短信編解碼方式

Android短信編碼在Framework層實現。 android

對於普通短信,其SmsHeader部分爲空。 git

Gsm: 數組

Gsm中,對於長短信,使用了SmsHeader類進行描述。SmsHeader.ConcatRef描述了長短信的refNumber(對於同一個短信,相同,使用SmsDispatcher.getNextConcatenatedRef方法獲取一個索引值,將其或上0x00FF),seqNumber(分段序列號,從1開始),msgCount(分段數量)。而後設置isEightBits爲假(爲了支持不一樣長度REFERENCE的短信,8位則REFERENCE爲8位,不然爲16位)。 編碼

短信頭的編碼位於SmsHeader.toByteArray中。 spa

1           寫入concatRef code

1.1          若爲8位REFERENCE,寫入ELT_ID_CONCATENATED_8_BIT_REFERENCE(00000000),寫入剩下concatRef的字節數(此處設爲00000011,表示3個字節),寫入refNumber(低8位) 對象

1.2          若爲16位,寫入ELT_ID_CONCATENATED_16_BIT_REFERENCE(00001000),寫入剩下concatRef的字節數(此處設爲00000100,表示4個字節),寫入refNumber(先寫入高8位,再寫入低8位) 索引

1.3          寫入msgCount(低8位)與seqNumber(低8位) ci

2           寫入PortAddrs 字符串

3           寫入specialSmsMsgList

4           寫入miscEltList

短信內容編碼從Gsm的SmsMessage.getSubmitPdu方法開始(com.android.internal.telephony.gsm.SmsMessage)

1           設置MTIBYTE(TP-Message-Type-Indicator,初始爲00000001)與UDHI。若SmsHeader不爲空,MTIBYTE爲01000001,不然爲00000001

2           調用Gsm的SmsMessage.getSubmitPduHeader寫入基本信息,返回一個ByteArrayOutputStream

1          

2          

2.1          設置SubmitPdu的服務中心地址

2.2          若須要接收報告,MTIBYTE|=00100000。(TP-Status-Report-Request)

2.3          將MTIBYTE寫入輸出流(低8位)

2.4          寫入一個字節00000000(TP-Message-Reference)

2.5          寫入目標號碼長度(有效數字個數,如號碼12345爲5)(低8位)

2.6          寫入目標號碼(第一個字節爲TOA,號碼中有加號則爲10010001,表示國際號碼,不然爲10000001未知,TS 24.008 10.5.4.7。後面爲BCD碼字節數組,每一個字節存兩個數,不存加號,順序是反的。如13 24 58表示號碼31 42 85。若是號碼長度是奇數,最後一個字節的高4位爲1111,如13 22 F5表示號碼31 22 5)

2.7          寫入一個字節00000000(TP-Protocol-Identifier)

3           若編碼方式未指定,默認使用7BIT

4           若是使用7BIT編碼,調用GsmAlphabet.stringToGsm7BitPacketedWithHeader進行編碼,獲取編碼後的字節數組

4.1          若是沒有Header,直接調用GsmAlphabet.stringToGsm7BitPacket獲取編碼後的數據字節數組(startingSeptetOffset=0)

4.1.1     調用GsmAlphabet.countGsmSeptets計算7BIT編碼所需的字節數(計算結果要加上頭部的7BIT編碼所需的字節數)

4.1.2     若超過255個字節,拋出異常

4.1.3     計算使用字節數組保存編碼後的7BIT數據所需的字節數(包括Header)

4.1.4     將數據以7BIT編碼的方式寫入字節數組

4.1.5     在數組的0號位寫入7BIT編碼所需的字節數(包括Header),返回

4.2          不然,先計算Header的7BIT編碼後的長度(包括表示頭長度的字節),調用GsmAlphabet.stringToGsm7BitPacket獲取編碼後的數據字節數組(startingSeptetOffset=Header的長度),在編碼後的數據字節數組前面預留出該長度的部分(同4.1.1-4.1.5)。在1號位寫入頭的長度,其後爲Header的內容(Header未進行7BIT編碼,但爲何後面的空間是按7BIT空出來?)

4.3          若所需字節數未超過MAX_USER_DATA_SEPTETS,往輸出流中寫入一個字節00000000(TP-Data-Coding-Scheme,默認編碼,未壓縮)

5           不然使用UCS-2方式,調用Gsm的SmsMessage.encodeUCS2進行編碼,獲取編碼後的字節數組

5.1          將消息正文轉成utf-16be格式

5.2          若包含Header,則建立一個新字節數組用於存放待輸出數據,0號位存放Header長度,後面接着Header和轉換後的正文。不然,僅輸出轉換後的消息正文

5.3          建立一個新的字節數組,長度比待輸出數據多一。0號位存放待輸出數據長度,後面跟着待輸出數據

5.4          若所需字節數未超過MAX_USER_DATA_BYTES,往輸出流中寫入一個字節00001011(TP-Data-Coding-Scheme,Class 3,UCS-2,未壓縮)

6           往輸出流中寫入編碼後的字節數組

Gsm短信發送(SMS-SUBMIT)編碼格式(不全,具體參見GSM短信標準中9.2.2.2 SMS SUBMIT type)以下圖所示:



 

用戶數據區域以下圖所示,UDL-用戶數據長度,UDHL-用戶數據頭長度,UDH-用戶數據頭。若沒有Header,則只包括TP-UDL和編碼後的短信內容(由TP-UDHI標識)。



 
對於Gsm,還要在RIL中,將smscPdu與內容pdu進行整合。smscPdu的格式與內容pdu相似,先是一個長度字節(後面號碼內容的長度),後面是號碼的BCD編碼(格式與目標號碼相同,也包括TOA)。

Gsm其它類型的PDU可參考標準中的說明,大體結構都差很少。總共包括如下六種。其中,SMS-DELIVER的解碼在RIL的processUnsolicited的RIL_UNSOL_RESPONSE_NEW_SMS中調用。

1.         SMS-DELIVER,包含從SC到MS的消息(手機收到的短信)。

2.         SMS-DELIVER-REPORT,包含

a)       失敗緣由(若是須要的話)

b)      對於SMS-DELIVER或SMS-STATUSREPORT的確認

3.         SMS-SUBMIT,包含從MS到SC的消息(從手機發出的短信)。

4.         SMS-SUBMIT-REPORT,包含

a)       失敗緣由(若是須要的話)

b)      對於SMS-SUBMIT或SMS-COMMAND的確認

5.         SMS-STATUS-REPORT,包含從SC到MS的狀態報告。

6.         SMS-COMMAND,包含從MS到SC的命令。

 

Cdma:

Cdma的SmsHeader的內容與Gsm幾乎沒有區別,只是isEightBits爲真。

Cdma使用了UserData來輔助構造SubmitPdu,其中的payloadStr存儲了不一樣分段的短信字符串,userDataHeader存儲了SmsHeader,msgEncoding存儲了編碼類型,msgEncodingSet用於指明是否指明編碼類型(此處設爲真)。

對於普通短信,其userDataHeader爲空,msgEncoding與msgEncodingSet均未設置。剩下的編碼過程與長短信相同。

Cdma的長短信內容編碼也是從Cdma的SmsMessage.getSubmitPdu方法開始,最後轉到Cdma的SmsMessage.privateGetSubmitPdu方法進行編碼。(com.android.internal.telephony.cdma.SmsMessage)

1           利用目標地址構造一個CdmaSmsAddress

2           構造BearerData對象用於輔助編碼,其中消息類型messageType設爲BearerData.MESSAGE_TYPE_SUBMIT(0x02),獲取了一個MessageId(自增,C.S0015-B,v2.0,4.3.1.5),deliveryAckReq爲是否須要接收報告,userAckReq、readAckReq、reportReq均爲假,userData爲UserData對象。

3           使用BearerData.encode方法對BearerData對象進行編碼,得到一個字節數組(3GPP2,C.R1001-F,v1.0,4.5)。其格式爲SUBPARAMETER_ID後面帶上相應的數據。

3.1          構造一個BitwiseOutputStream進行數據的編碼,該輸出流可以往其中寫入任意位數的數據,而不限於字節的邊界

3.2          往輸出流中寫入SUBPARAM_MESSAGE_IDENTIFIER(0x00)(3GPP2 C.S0015-B,v2.0表4.5-1),8位

3.3          往輸出流中寫入MessageId(3GPP2 C.S0015-B,4.5.1 Message Identifier)

3.3.1     寫入00000011,8位

3.3.2     寫入消息類型,4位

3.3.3     寫入MessageId的高8位

3.3.4     寫入MessageId的低8位

3.3.5     寫入是否有用戶數據頭,1有,0無,1位

3.3.6     跳過3位(留做備用)

3.4          若userData不空,輸出(3GPP2 C.S0015-B,4.5.2 User Data)

3.4.1     寫入SUBPARAM_USER_DATA(0x01),8位

3.4.2     對UserData的payload使用Bearer.encodeUserDataPayload進行編碼

3.4.2.1    若payloadStr爲空且msgEncoding不是ENCODING_OCTET(3GPP2 C.R1001-F,v1.0,表9.1-1),設payloadStr爲空串

3.4.2.2    若userDataHeader不空,編碼並返回

3.4.2.2.1   使用SmsHeader的toByteArray方法將SmsHeader編碼爲字節數組(具體過程前面介紹了)

3.4.2.2.2   根據編碼類型msgEncoding的不一樣,分別調用不一樣方法,將SmsHeader數組編碼成不一樣的字節數組。若沒指定字符集,默認使用7BIT_ALPHABET。若出現異常,再使用UNICODE16。

3.4.2.2.2.1       若是是ENCODING_GSM_7BIT_ALPHABET,調用BearerData.encode7bitEms,同Gsm的4.2,將編碼後的字節數組存入UserData的payload,格式同Gsm的用戶數據區域(去掉了第一個字節,即UDL)。將整個用戶數據區域的長度存入UserData的numFields。

3.4.2.2.2.2       若是是ENCODING_UNICODE_16,調用BearerData.encode16bitEms,將payloadStr編碼爲Utf-16be字節數組。將頭的長度存入payload的0號字節,後面加上頭的字節數組。若是前面的字節數爲奇數,還要補上一個字節做爲udhPadding以保證字節邊界對齊。而後接上編碼後的payloadStr。此處的numFields存的是16位單元的長度,即payload字節數除2。

3.4.2.2.2.3       若是是ENCODING_7BIT_ASCII,調用BearerData.encode7bitEmsAscii,過程與ENCODING_GSM_7BIT_ALPHABET相同,只是字符集不一樣。

3.4.2.3    不然,說明爲普通短信,沒有Header。

3.4.2.3.1   若是設置了編碼字符集,根據編碼類型msgEncoding的不一樣設置相應的payload值。

3.4.2.3.1.1       若是是ENCODING_OCTET。若 UserData.payload爲空,payload設爲一個空字節(00000000),numFields爲0。若UserData.payload不空,numFields爲payload的字節數。

3.4.2.3.1.2       若是不是ENCODING_OCTET。若payloadStr爲空,設其爲空串。不然,根據編碼字符集的不一樣,調用相應方法將payloadStr編碼,具體方法同3.4.2.2.2,只是沒有UDHL和UDH部分,只含編碼後的短信內容。

3.4.2.3.2   若沒有設置編碼字符集,嘗試使用7BIT_ASCII進行編碼,若出現異常,再採用UNICODE_16。而後,將numFields設爲payloadStr的長度(即字符個數)。

3.4.3     計算後面數據與參數的總字節數paramBytes(若是編碼方式爲ENCODING_IS91_EXTENDED_PROTOCOL或ENCODING_GSM_DCS,多加1字節),以及爲了保證數據對齊所需的位數paddingBits

3.4.4     寫入paramBytes,8位(SUBPARAM_LEN)

3.4.5     寫入UserData的編碼方式msgEncoding,5位

3.4.6     若是編碼方式爲ENCODING_IS91_EXTENDED_PROTOCOL或ENCODING_GSM_DCS,輸出UserData的消息類型msgType,8位

3.4.7     寫入UserData的numFields,8位

3.4.8     寫入UserData的payload

3.4.9     若是須要補全,寫入所需的空位paddingBits

3.5          若callbackNumber不空,輸出(目前未設置,3GPP2 C.S0015-B,v2,4.5.15)

3.6          若userAckReq,deliveryAckReq,readAckReq,reportReq有一個爲真,寫入SUBPARAM_REPLY_OPTION(00001010),8位

3.6.1     寫入00000001,8位

3.6.2     寫入userAckReq,1位

3.6.3     寫入deliveryAckReq,1位

3.6.4     寫入readAckReq,1位

3.6.5     寫入reportReq,1位

3.6.6     寫入0000,4位

3.7          若numberOfMessage不爲0,輸出(Voice Mail中的)

3.7.1     寫入SUBPARAM_NUMBER_OF_MESSAGE(3GPP2 C.S0015-B,v2.0,4.5.12),8位

3.7.2     寫入00000001,8位

3.7.3     寫入BearerData的numberOfMessage,8位

3.8          若validityPeriodRelativeSet爲真,輸出(目前未設置)——SUBPARAM_VALIDITY_PERIOD_RELATIVE

3.9          若privacyIndicatorSet爲真,輸出(目前未設置)——SUBPARAM_PRIVACY_INDICATOR

3.10       若languageIndicatorSet爲真,輸出(目前未設置)——SUBPARAM_LANGUAGE_INDICATOR

3.11       若displayModeSet爲真,輸出(目前未設置)——SUBPARAM_MESSAGE_DISPLAY_MODE

3.12       若priorityIndicatorSet爲真,輸出(目前未設置)——SUBPARAM_PRIORITY_INDICATOR

3.13       若alertIndicator爲真,輸出(目前未設置)——SUBPARAM_ALERT_ON_MESSAGE_DELIVERY

3.14       若messageStatusSet爲真,輸出(目前未設置)——SUBPARAM_MESSAGE_STATUS

4           如有SmsHeader,teleservice爲SmsEnvelope.TELESERVICE_WEMT,不然爲SmsEnvelope.TELESERVICE_WMT

5           構造一個ByteArrayOutputStream,使用DataOutputStream進行封裝

6           往輸出流中寫入teleservice,Int

7           往輸出流中寫入0(Service Present),Int

8           往輸出流中寫入0(Service Category),Int

9           往輸出流中寫入digitMode(3GPP2,C.S0015-B,v2.0,3.4.3.3),byte

10        往輸出流中寫入numberMode(3GPP2,C.S0015-B,v2.0,3.4.3.3),byte

11        往輸出流中寫入ton,即numberType(TS 23.040 9.1.2.5,TS 24.008表10.5.118,C.S0005-D表2.7.1.3.2.4-2),byte

12        往輸出流中寫入numberPlan(3GPP2,C.S0015-B,v2.0,3.4.3.3,C.S005-D表2.7.1.3.2.4-3),byte

13        往輸出流中寫入地址長度numberOfDigits(3GPP2,C.S0015-B,v2.0,3.4.3.3),byte

14        往輸出流中寫入CdmaSmsAddress中編碼後的目標地址,byte[]

15        SubAddress不支持,故輸出三個字節00000000

15.1       往輸出流中寫入0(subaddressType),byte

15.2       往輸出流中寫入0(subaddress_odd),byte

15.3       往輸出流中寫入0(subaddress_nbr_of_digits),byte

16        往輸出流中寫入編碼後的BearerData字節數組的長度,byte

17        往輸出流中寫入編碼後的BearerData字節數組,byte[]

對於CDMA短信的解碼,在RIL的processUnsolicited的RIL_UNSOL_RESPONSE_CDMA_NEW_SMS中調用,實際代碼在com.android.internal.telephony.cdma.SmsMessage.newFromParcel中,解碼格式與編碼格式相同,只是存的地址變成了發出消息的手機的地址。

相關文章
相關標籤/搜索