1c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville/*
2c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * Copyright (C) 2006 The Android Open Source Project
3c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *
4c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * Licensed under the Apache License, Version 2.0 (the "License");
5c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * you may not use this file except in compliance with the License.
6c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * You may obtain a copy of the License at
7c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *
8c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *      http://www.apache.org/licenses/LICENSE-2.0
9c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *
10c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * Unless required by applicable law or agreed to in writing, software
11c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * distributed under the License is distributed on an "AS IS" BASIS,
12c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * See the License for the specific language governing permissions and
14c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * limitations under the License.
15c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville */
16c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
17c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savillepackage com.android.internal.telephony.gsm;
18c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
19c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.telephony.PhoneNumberUtils;
20c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.text.format.Time;
21ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Savilleimport android.telephony.Rlog;
22c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
23c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.EncodeException;
24c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.GsmAlphabet;
25c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
26d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenkaimport com.android.internal.telephony.uicc.IccUtils;
27c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SmsHeader;
28c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SmsMessageBase;
29c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
30c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport java.io.ByteArrayOutputStream;
31c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport java.io.UnsupportedEncodingException;
325ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumarimport java.text.ParseException;
33c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
34c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.MessageClass;
35c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
36c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
37c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
38c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
39c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
40c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
41c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES;
42c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
43c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
44c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville/**
45c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * A Short Message Service message.
46c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *
47c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville */
48c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savillepublic class SmsMessage extends SmsMessageBase {
49cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville    static final String LOG_TAG = "SmsMessage";
50cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville    private static final boolean VDBG = false;
51c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
52c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private MessageClass messageClass;
53c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
54c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
55c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * TP-Message-Type-Indicator
56c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * 9.2.3
57c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
5822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private int mMti;
59c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
60c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** TP-Protocol-Identifier (TP-PID) */
6122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private int mProtocolIdentifier;
62c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
63c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // TP-Data-Coding-Scheme
64c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // see TS 23.038
6522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private int mDataCodingScheme;
66c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
67c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // TP-Reply-Path
68c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // e.g. 23.040 9.2.2.1
6922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private boolean mReplyPathPresent = false;
70c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
71c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** The address of the receiver. */
7222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private GsmSmsAddress mRecipientAddress;
73c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
74c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
75c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  TP-Status - status of a previously submitted SMS.
76c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  This field applies to SMS-STATUS-REPORT messages.  0 indicates success;
77c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  see TS 23.040, 9.2.3.15 for description of other possible values.
78c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
7922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private int mStatus;
80c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
81c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
82c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  TP-Status - status of a previously submitted SMS.
83c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  This field is true iff the message is a SMS-STATUS-REPORT message.
84c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
8522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private boolean mIsStatusReportMessage = false;
86c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
87c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static class SubmitPdu extends SubmitPduBase {
88c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
89c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
90c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
91c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Create an SmsMessage from a raw PDU.
92c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
93c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage createFromPdu(byte[] pdu) {
94c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
95c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
96c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(pdu);
97c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
98c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
99ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
100c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
1011260f1c6c909f2940989b72afe1b91fd83845eaaSukanya Rajkhowa        } catch (OutOfMemoryError e) {
1021260f1c6c909f2940989b72afe1b91fd83845eaaSukanya Rajkhowa            Rlog.e(LOG_TAG, "SMS PDU parsing failed with out of memory: ", e);
1031260f1c6c909f2940989b72afe1b91fd83845eaaSukanya Rajkhowa            return null;
104c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
105c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
106c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
107c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
108c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * 3GPP TS 23.040 9.2.3.9 specifies that Type Zero messages are indicated
109c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * by TP_PID field set to value 0x40
110c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
111c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isTypeZero() {
11222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return (mProtocolIdentifier == 0x40);
113c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
114c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
115c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
116c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
117c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * +CMT unsolicited response (PDU mode, of course)
118c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
119c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
120c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Only public for debugging
121c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
122c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * {@hide}
123c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
124c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage newFromCMT(String[] lines) {
125c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
126c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
127c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(IccUtils.hexStringToBytes(lines[1]));
128c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
129c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
130ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
131c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
132c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
133c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
134c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
135c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** @hide */
136c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage newFromCDS(String line) {
137c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
138c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
139c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(IccUtils.hexStringToBytes(line));
140c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
141c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
142ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville            Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex);
143c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
144c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
145c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
146c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
147c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
148c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Create an SmsMessage from an SMS EF record.
149c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
150c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param index Index of SMS record. This should be index in ArrayList
151c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *              returned by SmsManager.getAllMessagesFromSim + 1.
152c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param data Record data.
153c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return An SmsMessage representing the record.
154c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
155c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @hide
156c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
157c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage createFromEfRecord(int index, byte[] data) {
158c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
159c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
160c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
16122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            msg.mIndexOnIcc = index;
162c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
163c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT,
164c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // or STORED_UNSENT
165c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // See TS 51.011 10.5.3
166c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((data[0] & 1) == 0) {
167ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.w(LOG_TAG,
168c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        "SMS parsing failed: Trying to parse a free record");
169c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
170c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
17122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                msg.mStatusOnIcc = data[0] & 0x07;
172c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
173c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
174c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int size = data.length - 1;
175c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
176c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Note: Data may include trailing FF's.  That's OK; message
177c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // should still parse correctly.
178c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            byte[] pdu = new byte[size];
179c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            System.arraycopy(data, 1, pdu, 0, size);
180c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(pdu);
181c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
182c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
183ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
184c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
185c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
186c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
187c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
188c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
189c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
190c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * length in bytes (not hex chars) less the SMSC header
191c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
192c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static int getTPLayerLengthForPDU(String pdu) {
193c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int len = pdu.length() / 2;
194c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
195c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
196c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return len - smscLen - 1;
197c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
198c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
199c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
200c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a destination address and a message
201c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
202c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address.  Null means use default.
203c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
204c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
205c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
206c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @hide
207c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
208c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
209c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, String message,
210c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested, byte[] header) {
211c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
212c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ENCODING_UNKNOWN, 0, 0);
213c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
214c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
215c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
216c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
217c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a destination address and a message using the
218c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * specified encoding.
219c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
220c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address.  Null means use default.
221c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param encoding Encoding defined by constants in
222c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *        com.android.internal.telephony.SmsConstants.ENCODING_*
223c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param languageTable
224c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param languageShiftTable
225c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
226c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
227c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
228c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @hide
229c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
230c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
231c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, String message,
232c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested, byte[] header, int encoding,
233c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int languageTable, int languageShiftTable) {
234c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
235c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Perform null parameter checks.
236c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (message == null || destinationAddress == null) {
237c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
238c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
239c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
240c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (encoding == ENCODING_UNKNOWN) {
241c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Find the best encoding to use
242c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            TextEncodingDetails ted = calculateLength(message, false);
243c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            encoding = ted.codeUnitSize;
244c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            languageTable = ted.languageTable;
245c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            languageShiftTable = ted.languageShiftTable;
246c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
247c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (encoding == ENCODING_7BIT &&
248c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    (languageTable != 0 || languageShiftTable != 0)) {
249c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                if (header != null) {
250c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    SmsHeader smsHeader = SmsHeader.fromByteArray(header);
251c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    if (smsHeader.languageTable != languageTable
252c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            || smsHeader.languageShiftTable != languageShiftTable) {
253ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                        Rlog.w(LOG_TAG, "Updating language table in SMS header: "
254c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                                + smsHeader.languageTable + " -> " + languageTable + ", "
255c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                                + smsHeader.languageShiftTable + " -> " + languageShiftTable);
256c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        smsHeader.languageTable = languageTable;
257c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        smsHeader.languageShiftTable = languageShiftTable;
258c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        header = SmsHeader.toByteArray(smsHeader);
259c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    }
260c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                } else {
261c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    SmsHeader smsHeader = new SmsHeader();
262c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    smsHeader.languageTable = languageTable;
263c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    smsHeader.languageShiftTable = languageShiftTable;
264c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    header = SmsHeader.toByteArray(smsHeader);
265c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
266c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
267c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
268c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
269c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SubmitPdu ret = new SubmitPdu();
270c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // MTI = SMS-SUBMIT, UDHI = header != null
271c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
272c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ByteArrayOutputStream bo = getSubmitPduHead(
273c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                scAddress, destinationAddress, mtiByte,
274c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                statusReportRequested, ret);
275c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
276c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // User Data (and length)
277c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] userData;
278c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
279c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (encoding == ENCODING_7BIT) {
280c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
281c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        languageTable, languageShiftTable);
282c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else { //assume UCS-2
283c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                try {
284c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    userData = encodeUCS2(message, header);
285c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                } catch(UnsupportedEncodingException uex) {
286ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                    Rlog.e(LOG_TAG,
287c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            "Implausible UnsupportedEncodingException ",
288c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            uex);
289c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    return null;
290c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
291c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
292c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (EncodeException ex) {
293c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Encoding to the 7-bit alphabet failed. Let's see if we can
294c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // send it as a UCS-2 encoded message
295c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            try {
296c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                userData = encodeUCS2(message, header);
297c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encoding = ENCODING_16BIT;
298c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } catch(UnsupportedEncodingException uex) {
299ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.e(LOG_TAG,
300c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        "Implausible UnsupportedEncodingException ",
301c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        uex);
302c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
303c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
304c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
305c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
306c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (encoding == ENCODING_7BIT) {
307c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
308c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Message too long
309ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
310c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
311c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
312c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Data-Coding-Scheme
313c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Default encoding, uncompressed
314c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // To test writing messages to the SIM card, change this value 0x00
315c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // to 0x12, which means "bits 1 and 0 contain message class, and the
316c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // class is 2". Note that this takes effect for the sender. In other
317c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // words, messages sent by the phone with this change will end up on
318c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // the receiver's SIM card. You can then send messages to yourself
319c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // (on a phone with this change) and they'll end up on the SIM card.
320c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            bo.write(0x00);
321c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else { // assume UCS-2
322c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
323c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Message too long
324ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
325c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
326c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
327c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Data-Coding-Scheme
328c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // UCS-2 encoding, uncompressed
329c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            bo.write(0x08);
330c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
331c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
332c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // (no TP-Validity-Period)
333c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(userData, 0, userData.length);
334c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ret.encodedMessage = bo.toByteArray();
335c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ret;
336c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
337c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
338c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
339c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Packs header and UCS-2 encoded message. Includes TP-UDL & TP-UDHL if necessary
340c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
341cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville     * @return encoded message as UCS2
342c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @throws UnsupportedEncodingException
343c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
344c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static byte[] encodeUCS2(String message, byte[] header)
345c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        throws UnsupportedEncodingException {
346c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] userData, textPart;
347c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        textPart = message.getBytes("utf-16be");
348c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
349c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (header != null) {
350c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Need 1 byte for UDHL
351c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userData = new byte[header.length + textPart.length + 1];
352c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
353c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userData[0] = (byte)header.length;
354c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            System.arraycopy(header, 0, userData, 1, header.length);
355c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
356c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
357c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        else {
358c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userData = textPart;
359c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
360c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] ret = new byte[userData.length+1];
361c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ret[0] = (byte) (userData.length & 0xff );
362c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        System.arraycopy(userData, 0, ret, 1, userData.length);
363c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ret;
364c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
365c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
366c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
367c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a destination address and a message
368c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
369c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address.  Null means use default.
370c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
371c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
372c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
373c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
374c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
375c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, String message,
376c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested) {
377c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
378c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null);
379c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
380c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
381c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
382c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
383c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
384c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address. null == use default
385c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param destinationAddress the address of the destination for the message
386c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param destinationPort the port to deliver the message to at the
387c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *        destination
388c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param data the data for the message
389c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
390c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
391c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
392c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
393c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
394c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, int destinationPort, byte[] data,
395c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested) {
396c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
397c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
398c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        portAddrs.destPort = destinationPort;
399c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        portAddrs.origPort = 0;
400c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        portAddrs.areEightBits = false;
401c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
402c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsHeader smsHeader = new SmsHeader();
403c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        smsHeader.portAddrs = portAddrs;
404c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
405c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader);
406c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
407c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) {
408ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville            Rlog.e(LOG_TAG, "SMS data message may only contain "
409c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    + (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes");
410c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
411c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
412c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
413c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SubmitPdu ret = new SubmitPdu();
414c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ByteArrayOutputStream bo = getSubmitPduHead(
415c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
416c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                                                            // TP-UDHI = true
417c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                statusReportRequested, ret);
418c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
419c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Data-Coding-Scheme
420c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // No class, 8 bit data
421c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(0x04);
422c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
423c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // (no TP-Validity-Period)
424c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
425c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Total size
426c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(data.length + smsHeaderData.length + 1);
427c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
428c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // User data header
429c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(smsHeaderData.length);
430c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(smsHeaderData, 0, smsHeaderData.length);
431c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
432c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // User data
433c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(data, 0, data.length);
434c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
435c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ret.encodedMessage = bo.toByteArray();
436c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ret;
437c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
438c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
439c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
440c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Create the beginning of a SUBMIT PDU.  This is the part of the
441c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu},
442c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * one of which takes a byte array and the other of which takes a
443c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * <code>String</code>.
444c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
445c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address. null == use default
446c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param destinationAddress the address of the destination for the message
447c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param mtiByte
448c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param ret <code>SubmitPdu</code> containing the encoded SC
449c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *        address, if applicable, and the encoded message
450c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
451c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static ByteArrayOutputStream getSubmitPduHead(
452c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String scAddress, String destinationAddress, byte mtiByte,
453c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested, SubmitPdu ret) {
454c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ByteArrayOutputStream bo = new ByteArrayOutputStream(
455c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                MAX_USER_DATA_BYTES + 40);
456c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
457c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // SMSC address with length octet, or 0
458c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (scAddress == null) {
459c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ret.encodedScAddress = null;
460c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
461c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
462c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    scAddress);
463c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
464c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
465c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Type-Indicator (and friends)
466c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (statusReportRequested) {
467c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Set TP-Status-Report-Request bit.
468c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mtiByte |= 0x20;
469cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville            if (VDBG) Rlog.d(LOG_TAG, "SMS status report requested");
470c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
471c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(mtiByte);
472c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
473c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // space for TP-Message-Reference
474c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(0);
475c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
476c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] daBytes;
477c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
478c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
479c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
480c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // destination address length in BCD digits, ignoring TON byte and pad
481c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TODO Should be better.
482c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write((daBytes.length - 1) * 2
483c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
484c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
485c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // destination address
486c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(daBytes, 0, daBytes.length);
487c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
488c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Protocol-Identifier
489c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(0);
490c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return bo;
491c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
492c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
493c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static class PduParser {
49422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        byte mPdu[];
49522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        int mCur;
49622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        SmsHeader mUserDataHeader;
49722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        byte[] mUserData;
498c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int mUserDataSeptetPadding;
499c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
500c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        PduParser(byte[] pdu) {
50122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mPdu = pdu;
50222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mCur = 0;
503c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mUserDataSeptetPadding = 0;
504c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
505c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
506c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
507c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Parse and return the SC address prepended to SMS messages coming via
508c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * the TS 27.005 / AT interface.  Returns null on invalid address
509c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
510c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getSCAddress() {
511c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int len;
512c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
513c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
514c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // length of SC Address
515c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            len = getByte();
516c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
517c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (len == 0) {
518c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // no SC address
519c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = null;
520c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
521c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // SC address
522c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                try {
523c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    ret = PhoneNumberUtils
52422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            .calledPartyBCDToString(mPdu, mCur, len);
525c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                } catch (RuntimeException tr) {
526ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                    Rlog.d(LOG_TAG, "invalid SC address: ", tr);
527c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    ret = null;
528c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
529c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
530c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
53122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mCur += len;
532c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
533c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
534c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
535c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
536c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
537c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * returns non-sign-extended byte value
538c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
539c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int getByte() {
54022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            return mPdu[mCur++] & 0xff;
541c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
542c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
543c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
544c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Any address except the SC address (eg, originating address) See TS
545c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * 23.040 9.1.2.5
546c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
547c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        GsmSmsAddress getAddress() {
548c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            GsmSmsAddress ret;
549c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
550c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // "The Address-Length field is an integer representation of
551c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // the number field, i.e. excludes any semi-octet containing only
552c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // fill bits."
553c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // The TOA field is not included as part of this
55422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int addressLength = mPdu[mCur] & 0xff;
555c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int lengthBytes = 2 + (addressLength + 1) / 2;
556c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
5575ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar            try {
55822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                ret = new GsmSmsAddress(mPdu, mCur, lengthBytes);
5595ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar            } catch (ParseException e) {
5605ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar                ret = null;
561ace9a749c5a2a5e07527f728b7331423d16c36cdSukanya Rajkhowa                //This is caught by createFromPdu(byte[] pdu)
562ace9a749c5a2a5e07527f728b7331423d16c36cdSukanya Rajkhowa                throw new RuntimeException(e.getMessage());
5635ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar            }
564c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
56522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mCur += lengthBytes;
566c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
567c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
568c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
569c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
570c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
571c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Parses an SC timestamp and returns a currentTimeMillis()-style
572c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * timestamp
573c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
574c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
575c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        long getSCTimestampMillis() {
576c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Service-Centre-Time-Stamp
57722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int year = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
57822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int month = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
57922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int day = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
58022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int hour = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
58122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int minute = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
58222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int second = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
583c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
584c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // For the timezone, the most significant bit of the
585c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // least significant nibble is the sign byte
586c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // (meaning the max range of this field is 79 quarter-hours,
587c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // which is more than enough)
588c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
58922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            byte tzByte = mPdu[mCur++];
590c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
591c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Mask out sign bit.
592c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
593c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
594c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
595c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
596c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Time time = new Time(Time.TIMEZONE_UTC);
597c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
598c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // It's 2006.  Should I really support years < 2000?
599c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.year = year >= 90 ? year + 1900 : year + 2000;
600c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.month = month - 1;
601c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.monthDay = day;
602c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.hour = hour;
603c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.minute = minute;
604c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.second = second;
605c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
606c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Timezone offset is in quarter hours.
607c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000);
608c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
609c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
610c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
611c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Pulls the user data out of the PDU, and separates the payload from
612c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * the header if there is one.
613c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
614c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param hasUserDataHeader true if there is a user data header
615c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param dataInSeptets true if the data payload is in septets instead
616c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *  of octets
617c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return the number of septets or octets in the user data payload
618c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
619c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {
62022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int offset = mCur;
62122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int userDataLength = mPdu[offset++] & 0xff;
622c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int headerSeptets = 0;
623c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int userDataHeaderLength = 0;
624c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
625c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (hasUserDataHeader) {
62622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                userDataHeaderLength = mPdu[offset++] & 0xff;
627c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
628c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                byte[] udh = new byte[userDataHeaderLength];
62922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                System.arraycopy(mPdu, offset, udh, 0, userDataHeaderLength);
63022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mUserDataHeader = SmsHeader.fromByteArray(udh);
631c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                offset += userDataHeaderLength;
632c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
633c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                int headerBits = (userDataHeaderLength + 1) * 8;
634c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                headerSeptets = headerBits / 7;
635c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
636c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mUserDataSeptetPadding = (headerSeptets * 7) - headerBits;
637c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
638c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
639c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int bufferLen;
640c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (dataInSeptets) {
641c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                /*
642c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * Here we just create the user data length to be the remainder of
643c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * the pdu minus the user data header, since userDataLength means
644c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * the number of uncompressed septets.
645c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 */
64622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                bufferLen = mPdu.length - offset;
647c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
648c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                /*
649c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * userDataLength is the count of octets, so just subtract the
650c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * user data header.
651c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 */
652c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                bufferLen = userDataLength - (hasUserDataHeader ? (userDataHeaderLength + 1) : 0);
653c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                if (bufferLen < 0) {
654c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    bufferLen = 0;
655c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
656c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
657c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
65822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mUserData = new byte[bufferLen];
65922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            System.arraycopy(mPdu, offset, mUserData, 0, mUserData.length);
66022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mCur = offset;
661c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
662c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (dataInSeptets) {
663c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Return the number of septets
664c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                int count = userDataLength - headerSeptets;
665c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // If count < 0, return 0 (means UDL was probably incorrect)
666c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return count < 0 ? 0 : count;
667c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
668c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Return the number of octets
66922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                return mUserData.length;
670c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
671c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
672c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
673c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
674c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Returns the user data payload, not including the headers
675c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
676c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return the user data payload, not including the headers
677c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
678c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] getUserData() {
67922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            return mUserData;
680c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
681c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
682c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
683c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Returns an object representing the user data headers
684c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
685c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * {@hide}
686c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
687c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsHeader getUserDataHeader() {
68822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            return mUserDataHeader;
689c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
690c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
691c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
692c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Interprets the user data payload as packed GSM 7bit characters, and
693c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * decodes them into a String.
694c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
695c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param septetCount the number of septets in the user data payload
696c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return a String with the decoded characters
697c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
698c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getUserDataGSM7Bit(int septetCount, int languageTable,
699c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                int languageShiftTable) {
700c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
701c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
70222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            ret = GsmAlphabet.gsm7BitPackedToString(mPdu, mCur, septetCount,
703c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    mUserDataSeptetPadding, languageTable, languageShiftTable);
704c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
70522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mCur += (septetCount * 7) / 8;
706c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
707c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
708c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
709c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
710c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
711c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Interprets the user data payload as UCS2 characters, and
712c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * decodes them into a String.
713c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
714c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param byteCount the number of bytes in the user data payload
715c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return a String with the decoded characters
716c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
717c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getUserDataUCS2(int byteCount) {
718c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
719c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
720c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            try {
72122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                ret = new String(mPdu, mCur, byteCount, "utf-16");
722c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } catch (UnsupportedEncodingException ex) {
723c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = "";
724ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
725c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
726c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
72722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mCur += byteCount;
728c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
729c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
730c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
731c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
732c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Interprets the user data payload as KSC-5601 characters, and
733c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * decodes them into a String.
734c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
735c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param byteCount the number of bytes in the user data payload
736c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return a String with the decoded characters
737c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
738c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getUserDataKSC5601(int byteCount) {
739c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
740c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
741c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            try {
74222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                ret = new String(mPdu, mCur, byteCount, "KSC5601");
743c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } catch (UnsupportedEncodingException ex) {
744c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = "";
745ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
746c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
747c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
74822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mCur += byteCount;
749c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
750c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
751c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
752c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean moreDataPresent() {
75322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            return (mPdu.length > mCur);
754c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
755c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
756c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
757c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
758c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Calculate the number of septets needed to encode the message.
759c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
760c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param msgBody the message to encode
761c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param use7bitOnly ignore (but still count) illegal characters if true
762c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return TextEncodingDetails
763c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
764c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static TextEncodingDetails calculateLength(CharSequence msgBody,
765c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean use7bitOnly) {
766c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(msgBody, use7bitOnly);
767c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (ted == null) {
768c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ted = new TextEncodingDetails();
769c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int octets = msgBody.length() * 2;
770c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ted.codeUnitCount = msgBody.length();
771c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (octets > MAX_USER_DATA_BYTES) {
772c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
773c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        MAX_USER_DATA_BYTES_WITH_HEADER;
774c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.codeUnitsRemaining = ((ted.msgCount *
775c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
776c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
777c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.msgCount = 1;
778c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
779c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
780c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ted.codeUnitSize = ENCODING_16BIT;
781c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
782c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ted;
783c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
784c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
785c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
786c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
787c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public int getProtocolIdentifier() {
78822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mProtocolIdentifier;
789c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
790c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
791c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
792c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Returns the TP-Data-Coding-Scheme byte, for acknowledgement of SMS-PP download messages.
793c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return the TP-DCS field of the SMS header
794c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
795c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    int getDataCodingScheme() {
79622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mDataCodingScheme;
797c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
798c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
799c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
800c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
801c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isReplace() {
80222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return (mProtocolIdentifier & 0xc0) == 0x40
80322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                && (mProtocolIdentifier & 0x3f) > 0
80422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                && (mProtocolIdentifier & 0x3f) < 8;
805c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
806c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
807c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
808c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
809c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isCphsMwiMessage() {
81022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageClear()
81122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                || ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet();
812c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
813c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
814c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
815c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
816c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isMWIClearMessage() {
81722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (mIsMwi && !mMwiSense) {
818c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return true;
819c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
820c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
82122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mOriginatingAddress != null
82222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageClear();
823c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
824c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
825c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
826c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
827c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isMWISetMessage() {
82822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (mIsMwi && mMwiSense) {
829c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return true;
830c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
831c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
83222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mOriginatingAddress != null
83322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet();
834c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
835c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
836c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
837c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
838c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isMwiDontStore() {
83922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (mIsMwi && mMwiDontStore) {
840c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return true;
841c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
842c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
843c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (isCphsMwiMessage()) {
844c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // See CPHS 4.2 Section B.4.2.1
845c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // If the user data is a single space char, do not store
846c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // the message. Otherwise, store and display as usual
847c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (" ".equals(getMessageBody())) {
848945e169166e9fd4bcb145d23dffb7c7b68cd6208Björn Ekberg                return true;
849c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
850c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
851c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
852c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return false;
853c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
854c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
855c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
856c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
857c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public int getStatus() {
85822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mStatus;
859c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
860c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
861c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
862c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
863c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isStatusReportMessage() {
86422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mIsStatusReportMessage;
865c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
866c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
867c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
868c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
869c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isReplyPathPresent() {
87022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        return mReplyPathPresent;
871c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
872c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
873c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
874c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * TS 27.005 3.1, &lt;pdu&gt; definition "In the case of SMS: 3GPP TS 24.011 [6]
875c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
876c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * ME/TA converts each octet of TP data unit into two IRA character long
877c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * hex number (e.g. octet with integer value 42 is presented to TE as two
878c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
879c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * something else...
880c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
881c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parsePdu(byte[] pdu) {
882c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mPdu = pdu;
883ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville        // Rlog.d(LOG_TAG, "raw sms message:");
884ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville        // Rlog.d(LOG_TAG, s);
885c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
886c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        PduParser p = new PduParser(pdu);
887c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
88822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mScAddress = p.getSCAddress();
889c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
89022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (mScAddress != null) {
89122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            if (VDBG) Rlog.d(LOG_TAG, "SMS SC address: " + mScAddress);
892c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
893c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
894c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TODO(mkf) support reply path, user data header indicator
895c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
896c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Type-Indicator
897c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // 9.2.3
898c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int firstByte = p.getByte();
899c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
90022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mMti = firstByte & 0x3;
90122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        switch (mMti) {
902c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Type-Indicator
903c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // 9.2.3
904c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case 0:
905c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
906c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                //This should be processed in the same way as MTI == 0 (Deliver)
907c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            parseSmsDeliver(p, firstByte);
908c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
9092cbcc15677797f5879cf8c907876b366b034d433Bin Li        case 1:
9102cbcc15677797f5879cf8c907876b366b034d433Bin Li            parseSmsSubmit(p, firstByte);
9112cbcc15677797f5879cf8c907876b366b034d433Bin Li            break;
912c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case 2:
913c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            parseSmsStatusReport(p, firstByte);
914c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
915c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        default:
916c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TODO(mkf) the rest of these
917c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            throw new RuntimeException("Unsupported message type");
918c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
919c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
920c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
921c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
922c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Parses a SMS-STATUS-REPORT message.
923c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
924c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param p A PduParser, cued past the first byte.
925c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param firstByte The first byte of the PDU, which contains MTI, etc.
926c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
927c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parseSmsStatusReport(PduParser p, int firstByte) {
92822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mIsStatusReportMessage = true;
929c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
930c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Reference
93122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mMessageRef = p.getByte();
932c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Recipient-Address
93322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mRecipientAddress = p.getAddress();
934c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Service-Centre-Time-Stamp
93522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mScTimeMillis = p.getSCTimestampMillis();
936cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        p.getSCTimestampMillis();
937c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Status
93822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mStatus = p.getByte();
939c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
940c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // The following are optional fields that may or may not be present.
941c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (p.moreDataPresent()) {
942c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Parameter-Indicator
943c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int extraParams = p.getByte();
944c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int moreExtraParams = extraParams;
945c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            while ((moreExtraParams & 0x80) != 0) {
946c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // We only know how to parse a few extra parameters, all
947c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // indicated in the first TP-PI octet, so skip over any
948c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // additional TP-PI octets.
949c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                moreExtraParams = p.getByte();
950c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
95156c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan            // As per 3GPP 23.040 section 9.2.3.27 TP-Parameter-Indicator,
95256c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan            // only process the byte if the reserved bits (bits3 to 6) are zero.
95356c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan            if ((extraParams & 0x78) == 0) {
95456c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                // TP-Protocol-Identifier
95556c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                if ((extraParams & 0x01) != 0) {
95622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    mProtocolIdentifier = p.getByte();
95756c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                }
95856c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                // TP-Data-Coding-Scheme
95956c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                if ((extraParams & 0x02) != 0) {
96022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    mDataCodingScheme = p.getByte();
96156c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                }
96256c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                // TP-User-Data-Length (implies existence of TP-User-Data)
96356c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                if ((extraParams & 0x04) != 0) {
96456c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                    boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
96556c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                    parseUserData(p, hasUserDataHeader);
96656c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                }
967c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
968c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
969c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
970c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
971c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parseSmsDeliver(PduParser p, int firstByte) {
97222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mReplyPathPresent = (firstByte & 0x80) == 0x80;
973c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
97422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mOriginatingAddress = p.getAddress();
975c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
97622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (mOriginatingAddress != null) {
977cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville            if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
97822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    + mOriginatingAddress.address);
979c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
980c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
981c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Protocol-Identifier (TP-PID)
982c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TS 23.040 9.2.3.9
98322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mProtocolIdentifier = p.getByte();
984c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
985c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Data-Coding-Scheme
986c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // see TS 23.038
98722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mDataCodingScheme = p.getByte();
988c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
989cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        if (VDBG) {
99022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier
99122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    + " data coding scheme: " + mDataCodingScheme);
992c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
993c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
99422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mScTimeMillis = p.getSCTimestampMillis();
995c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
99622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);
997c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
998c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
999c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1000c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        parseUserData(p, hasUserDataHeader);
1001c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1002c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1003c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
10042cbcc15677797f5879cf8c907876b366b034d433Bin Li     * Parses a SMS-SUBMIT message.
10052cbcc15677797f5879cf8c907876b366b034d433Bin Li     *
10062cbcc15677797f5879cf8c907876b366b034d433Bin Li     * @param p A PduParser, cued past the first byte.
10072cbcc15677797f5879cf8c907876b366b034d433Bin Li     * @param firstByte The first byte of the PDU, which contains MTI, etc.
10082cbcc15677797f5879cf8c907876b366b034d433Bin Li     */
10092cbcc15677797f5879cf8c907876b366b034d433Bin Li    private void parseSmsSubmit(PduParser p, int firstByte) {
101022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mReplyPathPresent = (firstByte & 0x80) == 0x80;
10112cbcc15677797f5879cf8c907876b366b034d433Bin Li
10122cbcc15677797f5879cf8c907876b366b034d433Bin Li        // TP-MR (TP-Message Reference)
101322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mMessageRef = p.getByte();
10142cbcc15677797f5879cf8c907876b366b034d433Bin Li
101522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mRecipientAddress = p.getAddress();
10162cbcc15677797f5879cf8c907876b366b034d433Bin Li
101722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (mRecipientAddress != null) {
101822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            if (VDBG) Rlog.v(LOG_TAG, "SMS recipient address: " + mRecipientAddress.address);
10192cbcc15677797f5879cf8c907876b366b034d433Bin Li        }
10202cbcc15677797f5879cf8c907876b366b034d433Bin Li
10212cbcc15677797f5879cf8c907876b366b034d433Bin Li        // TP-Protocol-Identifier (TP-PID)
10222cbcc15677797f5879cf8c907876b366b034d433Bin Li        // TS 23.040 9.2.3.9
102322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mProtocolIdentifier = p.getByte();
10242cbcc15677797f5879cf8c907876b366b034d433Bin Li
10252cbcc15677797f5879cf8c907876b366b034d433Bin Li        // TP-Data-Coding-Scheme
10262cbcc15677797f5879cf8c907876b366b034d433Bin Li        // see TS 23.038
102722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mDataCodingScheme = p.getByte();
10282cbcc15677797f5879cf8c907876b366b034d433Bin Li
1029cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        if (VDBG) {
103022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier
103122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    + " data coding scheme: " + mDataCodingScheme);
10322cbcc15677797f5879cf8c907876b366b034d433Bin Li        }
10332cbcc15677797f5879cf8c907876b366b034d433Bin Li
10342cbcc15677797f5879cf8c907876b366b034d433Bin Li        // TP-Validity-Period-Format
10352cbcc15677797f5879cf8c907876b366b034d433Bin Li        int validityPeriodLength = 0;
10362cbcc15677797f5879cf8c907876b366b034d433Bin Li        int validityPeriodFormat = ((firstByte>>3) & 0x3);
10372cbcc15677797f5879cf8c907876b366b034d433Bin Li        if (0x0 == validityPeriodFormat) /* 00, TP-VP field not present*/
10382cbcc15677797f5879cf8c907876b366b034d433Bin Li        {
10392cbcc15677797f5879cf8c907876b366b034d433Bin Li            validityPeriodLength = 0;
10402cbcc15677797f5879cf8c907876b366b034d433Bin Li        }
10412cbcc15677797f5879cf8c907876b366b034d433Bin Li        else if (0x2 == validityPeriodFormat) /* 10, TP-VP: relative format*/
10422cbcc15677797f5879cf8c907876b366b034d433Bin Li        {
10432cbcc15677797f5879cf8c907876b366b034d433Bin Li            validityPeriodLength = 1;
10442cbcc15677797f5879cf8c907876b366b034d433Bin Li        }
10452cbcc15677797f5879cf8c907876b366b034d433Bin Li        else /* other case, 11 or 01, TP-VP: absolute or enhanced format*/
10462cbcc15677797f5879cf8c907876b366b034d433Bin Li        {
10472cbcc15677797f5879cf8c907876b366b034d433Bin Li            validityPeriodLength = 7;
10482cbcc15677797f5879cf8c907876b366b034d433Bin Li        }
10492cbcc15677797f5879cf8c907876b366b034d433Bin Li
10502cbcc15677797f5879cf8c907876b366b034d433Bin Li        // TP-Validity-Period is not used on phone, so just ignore it for now.
10512cbcc15677797f5879cf8c907876b366b034d433Bin Li        while (validityPeriodLength-- > 0)
10522cbcc15677797f5879cf8c907876b366b034d433Bin Li        {
10532cbcc15677797f5879cf8c907876b366b034d433Bin Li            p.getByte();
10542cbcc15677797f5879cf8c907876b366b034d433Bin Li        }
10552cbcc15677797f5879cf8c907876b366b034d433Bin Li
10562cbcc15677797f5879cf8c907876b366b034d433Bin Li        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
10572cbcc15677797f5879cf8c907876b366b034d433Bin Li
10582cbcc15677797f5879cf8c907876b366b034d433Bin Li        parseUserData(p, hasUserDataHeader);
10592cbcc15677797f5879cf8c907876b366b034d433Bin Li    }
10602cbcc15677797f5879cf8c907876b366b034d433Bin Li
10612cbcc15677797f5879cf8c907876b366b034d433Bin Li    /**
1062c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Parses the User Data of an SMS.
1063c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
1064c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param p The current PduParser.
1065c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param hasUserDataHeader Indicates whether a header is present in the
1066c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *                          User Data.
1067c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
1068c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parseUserData(PduParser p, boolean hasUserDataHeader) {
1069c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean hasMessageClass = false;
1070c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean userDataCompressed = false;
1071c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1072c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int encodingType = ENCODING_UNKNOWN;
1073c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1074c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Look up the data encoding scheme
107522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if ((mDataCodingScheme & 0x80) == 0) {
107622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            userDataCompressed = (0 != (mDataCodingScheme & 0x20));
107722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            hasMessageClass = (0 != (mDataCodingScheme & 0x10));
1078c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1079c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (userDataCompressed) {
1080ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
108122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        + "(compression) " + (mDataCodingScheme & 0xff));
1082c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
108322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                switch ((mDataCodingScheme >> 2) & 0x3) {
1084c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 0: // GSM 7 bit default alphabet
1085c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    encodingType = ENCODING_7BIT;
1086c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
1087c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1088c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 2: // UCS 2 (16bit)
1089c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    encodingType = ENCODING_16BIT;
1090c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
1091c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1092c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 1: // 8 bit data
1093c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 3: // reserved
1094ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                    Rlog.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
109522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            + (mDataCodingScheme & 0xff));
1096c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    encodingType = ENCODING_8BIT;
1097c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
1098c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
1099c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
110022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        } else if ((mDataCodingScheme & 0xf0) == 0xf0) {
1101c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            hasMessageClass = true;
1102c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userDataCompressed = false;
1103c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
110422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            if (0 == (mDataCodingScheme & 0x04)) {
1105c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // GSM 7 bit default alphabet
1106c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_7BIT;
1107c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1108c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // 8 bit data
1109c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_8BIT;
1110c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
111122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        } else if ((mDataCodingScheme & 0xF0) == 0xC0
111222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                || (mDataCodingScheme & 0xF0) == 0xD0
111322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                || (mDataCodingScheme & 0xF0) == 0xE0) {
1114c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
1115c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1116c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0xC0 == 7 bit, don't store
1117c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0xD0 == 7 bit, store
1118c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0xE0 == UCS-2, store
1119c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
112022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            if ((mDataCodingScheme & 0xF0) == 0xE0) {
1121c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_16BIT;
1122c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1123c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_7BIT;
1124c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1125c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1126c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userDataCompressed = false;
112722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            boolean active = ((mDataCodingScheme & 0x08) == 0x08);
1128c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1129c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // bit 0x04 reserved
1130c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
113122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            if ((mDataCodingScheme & 0x03) == 0x00) {
113222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mIsMwi = true;
113322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mMwiSense = active;
113422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mMwiDontStore = ((mDataCodingScheme & 0xF0) == 0xC0);
1135c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
113622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mIsMwi = false;
1137c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1138ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.w(LOG_TAG, "MWI for fax, email, or other "
113922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        + (mDataCodingScheme & 0xff));
1140c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
114122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        } else if ((mDataCodingScheme & 0xC0) == 0x80) {
1142c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
1143c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0x80..0xBF == Reserved coding groups
114422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            if (mDataCodingScheme == 0x84) {
1145c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // This value used for KSC5601 by carriers in Korea.
1146c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_KSC5601;
1147c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1148ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville                Rlog.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
114922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        + (mDataCodingScheme & 0xff));
1150c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1151c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
1152ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville            Rlog.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
115322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    + (mDataCodingScheme & 0xff));
1154c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1155c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1156c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // set both the user data and the user data header.
1157c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int count = p.constructUserData(hasUserDataHeader,
1158c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType == ENCODING_7BIT);
115922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        this.mUserData = p.getUserData();
116022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        this.mUserDataHeader = p.getUserDataHeader();
1161c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1162c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        switch (encodingType) {
1163c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_UNKNOWN:
1164c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_8BIT:
116522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mMessageBody = null;
1166c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1167c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1168c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_7BIT:
116922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mMessageBody = p.getUserDataGSM7Bit(count,
117022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    hasUserDataHeader ? mUserDataHeader.languageTable : 0,
117122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    hasUserDataHeader ? mUserDataHeader.languageShiftTable : 0);
1172c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1173c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1174c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_16BIT:
117522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mMessageBody = p.getUserDataUCS2(count);
1176c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1177c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1178c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_KSC5601:
117922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mMessageBody = p.getUserDataKSC5601(count);
1180c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1181c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1182c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
118322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (VDBG) Rlog.v(LOG_TAG, "SMS message body (raw): '" + mMessageBody + "'");
1184c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
118522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        if (mMessageBody != null) {
1186c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            parseMessageBody();
1187c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1188c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1189c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (!hasMessageClass) {
1190c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            messageClass = MessageClass.UNKNOWN;
1191c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
119222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            switch (mDataCodingScheme & 0x3) {
1193c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 0:
1194c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_0;
1195c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1196c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 1:
1197c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_1;
1198c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1199c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 2:
1200c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_2;
1201c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1202c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 3:
1203c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_3;
1204c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1205c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1206c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1207c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1208c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1209c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
1210c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * {@inheritDoc}
1211c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
1212c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
1213c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public MessageClass getMessageClass() {
1214c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return messageClass;
1215c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1216c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1217c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
1218c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Returns true if this is a (U)SIM data download type SM.
1219c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * See 3GPP TS 31.111 section 9.1 and TS 23.040 section 9.2.3.9.
1220c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
1221c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return true if this is a USIM data download message; false otherwise
1222c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
1223c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    boolean isUsimDataDownload() {
1224c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return messageClass == MessageClass.CLASS_2 &&
122522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                (mProtocolIdentifier == 0x7f || mProtocolIdentifier == 0x7c);
1226c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1227c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville}
1228