13e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby/* 23e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Copyright (C) 2012 The Android Open Source Project 33e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * 43e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Licensed under the Apache License, Version 2.0 (the "License"); 53e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * you may not use this file except in compliance with the License. 63e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * You may obtain a copy of the License at 73e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * 83e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * http://www.apache.org/licenses/LICENSE-2.0 93e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * 103e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Unless required by applicable law or agreed to in writing, software 113e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * distributed under the License is distributed on an "AS IS" BASIS, 123e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * See the License for the specific language governing permissions and 143e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * limitations under the License. 153e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 163e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 173e3c3f80a90b156ff500076f8655647dfb317acfJake Hambypackage com.android.internal.telephony.gsm; 183e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 193e3c3f80a90b156ff500076f8655647dfb317acfJake Hambyimport android.telephony.SmsCbLocation; 203e3c3f80a90b156ff500076f8655647dfb317acfJake Hambyimport android.telephony.SmsCbMessage; 213e3c3f80a90b156ff500076f8655647dfb317acfJake Hambyimport android.util.Pair; 223e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 233e3c3f80a90b156ff500076f8655647dfb317acfJake Hambyimport com.android.internal.telephony.GsmAlphabet; 243e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 253e3c3f80a90b156ff500076f8655647dfb317acfJake Hambyimport java.io.UnsupportedEncodingException; 263e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 273e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby/** 283e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is 293e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * public because {@link #createSmsCbMessage(SmsCbLocation, byte[][])} is used by some test cases. 303e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 313e3c3f80a90b156ff500076f8655647dfb317acfJake Hambypublic class GsmSmsCbMessage { 323e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 333e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby /** 343e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5. 353e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 363e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby private static final String[] LANGUAGE_CODES_GROUP_0 = { 373e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu", 383e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby "pl", null 393e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby }; 403e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 413e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby /** 423e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5. 433e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 443e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby private static final String[] LANGUAGE_CODES_GROUP_2 = { 453e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null, 463e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby null, null 473e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby }; 483e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 493e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby private static final char CARRIAGE_RETURN = 0x0d; 503e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 513e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby private static final int PDU_BODY_PAGE_LENGTH = 82; 523e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 533e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby /** Utility class with only static methods. */ 543e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby private GsmSmsCbMessage() { } 553e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 563e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby /** 573e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Create a new SmsCbMessage object from a header object plus one or more received PDUs. 583e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * 593e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param pdus PDU bytes 603e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 613e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby static SmsCbMessage createSmsCbMessage(SmsCbHeader header, SmsCbLocation location, 623e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby byte[][] pdus) throws IllegalArgumentException { 633e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (header.isEtwsPrimaryNotification()) { 643e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP, 653e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby header.getGeographicalScope(), header.getSerialNumber(), 663e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby location, header.getServiceCategory(), 673e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby null, "ETWS", SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, 683e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby header.getEtwsInfo(), header.getCmasInfo()); 693e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } else { 703e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby String language = null; 713e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby StringBuilder sb = new StringBuilder(); 723e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby for (byte[] pdu : pdus) { 733e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby Pair<String, String> p = parseBody(header, pdu); 743e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby language = p.first; 753e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby sb.append(p.second); 763e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 773e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY 783e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby : SmsCbMessage.MESSAGE_PRIORITY_NORMAL; 793e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 803e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP, 813e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby header.getGeographicalScope(), header.getSerialNumber(), location, 823e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby header.getServiceCategory(), language, sb.toString(), priority, 833e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby header.getEtwsInfo(), header.getCmasInfo()); 843e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 853e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 863e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 873e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby /** 883e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Create a new SmsCbMessage object from one or more received PDUs. This is used by some 893e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * CellBroadcastReceiver test cases, because SmsCbHeader is now package local. 903e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * 913e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param location the location (geographical scope) for the message 923e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param pdus PDU bytes 933e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 943e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby public static SmsCbMessage createSmsCbMessage(SmsCbLocation location, byte[][] pdus) 953e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby throws IllegalArgumentException { 963e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby SmsCbHeader header = new SmsCbHeader(pdus[0]); 973e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby return createSmsCbMessage(header, location, pdus); 983e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 993e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1003e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby /** 1013e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Parse and unpack the body text according to the encoding in the DCS. 1023e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * After completing successfully this method will have assigned the body 1033e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * text into mBody, and optionally the language code into mLanguage 1043e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * 1053e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param header the message header to use 1063e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param pdu the PDU to decode 1073e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @return a Pair of Strings containing the language and body of the message 1083e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 1093e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby private static Pair<String, String> parseBody(SmsCbHeader header, byte[] pdu) { 1103e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int encoding; 1113e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby String language = null; 1123e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby boolean hasLanguageIndicator = false; 1133e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int dataCodingScheme = header.getDataCodingScheme(); 1143e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1153e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Extract encoding and language from DCS, as defined in 3gpp TS 23.038, 1163e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // section 5. 1173e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby switch ((dataCodingScheme & 0xf0) >> 4) { 1183e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x00: 1193e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_7BIT; 1203e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f]; 1213e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1223e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1233e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x01: 1243e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby hasLanguageIndicator = true; 1253e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if ((dataCodingScheme & 0x0f) == 0x01) { 1263e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_16BIT; 1273e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } else { 1283e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_7BIT; 1293e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 1303e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1313e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1323e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x02: 1333e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_7BIT; 1343e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f]; 1353e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1363e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1373e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x03: 1383e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_7BIT; 1393e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1403e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1413e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x04: 1423e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x05: 1433e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby switch ((dataCodingScheme & 0x0c) >> 2) { 1443e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x01: 1453e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_8BIT; 1463e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1473e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1483e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x02: 1493e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_16BIT; 1503e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1513e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1523e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x00: 1533e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby default: 1543e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_7BIT; 1553e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1563e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 1573e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1583e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1593e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x06: 1603e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x07: 1613e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Compression not supported 1623e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x09: 1633e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // UDH structure not supported 1643e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x0e: 1653e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Defined by the WAP forum not supported 1663e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby throw new IllegalArgumentException("Unsupported GSM dataCodingScheme " 1673e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby + dataCodingScheme); 1683e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1693e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case 0x0f: 1703e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (((dataCodingScheme & 0x04) >> 2) == 0x01) { 1713e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_8BIT; 1723e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } else { 1733e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_7BIT; 1743e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 1753e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1763e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1773e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby default: 1783e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Reserved values are to be treated as 7-bit 1793e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby encoding = android.telephony.SmsMessage.ENCODING_7BIT; 1803e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 1813e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 1823e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1833e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (header.isUmtsFormat()) { 1843e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Payload may contain multiple pages 1853e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH]; 1863e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1873e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) 1883e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * nrPages) { 1893e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match " 1903e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby + nrPages + " pages"); 1913e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 1923e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1933e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby StringBuilder sb = new StringBuilder(); 1943e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 1953e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby for (int i = 0; i < nrPages; i++) { 1963e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Each page is 82 bytes followed by a length octet indicating 1973e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // the number of useful octets within those 82 1983e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i; 1993e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int length = pdu[offset + PDU_BODY_PAGE_LENGTH]; 2003e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2013e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (length > PDU_BODY_PAGE_LENGTH) { 2023e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby throw new IllegalArgumentException("Page length " + length 2033e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH); 2043e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2053e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2063e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby Pair<String, String> p = unpackBody(pdu, encoding, offset, length, 2073e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby hasLanguageIndicator, language); 2083e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby language = p.first; 2093e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby sb.append(p.second); 2103e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2113e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby return new Pair<String, String>(language, sb.toString()); 2123e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } else { 2133e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Payload is one single page 2143e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int offset = SmsCbHeader.PDU_HEADER_LENGTH; 2153e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby int length = pdu.length - offset; 2163e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2173e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby return unpackBody(pdu, encoding, offset, length, hasLanguageIndicator, language); 2183e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2193e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2203e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2213e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby /** 2223e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * Unpack body text from the pdu using the given encoding, position and 2233e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * length within the pdu 2243e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * 2253e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param pdu The pdu 2263e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param encoding The encoding, as derived from the DCS 2273e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param offset Position of the first byte to unpack 2283e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param length Number of bytes to unpack 2293e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param hasLanguageIndicator true if the body text is preceded by a 2303e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * language indicator. If so, this method will as a side-effect 2313e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * assign the extracted language code into mLanguage 2323e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @param language the language to return if hasLanguageIndicator is false 2333e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby * @return a Pair of Strings containing the language and body of the message 2343e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby */ 2353e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby private static Pair<String, String> unpackBody(byte[] pdu, int encoding, int offset, int length, 2363e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby boolean hasLanguageIndicator, String language) { 2373e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby String body = null; 2383e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2393e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby switch (encoding) { 2403e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case android.telephony.SmsMessage.ENCODING_7BIT: 2413e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7); 2423e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2433e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (hasLanguageIndicator && body != null && body.length() > 2) { 2443e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Language is two GSM characters followed by a CR. 2453e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // The actual body text is offset by 3 characters. 2463e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby language = body.substring(0, 2); 2473e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby body = body.substring(3); 2483e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2493e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 2503e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2513e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby case android.telephony.SmsMessage.ENCODING_16BIT: 2523e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (hasLanguageIndicator && pdu.length >= offset + 2) { 2533e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Language is two GSM characters. 2543e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // The actual body text is offset by 2 bytes. 2553e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby language = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2); 2563e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby offset += 2; 2573e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby length -= 2; 2583e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2593e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2603e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby try { 2613e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby body = new String(pdu, offset, (length & 0xfffe), "utf-16"); 2623e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } catch (UnsupportedEncodingException e) { 2633e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Apparently it wasn't valid UTF-16. 2643e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby throw new IllegalArgumentException("Error decoding UTF-16 message", e); 2653e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2663e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 2673e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2683e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby default: 2693e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 2703e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2713e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2723e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (body != null) { 2733e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby // Remove trailing carriage return 2743e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby for (int i = body.length() - 1; i >= 0; i--) { 2753e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby if (body.charAt(i) != CARRIAGE_RETURN) { 2763e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby body = body.substring(0, i + 1); 2773e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby break; 2783e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2793e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2803e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } else { 2813e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby body = ""; 2823e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2833e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby 2843e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby return new Pair<String, String>(language, body); 2853e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby } 2863e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby} 287