1 /* 2 * Copyright (C) 2007-2008 Esmertec AG. 3 * Copyright (C) 2007-2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mms.transaction; 19 20 import java.util.Arrays; 21 22 import android.content.ContentUris; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.database.sqlite.SqliteWrapper; 26 import android.net.Uri; 27 import android.provider.Telephony.Mms; 28 import android.provider.Telephony.Mms.Sent; 29 import android.text.TextUtils; 30 import android.util.Log; 31 32 import com.android.mms.LogTag; 33 import com.android.mms.ui.MessageUtils; 34 import com.android.mms.util.RateController; 35 import com.android.mms.util.SendingProgressTokenManager; 36 import com.google.android.mms.pdu.EncodedStringValue; 37 import com.google.android.mms.pdu.PduComposer; 38 import com.google.android.mms.pdu.PduHeaders; 39 import com.google.android.mms.pdu.PduParser; 40 import com.google.android.mms.pdu.PduPersister; 41 import com.google.android.mms.pdu.SendConf; 42 import com.google.android.mms.pdu.SendReq; 43 44 /** 45 * The SendTransaction is responsible for sending multimedia messages 46 * (M-Send.req) to the MMSC server. It: 47 * 48 * <ul> 49 * <li>Loads the multimedia message from storage (Outbox). 50 * <li>Packs M-Send.req and sends it. 51 * <li>Retrieves confirmation data from the server (M-Send.conf). 52 * <li>Parses confirmation message and handles it. 53 * <li>Moves sent multimedia message from Outbox to Sent. 54 * <li>Notifies the TransactionService about successful completion. 55 * </ul> 56 */ 57 public class SendTransaction extends Transaction implements Runnable { 58 private static final String TAG = "SendTransaction"; 59 60 private Thread mThread; 61 public final Uri mSendReqURI; 62 63 public SendTransaction(Context context, 64 int transId, TransactionSettings connectionSettings, String uri) { 65 super(context, transId, connectionSettings); 66 mSendReqURI = Uri.parse(uri); 67 mId = uri; 68 69 // Attach the transaction to the instance of RetryScheduler. 70 attach(RetryScheduler.getInstance(context)); 71 } 72 73 /* 74 * (non-Javadoc) 75 * @see com.android.mms.Transaction#process() 76 */ 77 @Override 78 public void process() { 79 mThread = new Thread(this, "SendTransaction"); 80 mThread.start(); 81 } 82 83 public void run() { 84 try { 85 RateController rateCtlr = RateController.getInstance(); 86 if (rateCtlr.isLimitSurpassed() && !rateCtlr.isAllowedByUser()) { 87 Log.e(TAG, "Sending rate limit surpassed."); 88 return; 89 } 90 91 // Load M-Send.req from outbox 92 PduPersister persister = PduPersister.getPduPersister(mContext); 93 SendReq sendReq = (SendReq) persister.load(mSendReqURI); 94 95 // Update the 'date' field of the PDU right before sending it. 96 long date = System.currentTimeMillis() / 1000L; 97 sendReq.setDate(date); 98 99 // Persist the new date value into database. 100 ContentValues values = new ContentValues(1); 101 values.put(Mms.DATE, date); 102 SqliteWrapper.update(mContext, mContext.getContentResolver(), 103 mSendReqURI, values, null, null); 104 105 // fix bug 2100169: insert the 'from' address per spec 106 String lineNumber = MessageUtils.getLocalNumber(); 107 if (!TextUtils.isEmpty(lineNumber)) { 108 sendReq.setFrom(new EncodedStringValue(lineNumber)); 109 } 110 111 // Pack M-Send.req, send it, retrieve confirmation data, and parse it 112 long tokenKey = ContentUris.parseId(mSendReqURI); 113 byte[] response = sendPdu(SendingProgressTokenManager.get(tokenKey), 114 new PduComposer(mContext, sendReq).make()); 115 SendingProgressTokenManager.remove(tokenKey); 116 117 if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { 118 String respStr = new String(response); 119 Log.d(TAG, "[SendTransaction] run: send mms msg (" + mId + "), resp=" + respStr); 120 } 121 122 SendConf conf = (SendConf) new PduParser(response).parse(); 123 if (conf == null) { 124 Log.e(TAG, "No M-Send.conf received."); 125 } 126 127 // Check whether the responding Transaction-ID is consistent 128 // with the sent one. 129 byte[] reqId = sendReq.getTransactionId(); 130 byte[] confId = conf.getTransactionId(); 131 if (!Arrays.equals(reqId, confId)) { 132 Log.e(TAG, "Inconsistent Transaction-ID: req=" 133 + new String(reqId) + ", conf=" + new String(confId)); 134 return; 135 } 136 137 // From now on, we won't save the whole M-Send.conf into 138 // our database. Instead, we just save some interesting fields 139 // into the related M-Send.req. 140 values = new ContentValues(2); 141 int respStatus = conf.getResponseStatus(); 142 values.put(Mms.RESPONSE_STATUS, respStatus); 143 144 if (respStatus != PduHeaders.RESPONSE_STATUS_OK) { 145 SqliteWrapper.update(mContext, mContext.getContentResolver(), 146 mSendReqURI, values, null, null); 147 Log.e(TAG, "Server returned an error code: " + respStatus); 148 return; 149 } 150 151 String messageId = PduPersister.toIsoString(conf.getMessageId()); 152 values.put(Mms.MESSAGE_ID, messageId); 153 SqliteWrapper.update(mContext, mContext.getContentResolver(), 154 mSendReqURI, values, null, null); 155 156 // Move M-Send.req from Outbox into Sent. 157 Uri uri = persister.move(mSendReqURI, Sent.CONTENT_URI); 158 159 mTransactionState.setState(TransactionState.SUCCESS); 160 mTransactionState.setContentUri(uri); 161 } catch (Throwable t) { 162 Log.e(TAG, Log.getStackTraceString(t)); 163 } finally { 164 if (mTransactionState.getState() != TransactionState.SUCCESS) { 165 mTransactionState.setState(TransactionState.FAILED); 166 mTransactionState.setContentUri(mSendReqURI); 167 Log.e(TAG, "Delivery failed."); 168 } 169 notifyObservers(); 170 } 171 } 172 173 @Override 174 public int getType() { 175 return SEND_TRANSACTION; 176 } 177 } 178