com.android.callstat.common.net.ConnectionThread.java Source code

Java tutorial

Introduction

Here is the source code for com.android.callstat.common.net.ConnectionThread.java

Source

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.callstat.common.net;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.params.ConnRouteParams;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;

import android.content.Context;
import android.os.Process;
import android.os.SystemClock;

import com.android.callstat.CallStatApplication;
import com.android.callstat.DebugFlags;
import com.android.callstat.common.CallStatUtils;
import com.android.callstat.common.StringUtil;

/**
 * Runs an actual download
 */
public class ConnectionThread extends Thread {

    private Context mContext;
    static final int WAIT_TIMEOUT = 5000;
    static final int WAIT_TICK = 1000;
    private static final int MIN_GZIP_SIZE = 512;

    // Performance probe
    long mCurrentThreadTime;
    long mTotalThreadTime;

    private volatile boolean mRunning = true;
    // private volatile boolean mCancaled = false;
    // private int mCancalExcGroupId;

    public static final int HTTP_POST_METHOD = 1;
    public static final int HTTP_GET_METHOD = 2;
    private SystemFacade mSystemFacade;
    private RequestFeeder mRequestFeeder;

    public ConnectionThread(Context context, int id, SystemFacade systemFacade, RequestFeeder requestFeeder) {
        super();
        mContext = context;
        setName("http-thread-" + id);
        mSystemFacade = systemFacade;
        mRequestFeeder = requestFeeder;
    }

    void requestStop() {
        synchronized (mRequestFeeder) {
            mRunning = false;
            mRequestFeeder.notify();
        }
    }

    /**
     * Loop until app shutdown.
     */
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // these are used to get performance data. When it is not in the timing,
        // mCurrentThreadTime is 0. When it starts timing, mCurrentThreadTime is
        // first set to -1, it will be set to the current thread time when the
        // next request starts.
        mCurrentThreadTime = 0;
        mTotalThreadTime = 0;

        MyHttpClient client = null;
        client = createHttpClient(mContext);

