HttpUtils.java revision 86a1cf79bf92dbb2b9d09431379bff6de83c2581
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;
19import org.apache.http.HttpEntity;
20import org.apache.http.HttpHost;
21import org.apache.http.HttpRequest;
22import org.apache.http.HttpResponse;
23import org.apache.http.StatusLine;
24import org.apache.http.client.HttpClient;
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;
30
31import com.android.mms.MmsConfig;
32
33import android.content.Context;
34import android.net.http.AndroidHttpClient;
35import android.provider.Settings;
36import android.util.Config;
37import android.util.Log;
38
39import java.io.DataInputStream;
40import java.io.IOException;
41import java.net.SocketException;
42import java.net.URI;
43import java.net.URISyntaxException;
44import java.util.Locale;
45
46public class HttpUtils {
47    private static final String TAG = "HttpUtils";
48    private static final boolean DEBUG = false;
49    private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
50
51    public static final int HTTP_POST_METHOD = 1;
52    public static final int HTTP_GET_METHOD = 2;
53
54    // This is the value to use for the "Accept-Language" header.
55    // Once it becomes possible for the user to change the locale
56    // setting, this should no longer be static.  We should call
57    // getHttpAcceptLanguage instead.
58    private static final String HDR_VALUE_ACCEPT_LANGUAGE;
59
60    static {
61        HDR_VALUE_ACCEPT_LANGUAGE = getHttpAcceptLanguage();
62    }
63
64    // Definition for necessary HTTP headers.
65    private static final String HDR_KEY_ACCEPT = "Accept";
66    private static final String HDR_KEY_ACCEPT_LANGUAGE = "Accept-Language";
67    private static final String HDR_KEY_X_WAP_PROFILE = "x-wap-profile";
68
69    private static final String HDR_VALUE_ACCEPT =
70        "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";
71
72    private HttpUtils() {
73        // To forbidden instantiate this class.
74    }
75
76    /**
77     * A helper method to send or retrieve data through HTTP protocol.
78     *
79     * @param token The token to identify the sending progress.
80     * @param url The URL used in a GET request. Null when the method is
81     *         HTTP_POST_METHOD.
82     * @param pdu The data to be POST. Null when the method is HTTP_GET_METHOD.
83     * @param method HTTP_POST_METHOD or HTTP_GET_METHOD.
84     * @return A byte array which contains the response data.
85     *         If an HTTP error code is returned, an IOException will be thrown.
86     * @throws IOException if any error occurred on network interface or
87     *         an HTTP error code(>=400) returned from the server.
88     */
89    protected static byte[] httpConnection(Context context, long token,
90            String url, byte[] pdu, int method, boolean isProxySet,
91            String proxyHost, int proxyPort) throws IOException {
92        if (url == null) {
93            throw new IllegalArgumentException("URL must not be null.");
94        }
95
96        if (LOCAL_LOGV) {
97            Log.v(TAG, "httpConnection: params list");
98            Log.v(TAG, "\ttoken\t\t= " + token);
99            Log.v(TAG, "\turl\t\t= " + url);
100            Log.v(TAG, "\tmethod\t\t= "
101                    + ((method == HTTP_POST_METHOD) ? "POST"
102                            : ((method == HTTP_GET_METHOD) ? "GET" : "UNKNOWN")));
103            Log.v(TAG, "\tisProxySet\t= " + isProxySet);
104            Log.v(TAG, "\tproxyHost\t= " + proxyHost);
105            Log.v(TAG, "\tproxyPort\t= " + proxyPort);
106            // TODO Print out binary data more readable.
107            //Log.v(TAG, "\tpdu\t\t= " + Arrays.toString(pdu));
108        }
109
110        AndroidHttpClient client = null;
111
112        try {
113            // Make sure to use a proxy which supports CONNECT.
114            URI hostUrl = new URI(url);
115            HttpHost target = new HttpHost(
116                    hostUrl.getHost(), hostUrl.getPort(),
117                    HttpHost.DEFAULT_SCHEME_NAME);
118
119            client = createHttpClient();
120            HttpRequest req = null;
121            switch(method) {
122                case HTTP_POST_METHOD:
123                    ProgressCallbackEntity entity = new ProgressCallbackEntity(
124                                                        context, token, pdu);
125                    // Set request content type.
126                    entity.setContentType("application/vnd.wap.mms-message");
127
128                    HttpPost post = new HttpPost(url);
129                    post.setEntity(entity);
130                    req = post;
131                    break;
132                case HTTP_GET_METHOD:
133                    req = new HttpGet(url);
134                    break;
135                default:
136                    Log.e(TAG, "Unknown HTTP method: " + method
137                            + ". Must be one of POST[" + HTTP_POST_METHOD
138                            + "] or GET[" + HTTP_GET_METHOD + "].");
139                    return null;
140            }
141
142            // Set route parameters for the request.
143            HttpParams params = client.getParams();
144            if (isProxySet) {
145                ConnRouteParams.setDefaultProxy(
146                        params, new HttpHost(proxyHost, proxyPort));
147            }
148            req.setParams(params);
149
150            // Set necessary HTTP headers for MMS transmission.
151            req.addHeader(HDR_KEY_ACCEPT, HDR_VALUE_ACCEPT);
152            {
153                String xWapProfileUrl = MmsConfig.getUaProfUrl();
154
155                if (xWapProfileUrl != null) {
156                    req.addHeader(HDR_KEY_X_WAP_PROFILE, xWapProfileUrl);
157                }
158            }
159            req.addHeader(HDR_KEY_ACCEPT_LANGUAGE, HDR_VALUE_ACCEPT_LANGUAGE);
160
161            HttpResponse response = client.execute(target, req);
162            StatusLine status = response.getStatusLine();
163            if (status.getStatusCode() != 200) { // HTTP 200 is success.
164                throw new IOException("HTTP error: " + status.getReasonPhrase());
165            }
166
167            HttpEntity entity = response.getEntity();
168            byte[] body = null;
169            if (entity != null) {
170                try {
171                    if (entity.getContentLength() > 0) {
172                        body = new byte[(int) entity.getContentLength()];
173                        DataInputStream dis = new DataInputStream(entity.getContent());
174                        try {
175                            dis.readFully(body);
176                        } finally {
177                            try {
178                                dis.close();
179                            } catch (IOException e) {
180                                Log.e(TAG, "Error closing input stream: " + e.getMessage());
181                            }
182                        }
183                    }
184                } finally {
185                    if (entity != null) {
186                        entity.consumeContent();
187                    }
188                }
189            }
190            return body;
191        } catch (URISyntaxException e) {
192            handleHttpConnectionException(e);
193        } catch (IllegalStateException e) {
194            handleHttpConnectionException(e);
195        } catch (IllegalArgumentException e) {
196            handleHttpConnectionException(e);
197        } catch (SocketException e) {
198            handleHttpConnectionException(e);
199        } catch (Exception e) {
200            handleHttpConnectionException(e);
201        }
202        finally {
203            if (client != null) {
204                client.close();
205            }
206        }
207        return null;
208    }
209
210    private static void handleHttpConnectionException(Exception exception)
211            throws IOException {
212        // Inner exception should be logged to make life easier.
213        Log.e(TAG, exception.getMessage());
214        throw new IOException(exception.getMessage());
215    }
216
217    private static AndroidHttpClient createHttpClient() {
218        AndroidHttpClient client
219                = AndroidHttpClient.newInstance("Android-Mms/0.1");
220        HttpParams params = client.getParams();
221        HttpProtocolParams.setContentCharset(params, "UTF-8");
222        return client;
223    }
224
225    /**
226     * Return the Accept-Language header.  Use the current locale plus
227     * US if we are in a different locale than US.
228     */
229    private static String getHttpAcceptLanguage() {
230        Locale locale = Locale.getDefault();
231        StringBuilder builder = new StringBuilder();
232
233        addLocaleToHttpAcceptLanguage(builder, locale);
234        if (!locale.equals(Locale.US)) {
235            if (builder.length() > 0) {
236                builder.append(", ");
237            }
238            addLocaleToHttpAcceptLanguage(builder, Locale.US);
239        }
240        return builder.toString();
241    }
242
243    private static void addLocaleToHttpAcceptLanguage(
244            StringBuilder builder, Locale locale) {
245        String language = locale.getLanguage();
246
247        if (language != null) {
248            builder.append(language);
249
250            String country = locale.getCountry();
251
252            if (country != null) {
253                builder.append("-");
254                builder.append(country);
255            }
256        }
257    }
258}
259