1fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie/*
2fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Copyright (C) 2013 Samsung System LSI
3fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Licensed under the Apache License, Version 2.0 (the "License");
4fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* you may not use this file except in compliance with the License.
5fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* You may obtain a copy of the License at
6fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie*
7fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie*      http://www.apache.org/licenses/LICENSE-2.0
8fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie*
9fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Unless required by applicable law or agreed to in writing, software
10fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* distributed under the License is distributed on an "AS IS" BASIS,
11fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* See the License for the specific language governing permissions and
13fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* limitations under the License.
14fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie*/
15fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xiepackage com.android.bluetooth.map;
16fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
17fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
18fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
19fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
20fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.ByteArrayInputStream;
21fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.ByteArrayOutputStream;
22fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.DataInputStream;
23fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.EOFException;
24fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.IOException;
25fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.UnsupportedEncodingException;
26fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.text.SimpleDateFormat;
27fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.util.ArrayList;
28fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.util.Calendar;
29fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.util.Date;
30fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.util.Random;
31fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
32fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.telephony.PhoneNumberUtils;
33fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.telephony.SmsMessage;
34fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.telephony.TelephonyManager;
35fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.util.Log;
36fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
37fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.internal.telephony.*;
38fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie/*import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
39fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.internal.telephony.SmsConstants;*/
40fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.internal.telephony.SmsHeader;
41fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.internal.telephony.SmsMessageBase;
42fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
43fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.internal.telephony.cdma.sms.*;
44fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.internal.telephony.gsm.SmsMessage.SubmitPdu;
45fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
46fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xiepublic class BluetoothMapSmsPdu {
47fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
48fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private static final String TAG = "BluetoothMapSmsPdu";
4970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz    private static final boolean V = false;
50fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private static int INVALID_VALUE = -1;
51fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public static int SMS_TYPE_GSM = 1;
52fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public static int SMS_TYPE_CDMA = 2;
53fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
54fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
55326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde    /* We need to handle the SC-address mentioned in errata 4335.
56fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * Since the definition could be read in three different ways, I have asked
57fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * the car working group for clarification, and are awaiting confirmation that
58fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * this clarification will go into the MAP spec:
59fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     *  "The native format should be <sc_addr><tpdu> where <sc_addr> is <length><ton><1..10 octet of address>
60fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     *   coded according to 24.011. The IEI is not to be used, as the fixed order of the data makes a type 4 LV
61fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     *   information element sufficient. <length> is a single octet which value is the length of the value-field
62fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     *   in octets including both the <ton> and the <address>."
63fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * */
64fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
65fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
66fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public static class SmsPdu {
67326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private byte[] mData;
68326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private byte[] mScAddress = {0}; // At the moment we do not use the scAddress, hence set the length to 0.
69326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private int mUserDataMsgOffset = 0;
70326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private int mEncoding;
71326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private int mLanguageTable;
72326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private int mLanguageShiftTable;
73326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private int mType;
74fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
75fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* Members used for pdu decoding */
76326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private int mUserDataSeptetPadding = INVALID_VALUE;
77326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        private int mMsgSeptetCount = 0;
78fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
79fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        SmsPdu(byte[] data, int type){
80326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mData = data;
81326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mEncoding = INVALID_VALUE;
82326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mType = type;
83326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mLanguageTable = INVALID_VALUE;
84326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mLanguageShiftTable = INVALID_VALUE;
85326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mUserDataMsgOffset = gsmSubmitGetTpUdOffset(); // Assume no user data header
86fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
87fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
88fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
89fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Create a pdu instance based on the data generated on this device.
90fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param data
91fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param encoding
92fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param type
93fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param languageTable
94fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
95fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        SmsPdu(byte[]data, int encoding, int type, int languageTable){
96326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mData = data;
97326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mEncoding = encoding;
98326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mType = type;
99326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mLanguageTable = languageTable;
100fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
101fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public byte[] getData(){
102326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mData;
103fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
104fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public byte[] getScAddress(){
105326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mScAddress;
106fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
107fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public void setEncoding(int encoding) {
108326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mEncoding = encoding;
109fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
110fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getEncoding(){
111326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mEncoding;
112fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
113fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getType(){
114326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mType;
115fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
116fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getUserDataMsgOffset() {
117326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mUserDataMsgOffset;
118fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
119fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /** The user data message payload size in bytes - excluding the user data header. */
120fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getUserDataMsgSize() {
121326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mData.length - mUserDataMsgOffset;
122fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
123fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
124fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getLanguageShiftTable() {
125326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mLanguageShiftTable;
126fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
127fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
128fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getLanguageTable() {
129326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mLanguageTable;
130fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
131fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
132fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getUserDataSeptetPadding() {
133326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mUserDataSeptetPadding;
134fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
135fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
136fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getMsgSeptetCount() {
137326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mMsgSeptetCount;
138fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
139fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
140fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
141fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* PDU parsing/modification functionality */
142fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte TELESERVICE_IDENTIFIER                    = 0x00;
143fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte SERVICE_CATEGORY                          = 0x01;
144fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte ORIGINATING_ADDRESS                       = 0x02;
145fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte ORIGINATING_SUB_ADDRESS                   = 0x03;
146fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte DESTINATION_ADDRESS                       = 0x04;
147fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte DESTINATION_SUB_ADDRESS                   = 0x05;
148fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte BEARER_REPLY_OPTION                       = 0x06;
149fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte CAUSE_CODES                               = 0x07;
150fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte BEARER_DATA                               = 0x08;
151fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
152fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
153fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Find and return the offset to the specified parameter ID
154fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param parameterId The parameter ID to find
155fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @return the offset in number of bytes to the parameterID entry in the pdu data.
156fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * The byte at the offset contains the parameter ID, the byte following contains the
157fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * parameter length, and offset + 2 is the first byte of the parameter data.
158fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
159fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private int cdmaGetParameterOffset(byte parameterId) {
160326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            ByteArrayInputStream pdu = new ByteArrayInputStream(mData);
161fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int offset = 0;
162fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            boolean found = false;
163fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
164fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            try {
165fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                pdu.skip(1); // Skip the message type
166fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
167fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                while (pdu.available() > 0) {
168fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    int currentId = pdu.read();
169fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    int currentLen = pdu.read();
170fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
171fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(currentId == parameterId) {
172fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        found = true;
173fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        break;
174fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
175fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    else {
176fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        pdu.skip(currentLen);
177fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        offset += 2 + currentLen;
178fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
179fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
180fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                pdu.close();
181fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } catch (Exception e) {
182fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.e(TAG, "cdmaGetParameterOffset: ", e);
183fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
184fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
185fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(found)
186fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return offset;
187fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else
188fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return 0;
189fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
190fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
191fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private final static byte BEARER_DATA_MSG_ID = 0x00;
192fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
193fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private int cdmaGetSubParameterOffset(byte subParameterId) {
194326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            ByteArrayInputStream pdu = new ByteArrayInputStream(mData);
195fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int offset = 0;
196fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            boolean found = false;
197fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            offset = cdmaGetParameterOffset(BEARER_DATA) + 2; // Add to offset the BEARER_DATA parameter id and length bytes
198fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            pdu.skip(offset);
199fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            try {
200fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
201fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                while (pdu.available() > 0) {
202fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    int currentId = pdu.read();
203fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    int currentLen = pdu.read();
204fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
205fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(currentId == subParameterId) {
206fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        found = true;
207fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        break;
208fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
209fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    else {
210fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        pdu.skip(currentLen);
211fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        offset += 2 + currentLen;
212fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
213fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
214fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                pdu.close();
215fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } catch (Exception e) {
216fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.e(TAG, "cdmaGetParameterOffset: ", e);
217fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
218fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
219fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(found)
220fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return offset;
221fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else
222fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return 0;
223fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
224fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
225fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
226fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public void cdmaChangeToDeliverPdu(long date){
227fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            /* Things to change:
228fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             *  - Message Type in bearer data (Not the overall point-to-point type)
229fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             *  - Change address ID from destination to originating (sub addresses are not used)
230fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             *  - A time stamp is not mandatory.
231fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             */
232fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int offset;
233326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mData == null) {
234326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                throw new IllegalArgumentException("Unable to convert PDU to Deliver type");
235326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            }
236fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            offset = cdmaGetParameterOffset(DESTINATION_ADDRESS);
237326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mData.length < offset) {
238326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                throw new IllegalArgumentException("Unable to convert PDU to Deliver type");
239326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            }
240326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mData[offset] = ORIGINATING_ADDRESS;
241326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde
242fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            offset = cdmaGetParameterOffset(DESTINATION_SUB_ADDRESS);
243326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mData.length < offset) {
244326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                throw new IllegalArgumentException("Unable to convert PDU to Deliver type");
245326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            }
246326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mData[offset] = ORIGINATING_SUB_ADDRESS;
247fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
248fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            offset = cdmaGetSubParameterOffset(BEARER_DATA_MSG_ID);
249fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
250326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mData.length > (2+offset)) {
251326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                int tmp = mData[offset+2] & 0xff; // Skip the subParam ID and length, and read the first byte.
252fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // Mask out the type
253fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                tmp &= 0x0f;
254fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // Set the new type
255fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                tmp |= ((BearerData.MESSAGE_TYPE_DELIVER << 4) & 0xf0);
256fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // Store the result
257326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                mData[offset+2] = (byte) tmp;
258fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
259326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            } else {
260326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                throw new IllegalArgumentException("Unable to convert PDU to Deliver type");
261326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            }
262fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                /* TODO: Do we need to change anything in the user data? Not sure if the user data is
263fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 *        just encoded using GSM encoding, or it is an actual GSM submit PDU embedded
264fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 *        in the user data?
265fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 */
266fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
267fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
268fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private static final byte TP_MIT_DELIVER       = 0x00; // bit 0 and 1
269fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private static final byte TP_MMS_NO_MORE       = 0x04; // bit 2
270fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private static final byte TP_RP_NO_REPLY_PATH  = 0x00; // bit 7
27170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        private static final byte TP_UDHI_MASK         = 0x40; // bit 6
272fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private static final byte TP_SRI_NO_REPORT     = 0x00; // bit 5
273fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
274fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private int gsmSubmitGetTpPidOffset() {
27570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            /* calculate the offset to TP_PID.
276fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             * The TP-DA has variable length, and the length excludes the 2 byte length and type headers.
277fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             * The TP-DA is two bytes within the PDU */
278326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            int offset = 2 + ((mData[2]+1) & 0xff)/2 + 2; // data[2] is the number of semi-octets in the phone number (ceil result)
279326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if((offset > mData.length) || (offset > (2 + 12))) // max length of TP_DA is 12 bytes + two byte offset.
28070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                throw new IllegalArgumentException("wrongly formatted gsm submit PDU. offset = " + offset);
281fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return offset;
282fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
283fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
284fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int gsmSubmitGetTpDcs() {
285326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mData[gsmSubmitGetTpDcsOffset()] & 0xff;
286fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
287fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
288fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public boolean gsmSubmitHasUserDataHeader() {
289326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return ((mData[0] & 0xff) & TP_UDHI_MASK) == TP_UDHI_MASK;
290fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
291fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
292fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private int gsmSubmitGetTpDcsOffset() {
293fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return gsmSubmitGetTpPidOffset() + 1;
294fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
295fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
296fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private int gsmSubmitGetTpUdlOffset() {
297326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            switch(((mData[0]  & 0xff) & (0x08 | 0x04))>>2) {
298fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case 0: // Not TP-VP present
299fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return gsmSubmitGetTpPidOffset() + 2;
300fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case 1: // TP-VP relative format
301fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return gsmSubmitGetTpPidOffset() + 2 + 1;
302fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case 2: // TP-VP enhanced format
303fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case 3: // TP-VP absolute format
304fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                break;
305fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
306fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return gsmSubmitGetTpPidOffset() + 2 + 7;
307fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
308fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private int gsmSubmitGetTpUdOffset() {
309fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return gsmSubmitGetTpUdlOffset() + 1;
310fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
311fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
312fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public void gsmDecodeUserDataHeader() {
313326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            ByteArrayInputStream pdu = new ByteArrayInputStream(mData);
314fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
315fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            pdu.skip(gsmSubmitGetTpUdlOffset());
316fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int userDataLength = pdu.read();
31770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if(gsmSubmitHasUserDataHeader() == true) {
31870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                int userDataHeaderLength = pdu.read();
31970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
32070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // This part is only needed to extract the language info, hence only needed for 7 bit encoding
321326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                if(mEncoding == SmsConstants.ENCODING_7BIT)
32270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                {
32370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    byte[] udh = new byte[userDataHeaderLength];
32470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    try {
32570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                        pdu.read(udh);
32670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    } catch (IOException e) {
32770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                        Log.w(TAG, "unable to read userDataHeader", e);
32870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    }
32970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    SmsHeader userDataHeader = SmsHeader.fromByteArray(udh);
330326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mLanguageTable = userDataHeader.languageTable;
331326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mLanguageShiftTable = userDataHeader.languageShiftTable;
33270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
33370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    int headerBits = (userDataHeaderLength + 1) * 8;
33470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    int headerSeptets = headerBits / 7;
33570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
336326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mUserDataSeptetPadding = (headerSeptets * 7) - headerBits;
337326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mMsgSeptetCount = userDataLength - headerSeptets;
338fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
339326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                mUserDataMsgOffset = gsmSubmitGetTpUdOffset() + userDataHeaderLength + 1; // Add the byte containing the length
34070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            }
34170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            else
34270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            {
343326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                mUserDataSeptetPadding = 0;
344326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                mMsgSeptetCount = userDataLength;
345326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                mUserDataMsgOffset = gsmSubmitGetTpUdOffset();
34670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            }
34770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if(V) {
348326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                Log.v(TAG, "encoding:" + mEncoding);
349326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                Log.v(TAG, "msgSeptetCount:" + mMsgSeptetCount);
350326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                Log.v(TAG, "userDataSeptetPadding:" + mUserDataSeptetPadding);
351326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                Log.v(TAG, "languageShiftTable:" + mLanguageShiftTable);
352326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                Log.v(TAG, "languageTable:" + mLanguageTable);
353326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                Log.v(TAG, "userDataMsgOffset:" + mUserDataMsgOffset);
354fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
355fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
356fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
357fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private void gsmWriteDate(ByteArrayOutputStream header, long time) throws UnsupportedEncodingException {
358fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss");
359fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            Date date = new Date(time);
360fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String timeStr = format.format(date); // Format to YYMMDDTHHMMSS UTC time
36170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if(V) Log.v(TAG, "Generated time string: " + timeStr);
362fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            byte[] timeChars = timeStr.getBytes("US-ASCII");
363fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
36470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            for(int i = 0, n = timeStr.length(); i < n; i+=2) {
365fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                header.write((timeChars[i+1]-0x30) << 4 | (timeChars[i]-0x30)); // Offset from ascii char to decimal value
366fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
367fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
368fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            Calendar cal = Calendar.getInstance();
369fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int offset = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (15 * 60 * 1000); /* offset in quarters of an hour */
370fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String offsetString;
371fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(offset < 0) {
372fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                offsetString = String.format("%1$02d", -(offset));
373fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                char[] offsetChars = offsetString.toCharArray();
374fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                header.write((offsetChars[1]-0x30) << 4 | 0x40 | (offsetChars[0]-0x30));
375fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
376fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else {
377fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                offsetString = String.format("%1$02d", offset);
378fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                char[] offsetChars = offsetString.toCharArray();
379fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                header.write((offsetChars[1]-0x30) << 4 | (offsetChars[0]-0x30));
380fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
381fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
382fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
383fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie/*        private void gsmSubmitExtractUserData() {
384fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int userDataLength = data[gsmSubmitGetTpUdlOffset()];
385fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            userData = new byte[userDataLength];
386fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            System.arraycopy(userData, 0, data, gsmSubmitGetTpUdOffset(), userDataLength);
387fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
388fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }*/
389fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
390fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
391fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Change the GSM Submit Pdu data in this object to a deliver PDU:
392fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - Build the new header with deliver PDU type, originator and time stamp.
393fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - Extract encoding details from the submit PDU
394fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - Extract user data length and user data from the submitPdu
395fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - Build the new PDU
396fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param date the time stamp to include (The value is the number of milliseconds since Jan. 1, 1970 GMT.)
397fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param originator the phone number to include in the deliver PDU header. Any undesired characters,
398fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *                    such as '-' will be striped from this string.
399fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
400fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public void gsmChangeToDeliverPdu(long date, String originator)
401fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        {
402fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            ByteArrayOutputStream newPdu = new ByteArrayOutputStream(22); // 22 is the max length of the deliver pdu header
403fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            byte[] encodedAddress;
404fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int userDataLength = 0;
405fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            try {
406fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                newPdu.write(TP_MIT_DELIVER | TP_MMS_NO_MORE | TP_RP_NO_REPLY_PATH | TP_SRI_NO_REPORT
407326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                             | (mData[0] & 0xff)  & TP_UDHI_MASK);
408fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                encodedAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(originator);
40970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                if(encodedAddress != null) {
41070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    int padding = (encodedAddress[encodedAddress.length-1] & 0xf0) == 0xf0 ? 1 : 0;
41170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    encodedAddress[0] = (byte)((encodedAddress[0]-1)*2 - padding); // Convert from octet length to semi octet length
41270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    // Insert originator address into the header - this includes the length
41370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    newPdu.write(encodedAddress);
41470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                } else {
41570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    newPdu.write(0);    /* zero length */
41670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    newPdu.write(0x81); /* International type */
41770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                }
41870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
419326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                newPdu.write(mData[gsmSubmitGetTpPidOffset()]);
420326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                newPdu.write(mData[gsmSubmitGetTpDcsOffset()]);
421fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // Generate service center time stamp
422fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                gsmWriteDate(newPdu, date);
423326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                userDataLength = (mData[gsmSubmitGetTpUdlOffset()] & 0xff);
424fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                newPdu.write(userDataLength);
425fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // Copy the pdu user data - keep in mind that the userDataLength is not the length in bytes for 7-bit encoding.
426326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                newPdu.write(mData, gsmSubmitGetTpUdOffset(), mData.length - gsmSubmitGetTpUdOffset());
427fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } catch (IOException e) {
428fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.e(TAG, "", e);
42970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                throw new IllegalArgumentException("Failed to change type to deliver PDU.");
430fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
431326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mData = newPdu.toByteArray();
432fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
433fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
434fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* SMS encoding to bmessage strings */
435fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /** get the encoding type as a bMessage string */
436fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public String getEncodingString(){
437326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mType == SMS_TYPE_GSM)
438fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            {
439326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                switch(mEncoding){
440fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_7BIT:
441326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    if(mLanguageTable == 0)
442fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        return "G-7BIT";
443fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    else
444fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        return "G-7BITEXT";
445fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_8BIT:
446fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "G-8BIT";
447fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_16BIT:
448fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "G-16BIT";
449fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_UNKNOWN:
450fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    default:
451fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "";
452fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
453fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } else /* SMS_TYPE_CDMA */ {
454326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                switch(mEncoding){
455fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_7BIT:
456fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "C-7ASCII";
457fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_8BIT:
458fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "C-8BIT";
459fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_16BIT:
460fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "C-UNICODE";
461fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_KSC5601:
462fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "C-KOREAN";
463fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case SmsMessage.ENCODING_UNKNOWN:
464fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    default:
465fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return "";
466fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
467fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
468fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
469fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
470fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
471fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private static int sConcatenatedRef = new Random().nextInt(256);
472fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
473fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    protected static int getNextConcatenatedRef() {
474fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sConcatenatedRef += 1;
475fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return sConcatenatedRef;
476fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
477fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public static ArrayList<SmsPdu> getSubmitPdus(String messageText, String address){
478fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* Use the generic GSM/CDMA SMS Message functionality within Android to generate the
479fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * SMS PDU's as once generated to send the SMS message.
480fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
481fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
482fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); // TODO: Change to use: ((TelephonyManager)myContext.getSystemService(Context.TELEPHONY_SERVICE))
483fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int phoneType;
484fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        GsmAlphabet.TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
485fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            com.android.internal.telephony.cdma.SmsMessage.calculateLength((CharSequence)messageText, false) :
486fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            com.android.internal.telephony.gsm.SmsMessage.calculateLength((CharSequence)messageText, false);
487fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
488fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        SmsPdu newPdu;
489fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String destinationAddress;
490fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int msgCount = ted.msgCount;
491fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int encoding;
492fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int languageTable;
493fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int languageShiftTable;
494fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int refNumber = getNextConcatenatedRef() & 0x00FF;
495fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        ArrayList<String> smsFragments = SmsMessage.fragmentText(messageText);
496fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        ArrayList<SmsPdu> pdus = new ArrayList<SmsPdu>(msgCount);
497fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        byte[] data;
498fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
499fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        // Default to GSM, as this code should not be used, if we neither have CDMA not GSM.
500fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        phoneType = (activePhone == PHONE_TYPE_CDMA) ? SMS_TYPE_CDMA : SMS_TYPE_GSM;
501fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        encoding = ted.codeUnitSize;
502fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        languageTable = ted.languageTable;
503fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        languageShiftTable = ted.languageShiftTable;
504fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        destinationAddress = PhoneNumberUtils.stripSeparators(address);
505fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(destinationAddress == null || destinationAddress.length() < 2) {
506fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            destinationAddress = "12"; // Ensure we add a number at least 2 digits as specified in the GSM spec.
507fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
508fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
509fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(msgCount == 1){
510fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            data = SmsMessage.getSubmitPdu(null, destinationAddress, smsFragments.get(0), false).encodedMessage;
511fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            newPdu = new SmsPdu(data, encoding, phoneType, languageTable);
512fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            pdus.add(newPdu);
513fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
51470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        else
51570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        {
51670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            /* This code is a reduced copy of the actual code used in the Android SMS sub system,
51770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz             * hence the comments have been left untouched. */
51870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            for(int i = 0; i < msgCount; i++){
51970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
52070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                concatRef.refNumber = refNumber;
52170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                concatRef.seqNumber = i + 1;  // 1-based sequence
52270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                concatRef.msgCount = msgCount;
52370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // We currently set this to true since our messaging app will never
52470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // send more than 255 parts (it converts the message to MMS well before that).
52570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // However, we should support 3rd party messaging apps that might need 16-bit
52670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // references
52770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // Note:  It's not sufficient to just flip this bit to true; it will have
52870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // ripple effects (several calculations assume 8-bit ref).
52970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                concatRef.isEightBits = true;
53070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                SmsHeader smsHeader = new SmsHeader();
53170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                smsHeader.concatRef = concatRef;
53270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
53370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                /* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding
53470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                 * will be determined(again) by getSubmitPdu().
53570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                 * All packets need to be encoded using the same encoding, as the bMessage
53670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                 * only have one filed to describe the encoding for all messages in a concatenated
53770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                 * SMS... */
538fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (encoding == SmsConstants.ENCODING_7BIT) {
53970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    smsHeader.languageTable = languageTable;
54070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    smsHeader.languageShiftTable = languageShiftTable;
54170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                }
54270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
54370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                if(phoneType == SMS_TYPE_GSM){
54470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null, destinationAddress,
54570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                            smsFragments.get(i), false, SmsHeader.toByteArray(smsHeader),
54670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                            encoding, languageTable, languageShiftTable).encodedMessage;
54770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                } else { // SMS_TYPE_CDMA
54870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    UserData uData = new UserData();
54970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    uData.payloadStr = smsFragments.get(i);
55070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    uData.userDataHeader = smsHeader;
55170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    if (encoding == SmsConstants.ENCODING_7BIT) {
55270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                        uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
55370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    } else { // assume UTF-16
55470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                        uData.msgEncoding = UserData.ENCODING_UNICODE_16;
55570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    }
55670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    uData.msgEncodingSet = true;
55770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(destinationAddress,
55870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                            uData, false).encodedMessage;
559fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
56070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                newPdu = new SmsPdu(data, encoding, phoneType, languageTable);
56170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                pdus.add(newPdu);
562fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
563fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
564fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
565fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return pdus;
566fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
567fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
568fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
569fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * Generate a list of deliver PDUs. The messageText and address parameters must be different from null,
570fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * for CDMA the date can be omitted (and will be ignored if supplied)
571fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param messageText The text to include.
572fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param address The originator address.
573fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param date The delivery time stamp.
574fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @return
575fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
576fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public static ArrayList<SmsPdu> getDeliverPdus(String messageText, String address, long date){
577fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        ArrayList<SmsPdu> deliverPdus = getSubmitPdus(messageText, address);
578fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
579fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /*
580fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * For CDMA the only difference between deliver and submit pdus are the messageType,
581fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * which is set in encodeMessageId, (the higher 4 bits of the 1st byte
582fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * of the Message identification sub parameter data.) and the address type.
583fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *
584fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * For GSM, a larger part of the header needs to be generated.
585fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
586fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        for(SmsPdu currentPdu : deliverPdus){
587fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(currentPdu.getType() == SMS_TYPE_CDMA){
588fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                currentPdu.cdmaChangeToDeliverPdu(date);
589fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } else { /* SMS_TYPE_GSM */
590fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                currentPdu.gsmChangeToDeliverPdu(date, address);
591fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
592fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
593fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
594fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return deliverPdus;
595fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
596fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
597fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
598fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
599fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * The decoding only supports decoding the actual textual content of the PDU received
600fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * from the MAP client. (As the Android system has no interface to send pre encoded PDUs)
601fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * The destination address must be extracted from the bmessage vCard(s).
602fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
603fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public static String decodePdu(byte[] data, int type) {
604fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String ret;
605fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(type == SMS_TYPE_CDMA) {
606fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            /* This is able to handle both submit and deliver PDUs */
607fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            ret = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(0, data).getMessageBody();
608fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } else {
609fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            /* For GSM, there is no submit pdu decoder, and most parser utils are private, and only minded for submit pdus */
610fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            ret = gsmParseSubmitPdu(data);
611fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
612fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return ret;
613fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
614fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
615fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /* At the moment we do not support using a SC-address. Use this function to strip off
616fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * the SC-address before parsing it to the SmsPdu. (this was added in errata 4335)
617fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
618fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private static byte[] gsmStripOffScAddress(byte[] data) {
619fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* The format of a native GSM SMS is: <sc-address><pdu> where sc-address is:
620fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * <length-byte><type-byte><number-bytes> */
621fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int addressLength = data[0] & 0xff; // Treat the byte value as an unsigned value
62270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        if(addressLength >= data.length) // We could verify that the address-length is no longer than 11 bytes
623fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            throw new IllegalArgumentException("Length of address exeeds the length of the PDU data.");
624fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int pduLength = data.length-(1+addressLength);
625fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        byte[] newData = new byte[pduLength];
626fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        System.arraycopy(data, 1+addressLength, newData, 0, pduLength);
627fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return newData;
628fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
629fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
630fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private static String gsmParseSubmitPdu(byte[] data) {
631fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* Things to do:
632fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - extract hasUsrData bit
633fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - extract TP-DCS -> Character set, compressed etc.
634fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - extract user data header to get the language properties
635fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - extract user data
636fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *  - decode the string */
637fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        //Strip off the SC-address before parsing
638fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        SmsPdu pdu = new SmsPdu(gsmStripOffScAddress(data), SMS_TYPE_GSM);
639fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        boolean userDataCompressed = false;
640fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int dataCodingScheme = pdu.gsmSubmitGetTpDcs();
641fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int encodingType =  SmsConstants.ENCODING_UNKNOWN;
642fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String messageBody = null;
643fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
644fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        // Look up the data encoding scheme
645fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if ((dataCodingScheme & 0x80) == 0) {
646fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // Bits 7..4 == 0xxx
647fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            userDataCompressed = (0 != (dataCodingScheme & 0x20));
648fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
649fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if (userDataCompressed) {
650fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.w(TAG, "4 - Unsupported SMS data coding scheme "
651fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        + "(compression) " + (dataCodingScheme & 0xff));
652fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } else {
653fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                switch ((dataCodingScheme >> 2) & 0x3) {
654fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case 0: // GSM 7 bit default alphabet
655fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    encodingType =  SmsConstants.ENCODING_7BIT;
656fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    break;
657fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
658fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case 2: // UCS 2 (16bit)
659fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    encodingType =  SmsConstants.ENCODING_16BIT;
660fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    break;
661fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
662fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case 1: // 8 bit data
663fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                case 3: // reserved
664fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    Log.w(TAG, "1 - Unsupported SMS data coding scheme "
665fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                            + (dataCodingScheme & 0xff));
666fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    encodingType =  SmsConstants.ENCODING_8BIT;
667fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    break;
668fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
669fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
670fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } else if ((dataCodingScheme & 0xf0) == 0xf0) {
671fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            userDataCompressed = false;
672fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
673fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if (0 == (dataCodingScheme & 0x04)) {
674fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // GSM 7 bit default alphabet
675fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                encodingType =  SmsConstants.ENCODING_7BIT;
676fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } else {
677fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // 8 bit data
678fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                encodingType =  SmsConstants.ENCODING_8BIT;
679fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
680fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } else if ((dataCodingScheme & 0xF0) == 0xC0
681fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                || (dataCodingScheme & 0xF0) == 0xD0
682fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                || (dataCodingScheme & 0xF0) == 0xE0) {
683fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
684fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
685fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // 0xC0 == 7 bit, don't store
686fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // 0xD0 == 7 bit, store
687fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // 0xE0 == UCS-2, store
688fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
689fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if ((dataCodingScheme & 0xF0) == 0xE0) {
690fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                encodingType =  SmsConstants.ENCODING_16BIT;
691fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } else {
692fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                encodingType =  SmsConstants.ENCODING_7BIT;
693fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
694fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
695fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            userDataCompressed = false;
696fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
697fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // bit 0x04 reserved
698fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } else if ((dataCodingScheme & 0xC0) == 0x80) {
699fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
700fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            // 0x80..0xBF == Reserved coding groups
701fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if (dataCodingScheme == 0x84) {
702fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                // This value used for KSC5601 by carriers in Korea.
703fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                encodingType =  SmsConstants.ENCODING_KSC5601;
704fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } else {
705fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.w(TAG, "5 - Unsupported SMS data coding scheme "
706fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        + (dataCodingScheme & 0xff));
707fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
708fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } else {
709fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            Log.w(TAG, "3 - Unsupported SMS data coding scheme "
710fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    + (dataCodingScheme & 0xff));
711fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
712fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
713fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        pdu.setEncoding(encodingType);
71470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        pdu.gsmDecodeUserDataHeader();
715fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
716fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        try {
717fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            switch (encodingType) {
718fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case  SmsConstants.ENCODING_UNKNOWN:
719fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case  SmsConstants.ENCODING_8BIT:
72070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                Log.w(TAG, "Unknown encoding type: " + encodingType);
721fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                messageBody = null;
722fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                break;
723fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
724fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case  SmsConstants.ENCODING_7BIT:
725fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                messageBody = GsmAlphabet.gsm7BitPackedToString(pdu.getData(), pdu.getUserDataMsgOffset(),
726fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                                pdu.getMsgSeptetCount(), pdu.getUserDataSeptetPadding(), pdu.getLanguageTable(),
727fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                                pdu.getLanguageShiftTable());
72870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                Log.i(TAG, "Decoded as 7BIT: " + messageBody);
729fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
730fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                break;
731fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
732fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case  SmsConstants.ENCODING_16BIT:
733fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                messageBody = new String(pdu.getData(), pdu.getUserDataMsgOffset(), pdu.getUserDataMsgSize(), "utf-16");
73470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                Log.i(TAG, "Decoded as 16BIT: " + messageBody);
735fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                break;
736fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
737fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            case SmsConstants.ENCODING_KSC5601:
738fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                messageBody = new String(pdu.getData(), pdu.getUserDataMsgOffset(), pdu.getUserDataMsgSize(), "KSC5601");
73970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                Log.i(TAG, "Decoded as KSC5601: " + messageBody);
740fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                break;
741fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
742fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } catch (UnsupportedEncodingException e) {
743fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            Log.e(TAG, "Unsupported encoding type???", e); // This should never happen.
744fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return null;
745fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
746fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
747fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return messageBody;
748fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
749fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
750fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie}
751