10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/* 20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2012 The Android Open Source Project 30825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 40825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Licensed under the Apache License, Version 2.0 (the "License"); 50825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * you may not use this file except in compliance with the License. 60825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * You may obtain a copy of the License at 70825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 80825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * http://www.apache.org/licenses/LICENSE-2.0 90825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unless required by applicable law or agreed to in writing, software 110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * distributed under the License is distributed on an "AS IS" BASIS, 120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * See the License for the specific language governing permissions and 140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * limitations under the License. 150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 170825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepackage com.android.internal.telephony.gsm; 180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 190825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.SmsCbLocation; 200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.SmsCbMessage; 210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.util.Pair; 220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 230825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.GsmAlphabet; 240825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsConstants; 250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 260825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.UnsupportedEncodingException; 270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/** 290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is 300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * public because {@link #createSmsCbMessage(SmsCbLocation, byte[][])} is used by some test cases. 310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class GsmSmsCbMessage { 330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5. 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String[] LANGUAGE_CODES_GROUP_0 = { 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu", 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "pl", null 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville }; 410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5. 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String[] LANGUAGE_CODES_GROUP_2 = { 460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null, 470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville null, null 480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville }; 490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final char CARRIAGE_RETURN = 0x0d; 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int PDU_BODY_PAGE_LENGTH = 82; 530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Utility class with only static methods. */ 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private GsmSmsCbMessage() { } 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Create a new SmsCbMessage object from a header object plus one or more received PDUs. 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdus PDU bytes 610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 623a96b0ee1d152549279c58ba24a0a035cc9a557bAmit Mahajan public static SmsCbMessage createSmsCbMessage(SmsCbHeader header, SmsCbLocation location, 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[][] pdus) throws IllegalArgumentException { 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (header.isEtwsPrimaryNotification()) { 65db2e012f5843255064f927f323bde20cdc2d7ef6Jack Yu // ETSI TS 23.041 ETWS Primary Notification message 66db2e012f5843255064f927f323bde20cdc2d7ef6Jack Yu // ETWS primary message only contains 4 fields including serial number, 67db2e012f5843255064f927f323bde20cdc2d7ef6Jack Yu // message identifier, warning type, and warning security information. 683a96b0ee1d152549279c58ba24a0a035cc9a557bAmit Mahajan // There is no field for the content/text. We hardcode "ETWS" in the 69db2e012f5843255064f927f323bde20cdc2d7ef6Jack Yu // text body so the user won't see an empty dialog without any text. 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP, 710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getGeographicalScope(), header.getSerialNumber(), 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville location, header.getServiceCategory(), 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville null, "ETWS", SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getEtwsInfo(), header.getCmasInfo()); 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String language = null; 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville StringBuilder sb = new StringBuilder(); 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (byte[] pdu : pdus) { 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pair<String, String> p = parseBody(header, pdu); 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = p.first; 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sb.append(p.second); 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : SmsCbMessage.MESSAGE_PRIORITY_NORMAL; 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP, 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getGeographicalScope(), header.getSerialNumber(), location, 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getServiceCategory(), language, sb.toString(), priority, 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getEtwsInfo(), header.getCmasInfo()); 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Create a new SmsCbMessage object from one or more received PDUs. This is used by some 950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * CellBroadcastReceiver test cases, because SmsCbHeader is now package local. 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param location the location (geographical scope) for the message 980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdus PDU bytes 990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public static SmsCbMessage createSmsCbMessage(SmsCbLocation location, byte[][] pdus) 1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws IllegalArgumentException { 1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsCbHeader header = new SmsCbHeader(pdus[0]); 1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return createSmsCbMessage(header, location, pdus); 1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Parse and unpack the body text according to the encoding in the DCS. 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * After completing successfully this method will have assigned the body 1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * text into mBody, and optionally the language code into mLanguage 1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param header the message header to use 1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdu the PDU to decode 1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a Pair of Strings containing the language and body of the message 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static Pair<String, String> parseBody(SmsCbHeader header, byte[] pdu) { 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int encoding; 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String language = null; 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean hasLanguageIndicator = false; 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int dataCodingScheme = header.getDataCodingScheme(); 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Extract encoding and language from DCS, as defined in 3gpp TS 23.038, 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // section 5. 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch ((dataCodingScheme & 0xf0) >> 4) { 1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x00: 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f]; 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x01: 1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hasLanguageIndicator = true; 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((dataCodingScheme & 0x0f) == 0x01) { 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_16BIT; 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x02: 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f]; 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x03: 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x04: 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x05: 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch ((dataCodingScheme & 0x0c) >> 2) { 1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x01: 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_8BIT; 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x02: 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_16BIT; 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x00: 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x06: 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x07: 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Compression not supported 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x09: 1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // UDH structure not supported 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x0e: 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Defined by the WAP forum not supported 1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Unsupported GSM dataCodingScheme " 1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + dataCodingScheme); 1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x0f: 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (((dataCodingScheme & 0x04) >> 2) == 0x01) { 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_8BIT; 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Reserved values are to be treated as 7-bit 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (header.isUmtsFormat()) { 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Payload may contain multiple pages 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH]; 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * nrPages) { 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match " 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + nrPages + " pages"); 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville StringBuilder sb = new StringBuilder(); 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < nrPages; i++) { 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Each page is 82 bytes followed by a length octet indicating 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // the number of useful octets within those 82 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i; 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int length = pdu[offset + PDU_BODY_PAGE_LENGTH]; 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (length > PDU_BODY_PAGE_LENGTH) { 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Page length " + length 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH); 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pair<String, String> p = unpackBody(pdu, encoding, offset, length, 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hasLanguageIndicator, language); 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = p.first; 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sb.append(p.second); 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new Pair<String, String>(language, sb.toString()); 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Payload is one single page 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int offset = SmsCbHeader.PDU_HEADER_LENGTH; 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int length = pdu.length - offset; 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return unpackBody(pdu, encoding, offset, length, hasLanguageIndicator, language); 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unpack body text from the pdu using the given encoding, position and 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * length within the pdu 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdu The pdu 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param encoding The encoding, as derived from the DCS 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param offset Position of the first byte to unpack 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param length Number of bytes to unpack 2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param hasLanguageIndicator true if the body text is preceded by a 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * language indicator. If so, this method will as a side-effect 2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * assign the extracted language code into mLanguage 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param language the language to return if hasLanguageIndicator is false 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a Pair of Strings containing the language and body of the message 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static Pair<String, String> unpackBody(byte[] pdu, int encoding, int offset, int length, 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean hasLanguageIndicator, String language) { 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String body = null; 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (encoding) { 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SmsConstants.ENCODING_7BIT: 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7); 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (hasLanguageIndicator && body != null && body.length() > 2) { 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Language is two GSM characters followed by a CR. 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // The actual body text is offset by 3 characters. 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = body.substring(0, 2); 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = body.substring(3); 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SmsConstants.ENCODING_16BIT: 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (hasLanguageIndicator && pdu.length >= offset + 2) { 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Language is two GSM characters. 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // The actual body text is offset by 2 bytes. 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2); 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville offset += 2; 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville length -= 2; 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = new String(pdu, offset, (length & 0xfffe), "utf-16"); 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (UnsupportedEncodingException e) { 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Apparently it wasn't valid UTF-16. 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Error decoding UTF-16 message", e); 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (body != null) { 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Remove trailing carriage return 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = body.length() - 1; i >= 0; i--) { 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (body.charAt(i) != CARRIAGE_RETURN) { 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = body.substring(0, i + 1); 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = ""; 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new Pair<String, String>(language, body); 2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 293