HttpUtils.java revision 4d33cdcbb402a87ffeeaddb58237c70c4608c6f5
1/*
2 * Copyright (C) 2008 Esmertec AG.
3 * Copyright (C) 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 org.apache.http.HttpEntity;
21import org.apache.http.HttpHost;
22import org.apache.http.HttpRequest;
23import org.apache.http.HttpResponse;
24import org.apache.http.StatusLine;
25import org.apache.http.client.methods.HttpGet;
26import org.apache.http.client.methods.HttpPost;
27import org.apache.http.conn.params.ConnRouteParams;
28import org.apache.http.params.HttpParams;
29import org.apache.http.params.HttpProtocolParams;
30import org.apache.http.params.HttpConnectionParams;
31
32import com.android.mms.MmsConfig;
33import com.android.mms.LogTag;
34
35import android.content.Context;
36import android.net.http.AndroidHttpClient;
37import android.telephony.TelephonyManager;
38import android.text.TextUtils;
39import android.util.Config;
40import android.util.Log;
41
42import java.io.DataInputStream;
43import java.io.IOException;
44import java.net.SocketException;
45import java.net.URI;
46import java.net.URISyntaxException;
47import java.util.Locale;
48
49public class HttpUtils {
50    private static final String TAG = LogTag.TRANSACTION;
51
52    private static final boolean DEBUG = false;
53    private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
54
55    public static final int HTTP_POST_METHOD = 1;
56    public static final int HTTP_GET_METHOD = 2;
57
58    // This is the value to use for the "Accept-Language" header.
59    // Once it becomes possible for the user to change the locale
60    // setting, this should no longer be static.  We should call
61    // getHttpAcceptLanguage instead.
62    private static final String HDR_VALUE_ACCEPT_LANGUAGE;
63
64    static {
65        HDR_VALUE_ACCEPT_LANGUAGE = getHttpAcceptLanguage();
66    }
67
68    // Definition for necessary HTTP headers.
69    private static final String HDR_KEY_ACCEPT = "Accept";
70    private static final String HDR_KEY_ACCEPT_LANGUAGE = "Accept-Language";
71
72    private static final String HDR_VALUE_ACCEPT =
73        "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";
74
75    private HttpUtils() {
76        // To forbidden instantiate this class.
77    }
78
79    /**
80     * A helper method to send or retrieve data through HTTP protocol.
81     *
82     * @param token The token to identify the sending progress.
83     * @param url The URL used in a GET request. Null when the method is
84     *         HTTP_POST_METHOD.
85     * @param pdu The data to be POST. Null when the method is HTTP_GET_METHOD.
86     * @param method HTTP_POST_METHOD or HTTP_GET_METHOD.
87     * @return A byte array which contains the response data.
88     *         If an HTTP error code is returned, an IOException will be thrown.
89     * @throws IOException if any error occurred on network interface or
90     *         an HTTP error code(>=400) returned from the server.
91     */
92    protected static byte[] httpConnection(Context context, long token,
93            String url, byte[] pdu, int method, boolean isProxySet,
94            String proxyHost, int proxyPort) throws IOException {
95        if (url == null) {
96            throw new IllegalArgumentException("URL must not be null.");
97        }
98
99        if (LOCAL_LOGV) {
100            Log.v(TAG, "httpConnection: params list");
101            Log.v(TAG, "\ttoken\t\t= " + token);
102            Log.v(TAG, "\turl\t\t= " + url);
103            Log.v(TAG, "\tmethod\t\t= "
104                    + ((method == HTTP_POST_METHOD) ? "POST"
105                            : ((method == HTTP_GET_METHOD) ? "GET" : "UNKNOWN")));
106            Log.v(TAG, "\tisProxySet\t= " + isProxySet);
107            Log.v(TAG, "\tproxyHost\t= " + proxyHost);
108            Log.v(TAG, "\tproxyPort\t= " + proxyPort);
109            // TODO Print out binary data more readable.
110            //Log.v(TAG, "\tpdu\t\t= " + Arrays.toString(pdu));
111        }
112
113        AndroidHttpClient client = null;
114
115        try {
116            // Make sure to use a proxy which supports CONNECT.
117            URI hostUrl = new URI(url);
118            HttpHost target = new HttpHost(
119                    hostUrl.getHost(), hostUrl.getPort(),
120                    HttpHost.DEFAULT_SCHEME_NAME);
121
122            client = createHttpClient(context);
123            HttpRequest req = null;
124            switch(method) {
125                case HTTP_POST_METHOD:
126                    ProgressCallbackEntity entity = new ProgressCallbackEntity(
127                                                        context, token, pdu);
128                    // Set request content type.
129                    entity.setContentType("application/vnd.wap.mms-message");
130
131                    HttpPost post = new HttpPost(url);
132                    post.setEntity(entity);
133                    req = post;
134                    break;
135                case HTTP_GET_METHOD:
136                    req = new HttpGet(url);
137                    break;
138                default:
139                    Log.e(TAG, "Unknown HTTP method: " + method
140                            + ". Must be one of POST[" + HTTP_POST_METHOD
141                            + "] or GET[" + HTTP_GET_METHOD + "].");
142                    return null;
143            }
144
145            // Set route parameters for the request.
146            HttpParams params = client.getParams();
147            if (isProxySet) {
148                ConnRouteParams.setDefaultProxy(
149                        params, new HttpHost(proxyHost, proxyPort));
150            }
151            req.setParams(params);
152
153            // Set necessary HTTP headers for MMS transmission.
154            req.addHeader(HDR_KEY_ACCEPT, HDR_VALUE_ACCEPT);
155            {
156                String xWapProfileTagName = MmsConfig.getUaProfTagName();
157                String xWapProfileUrl = MmsConfig.getUaProfUrl();
158
159                if (xWapProfileUrl != null) {
160                    if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
161                        Log.d(LogTag.TRANSACTION,
162                                "[HttpUtils] httpConn: xWapProfUrl=" + xWapProfileUrl);
163                    }
164                    req.addHeader(xWapProfileTagName, xWapProfileUrl);
165                }
166            }
167
168            // Extra http parameters. Split by '|' to get a list of value pairs.
169            // Separate each pair by the first occurrence of ':' to obtain a name and
170            // value. Replace the occurrence of the string returned by
171            // MmsConfig.getHttpParamsLine1Key() with the users telephone number inside
172            // the value.
173            String extraHttpParams = MmsConfig.getHttpParams();
174
175            if (extraHttpParams != null) {
176                String line1Number = ((TelephonyManager)context
177                        .getSystemService(Context.TELEPHONY_SERVICE))
178                        .getLine1Number();
179                String line1Key = MmsConfig.getHttpParamsLine1Key();
180                String paramList[] = extraHttpParams.split("\\|");
181
182                for (String paramPair : paramList) {
183                    String splitPair[] = paramPair.split(":", 2);
184
185                    if (splitPair.length == 2) {
186                        String name = splitPair[0].trim();
187                        String value = splitPair[1].trim();
188
189                        if (line1Key != null) {
190                            value = value.replace(line1Key, line1Number);
191                        }
192                        if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {
193                            req.addHeader(name, value);
194                        }
195                    }
196                }
197            }
198            req.addHeader(HDR_KEY_ACCEPT_LANGUAGE, HDR_VALUE_ACCEPT_LANGUAGE);
199
200            HttpResponse response = client.execute(target, req);
201            StatusLine status = response.getStatusLine();
202            if (status.getStatusCode() != 200) { // HTTP 200 is success.
203                throw new IOException("HTTP error: " + status.getReasonPhrase());
204            }
205
206            HttpEntity entity = response.getEntity();
207            byte[] body = null;
208            if (entity != null) {
209                try {
210                    if (entity.getContentLength() > 0) {
211                        body = new byte[(int) entity.getContentLength()];
212                        DataInputStream dis = new DataInputStream(entity.getContent());
213                        try {
214                            dis.readFully(body);
215                        } finally {
216                            try {
217                                dis.close();
218                            } catch (IOException e) {
219                                Log.e(TAG, "Error closing input stream: " + e.getMessage());
220                            }
221                        }
222                    }
223                } finally {
224                    if (entity != null) {
225                        entity.consumeContent();
226                    }
227                }
228            }
229            return body;
230        } catch (URISyntaxException e) {
231            handleHttpConnectionException(e, url);
232        } catch (IllegalStateException e) {
233            handleHttpConnectionException(e, url);
234        } catch (IllegalArgumentException e) {
235            handleHttpConnectionException(e, url);
236        } catch (SocketException e) {
237            handleHttpConnectionException(e, url);
238        } catch (Exception e) {
239            handleHttpConnectionException(e, url);
240        }
241        finally {
242            if (client != null) {
243                client.close();
244            }
245        }
246        return null;
247    }
248
249    private static void handleHttpConnectionException(Exception exception, String url)
250            throws IOException {
251        // Inner exception should be logged to make life easier.
252        Log.e(TAG, "Url: " + url + "\n" + exception.getMessage());
253        IOException e = new IOException(exception.getMessage());
254        e.initCause(exception);
255        throw e;
256    }
257
258    private static AndroidHttpClient createHttpClient(Context context) {
259        String userAgent = MmsConfig.getUserAgent();
260        AndroidHttpClient client = AndroidHttpClient.newInstance(userAgent, context);
261        HttpParams params = client.getParams();
262        HttpProtocolParams.setContentCharset(params, "UTF-8");
263
264        // set the socket timeout
265        int soTimeout = MmsConfig.getHttpSocketTimeout();
266
267        if (Log.isLoggable(LogTag.TRANSACTION, Log.DEBUG)) {
268            Log.d(TAG, "[HttpUtils] createHttpClient w/ socket timeout " + soTimeout + " ms, "
269                    + ", UA=" + userAgent);
270        }
271        HttpConnectionParams.setSoTimeout(params, soTimeout);
272        return client;
273    }
274
275    /**
276     * Return the Accept-Language header.  Use the current locale plus
277     * US if we are in a different locale than US.
278     */
279    private static String getHttpAcceptLanguage() {
280        Locale locale = Locale.getDefault();
281        StringBuilder builder = new StringBuilder();
282
283        addLocaleToHttpAcceptLanguage(builder, locale);
284        if (!locale.equals(Locale.US)) {
285            if (builder.length() > 0) {
286                builder.append(", ");
287            }
288            addLocaleToHttpAcceptLanguage(builder, Locale.US);
289        }
290        return builder.toString();
291    }
292
293    private static void addLocaleToHttpAcceptLanguage(
294            StringBuilder builder, Locale locale) {
295        String language = locale.getLanguage();
296
297        if (language != null) {
298            builder.append(language);
299
300            String country = locale.getCountry();
301
302            if (country != null) {
303                builder.append("-");
304                builder.append(country);
305            }
306        }
307    }
308}
309