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