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