Home | History | Annotate | Download | only in transaction
      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 
     18 package com.android.mms.transaction;
     19 
     20 import org.apache.http.HttpEntity;
     21 import org.apache.http.HttpHost;
     22 import org.apache.http.HttpRequest;
     23 import org.apache.http.HttpResponse;
     24 import org.apache.http.StatusLine;
     25 import org.apache.http.client.methods.HttpGet;
     26 import org.apache.http.client.methods.HttpPost;
     27 import org.apache.http.conn.params.ConnRouteParams;
     28 import org.apache.http.params.HttpParams;
     29 import org.apache.http.params.HttpProtocolParams;
     30 import org.apache.http.params.HttpConnectionParams;
     31 
     32 import com.android.mms.MmsConfig;
     33 import com.android.mms.LogTag;
     34 
     35 import android.content.Context;
     36 import android.net.http.AndroidHttpClient;
     37 import android.telephony.TelephonyManager;
     38 import android.text.TextUtils;
     39 import android.util.Config;
     40 import android.util.Log;
     41 
     42 import java.io.DataInputStream;
     43 import java.io.IOException;
     44 import java.net.SocketException;
     45 import java.net.URI;
     46 import java.net.URISyntaxException;
     47 import java.util.Locale;
     48 
     49 public 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