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