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中,解碼格式與編碼格式相同,只是存的地址變成了發出消息的手機的地址。