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
18package com.android.mms.transaction;
19
20import java.io.IOException;
21import java.net.InetAddress;
22import java.net.UnknownHostException;
23
24import android.content.Context;
25import android.net.ConnectivityManager;
26import android.net.Uri;
27
28import com.android.mms.util.SendingProgressTokenManager;
29import com.google.android.mms.MmsException;
30
31/**
32 * Transaction is an abstract class for notification transaction, send transaction
33 * and other transactions described in MMS spec.
34 * It provides the interfaces of them and some common methods for them.
35 */
36public abstract class Transaction extends Observable {
37    private final int mServiceId;
38
39    protected Context mContext;
40    protected String mId;
41    protected TransactionState mTransactionState;
42    protected TransactionSettings mTransactionSettings;
43
44    /**
45     * Identifies push requests.
46     */
47    public static final int NOTIFICATION_TRANSACTION = 0;
48    /**
49     * Identifies deferred retrieve requests.
50     */
51    public static final int RETRIEVE_TRANSACTION     = 1;
52    /**
53     * Identifies send multimedia message requests.
54     */
55    public static final int SEND_TRANSACTION         = 2;
56    /**
57     * Identifies send read report requests.
58     */
59    public static final int READREC_TRANSACTION      = 3;
60
61    public Transaction(Context context, int serviceId,
62            TransactionSettings settings) {
63        mContext = context;
64        mTransactionState = new TransactionState();
65        mServiceId = serviceId;
66        mTransactionSettings = settings;
67    }
68
69    /**
70     * Returns the transaction state of this transaction.
71     *
72     * @return Current state of the Transaction.
73     */
74    @Override
75    public TransactionState getState() {
76        return mTransactionState;
77    }
78
79    /**
80     * An instance of Transaction encapsulates the actions required
81     * during a MMS Client transaction.
82     */
83    public abstract void process();
84
85    /**
86     * Used to determine whether a transaction is equivalent to this instance.
87     *
88     * @param transaction the transaction which is compared to this instance.
89     * @return true if transaction is equivalent to this instance, false otherwise.
90     */
91    public boolean isEquivalent(Transaction transaction) {
92        return mId.equals(transaction.mId);
93    }
94
95    /**
96     * Get the service-id of this transaction which was assigned by the framework.
97     * @return the service-id of the transaction
98     */
99    public int getServiceId() {
100        return mServiceId;
101    }
102
103    public TransactionSettings getConnectionSettings() {
104        return mTransactionSettings;
105    }
106    public void setConnectionSettings(TransactionSettings settings) {
107        mTransactionSettings = settings;
108    }
109
110    /**
111     * A common method to send a PDU to MMSC.
112     *
113     * @param pdu A byte array which contains the data of the PDU.
114     * @return A byte array which contains the response data.
115     *         If an HTTP error code is returned, an IOException will be thrown.
116     * @throws IOException if any error occurred on network interface or
117     *         an HTTP error code(>=400) returned from the server.
118     * @throws MmsException if pdu is null.
119     */
120    protected byte[] sendPdu(byte[] pdu) throws IOException, MmsException {
121        return sendPdu(SendingProgressTokenManager.NO_TOKEN, pdu,
122                mTransactionSettings.getMmscUrl());
123    }
124
125    /**
126     * A common method to send a PDU to MMSC.
127     *
128     * @param pdu A byte array which contains the data of the PDU.
129     * @param mmscUrl Url of the recipient MMSC.
130     * @return A byte array which contains the response data.
131     *         If an HTTP error code is returned, an IOException will be thrown.
132     * @throws IOException if any error occurred on network interface or
133     *         an HTTP error code(>=400) returned from the server.
134     * @throws MmsException if pdu is null.
135     */
136    protected byte[] sendPdu(byte[] pdu, String mmscUrl) throws IOException, MmsException {
137        return sendPdu(SendingProgressTokenManager.NO_TOKEN, pdu, mmscUrl);
138    }
139
140    /**
141     * A common method to send a PDU to MMSC.
142     *
143     * @param token The token to identify the sending progress.
144     * @param pdu A byte array which contains the data of the PDU.
145     * @return A byte array which contains the response data.
146     *         If an HTTP error code is returned, an IOException will be thrown.
147     * @throws IOException if any error occurred on network interface or
148     *         an HTTP error code(>=400) returned from the server.
149     * @throws MmsException if pdu is null.
150     */
151    protected byte[] sendPdu(long token, byte[] pdu) throws IOException, MmsException {
152        return sendPdu(token, pdu, mTransactionSettings.getMmscUrl());
153    }
154
155    /**
156     * A common method to send a PDU to MMSC.
157     *
158     * @param token The token to identify the sending progress.
159     * @param pdu A byte array which contains the data of the PDU.
160     * @param mmscUrl Url of the recipient MMSC.
161     * @return A byte array which contains the response data.
162     *         If an HTTP error code is returned, an IOException will be thrown.
163     * @throws IOException if any error occurred on network interface or
164     *         an HTTP error code(>=400) returned from the server.
165     * @throws MmsException if pdu is null.
166     */
167    protected byte[] sendPdu(long token, byte[] pdu,
168            String mmscUrl) throws IOException, MmsException {
169        if (pdu == null) {
170            throw new MmsException();
171        }
172
173        ensureRouteToHost(mmscUrl, mTransactionSettings);
174        return HttpUtils.httpConnection(
175                mContext, token,
176                mmscUrl,
177                pdu, HttpUtils.HTTP_POST_METHOD,
178                mTransactionSettings.isProxySet(),
179                mTransactionSettings.getProxyAddress(),
180                mTransactionSettings.getProxyPort());
181    }
182
183    /**
184     * A common method to retrieve a PDU from MMSC.
185     *
186     * @param url The URL of the message which we are going to retrieve.
187     * @return A byte array which contains the data of the PDU.
188     *         If the status code is not correct, an IOException will be thrown.
189     * @throws IOException if any error occurred on network interface or
190     *         an HTTP error code(>=400) returned from the server.
191     */
192    protected byte[] getPdu(String url) throws IOException {
193        ensureRouteToHost(url, mTransactionSettings);
194        return HttpUtils.httpConnection(
195                mContext, SendingProgressTokenManager.NO_TOKEN,
196                url, null, HttpUtils.HTTP_GET_METHOD,
197                mTransactionSettings.isProxySet(),
198                mTransactionSettings.getProxyAddress(),
199                mTransactionSettings.getProxyPort());
200    }
201
202    /**
203     * Make sure that a network route exists to allow us to reach the host in the
204     * supplied URL, and to the MMS proxy host as well, if a proxy is used.
205     * @param url The URL of the MMSC to which we need a route
206     * @param settings Specifies the address of the proxy host, if any
207     * @throws IOException if the host doesn't exist, or adding the route fails.
208     */
209    private void ensureRouteToHost(String url, TransactionSettings settings) throws IOException {
210        ConnectivityManager connMgr =
211                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
212
213        InetAddress inetAddr;
214        if (settings.isProxySet()) {
215            String proxyAddr = settings.getProxyAddress();
216            try {
217              inetAddr = InetAddress.getByName(proxyAddr);
218            } catch (UnknownHostException e) {
219                throw new IOException("Cannot establish route for " + url +
220                                      ": Unknown proxy " + proxyAddr);
221            }
222            if (!connMgr.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_MMS, inetAddr)) {
223                throw new IOException("Cannot establish route to proxy " + inetAddr);
224            }
225        } else {
226            Uri uri = Uri.parse(url);
227            try {
228                inetAddr = InetAddress.getByName(uri.getHost());
229            } catch (UnknownHostException e) {
230                throw new IOException("Cannot establish route for " + url + ": Unknown host");
231            }
232            if (!connMgr.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_MMS, inetAddr)) {
233                throw new IOException("Cannot establish route to " + inetAddr + " for " + url);
234            }
235        }
236    }
237
238    @Override
239    public String toString() {
240        return getClass().getName() + ": serviceId=" + mServiceId;
241    }
242
243    /**
244     * Get the type of the transaction.
245     *
246     * @return Transaction type in integer.
247     */
248    abstract public int getType();
249}
250