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;
21c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.util.Log;
22c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
23c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.EncodeException;
24c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.GsmAlphabet;
25c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
26c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.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 {
49c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    static final String LOG_TAG = "GSM";
50c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
51c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private MessageClass messageClass;
52c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
53c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
54c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * TP-Message-Type-Indicator
55c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * 9.2.3
56c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
57c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private int mti;
58c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
59c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** TP-Protocol-Identifier (TP-PID) */
60c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private int protocolIdentifier;
61c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
62c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // TP-Data-Coding-Scheme
63c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // see TS 23.038
64c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private int dataCodingScheme;
65c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
66c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // TP-Reply-Path
67c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // e.g. 23.040 9.2.2.1
68c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private boolean replyPathPresent = false;
69c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
70c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // "Message Marked for Automatic Deletion Group"
71c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // 23.038 Section 4
72c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private boolean automaticDeletion;
73c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
74c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** True if Status Report is for SMS-SUBMIT; false for SMS-COMMAND. */
75c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private boolean forSubmit;
76c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
77c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** The address of the receiver. */
78c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private GsmSmsAddress recipientAddress;
79c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
80c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** Time when SMS-SUBMIT was delivered from SC to MSE. */
81c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private long dischargeTimeMillis;
82c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
83c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
84c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  TP-Status - status of a previously submitted SMS.
85c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  This field applies to SMS-STATUS-REPORT messages.  0 indicates success;
86c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  see TS 23.040, 9.2.3.15 for description of other possible values.
87c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
88c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private int status;
89c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
90c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
91c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  TP-Status - status of a previously submitted SMS.
92c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  This field is true iff the message is a SMS-STATUS-REPORT message.
93c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
94c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private boolean isStatusReportMessage = false;
95c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
96c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static class SubmitPdu extends SubmitPduBase {
97c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
98c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
99c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
100c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Create an SmsMessage from a raw PDU.
101c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
102c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage createFromPdu(byte[] pdu) {
103c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
104c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
105c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(pdu);
106c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
107c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
108c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
109c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
110c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
111c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
112c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
113c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
114c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * 3GPP TS 23.040 9.2.3.9 specifies that Type Zero messages are indicated
115c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * by TP_PID field set to value 0x40
116c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
117c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isTypeZero() {
118c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return (protocolIdentifier == 0x40);
119c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
120c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
121c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
122c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
123c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * +CMT unsolicited response (PDU mode, of course)
124c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
125c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
126c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Only public for debugging
127c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
128c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * {@hide}
129c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
130c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage newFromCMT(String[] lines) {
131c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
132c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
133c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(IccUtils.hexStringToBytes(lines[1]));
134c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
135c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
136c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
137c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
138c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
139c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
140c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
141c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** @hide */
142c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage newFromCDS(String line) {
143c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
144c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
145c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(IccUtils.hexStringToBytes(line));
146c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
147c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
148c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex);
149c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
150c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
151c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
152c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
153c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
154c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Create an SmsMessage from an SMS EF record.
155c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
156c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param index Index of SMS record. This should be index in ArrayList
157c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *              returned by SmsManager.getAllMessagesFromSim + 1.
158c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param data Record data.
159c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return An SmsMessage representing the record.
160c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
161c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @hide
162c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
163c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SmsMessage createFromEfRecord(int index, byte[] data) {
164c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
165c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsMessage msg = new SmsMessage();
166c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
167c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.indexOnIcc = index;
168c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
169c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT,
170c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // or STORED_UNSENT
171c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // See TS 51.011 10.5.3
172c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((data[0] & 1) == 0) {
173c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.w(LOG_TAG,
174c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        "SMS parsing failed: Trying to parse a free record");
175c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
176c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
177c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                msg.statusOnIcc = data[0] & 0x07;
178c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
179c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
180c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int size = data.length - 1;
181c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
182c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Note: Data may include trailing FF's.  That's OK; message
183c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // should still parse correctly.
184c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            byte[] pdu = new byte[size];
185c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            System.arraycopy(data, 1, pdu, 0, size);
186c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            msg.parsePdu(pdu);
187c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return msg;
188c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException ex) {
189c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
190c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
191c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
192c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
193c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
194c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
195c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
196c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * length in bytes (not hex chars) less the SMSC header
197c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
198c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static int getTPLayerLengthForPDU(String pdu) {
199c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int len = pdu.length() / 2;
200c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
201c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
202c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return len - smscLen - 1;
203c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
204c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
205c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
206c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a destination address and a message
207c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
208c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address.  Null means use default.
209c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
210c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
211c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
212c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @hide
213c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
214c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
215c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, String message,
216c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested, byte[] header) {
217c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
218c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ENCODING_UNKNOWN, 0, 0);
219c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
220c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
221c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
222c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
223c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a destination address and a message using the
224c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * specified encoding.
225c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
226c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address.  Null means use default.
227c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param encoding Encoding defined by constants in
228c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *        com.android.internal.telephony.SmsConstants.ENCODING_*
229c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param languageTable
230c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param languageShiftTable
231c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
232c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
233c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
234c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @hide
235c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
236c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
237c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, String message,
238c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested, byte[] header, int encoding,
239c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int languageTable, int languageShiftTable) {
240c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
241c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Perform null parameter checks.
242c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (message == null || destinationAddress == null) {
243c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
244c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
245c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
246c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (encoding == ENCODING_UNKNOWN) {
247c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Find the best encoding to use
248c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            TextEncodingDetails ted = calculateLength(message, false);
249c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            encoding = ted.codeUnitSize;
250c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            languageTable = ted.languageTable;
251c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            languageShiftTable = ted.languageShiftTable;
252c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
253c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (encoding == ENCODING_7BIT &&
254c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    (languageTable != 0 || languageShiftTable != 0)) {
255c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                if (header != null) {
256c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    SmsHeader smsHeader = SmsHeader.fromByteArray(header);
257c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    if (smsHeader.languageTable != languageTable
258c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            || smsHeader.languageShiftTable != languageShiftTable) {
259c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        Log.w(LOG_TAG, "Updating language table in SMS header: "
260c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                                + smsHeader.languageTable + " -> " + languageTable + ", "
261c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                                + smsHeader.languageShiftTable + " -> " + languageShiftTable);
262c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        smsHeader.languageTable = languageTable;
263c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        smsHeader.languageShiftTable = languageShiftTable;
264c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        header = SmsHeader.toByteArray(smsHeader);
265c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    }
266c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                } else {
267c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    SmsHeader smsHeader = new SmsHeader();
268c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    smsHeader.languageTable = languageTable;
269c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    smsHeader.languageShiftTable = languageShiftTable;
270c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    header = SmsHeader.toByteArray(smsHeader);
271c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
272c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
273c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
274c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
275c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SubmitPdu ret = new SubmitPdu();
276c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // MTI = SMS-SUBMIT, UDHI = header != null
277c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
278c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ByteArrayOutputStream bo = getSubmitPduHead(
279c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                scAddress, destinationAddress, mtiByte,
280c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                statusReportRequested, ret);
281c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
282c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // User Data (and length)
283c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] userData;
284c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
285c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (encoding == ENCODING_7BIT) {
286c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
287c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        languageTable, languageShiftTable);
288c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else { //assume UCS-2
289c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                try {
290c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    userData = encodeUCS2(message, header);
291c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                } catch(UnsupportedEncodingException uex) {
292c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    Log.e(LOG_TAG,
293c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            "Implausible UnsupportedEncodingException ",
294c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            uex);
295c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    return null;
296c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
297c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
298c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (EncodeException ex) {
299c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Encoding to the 7-bit alphabet failed. Let's see if we can
300c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // send it as a UCS-2 encoded message
301c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            try {
302c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                userData = encodeUCS2(message, header);
303c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encoding = ENCODING_16BIT;
304c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } catch(UnsupportedEncodingException uex) {
305c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.e(LOG_TAG,
306c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        "Implausible UnsupportedEncodingException ",
307c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        uex);
308c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
309c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
310c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
311c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
312c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (encoding == ENCODING_7BIT) {
313c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
314c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Message too long
315c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
316c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
317c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
318c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Data-Coding-Scheme
319c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Default encoding, uncompressed
320c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // To test writing messages to the SIM card, change this value 0x00
321c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // to 0x12, which means "bits 1 and 0 contain message class, and the
322c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // class is 2". Note that this takes effect for the sender. In other
323c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // words, messages sent by the phone with this change will end up on
324c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // the receiver's SIM card. You can then send messages to yourself
325c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // (on a phone with this change) and they'll end up on the SIM card.
326c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            bo.write(0x00);
327c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else { // assume UCS-2
328c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
329c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Message too long
330c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
331c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return null;
332c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
333c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Data-Coding-Scheme
334c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // UCS-2 encoding, uncompressed
335c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            bo.write(0x08);
336c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
337c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
338c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // (no TP-Validity-Period)
339c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(userData, 0, userData.length);
340c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ret.encodedMessage = bo.toByteArray();
341c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ret;
342c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
343c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
344c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
345c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Packs header and UCS-2 encoded message. Includes TP-UDL & TP-UDHL if necessary
346c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
347c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return
348c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @throws UnsupportedEncodingException
349c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
350c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static byte[] encodeUCS2(String message, byte[] header)
351c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        throws UnsupportedEncodingException {
352c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] userData, textPart;
353c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        textPart = message.getBytes("utf-16be");
354c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
355c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (header != null) {
356c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Need 1 byte for UDHL
357c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userData = new byte[header.length + textPart.length + 1];
358c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
359c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userData[0] = (byte)header.length;
360c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            System.arraycopy(header, 0, userData, 1, header.length);
361c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
362c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
363c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        else {
364c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userData = textPart;
365c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
366c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] ret = new byte[userData.length+1];
367c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ret[0] = (byte) (userData.length & 0xff );
368c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        System.arraycopy(userData, 0, ret, 1, userData.length);
369c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ret;
370c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
371c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
372c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
373c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a destination address and a message
374c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
375c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address.  Null means use default.
376c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
377c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
378c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
379c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
380c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
381c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, String message,
382c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested) {
383c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
384c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null);
385c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
386c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
387c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
388c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
389c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
390c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address. null == use default
391c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param destinationAddress the address of the destination for the message
392c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param destinationPort the port to deliver the message to at the
393c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *        destination
394c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param data the data for the message
395c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return a <code>SubmitPdu</code> containing the encoded SC
396c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         address, if applicable, and the encoded message.
397c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *         Returns null on encode error.
398c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
399c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static SubmitPdu getSubmitPdu(String scAddress,
400c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String destinationAddress, int destinationPort, byte[] data,
401c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested) {
402c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
403c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
404c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        portAddrs.destPort = destinationPort;
405c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        portAddrs.origPort = 0;
406c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        portAddrs.areEightBits = false;
407c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
408c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsHeader smsHeader = new SmsHeader();
409c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        smsHeader.portAddrs = portAddrs;
410c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
411c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader);
412c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
413c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) {
414c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(LOG_TAG, "SMS data message may only contain "
415c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    + (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes");
416c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return null;
417c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
418c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
419c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SubmitPdu ret = new SubmitPdu();
420c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ByteArrayOutputStream bo = getSubmitPduHead(
421c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
422c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                                                            // TP-UDHI = true
423c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                statusReportRequested, ret);
424c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
425c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Data-Coding-Scheme
426c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // No class, 8 bit data
427c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(0x04);
428c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
429c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // (no TP-Validity-Period)
430c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
431c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Total size
432c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(data.length + smsHeaderData.length + 1);
433c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
434c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // User data header
435c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(smsHeaderData.length);
436c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(smsHeaderData, 0, smsHeaderData.length);
437c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
438c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // User data
439c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(data, 0, data.length);
440c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
441c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ret.encodedMessage = bo.toByteArray();
442c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ret;
443c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
444c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
445c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
446c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Create the beginning of a SUBMIT PDU.  This is the part of the
447c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu},
448c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * one of which takes a byte array and the other of which takes a
449c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * <code>String</code>.
450c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
451c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param scAddress Service Centre address. null == use default
452c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param destinationAddress the address of the destination for the message
453c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param mtiByte
454c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param ret <code>SubmitPdu</code> containing the encoded SC
455c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *        address, if applicable, and the encoded message
456c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
457c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static ByteArrayOutputStream getSubmitPduHead(
458c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String scAddress, String destinationAddress, byte mtiByte,
459c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean statusReportRequested, SubmitPdu ret) {
460c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        ByteArrayOutputStream bo = new ByteArrayOutputStream(
461c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                MAX_USER_DATA_BYTES + 40);
462c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
463c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // SMSC address with length octet, or 0
464c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (scAddress == null) {
465c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ret.encodedScAddress = null;
466c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
467c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
468c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    scAddress);
469c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
470c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
471c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Type-Indicator (and friends)
472c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (statusReportRequested) {
473c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Set TP-Status-Report-Request bit.
474c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mtiByte |= 0x20;
475c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (false) Log.d(LOG_TAG, "SMS status report requested");
476c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
477c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(mtiByte);
478c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
479c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // space for TP-Message-Reference
480c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(0);
481c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
482c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] daBytes;
483c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
484c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
485c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
486c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // destination address length in BCD digits, ignoring TON byte and pad
487c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TODO Should be better.
488c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write((daBytes.length - 1) * 2
489c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
490c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
491c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // destination address
492c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(daBytes, 0, daBytes.length);
493c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
494c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Protocol-Identifier
495c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        bo.write(0);
496c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return bo;
497c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
498c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
499c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static class PduParser {
500c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte pdu[];
501c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int cur;
502c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsHeader userDataHeader;
503c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] userData;
504c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int mUserDataSeptetPadding;
505c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int mUserDataSize;
506c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
507c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        PduParser(byte[] pdu) {
508c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            this.pdu = pdu;
509c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            cur = 0;
510c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mUserDataSeptetPadding = 0;
511c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
512c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
513c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
514c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Parse and return the SC address prepended to SMS messages coming via
515c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * the TS 27.005 / AT interface.  Returns null on invalid address
516c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
517c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getSCAddress() {
518c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int len;
519c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
520c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
521c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // length of SC Address
522c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            len = getByte();
523c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
524c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (len == 0) {
525c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // no SC address
526c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = null;
527c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
528c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // SC address
529c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                try {
530c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    ret = PhoneNumberUtils
531c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            .calledPartyBCDToString(pdu, cur, len);
532c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                } catch (RuntimeException tr) {
533c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    Log.d(LOG_TAG, "invalid SC address: ", tr);
534c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    ret = null;
535c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
536c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
537c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
538c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            cur += len;
539c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
540c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
541c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
542c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
543c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
544c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * returns non-sign-extended byte value
545c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
546c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int getByte() {
547c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return pdu[cur++] & 0xff;
548c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
549c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
550c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
551c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Any address except the SC address (eg, originating address) See TS
552c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * 23.040 9.1.2.5
553c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
554c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        GsmSmsAddress getAddress() {
555c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            GsmSmsAddress ret;
556c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
557c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // "The Address-Length field is an integer representation of
558c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // the number field, i.e. excludes any semi-octet containing only
559c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // fill bits."
560c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // The TOA field is not included as part of this
561c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int addressLength = pdu[cur] & 0xff;
562c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int lengthBytes = 2 + (addressLength + 1) / 2;
563c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
5645ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar            try {
5655ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar                ret = new GsmSmsAddress(pdu, cur, lengthBytes);
5665ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar            } catch (ParseException e) {
5675ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar                Log.e(LOG_TAG, e.getMessage());
5685ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar                ret = null;
5695ad6947b2d955a4e972d556090922d77aa6a2641Rekha Kumar            }
570c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
571c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            cur += lengthBytes;
572c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
573c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
574c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
575c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
576c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
577c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Parses an SC timestamp and returns a currentTimeMillis()-style
578c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * timestamp
579c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
580c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
581c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        long getSCTimestampMillis() {
582c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Service-Centre-Time-Stamp
583c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int year = IccUtils.gsmBcdByteToInt(pdu[cur++]);
584c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int month = IccUtils.gsmBcdByteToInt(pdu[cur++]);
585c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int day = IccUtils.gsmBcdByteToInt(pdu[cur++]);
586c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int hour = IccUtils.gsmBcdByteToInt(pdu[cur++]);
587c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int minute = IccUtils.gsmBcdByteToInt(pdu[cur++]);
588c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int second = IccUtils.gsmBcdByteToInt(pdu[cur++]);
589c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
590c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // For the timezone, the most significant bit of the
591c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // least significant nibble is the sign byte
592c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // (meaning the max range of this field is 79 quarter-hours,
593c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // which is more than enough)
594c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
595c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            byte tzByte = pdu[cur++];
596c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
597c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Mask out sign bit.
598c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
599c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
600c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
601c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
602c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Time time = new Time(Time.TIMEZONE_UTC);
603c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
604c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // It's 2006.  Should I really support years < 2000?
605c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.year = year >= 90 ? year + 1900 : year + 2000;
606c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.month = month - 1;
607c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.monthDay = day;
608c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.hour = hour;
609c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.minute = minute;
610c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            time.second = second;
611c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
612c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Timezone offset is in quarter hours.
613c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000);
614c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
615c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
616c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
617c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Pulls the user data out of the PDU, and separates the payload from
618c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * the header if there is one.
619c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
620c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param hasUserDataHeader true if there is a user data header
621c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param dataInSeptets true if the data payload is in septets instead
622c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *  of octets
623c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return the number of septets or octets in the user data payload
624c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
625c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {
626c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int offset = cur;
627c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int userDataLength = pdu[offset++] & 0xff;
628c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int headerSeptets = 0;
629c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int userDataHeaderLength = 0;
630c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
631c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (hasUserDataHeader) {
632c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                userDataHeaderLength = pdu[offset++] & 0xff;
633c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
634c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                byte[] udh = new byte[userDataHeaderLength];
635c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);
636c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                userDataHeader = SmsHeader.fromByteArray(udh);
637c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                offset += userDataHeaderLength;
638c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
639c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                int headerBits = (userDataHeaderLength + 1) * 8;
640c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                headerSeptets = headerBits / 7;
641c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
642c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mUserDataSeptetPadding = (headerSeptets * 7) - headerBits;
643c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
644c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
645c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int bufferLen;
646c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (dataInSeptets) {
647c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                /*
648c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * Here we just create the user data length to be the remainder of
649c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * the pdu minus the user data header, since userDataLength means
650c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * the number of uncompressed septets.
651c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 */
652c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                bufferLen = pdu.length - offset;
653c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
654c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                /*
655c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * userDataLength is the count of octets, so just subtract the
656c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 * user data header.
657c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                 */
658c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                bufferLen = userDataLength - (hasUserDataHeader ? (userDataHeaderLength + 1) : 0);
659c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                if (bufferLen < 0) {
660c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    bufferLen = 0;
661c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
662c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
663c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
664c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userData = new byte[bufferLen];
665c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            System.arraycopy(pdu, offset, userData, 0, userData.length);
666c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            cur = offset;
667c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
668c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (dataInSeptets) {
669c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Return the number of septets
670c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                int count = userDataLength - headerSeptets;
671c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // If count < 0, return 0 (means UDL was probably incorrect)
672c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return count < 0 ? 0 : count;
673c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
674c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Return the number of octets
675c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return userData.length;
676c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
677c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
678c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
679c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
680c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Returns the user data payload, not including the headers
681c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
682c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return the user data payload, not including the headers
683c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
684c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte[] getUserData() {
685c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return userData;
686c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
687c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
688c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
689c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Returns the number of padding bits at the beginning of the user data
690c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * array before the start of the septets.
691c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
692c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return the number of padding bits at the beginning of the user data
693c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * array before the start of the septets
694c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
695c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int getUserDataSeptetPadding() {
696c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return mUserDataSeptetPadding;
697c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
698c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
699c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
700c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Returns an object representing the user data headers
701c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
702c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * {@hide}
703c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
704c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsHeader getUserDataHeader() {
705c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return userDataHeader;
706c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
707c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
708c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
709c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Interprets the user data payload as packed GSM 7bit characters, and
710c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * decodes them into a String.
711c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
712c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param septetCount the number of septets in the user data payload
713c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return a String with the decoded characters
714c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
715c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getUserDataGSM7Bit(int septetCount, int languageTable,
716c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                int languageShiftTable) {
717c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
718c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
719c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ret = GsmAlphabet.gsm7BitPackedToString(pdu, cur, septetCount,
720c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    mUserDataSeptetPadding, languageTable, languageShiftTable);
721c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
722c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            cur += (septetCount * 7) / 8;
723c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
724c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
725c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
726c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
727c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
728c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Interprets the user data payload as UCS2 characters, and
729c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * decodes them into a String.
730c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
731c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param byteCount the number of bytes in the user data payload
732c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return a String with the decoded characters
733c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
734c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getUserDataUCS2(int byteCount) {
735c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
736c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
737c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            try {
738c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = new String(pdu, cur, byteCount, "utf-16");
739c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } catch (UnsupportedEncodingException ex) {
740c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = "";
741c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
742c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
743c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
744c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            cur += byteCount;
745c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
746c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
747c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
748c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
749c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Interprets the user data payload as KSC-5601 characters, and
750c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * decodes them into a String.
751c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
752c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param byteCount the number of bytes in the user data payload
753c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return a String with the decoded characters
754c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
755c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String getUserDataKSC5601(int byteCount) {
756c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String ret;
757c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
758c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            try {
759c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = new String(pdu, cur, byteCount, "KSC5601");
760c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } catch (UnsupportedEncodingException ex) {
761c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ret = "";
762c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
763c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
764c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
765c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            cur += byteCount;
766c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return ret;
767c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
768c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
769c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean moreDataPresent() {
770c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return (pdu.length > cur);
771c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
772c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
773c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
774c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
775c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Calculate the number of septets needed to encode the message.
776c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
777c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param msgBody the message to encode
778c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param use7bitOnly ignore (but still count) illegal characters if true
779c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return TextEncodingDetails
780c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
781c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public static TextEncodingDetails calculateLength(CharSequence msgBody,
782c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean use7bitOnly) {
783c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(msgBody, use7bitOnly);
784c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (ted == null) {
785c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ted = new TextEncodingDetails();
786c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int octets = msgBody.length() * 2;
787c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ted.codeUnitCount = msgBody.length();
788c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (octets > MAX_USER_DATA_BYTES) {
789c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
790c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        MAX_USER_DATA_BYTES_WITH_HEADER;
791c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.codeUnitsRemaining = ((ted.msgCount *
792c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
793c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
794c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.msgCount = 1;
795c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
796c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
797c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            ted.codeUnitSize = ENCODING_16BIT;
798c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
799c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ted;
800c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
801c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
802c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
803c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
804c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public int getProtocolIdentifier() {
805c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return protocolIdentifier;
806c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
807c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
808c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
809c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Returns the TP-Data-Coding-Scheme byte, for acknowledgement of SMS-PP download messages.
810c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return the TP-DCS field of the SMS header
811c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
812c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    int getDataCodingScheme() {
813c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return dataCodingScheme;
814c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
815c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
816c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
817c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
818c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isReplace() {
819c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return (protocolIdentifier & 0xc0) == 0x40
820c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                && (protocolIdentifier & 0x3f) > 0
821c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                && (protocolIdentifier & 0x3f) < 8;
822c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
823c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
824c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
825c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
826c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isCphsMwiMessage() {
827c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear()
828c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                || ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet();
829c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
830c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
831c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
832c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
833c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isMWIClearMessage() {
834c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (isMwi && !mwiSense) {
835c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return true;
836c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
837c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
838c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return originatingAddress != null
839c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                && ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear();
840c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
841c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
842c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
843c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
844c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isMWISetMessage() {
845c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (isMwi && mwiSense) {
846c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return true;
847c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
848c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
849c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return originatingAddress != null
850c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                && ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet();
851c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
852c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
853c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
854c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
855c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isMwiDontStore() {
856c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (isMwi && mwiDontStore) {
857c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return true;
858c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
859c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
860c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (isCphsMwiMessage()) {
861c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // See CPHS 4.2 Section B.4.2.1
862c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // If the user data is a single space char, do not store
863c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // the message. Otherwise, store and display as usual
864c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (" ".equals(getMessageBody())) {
865c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                ;
866c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
867c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return true;
868c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
869c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
870c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return false;
871c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
872c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
873c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
874c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
875c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public int getStatus() {
876c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return status;
877c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
878c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
879c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
880c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
881c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isStatusReportMessage() {
882c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return isStatusReportMessage;
883c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
884c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
885c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
886c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
887c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public boolean isReplyPathPresent() {
888c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return replyPathPresent;
889c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
890c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
891c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
892c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * TS 27.005 3.1, &lt;pdu&gt; definition "In the case of SMS: 3GPP TS 24.011 [6]
893c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
894c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * ME/TA converts each octet of TP data unit into two IRA character long
895c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * hex number (e.g. octet with integer value 42 is presented to TE as two
896c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
897c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * something else...
898c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
899c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parsePdu(byte[] pdu) {
900c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mPdu = pdu;
901c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Log.d(LOG_TAG, "raw sms message:");
902c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Log.d(LOG_TAG, s);
903c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
904c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        PduParser p = new PduParser(pdu);
905c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
906c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        scAddress = p.getSCAddress();
907c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
908c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (scAddress != null) {
909c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (false) Log.d(LOG_TAG, "SMS SC address: " + scAddress);
910c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
911c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
912c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TODO(mkf) support reply path, user data header indicator
913c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
914c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Type-Indicator
915c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // 9.2.3
916c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int firstByte = p.getByte();
917c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
918c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mti = firstByte & 0x3;
919c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        switch (mti) {
920c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Type-Indicator
921c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // 9.2.3
922c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case 0:
923c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
924c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                //This should be processed in the same way as MTI == 0 (Deliver)
925c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            parseSmsDeliver(p, firstByte);
926c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
927c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case 2:
928c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            parseSmsStatusReport(p, firstByte);
929c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
930c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        default:
931c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TODO(mkf) the rest of these
932c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            throw new RuntimeException("Unsupported message type");
933c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
934c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
935c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
936c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
937c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Parses a SMS-STATUS-REPORT message.
938c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
939c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param p A PduParser, cued past the first byte.
940c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param firstByte The first byte of the PDU, which contains MTI, etc.
941c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
942c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parseSmsStatusReport(PduParser p, int firstByte) {
943c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        isStatusReportMessage = true;
944c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
945c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Status-Report-Qualifier bit == 0 for SUBMIT
946c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        forSubmit = (firstByte & 0x20) == 0x00;
947c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Message-Reference
948c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        messageRef = p.getByte();
949c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Recipient-Address
950c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        recipientAddress = p.getAddress();
951c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Service-Centre-Time-Stamp
952c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        scTimeMillis = p.getSCTimestampMillis();
953c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Discharge-Time
954c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        dischargeTimeMillis = p.getSCTimestampMillis();
955c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Status
956c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        status = p.getByte();
957c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
958c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // The following are optional fields that may or may not be present.
959c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (p.moreDataPresent()) {
960c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // TP-Parameter-Indicator
961c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int extraParams = p.getByte();
962c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int moreExtraParams = extraParams;
963c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            while ((moreExtraParams & 0x80) != 0) {
964c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // We only know how to parse a few extra parameters, all
965c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // indicated in the first TP-PI octet, so skip over any
966c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // additional TP-PI octets.
967c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                moreExtraParams = p.getByte();
968c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
96956c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan            // As per 3GPP 23.040 section 9.2.3.27 TP-Parameter-Indicator,
97056c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan            // only process the byte if the reserved bits (bits3 to 6) are zero.
97156c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan            if ((extraParams & 0x78) == 0) {
97256c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                // TP-Protocol-Identifier
97356c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                if ((extraParams & 0x01) != 0) {
97456c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                    protocolIdentifier = p.getByte();
97556c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                }
97656c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                // TP-Data-Coding-Scheme
97756c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                if ((extraParams & 0x02) != 0) {
97856c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                    dataCodingScheme = p.getByte();
97956c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                }
98056c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                // TP-User-Data-Length (implies existence of TP-User-Data)
98156c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                if ((extraParams & 0x04) != 0) {
98256c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                    boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
98356c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                    parseUserData(p, hasUserDataHeader);
98456c949ded0e352444806dd04bbca1895a2c75a9bHuanze Lan                }
985c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
986c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
987c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
988c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
989c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parseSmsDeliver(PduParser p, int firstByte) {
990c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        replyPathPresent = (firstByte & 0x80) == 0x80;
991c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
992c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        originatingAddress = p.getAddress();
993c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
994c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (originatingAddress != null) {
995c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (false) Log.v(LOG_TAG, "SMS originating address: "
996c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    + originatingAddress.address);
997c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
998c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
999c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Protocol-Identifier (TP-PID)
1000c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TS 23.040 9.2.3.9
1001c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        protocolIdentifier = p.getByte();
1002c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1003c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // TP-Data-Coding-Scheme
1004c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // see TS 23.038
1005c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        dataCodingScheme = p.getByte();
1006c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1007c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (false) {
1008c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.v(LOG_TAG, "SMS TP-PID:" + protocolIdentifier
1009c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    + " data coding scheme: " + dataCodingScheme);
1010c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1011c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1012c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        scTimeMillis = p.getSCTimestampMillis();
1013c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1014c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (false) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
1015c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1016c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
1017c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1018c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        parseUserData(p, hasUserDataHeader);
1019c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1020c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1021c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
1022c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Parses the User Data of an SMS.
1023c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
1024c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param p The current PduParser.
1025c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param hasUserDataHeader Indicates whether a header is present in the
1026c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *                          User Data.
1027c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
1028c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void parseUserData(PduParser p, boolean hasUserDataHeader) {
1029c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean hasMessageClass = false;
1030c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean userDataCompressed = false;
1031c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1032c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int encodingType = ENCODING_UNKNOWN;
1033c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1034c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Look up the data encoding scheme
1035c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if ((dataCodingScheme & 0x80) == 0) {
1036c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Bits 7..4 == 0xxx
1037c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            automaticDeletion = (0 != (dataCodingScheme & 0x40));
1038c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userDataCompressed = (0 != (dataCodingScheme & 0x20));
1039c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            hasMessageClass = (0 != (dataCodingScheme & 0x10));
1040c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1041c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (userDataCompressed) {
1042c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
1043c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        + "(compression) " + (dataCodingScheme & 0xff));
1044c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1045c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                switch ((dataCodingScheme >> 2) & 0x3) {
1046c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 0: // GSM 7 bit default alphabet
1047c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    encodingType = ENCODING_7BIT;
1048c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
1049c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1050c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 2: // UCS 2 (16bit)
1051c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    encodingType = ENCODING_16BIT;
1052c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
1053c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1054c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 1: // 8 bit data
1055c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case 3: // reserved
1056c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    Log.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
1057c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            + (dataCodingScheme & 0xff));
1058c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    encodingType = ENCODING_8BIT;
1059c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
1060c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
1061c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1062c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else if ((dataCodingScheme & 0xf0) == 0xf0) {
1063c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            automaticDeletion = false;
1064c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            hasMessageClass = true;
1065c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userDataCompressed = false;
1066c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1067c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (0 == (dataCodingScheme & 0x04)) {
1068c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // GSM 7 bit default alphabet
1069c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_7BIT;
1070c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1071c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // 8 bit data
1072c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_8BIT;
1073c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1074c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else if ((dataCodingScheme & 0xF0) == 0xC0
1075c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                || (dataCodingScheme & 0xF0) == 0xD0
1076c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                || (dataCodingScheme & 0xF0) == 0xE0) {
1077c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
1078c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1079c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0xC0 == 7 bit, don't store
1080c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0xD0 == 7 bit, store
1081c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0xE0 == UCS-2, store
1082c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1083c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((dataCodingScheme & 0xF0) == 0xE0) {
1084c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_16BIT;
1085c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1086c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_7BIT;
1087c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1088c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1089c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            userDataCompressed = false;
1090c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean active = ((dataCodingScheme & 0x08) == 0x08);
1091c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1092c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // bit 0x04 reserved
1093c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1094c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if ((dataCodingScheme & 0x03) == 0x00) {
1095c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                isMwi = true;
1096c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mwiSense = active;
1097c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mwiDontStore = ((dataCodingScheme & 0xF0) == 0xC0);
1098c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1099c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                isMwi = false;
1100c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1101c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.w(LOG_TAG, "MWI for fax, email, or other "
1102c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        + (dataCodingScheme & 0xff));
1103c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1104c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else if ((dataCodingScheme & 0xC0) == 0x80) {
1105c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
1106c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // 0x80..0xBF == Reserved coding groups
1107c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (dataCodingScheme == 0x84) {
1108c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // This value used for KSC5601 by carriers in Korea.
1109c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType = ENCODING_KSC5601;
1110c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
1111c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
1112c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        + (dataCodingScheme & 0xff));
1113c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1114c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
1115c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
1116c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    + (dataCodingScheme & 0xff));
1117c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1118c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1119c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // set both the user data and the user data header.
1120c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        int count = p.constructUserData(hasUserDataHeader,
1121c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encodingType == ENCODING_7BIT);
1122c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        this.userData = p.getUserData();
1123c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        this.userDataHeader = p.getUserDataHeader();
1124c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1125c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        switch (encodingType) {
1126c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_UNKNOWN:
1127c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_8BIT:
1128c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            messageBody = null;
1129c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1130c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1131c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_7BIT:
1132c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            messageBody = p.getUserDataGSM7Bit(count,
1133c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    hasUserDataHeader ? userDataHeader.languageTable : 0,
1134c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);
1135c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1136c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1137c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_16BIT:
1138c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            messageBody = p.getUserDataUCS2(count);
1139c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1140c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1141c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case ENCODING_KSC5601:
1142c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            messageBody = p.getUserDataKSC5601(count);
1143c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
1144c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1145c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1146c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (false) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");
1147c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1148c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (messageBody != null) {
1149c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            parseMessageBody();
1150c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1151c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1152c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (!hasMessageClass) {
1153c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            messageClass = MessageClass.UNKNOWN;
1154c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
1155c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            switch (dataCodingScheme & 0x3) {
1156c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 0:
1157c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_0;
1158c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1159c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 1:
1160c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_1;
1161c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1162c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 2:
1163c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_2;
1164c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1165c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case 3:
1166c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                messageClass = MessageClass.CLASS_3;
1167c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                break;
1168c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
1169c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
1170c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1171c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1172c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
1173c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * {@inheritDoc}
1174c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
1175c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
1176c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public MessageClass getMessageClass() {
1177c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return messageClass;
1178c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1179c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
1180c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
1181c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Returns true if this is a (U)SIM data download type SM.
1182c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * See 3GPP TS 31.111 section 9.1 and TS 23.040 section 9.2.3.9.
1183c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
1184c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @return true if this is a USIM data download message; false otherwise
1185c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
1186c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    boolean isUsimDataDownload() {
1187c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return messageClass == MessageClass.CLASS_2 &&
1188c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                (protocolIdentifier == 0x7f || protocolIdentifier == 0x7c);
1189c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
1190c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville}
1191