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 */ 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static SmsCbMessage createSmsCbMessage(SmsCbHeader header, SmsCbLocation location, 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[][] pdus) throws IllegalArgumentException { 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (header.isEtwsPrimaryNotification()) { 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP, 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getGeographicalScope(), header.getSerialNumber(), 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville location, header.getServiceCategory(), 680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville null, "ETWS", SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, 690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getEtwsInfo(), header.getCmasInfo()); 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String language = null; 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville StringBuilder sb = new StringBuilder(); 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (byte[] pdu : pdus) { 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pair<String, String> p = parseBody(header, pdu); 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = p.first; 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sb.append(p.second); 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : SmsCbMessage.MESSAGE_PRIORITY_NORMAL; 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP, 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getGeographicalScope(), header.getSerialNumber(), location, 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getServiceCategory(), language, sb.toString(), priority, 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville header.getEtwsInfo(), header.getCmasInfo()); 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Create a new SmsCbMessage object from one or more received PDUs. This is used by some 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * CellBroadcastReceiver test cases, because SmsCbHeader is now package local. 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param location the location (geographical scope) for the message 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdus PDU bytes 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public static SmsCbMessage createSmsCbMessage(SmsCbLocation location, byte[][] pdus) 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws IllegalArgumentException { 970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsCbHeader header = new SmsCbHeader(pdus[0]); 980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return createSmsCbMessage(header, location, pdus); 990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Parse and unpack the body text according to the encoding in the DCS. 1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * After completing successfully this method will have assigned the body 1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * text into mBody, and optionally the language code into mLanguage 1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param header the message header to use 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdu the PDU to decode 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a Pair of Strings containing the language and body of the message 1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static Pair<String, String> parseBody(SmsCbHeader header, byte[] pdu) { 1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int encoding; 1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String language = null; 1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean hasLanguageIndicator = false; 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int dataCodingScheme = header.getDataCodingScheme(); 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Extract encoding and language from DCS, as defined in 3gpp TS 23.038, 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // section 5. 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch ((dataCodingScheme & 0xf0) >> 4) { 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x00: 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f]; 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x01: 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hasLanguageIndicator = true; 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((dataCodingScheme & 0x0f) == 0x01) { 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_16BIT; 1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x02: 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f]; 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x03: 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x04: 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x05: 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch ((dataCodingScheme & 0x0c) >> 2) { 1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x01: 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_8BIT; 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x02: 1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_16BIT; 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x00: 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x06: 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x07: 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Compression not supported 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x09: 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // UDH structure not supported 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x0e: 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Defined by the WAP forum not supported 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Unsupported GSM dataCodingScheme " 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + dataCodingScheme); 1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 0x0f: 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (((dataCodingScheme & 0x04) >> 2) == 0x01) { 1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_8BIT; 1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Reserved values are to be treated as 7-bit 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville encoding = SmsConstants.ENCODING_7BIT; 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (header.isUmtsFormat()) { 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Payload may contain multiple pages 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH]; 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * nrPages) { 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match " 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + nrPages + " pages"); 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville StringBuilder sb = new StringBuilder(); 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < nrPages; i++) { 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Each page is 82 bytes followed by a length octet indicating 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // the number of useful octets within those 82 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i; 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int length = pdu[offset + PDU_BODY_PAGE_LENGTH]; 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (length > PDU_BODY_PAGE_LENGTH) { 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Page length " + length 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH); 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pair<String, String> p = unpackBody(pdu, encoding, offset, length, 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hasLanguageIndicator, language); 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = p.first; 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sb.append(p.second); 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new Pair<String, String>(language, sb.toString()); 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Payload is one single page 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int offset = SmsCbHeader.PDU_HEADER_LENGTH; 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int length = pdu.length - offset; 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return unpackBody(pdu, encoding, offset, length, hasLanguageIndicator, language); 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unpack body text from the pdu using the given encoding, position and 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * length within the pdu 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdu The pdu 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param encoding The encoding, as derived from the DCS 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param offset Position of the first byte to unpack 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param length Number of bytes to unpack 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param hasLanguageIndicator true if the body text is preceded by a 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * language indicator. If so, this method will as a side-effect 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * assign the extracted language code into mLanguage 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param language the language to return if hasLanguageIndicator is false 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a Pair of Strings containing the language and body of the message 2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static Pair<String, String> unpackBody(byte[] pdu, int encoding, int offset, int length, 2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean hasLanguageIndicator, String language) { 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String body = null; 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (encoding) { 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SmsConstants.ENCODING_7BIT: 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7); 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (hasLanguageIndicator && body != null && body.length() > 2) { 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Language is two GSM characters followed by a CR. 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // The actual body text is offset by 3 characters. 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = body.substring(0, 2); 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = body.substring(3); 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SmsConstants.ENCODING_16BIT: 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (hasLanguageIndicator && pdu.length >= offset + 2) { 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Language is two GSM characters. 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // The actual body text is offset by 2 bytes. 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville language = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2); 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville offset += 2; 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville length -= 2; 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = new String(pdu, offset, (length & 0xfffe), "utf-16"); 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (UnsupportedEncodingException e) { 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Apparently it wasn't valid UTF-16. 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException("Error decoding UTF-16 message", e); 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (body != null) { 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Remove trailing carriage return 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = body.length() - 1; i >= 0; i--) { 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (body.charAt(i) != CARRIAGE_RETURN) { 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = body.substring(0, i + 1); 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville body = ""; 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new Pair<String, String>(language, body); 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 288