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