172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project/*
272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Copyright (C) 2007-2008 Esmertec AG.
372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Copyright (C) 2007-2008 The Android Open Source Project
472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * you may not use this file except in compliance with the License.
772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * You may obtain a copy of the License at
872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
1072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
1172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * See the License for the specific language governing permissions and
1572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * limitations under the License.
1672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project */
1772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
1872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectpackage com.android.mms.transaction;
1972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
2072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.content.ContentUris;
2172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.content.ContentValues;
2272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.content.Context;
23d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.database.sqlite.SqliteWrapper;
2472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.net.Uri;
25f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms;
26f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms.Sent;
279653d43dfe3713df14ef3567b124c5729543c5f2Tom Taylorimport android.text.TextUtils;
2872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.util.Log;
2972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
30d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.LogTag;
31d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.ui.MessageUtils;
32d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.util.RateController;
33d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.util.SendingProgressTokenManager;
34d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.google.android.mms.pdu.EncodedStringValue;
35d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.google.android.mms.pdu.PduComposer;
36d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.google.android.mms.pdu.PduHeaders;
37d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.google.android.mms.pdu.PduParser;
38d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.google.android.mms.pdu.PduPersister;
39d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.google.android.mms.pdu.SendConf;
40d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.google.android.mms.pdu.SendReq;
4172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
42178eb8396bd0cc972542798b79218cdf32af4b35Ye Wenimport java.util.Arrays;
43178eb8396bd0cc972542798b79218cdf32af4b35Ye Wen
4472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project/**
4572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * The SendTransaction is responsible for sending multimedia messages
4672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * (M-Send.req) to the MMSC server.  It:
4772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
4872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * <ul>
4972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * <li>Loads the multimedia message from storage (Outbox).
5072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * <li>Packs M-Send.req and sends it.
5172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * <li>Retrieves confirmation data from the server  (M-Send.conf).
5272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * <li>Parses confirmation message and handles it.
5372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * <li>Moves sent multimedia message from Outbox to Sent.
5472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * <li>Notifies the TransactionService about successful completion.
5572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * </ul>
5672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project */
5772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectpublic class SendTransaction extends Transaction implements Runnable {
58ab845dee6565a8dfc384186bc8f2e801a2b087e1Ye Wen    private static final String TAG = LogTag.TAG;
5972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
6072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private Thread mThread;
61c65a9fdb8e1c936a2b8ce9312c125d4046fbae46Tom Taylor    public final Uri mSendReqURI;
6272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
6372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public SendTransaction(Context context,
64a277f438d33872b9a0f1611fb8a86a918765f04bTom Taylor            int transId, TransactionSettings connectionSettings, String uri) {
65a277f438d33872b9a0f1611fb8a86a918765f04bTom Taylor        super(context, transId, connectionSettings);
6672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        mSendReqURI = Uri.parse(uri);
6772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        mId = uri;
6872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
6972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        // Attach the transaction to the instance of RetryScheduler.
7072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        attach(RetryScheduler.getInstance(context));
7172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
7272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
7372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    /*
7472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project     * (non-Javadoc)
7572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project     * @see com.android.mms.Transaction#process()
7672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project     */
7772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    @Override
7872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public void process() {
79ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev        mThread = new Thread(this, "SendTransaction");
8072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        mThread.start();
8172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
8272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
8372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public void run() {
8472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        try {
8572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            RateController rateCtlr = RateController.getInstance();
8672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (rateCtlr.isLimitSurpassed() && !rateCtlr.isAllowedByUser()) {
8772b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                Log.e(TAG, "Sending rate limit surpassed.");
8872b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                return;
8972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
9072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
9172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // Load M-Send.req from outbox
9272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            PduPersister persister = PduPersister.getPduPersister(mContext);
9372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            SendReq sendReq = (SendReq) persister.load(mSendReqURI);
9472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
9572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // Update the 'date' field of the PDU right before sending it.
9672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            long date = System.currentTimeMillis() / 1000L;
9772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            sendReq.setDate(date);
9872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
9972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // Persist the new date value into database.
10072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            ContentValues values = new ContentValues(1);
10172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            values.put(Mms.DATE, date);
10272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            SqliteWrapper.update(mContext, mContext.getContentResolver(),
10372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                                 mSendReqURI, values, null, null);
10472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
10513dbe96fc54f9b7190fd415d737f9a56dc409d10Wei Huang            // fix bug 2100169: insert the 'from' address per spec
106a277f438d33872b9a0f1611fb8a86a918765f04bTom Taylor            String lineNumber = MessageUtils.getLocalNumber();
1079653d43dfe3713df14ef3567b124c5729543c5f2Tom Taylor            if (!TextUtils.isEmpty(lineNumber)) {
1089653d43dfe3713df14ef3567b124c5729543c5f2Tom Taylor                sendReq.setFrom(new EncodedStringValue(lineNumber));
1099653d43dfe3713df14ef3567b124c5729543c5f2Tom Taylor            }
11013dbe96fc54f9b7190fd415d737f9a56dc409d10Wei Huang
11172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // Pack M-Send.req, send it, retrieve confirmation data, and parse it
11272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            long tokenKey = ContentUris.parseId(mSendReqURI);
11372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            byte[] response = sendPdu(SendingProgressTokenManager.get(tokenKey),
11472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                                      new PduComposer(mContext, sendReq).make());
11572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            SendingProgressTokenManager.remove(tokenKey);
11672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
1170d798c0853ead129b245ac7e8700f3a4aba92ecbWei Huang            if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
1180d798c0853ead129b245ac7e8700f3a4aba92ecbWei Huang                String respStr = new String(response);
1190d798c0853ead129b245ac7e8700f3a4aba92ecbWei Huang                Log.d(TAG, "[SendTransaction] run: send mms msg (" + mId + "), resp=" + respStr);
1200d798c0853ead129b245ac7e8700f3a4aba92ecbWei Huang            }
1210d798c0853ead129b245ac7e8700f3a4aba92ecbWei Huang
122178eb8396bd0cc972542798b79218cdf32af4b35Ye Wen            SendConf conf = (SendConf) new PduParser(
123178eb8396bd0cc972542798b79218cdf32af4b35Ye Wen                    response, PduParserUtil.shouldParseContentDisposition()).parse();
12472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (conf == null) {
12572b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                Log.e(TAG, "No M-Send.conf received.");
12672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
12772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
12872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // Check whether the responding Transaction-ID is consistent
12972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // with the sent one.
13072b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project            byte[] reqId = sendReq.getTransactionId();
13172b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project            byte[] confId = conf.getTransactionId();
13272b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project            if (!Arrays.equals(reqId, confId)) {
13372b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                Log.e(TAG, "Inconsistent Transaction-ID: req="
13472b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                        + new String(reqId) + ", conf=" + new String(confId));
13572b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                return;
13672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
13772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
13872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // From now on, we won't save the whole M-Send.conf into
13972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // our database. Instead, we just save some interesting fields
14072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // into the related M-Send.req.
14172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            values = new ContentValues(2);
14272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            int respStatus = conf.getResponseStatus();
14372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            values.put(Mms.RESPONSE_STATUS, respStatus);
14472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
14572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (respStatus != PduHeaders.RESPONSE_STATUS_OK) {
14672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                SqliteWrapper.update(mContext, mContext.getContentResolver(),
14772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                                     mSendReqURI, values, null, null);
14872b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                Log.e(TAG, "Server returned an error code: " + respStatus);
14972b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project                return;
15072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
15172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
15272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            String messageId = PduPersister.toIsoString(conf.getMessageId());
15372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            values.put(Mms.MESSAGE_ID, messageId);
15472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            SqliteWrapper.update(mContext, mContext.getContentResolver(),
15572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                                 mSendReqURI, values, null, null);
15672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
15772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // Move M-Send.req from Outbox into Sent.
15872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            Uri uri = persister.move(mSendReqURI, Sent.CONTENT_URI);
15972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
16072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            mTransactionState.setState(TransactionState.SUCCESS);
16172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            mTransactionState.setContentUri(uri);
16272b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project        } catch (Throwable t) {
16372b7fda4ff5e94ba1a13c41601ac1cd61c0d6663The Android Open Source Project            Log.e(TAG, Log.getStackTraceString(t));
16472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        } finally {
16572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (mTransactionState.getState() != TransactionState.SUCCESS) {
16672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                mTransactionState.setState(TransactionState.FAILED);
16772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                mTransactionState.setContentUri(mSendReqURI);
16872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                Log.e(TAG, "Delivery failed.");
16972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
17072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            notifyObservers();
17172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
17272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
17372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
17472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    @Override
17572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public int getType() {
17672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return SEND_TRANSACTION;
17772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
17872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project}
179