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