        while (mRunning) {
            if (mCurrentThreadTime == -1) {
                mCurrentThreadTime = SystemClock.currentThreadTimeMillis();
            }

            Request request;

            // mCancaled = false;
            /* Get a request to process */
            request = mRequestFeeder.getRequest();

            /* wait for work */
            if (request == null) {
                synchronized (mRequestFeeder) {
                    if (DebugFlags.CONNECTION_THREAD)
                        HttpLog.v("ConnectionThread: Waiting for work");
                    try {
                        mRequestFeeder.wait();
                    } catch (InterruptedException e) {
                    }
                    if (mCurrentThreadTime != 0) {
                        mCurrentThreadTime = SystemClock.currentThreadTimeMillis();
                    }
                }
            } else {
                if (DebugFlags.CONNECTION_THREAD) {
                    // HttpLog.v("ConnectionThread: new request " +
                    // request.mHost + " " + request );
                }
                try {
                    httpConnection(mContext, false, client, null, 0, request);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        if (client != null) {
            client.close();
        }
    }

    /**
     * A helper method to send or retrieve data through HTTP protocol.
     * 
     * @param token
     *            The token to identify the sending progress.
     * @param url
     *            The URL used in a GET request. Null when the method is
     *            HTTP_POST_METHOD.
     * @param pdu
     *            The data to be POST. Null when the method is HTTP_GET_METHOD.
     * @param method
     *            HTTP_POST_METHOD or HTTP_GET_METHOD.
     * @return A byte array which contains the response data. If an HTTP error
     *         code is returned, an IOException will be thrown.
     * @throws IOException
     *             if any error occurred on network interface or an HTTP error
     *             code(>=400) returned from the server.
     */
    private byte[] httpConnection(Context context, boolean isProxySet, MyHttpClient client, String proxyHost,
            int proxyPort, Request request) throws IOException {
        if (request.getUri() == null) {
            throw new IllegalArgumentException("URL must not be null.");
        }
        int timeout = (int) request.getmTimeout();
        if (timeout != 0 && timeout < 5000) {
            timeout = 5000;
        }
        client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
        client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
        try {
            URI hostUrl = new URI(request.getUri());
            HttpHost target = new HttpHost(hostUrl.getHost(), hostUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME);
            HttpLog.d("URL:" + request.getUri() + " Host:" + hostUrl.getHost() + " Port:" + hostUrl.getPort());

            HttpRequest req = null;
            switch (request.getMethod()) {
            case HTTP_POST_METHOD:
                HttpPost post = new HttpPost(request.getUri());
                HttpEntity content = request.getHttpEntity();
                if (content != null) {
                    post.setEntity(content);
                    if (content instanceof StringEntity) {
                        final StringEntity stringEntity = (StringEntity) content;
                        post.setHeader(stringEntity.getContentEncoding());
                        post.setHeader(stringEntity.getContentType());
                    }
                }
                req = post;
                break;
            case HTTP_GET_METHOD:
                req = new HttpGet(request.getUri());
                break;
            default:
                HttpLog.e("Unknown HTTP method: " + request.getMethod() + ". Must be one of POST["
                        + HTTP_POST_METHOD + "] or GET[" + HTTP_GET_METHOD + "].");
                return null;
            }
            HttpResponse response = null;
            if (CallStatUtils.isOMS()) {
                setRequest(response, isProxySet, req, client, proxyHost, proxyPort, request);
                response = client.execute(target, req);
            } else {
                if (CallStatUtils.isMOBILE(mContext)) {
                    String apn = CallStatUtils.getAPN(mContext);
                    if (!StringUtil.isNullOrWhitespaces(apn)) {
                        if (apn.equals("CMWAP")) {
                            HttpClient httpclient = new DefaultHttpClient();
                            HttpHost proxy = new HttpHost("10.0.0.172", 80, "http");
                            httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
                            response = httpclient.execute(target, req);
                        } else {
                            setRequest(response, isProxySet, req, client, proxyHost, proxyPort, request);
                            response = client.execute(target, req);
                        }
                    } else {
                        setRequest(response, isProxySet, req, client, proxyHost, proxyPort, request);
                        response = client.execute(target, req);
                    }
                } else {
                    setRequest(response, isProxySet, req, client, proxyHost, proxyPort, request);
                    response = client.execute(target, req);
                }

            }
            StatusLine status = response.getStatusLine();
            request.getEventHandler().status(status.getProtocolVersion().getMajor(),
                    status.getProtocolVersion().getMinor(), status.getStatusCode(), status.getReasonPhrase());
            switch (status.getStatusCode()) {
            case 200:
                break;
            case 304:
                request.getEventHandler().endData(null, 0);
                return null;
            default:
                request.getEventHandler().endData(null, 0);
                throw new IOException(
                        "HTTP error: " + status.getReasonPhrase() + " CODE:" + status.getStatusCode());
            }
            Headers headers = new Headers();
            readResponseHeaders(headers, response);
            request.getEventHandler().headers(headers);

            HttpEntity entity = response.getEntity();
            byte[] body = null;
            if (entity != null) {
                try {
                    int contentLength = (int) entity.getContentLength();
                    if (contentLength > 0) {
                        body = new byte[contentLength];
                        // DataInputStream dis = new
                        // DataInputStream(entity.getContent());
                        InputStream in = entity.getContent();
                        int offset = 0;
                        int length = contentLength;
                        try {
                            while (length > 0) {
                                int result = in.read(body, offset, length);
                                HttpLog.v("################result:" + result);
                                offset += result;
                                length -= result;
                                if (length <= 0) {
                                    request.getEventHandler().endData(body, contentLength);
                                }
                            }
                        } finally {
                            try {
                                in.close();
                                // request.mLoadListener.loaded(body,
                                // contentLength);
                                // if(length == 0)
                                // CallbackProxy.getHandler().onFinishResourceLoading(body,
                                // contentLength, request.mLoadListener);
                            } catch (IOException e) {
                                HttpLog.e("Error closing input stream: " + e.getMessage());
                            }
                        }
                    } else {
                        ByteArrayBuilder dataBuilder = new ByteArrayBuilder();
                        body = new byte[8192];
                        InputStream in = entity.getContent();
                        int result = 0;
                        int count = 0;
                        int lowWater = body.length / 2;
                        try {
                            while (result != -1) {
                                result = in.read(body, count, body.length - count);
                                if (result != -1) {
                                    HttpLog.v("################result:" + result);
                                    count += result;
                                }
                                if (result == -1 || count >= lowWater) {
                                    dataBuilder.append(body, 0, count);
                                    count = 0;
                                }
                                if (result == -1) {
                                    if (dataBuilder.getByteSize() > 0) {
                                        byte[] cert = new byte[dataBuilder.getByteSize()];
                                        int offset = 0;
                                        while (true) {
                                            ByteArrayBuilder.Chunk c = dataBuilder.getFirstChunk();
                                            if (c == null)
                                                break;
                                            if (c.mLength != 0) {
                                                System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
                                                offset += c.mLength;
                                            }
                                            c.release();
                                        }
                                        request.getEventHandler().endData(cert, cert.length);
                                    }
                                }
                            }
                        } finally {
                            try {
                                in.close();
                            } catch (IOException e) {
                                HttpLog.e("Error closing input stream: " + e.getMessage());
                            }
                        }

                    }
                } finally {
                    if (entity != null) {
                        entity.consumeContent();
                    }
                }
            }
            return body;
        } catch (URISyntaxException e) {
            // TODO Auto-generated catch block
            request.getEventHandler().error(EventHandler.ERROR_BAD_URL, e.getMessage());
            e.printStackTrace();
        } catch (IllegalStateException e) {
            request.getEventHandler().error(EventHandler.ERROR, e.getMessage());
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            request.getEventHandler().error(EventHandler.ERROR_BAD_URL, e.getMessage());
            e.printStackTrace();
        } catch (SocketException e) {
            request.getEventHandler().error(EventHandler.ERROR, e.getMessage());
            e.printStackTrace();
        } catch (ConnectTimeoutException e) {
            request.getEventHandler().error(EventHandler.ERROR_TIMEOUT, e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            request.getEventHandler().error(EventHandler.ERROR, e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    void setRequest(HttpResponse response, boolean isProxySet, HttpRequest req, MyHttpClient client,
            String proxyHost, int proxyPort, Request request) {
        // Set route parameters for the request.
        HttpParams params = client.getParams();
        if (isProxySet) {
            ConnRouteParams.setDefaultProxy(params, new HttpHost(proxyHost, proxyPort));
        }
        req.setParams(params);
        // Set necessary HTTP headers
        addHeaders(req, request.getHeaders());
    }

    /**
     * Read headers from the HTTP response and store them into Headers.
     */
    private void readResponseHeaders(Headers headers, HttpResponse response) {
        Header[] http_headers = response.getAllHeaders();
        for (int i = 0; i < http_headers.length; i++) {
            headers.setHeader(http_headers[i]);
        }
    }

    /**
     * Add header represented by given pair to request. Header will be formatted
     * in request as "name: value\r\n".
     * 
     * @param name
     *            of header
     * @param value
     *            of header
     */
    void addHeader(HttpRequest req, String name, String value) {
        if (name == null) {
            String damage = "Null http header name";
            HttpLog.e(damage);
            throw new NullPointerException(damage);
        }
        if (value == null || value.length() == 0) {
            String damage = "Null or empty value for header \"" + name + "\"";
            HttpLog.e(damage);
            throw new RuntimeException(damage);
        }
        req.addHeader(name, value);
    }

    /**
     * Add all headers in given map to this request. This is a helper method: it
     * calls addHeader for each pair in the map.
     */
    void addHeaders(HttpRequest req, Map<String, String> headers) {
        if (headers == null) {
            return;
        }

        Entry<String, String> entry;
        Iterator<Entry<String, String>> i = headers.entrySet().iterator();
        while (i.hasNext()) {
            entry = i.next();
            addHeader(req, entry.getKey(), entry.getValue());
        }
    }

    private static MyHttpClient createHttpClient(Context context) {
        String userAgent = CallStatApplication.DEFAULT_USER_AGENT;

        MyHttpClient client = MyHttpClient.newInstance(userAgent, context);
        HttpParams params = client.getParams();
        HttpProtocolParams.setContentCharset(params, "UTF-8");
        // HttpClientParams.setRedirecting(params, true);
        // set the socket timeout
        int soTimeout = CallStatApplication.HTTP_SOCKET_TIMEOUT;

        if (DebugFlags.CONNECTION_THREAD) {
            HttpLog.v(
                    "[HttpUtils] createHttpClient w/ socket timeout " + soTimeout + " ms, " + ", UA=" + userAgent);
        }

        HttpConnectionParams.setSoTimeout(params, soTimeout);
        return client;
    }

}